Atmega8 + Sensor de humedad DHT11

Queria saber si alguien tiene experiencia con este sensor y el uC Atmega, ya que estoy teniendo problemas a la hora de leerlo. Mas que nada me interesa saber si el manejo de los puertos es el correcto, y si me estoy mandando alguna ca...ada y no me doy cuenta.

El codigo que uso es este y el uC esta configurado para que funcione con el oscilador interno de 1MHz:

Código:
#define u8 unsigned char
#define u16 unsigned int
#define dht11_data	(1<<PD3)

void configura_dht()
{
	DDRD|=dht11_data;
	set_bits(&PORTD,dht11_data);	 //Queda como salida=1
}

u8 verificar_datos(struct datos_humedad valor)
{
	u8 suma=0,cont;

	for(cont=0;cont<4;cont++)
		suma+=valor.data_humedad[cont];	
	
	if(suma==valor.data_humedad[4])
		suma=1;
	else
		suma=2;
	return suma;
}

struct datos_humedad obtiene_valor_humedad(struct datos_humedad info)
{
	
	u8 n_bit=0,n_byte=0;
	u8 bit_arranque=0;
	
	delay_s(1);	
	clear_bits(&PORTD,dht11_data);	//Mando salida=0
	
        delay_ms(20);
	
	set_bits(&PORTD,dht11_data);	 //Mando salida=1
	DDRD&=~dht11_data;				//Paso a entrada con pull-up interno
	asm("nop");
	
	configura_timer0(0xEC,5); //10mSeg de time-out
	flag_timer0=0;
		
	while((PIND&dht11_data)&&(!flag_timer0));	//Espera respuesta del Sensor
	
	if(flag_timer0)
		{
		imprime_pantalla("While 1     \n","\n",0x00);
		delay_s(2);
		info.codigo=0;
		
		DDRD|=dht11_data;	//Queda como salida=1
		
		return info;
		}
	

	while((!(PIND&dht11_data))&&(!flag_timer0));	//Espera pull-up del Sensor
	
	if(flag_timer0)
		{
		imprime_pantalla("While 2     \n","\n",0x00);
		delay_s(2);
		info.codigo=0;
		
		DDRD|=dht11_data;	//Queda como salida=1
		
		return info;
		}	
	
	
	do{
		if(!bit_arranque)
			{
			
			while((PIND&dht11_data)&&(!flag_timer0)); //Espera bit de arranque
			
			if(flag_timer0)
				{
				n_byte+=0x30;
				imprime_pantalla("While arrq    \n","\n",0x00);
				comando_lcd_4bit(0x80|12);
				dato_lcd_4bit(n_byte);
				delay_s(2);
				info.codigo=0;
				
				DDRD|=dht11_data;	//Queda como salida=1
				
				return info;
				} 	
			}
		else
			bit_arranque=0;
		
		while((!(PIND&dht11_data))&&(!flag_timer0)); //Espera bit de datos
		
		if(flag_timer0)
			{
			imprime_pantalla("While datos \n","\n",0x00);
			delay_s(2);
			info.codigo=0;
			
			DDRD|=dht11_data;	//Queda como salida=1
			
			return info;
			} 
		
		delay_30us();
		
		if(!(PIND&dht11_data)) // Si el bit es 0 => implica bit de arranque y que el bit de dato fue 0
			{
			if(!n_bit)
				info.data_humedad[n_byte]=0;
			bit_arranque=1;
			}
		else   	// Si el bit no es 0 durante los 30uSec => el bit de datos es 1
			{
			if(!n_bit)
				info.data_humedad[n_byte]=0;
			info.data_humedad[n_byte]|=(0x80>>n_bit);
			}//Bit=1

		n_bit++;
		
		if(n_bit==8)
			{
			n_bit=0;
			n_byte++;
			}

	}while(n_byte<5);

	info.codigo=verificar_datos(info);
	
	DDRD|=dht11_data;	//Queda como salida=1
	
	return info;		
}

Las rutinas de delay que uso son estas:

Código:
void  delay_30us(void)
{
    u8 i;
	for(i=0;i<75;i++);
}

void delay_ms(u16 cont)
{
	u16 aux,aux2;
	for(aux=0;aux<cont;aux++)
		{
		for(aux2=0;aux2<50;aux2++);
		
		}
}

void delay_s(u16 cont)
{
	u16 aux;
	for(aux=0;aux<cont;aux++)
		delay_ms(1000);
}

El retardo de 20mSeg lo pude comprobar con el osciloscopio.

Estas son las rutinas para el manejo del puerto:

Código:
void set_bits (volatile u8 *port, u8 mask)
{
	*port |= mask;
}

void clear_bits (volatile u8 *port, u8 mask)
{
	*port &= ~mask;
}

En las hojas de datos del DHT11, me da estas curvas:



Cuando lo pruebo, siempre salta el Time-out (le puse un time out con el T0 para que no quede parado el programa en caso de error) en el While de arranque (arrq) y en el 1er Byte, nunca paso ese while.

Tambien probe usando pull-up externo como indica la figura, es decir que el puerto funcione como Alta Z y a la hora de generar un nivel logico "0" paso a modo salida. Obtuve el mismo resultado, se queda en el mismo While.

El sensor se que funciona bien porque 1ero lo use con un 8051 y no tiene problemas en leerlo, pero algo estare haciendo mal con el Atmega que no lo pudo leer bien el sensor (ni siquiera llego a la parte del checksum).
 
Última edición:
Hola, mira el DHT11 a mi me funciono perfecto, en un Arduino Mega Atmega1280, y en un Duemillanove Atmega328,
pin 1 VDD
pin 2 Atmega ADC0 (Puerto Analógico 0)
pin 4 GND


El código:

Código:
#define DHT11_PIN 0      // ADC0

byte read_dht11_dat()
{
	byte i = 0;
	byte result=0;
	for(i=0; i< 8; i++){
		
		
		while(!(PINC & _BV(DHT11_PIN)));  // wait for 50us
		delayMicroseconds(30);
		
		if(PINC & _BV(DHT11_PIN)) 
			result |=(1<<(7-i));
              while((PINC & _BV(DHT11_PIN)));  // wait '1' finish
         
		
	}
	return result;
}


void setup()
{
	DDRC |= _BV(DHT11_PIN);
	PORTC |= _BV(DHT11_PIN);
	
	  Serial.begin(9600);
  
Serial.println("Ready");
	}
	
void loop()
{
	byte dht11_dat[5];
	byte dht11_in;
	byte i;
	// start condition
	// 1. pull-down i/o pin from 18ms
	PORTC &= ~_BV(DHT11_PIN);
	delay(18);
	PORTC |= _BV(DHT11_PIN);
	delayMicroseconds(40);
	
	DDRC &= ~_BV(DHT11_PIN);
	delayMicroseconds(40);
	
	dht11_in = PINC & _BV(DHT11_PIN);
	
	if(dht11_in){
		Serial.println("dht11 start condition 1 not met");
		return;
	}
	delayMicroseconds(80);
	
	dht11_in = PINC & _BV(DHT11_PIN);
	
	if(!dht11_in){
		Serial.println("dht11 start condition 2 not met");
		return;
	}
	delayMicroseconds(80);
	// now ready for data reception
	for (i=0; i<5; i++)
		dht11_dat[i] = read_dht11_dat();
		
	DDRC |= _BV(DHT11_PIN);
	PORTC |= _BV(DHT11_PIN);
	
        byte dht11_check_sum = dht11_dat[0]+dht11_dat[1]+dht11_dat[2]+dht11_dat[3];
	// check check_sum
	if(dht11_dat[4]!= dht11_check_sum)
	{
		Serial.println("DHT11 checksum error");
	}
	
	Serial.print("Current humdity = ");
	Serial.print(dht11_dat[0], DEC);
	Serial.print(".");
	Serial.print(dht11_dat[1], DEC);
	Serial.print("%  ");
	Serial.print("temperature = ");
	Serial.print(dht11_dat[2], DEC);
	Serial.print(".");
	Serial.print(dht11_dat[3], DEC);
	Serial.println("C  ");
	
	delay(2000);
}
 
Te comento que al final el problema era que los tiempos que da el sensor no me daban para el uC usandolo con 1MHz, tardaba demasiado (esto me paso por no darle bola al codigo en assembler y avivarme que el C de pocas lineas que publique en assembler es enorme).

Lo solucione usando el uC con el oscilador interno en 8MHz, no me quise meter con assembler :LOL:.
 
El que publiqué arriba funciona bien.

Igual te dejo la versión más pulida para atemega16, lo único que tenés que hacer es modificar los defines para los puertos que vas a usar.

PHP:
#define DHT11_DATA	(1<<PA7)
#define DHT11_PORT	PORTA
#define DHT11_DDR	DDRA
#define DHT11_PIN	PINA

#define INDICE_DATO_HUMEDAD 0
#define INDICE_DATO_TEMP	2

#define DHT11_ERR		0
#define DHT11_CHECK_SUM_OK 	1
#define DHT11_CHECK_SUM_ERR	2

struct datos_humedad{
	u8 data_humedad[5];
	u8 codigo;
	}; 

void configura_dht()
{
	clear_bits(&DHT11_PORT,DHT11_DATA);	 //Queda en Alta Z --> pull-up externo
	DHT11_DDR&=~DHT11_DATA;				 //Paso a entrada con pull-up externo (Z alta)
}

u8 verificar_datos(struct datos_humedad *valor)
{
	u8 suma=0,cont;

	for(cont=0;cont<4;cont++)
		suma+=valor->data_humedad[cont];	
	
	if(suma==valor->data_humedad[4])
		suma=DHT11_CHECK_SUM_OK;
	else
		suma=DHT11_CHECK_SUM_ERR;
	return suma;
}

void obtiene_valor_humedad(struct datos_humedad *info)
{
	
	u8 n_bit=0,n_byte=0;
	u8 bit_arranque=0;
	
	delay_s(1);	
		
	DHT11_DDR|=DHT11_DATA;					//Mando salida=0
	
	delay_ms(20);
	
	DHT11_DDR&=~DHT11_DATA;					//Paso a entrada con pull-up externo (Z alta)
	
	configura_timer0(0x00,5); //32mSeg de time-out --> 8MHz
	flag_timer0=0;
		
	while((DHT11_PIN&DHT11_DATA)&&(!flag_timer0));	//Espera respuesta del Sensor
	
	if(flag_timer0)
		{
		configura_timer0(0x00,0); //Paro el timmer0
		flag_timer0=0;
		info->codigo=DHT11_ERR;
		return;
		}
	

	while((!(DHT11_PIN&DHT11_DATA))&&(!flag_timer0));	//Espera pull-up del Sensor

	if(flag_timer0)
		{
		configura_timer0(0x00,0); //Paro el timmer0
		flag_timer0=0;
		info->codigo=DHT11_ERR;
		return;
		}	
	
	
	do{
		if(!bit_arranque)
			{
			
			while((DHT11_PIN&DHT11_DATA)&&(!flag_timer0)); //Espera bit de arranque
			
						
			if(flag_timer0)
				{
				configura_timer0(0x00,0); //Paro el timmer0
				flag_timer0=0;
				info->codigo=DHT11_ERR;
				return;
				} 	
			}
		else
			bit_arranque=0;
		
		while((!(DHT11_PIN&DHT11_DATA))&&(!flag_timer0)); //Espera bit de datos
		
		if(flag_timer0)
			{
			configura_timer0(0x00,0); //Paro el timmer0
			flag_timer0=0;
			info->codigo=DHT11_ERR;
			return;
			} 
		
		delay_10us();
		delay_10us();
		delay_10us();
		
		if(!(DHT11_PIN&DHT11_DATA)) // Si el bit es 0 => implica bit de arranque y que el bit de dato fue 0
			{
			if(!n_bit)
				info->data_humedad[n_byte]=0;
			bit_arranque=1;
			}
		else   	// Si el bit no es 0 durante los 30uSec => el bit de datos es 1
			{
			if(!n_bit)
				info->data_humedad[n_byte]=0;
			info->data_humedad[n_byte]|=(0x80>>n_bit);
			}//Bit=1

		n_bit++;
		
		if(n_bit==8)
			{
			n_bit=0;
			n_byte++;
			}

	}while(n_byte<5);

	info->codigo=verificar_datos(info);		
}

Y las demoras para 8MHz:

PHP:
void  delay_10us(void)	// 8MHZ
{
    volatile u8 aux2;
	for(aux2=0;aux2<5;aux2++);
}

Lo ideal para esa demora es usar assembler, pero con esa función va bien.
 
Tenés razón, faltan un par de librerías.

Te subo el proyecto en Code-Blocks, si lo querés levantar con el Avr-Studio, simplemente copiate los archivos ".C". En el proyecto incluí:

  • Sensor_humedad_DHT11.c: todas las funciones relacionadas con el sensor. Modifique el puerto para trabajar con el pin PB.7.
  • funciones_delay.c: rutinas de demora necesarias para comunicarse con el sensor.
  • funciones_perifericos.c: funciones para configurar el timer0 para el time-out, set y clear bits.
  • rutinas_de_interrupcion.c: rutina de interrupción del timer0 y función de configuración de las interrupciones.
  • main.c: te vas a encontrar un ejemplo de como usar las funciones para leer el sensor. Solo falta agregar la acción a tomar dentro del switch.

Main.c:

PHP:
#include <avr/io.h>
#include <avr/interrupt.h>

//-------------- Definiciones de tipos de variables
typedef unsigned char u8;
typedef unsigned int u16;
//-------------- Definiciones de tipos de variables

//-------------- Variables globales de interrupciones
u8 flag_timer0=0;
//-------------- Variables globales de interrupciones

#include "rutinas_de_interrupcion.c"
#include "funciones_perifericos.c"
#include "funciones_delay.c"
#include "Sensor_humedad_DHT11.c"

int main(void)
{

    //------------- Estructura para la rutina del sensor de humedad --------------//
	struct datos_humedad estrutura_dht11;

	estrutura_dht11.data_humedad[0]=0;  //Byte humedad => Parte entera
	estrutura_dht11.data_humedad[1]=0;  //Byte humedad => Parte decimal (siempre 0 en el DHT11)
	estrutura_dht11.data_humedad[2]=0;  //Byte ºT => Parte entera
	estrutura_dht11.data_humedad[3]=0;  //Byte ºT => Parte decimal (siempre 0 en el DHT11)
	estrutura_dht11.data_humedad[4]=0;  //Check-Sum
	estrutura_dht11.codigo=0;
	//------------- Estructura para la rutina del sensor de humedad --------------//

    configura_dht();
    configura_interrupciones(0);

    while(1)
        {
            //-------------- Obtención de Humedad y Temperatura -------------//
			obtiene_valor_humedad(&estrutura_dht11);
			switch(estrutura_dht11.codigo)
                {
                    case DHT11_ERR:{/*Time-Out => no hubo rta del sensor*/ break;}
					case DHT11_CHECK_SUM_OK:{/*Datos correctos del sensor*/ break;}
					case DHT11_CHECK_SUM_ERR:{/*Check-Sum fail!*/ break;}
                }
            //-------------- Obtención de Humedad y Temperatura -------------//
        }

    return 0;
}
 

Adjuntos

  • Sensor_DHT11.zip
    24.4 KB · Visitas: 103
Buenas noches.

Estoy tratando de leer el sensor con una cubeboard1, pero no logro tener valores coherentes.
Tengo el error de suma de datos y no muestra nunca los valores.

Por favor, requiero de su ayuda.

Gracias.

PHP:
//## dht.c
//  How to access GPIO registers from C-code on the Raspberry-Pi
//  Example program
//  15-January-2012
//  Dom and Gert
//
// Access from ARM Running Linux
//##define BCM2708_PERI_BASE        0x20000000
//#define GPIO_BASE                (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */

//################################################################################​###########
//########################### Geändert zur verwendung auf dem Cubietruck
//################################################################################​##########
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
#include <assert.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include "gpio/gpio_lib.h"
#define MAXTIMINGS 500
#define gpio13_pi5  SUNXI_GPI(5)

int dhtpin = gpio13_pi5;//##################### Den verwendeten PIN hier einstellen

//#define DEBUG
#define DHT11 11
#define DHT22 22
#define AM2302 22

int readDHT(int type, int pin);

int main(int argc, char **argv)

{
if(SETUP_OK!=sunxi_gpio_init()){

        printf("Fehler bei initialisierung!\n");

return -1;

}
  if (argc != 2) {

    printf("usage: %s [11|22|2302]#\n", argv[0]);

    //printf("example: %s 2302 4 - Read from an AM2302 connected to GPIO #4\n", argv[0]);

    return 2;

  }

  int type = 0;
  if (strcmp(argv[1], "11") == 0) type = DHT11;
  if (strcmp(argv[1], "22") == 0) type = DHT22;
  if (strcmp(argv[1], "2302") == 0) type = AM2302;
  if (type == 0) {
    printf("Select 11, 22, 2302 as type!\n");
    return 3;
  }
//atoi(argv[2]);
// if (dhtpin <= 0) {
//    printf("Please select a valid GPIO pin #\n");
//    return 3;
//  }
  readDHT(type, dhtpin);
  return 0;
} // main
int bits[250], data[200];
int bitidx = 0;
int readDHT(int type, int pin) {
  int counter = 0;
  int laststate = HIGH;
  int j=0;
  // Set GPIO pin to output
      sunxi_gpio_set_cfgpin(pin,OUTPUT);
  sunxi_gpio_output(pin,HIGH);
  usleep(500000);
  sunxi_gpio_output(pin,LOW);
  usleep(18000);
sunxi_gpio_output(pin,HIGH);
usleep(18);
    sunxi_gpio_set_cfgpin(pin,INPUT);

  data[0] = data[1] = data[2] = data[3] = data[4] = 0;
  while (sunxi_gpio_input(pin) == 1) {
       printf(" wait for pin to drop");
    usleep(1);
  }
  // read data!
  int i;
  for (i=0; i< MAXTIMINGS; i++) {
    counter = 0;
    while (sunxi_gpio_input(pin) == laststate) {
    counter++;
    //usleep(1);
        if (counter == 1000)
      break;

    }
    laststate = sunxi_gpio_input(pin);
    if (counter == 1000) break;
    bits[bitidx++] = counter;
    if ((i>3) && (i%2 == 0)) {
      // shove each bit into the storage bytes
      data[j/8] <<= 1;
      if (counter > 200)
        data[j/8] |= 1;
      j++;
    }
  }

#ifdef DEBUG
  for ( i=3; i<bitidx; i+=2) {
    printf("bit %d: %d\n", i-3, bits[i]);
    printf("bit %d: %d (%d)\n", i-2, bits[i+1], bits[i+1] > 200);
  }

#endif

  printf("Dato (%d): 0x%x 0x%x 0x%x 0x%x 0x%x\n", j, data[0], data[1], data[2], data[3], data[4]);

  if  (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF))  // **** esto nunca se cumple ****
 {
     // yay!
     if (type == DHT11)
    printf("Temp = %d *C, Hum = %d \%\n", data[2], data[0]);   
     if (type == DHT22) {
    float f, h;
    h = data[0] * 256 + data[1];
    h /= 10;
    f = (data[2] & 0x7F)* 256 + data[3];
        f /= 10.0;
        if (data[2] & 0x80)  f *= -1;
    printf("Temp =  %%.1f *C, Hum = %%.1f \%%\n", f, h);
    }

    return 1;

  }
  return 0;

}
 
Última edición por un moderador:
Tenés razón, faltan un par de librerías.

Te subo el proyecto en Code-Blocks, si lo querés levantar con el Avr-Studio, simplemente copiate los archivos ".C". En el proyecto incluí:

Me podrias explicar mejor, lo quiero montar en atmel studio para usar con un atmega2560, mas especificamente el arduino mega, pero no se donde meter los archivos que nos compartes
 
Última edición por un moderador:
Hola
Me podrias explicar mejor, lo quiero montar en atmel studio para usar con un atmega2560, mas especificamente el arduino mega, pero no se donde meter los archivos que nos compartes

Tené presente que ésto está compilado en lenguaje C​
El arduino (cualquiera) ., usa lenguaje C++ ., que no es igual​
Algunas (o casi todas) de las funciones creadas por COSME (genio total) ., NO son funciones "enmascaradas"​
El compilador (si bien el AS7) tiene las funciones de compilacion en ASM ., C y C++ ., no te las "traspasa"​
Ese es un trabajo ., que tendras que hacer individualmente​
 
Última edición por un moderador:
Tengo pedidos un par de SHT20 para adaptar un par de termostatos W1209 para que controlen también humedad (estoy haciendo un miniplantel que quiero climatizar con una peltier, un vaporizador, y un par de ventiladores). He echado un vistazo a las funciones de C de este hilo y parece que serían compatibles (adaptando a la plataforma de desarrollo, claro está) pero no estoy seguro. ¿Habéis probado alguien ambos sensores, el DHT11 y el SHT20?
 
Tengo pedidos un par de SHT20 para adaptar un par de termostatos W1209 para que controlen también humedad (estoy haciendo un miniplantel que quiero climatizar con una peltier, un vaporizador, y un par de ventiladores). He echado un vistazo a las funciones de C de este hilo y parece que serían compatibles (adaptando a la plataforma de desarrollo, claro está) pero no estoy seguro. ¿Habéis probado alguien ambos sensores, el DHT11 y el SHT20?

No, ese sensor que mencionás trabaja por I2C y el DHT11 es one-wire. Por cierto, está interesante ese sensor, a tenerlo en cuenta. (y)

danielvr20 dijo:
Me podrias explicar mejor, lo quiero montar en atmel studio para usar con un atmega2560, mas especificamente el arduino mega, pero no se donde meter los archivos que nos compartes

Con el AVR Studio no deberías tener problemas, supongo que deberías usar el Arduino Mega como si fuera un uC AVR y dejando de lado la capa del Arduino. De lo contrario, deberías ver como podés juntar las librerías que ofrece el AVR Studio con las del Arduino.

Subo una versión más "prolija" (aunque no tanto :rolleyes:), con el proyecto en AVR Studio 6.

Advertencia: Esta versión no la probé, pero debería funcionar igual que la otra.
 

Adjuntos

  • DHT11.zip
    55.9 KB · Visitas: 11
No, ese sensor que mencionás trabaja por I2C y el DHT11 es one-wire. Por cierto, está interesante ese sensor, a tenerlo en cuenta. (y)

Fallo mío, cuando compré los sensores me fijé que el vendedor puso en el título "SHT20 compatible AM2320", debí haberme dado cuenta que la traducción del título del inglés al español era literal. La foto del sensor es del AM2320 así que supongo que habrán mandado esos.

El AM2320 puede comunicarse por I2C, pero si cableas clk a gnd el sensor inicia en modo one wire, lo que me interesa bastante porque quiero sustituir una NTC por el sensor.

Ahora bien, no sé si el modo que implementa el onewire el fabricante es compatible con otros sensores, o cada fabricante chino lo implementa como le da la gana :(
 
Daría la impresión que si es compatible, el DHT11 se comunica de esta forma (la forma en que se obtiene los niveles lógicos):

DHT11_2.jpg


Tira una trama de 40bits: Humedad (parte entera - 8 bits) - Humedad (parte decimal - 8 bits) - ºT (parte entera - 8 bits) - ºT (parte decimal - 8 bits) - Checksum (8 bits)

El cambio en el AM2320, trabaja igual:

"High humidity 8 Low humidity 8 High temperature 8 Low temperature8 Parity bit"

Pero hay que verificar los tiempos.
 
Gracias, cuando tenga los sensores ya os cuento. Por el momento voy a tratar de liberar algunas GPIO del micro del diseño original para disponer de más salidas de control.
 
Atrás
Arriba