jueves, 6 de junio de 2019

1-WIRE Lectura de Temperatura y Humedad con DHT-11/22

Sensor Humedad/Temperatura DHT con PIC


Muchas gracias por tu visita a este blog relacionado con la programación de micro-controladores PIC, en esta entrada quiero mostrarles como utilizar los sensores de humedad y temperatura DHT que nos sera de gran utilidad en aplicaciones que requieran medir información ambiental o meteorología.

 El objetivo de esta entrada es realizar la lectura adecuada del sensor DHT con un microcontrolador PIC16F, para lo cual se implementara la comunicación con el protocolo 1-Wire respetando las especificaciones técnicas del sensor.

La programación se realizara utilizando el compilador XC8 y el entorno de desarrollo MPLABX, ambos disponibles de forma gratuita en la pagina de microchip.
Paso 1.Sobre el Sensor DHT

Es un sensor que mide Humedad y Temperatura de forma simultanea, cuenta con un procesador interno que realiza la medición al elemento capacitivo y termistor integrado, proporcionando el resultado a través de una señal digital. El proceso de comunicación con el PIC utiliza un único pin gracias al protocolo 1-Wire. Describiremos dos modelos identificados como DHT11 y DHT22 que poseen un encapsulado y disposición similar pero con las siguientes diferencias  técnicas:

  • DHT11: Trabaja con un rango de medición de temperatura de 0 a 50 °C con precisión de ±2.0 y un rango de humedad de 20% a 90% RH con precisión de 4%. Los ciclos de lectura debe ser como mínimo 1 o 2 segundos.
  • DHT22: El rango de  medición de temperatura es de  -40°C a 80 °C con precisión de ±0.5 y rango de humedad de 0 a 100% RH con precisión de 2%, el tiempo entre lecturas debe ser de 2 segundos.
A simple vista se puede observar que la diferencia con respecto a la medición esta en el rango y la precisión, pero revisando con mas detalle las especificaciones el pulso de inicio 1-Wire utiliza diferentes tiempos y el resultado se representa en un formato diferente, además el DHT11 soportar ciclos mas rápidos de lectura.


Paso 2. Conectar el Modulo al PIC16F.

El voltaje de operación nominal es 5V, la salida requiere utilizar una resistencia pull-up(entre 1-5k) para asegurar los niveles lógicos con el Microcontrolador, el manual también recomienda agregar un capacitor de 100nF entre VDD y GND para un mejor filtrado de la señal.


Es posible utilizar cualquier pin digital del PIC, eso si debe asegurarse de que sea bidireccional, algo que también debemos considerar es la distancia a la que puede estar el sensor del cual no tengo un dato exacto del limite máximo, pero he realizado pruebas sin inconvenientes hasta una distancia de 30mts, y considero que a mayores distancias se deberá utilizar un resistencia pull-up mínima(1k) considerando la perdida de voltaje en las lineas y posibles interferencias que puedan afectar a la señal de los datos.

Paso 3. Programación y lectura del sensor.

A fin de facilitar la rápida implementación del modulo, se crearon procedimientos en el archivo dht.c incluidos en la carpeta del proyecto MPLABX, en la ultima sección veras el enlace de descarga. 
El programa principal debe incluir las siguientes lineas que permiten definir el tipo y los pines utilizados con este sensor.
 
#define _XTAL_FREQ 4000000
#define DHTTYPE 11 // Modelo de sensor
#define DHTpin PORTBbits.RB0  //Pin de comunicación DTH
#define DHTtris TRISBbits.TRISB0 //Bit de control
#include "dht.h"

Para establecer la comunicación bidireccional utilizando solo un conductor, el sensor utiliza una variante del protocolo 1-Wire que establece reglas de temporización para las señales lógicas del microcontrolador.
 
En cada señal de lectura(Start signal) que realiza el microcontrolador, el sensor envía una señal de respuesta(Response signal) seguidos de una trama de 40bits, la duración de esta trama es variable en función de los niveles lógicos de cada bit recibido. 
Normalmente el pin del bus se encuentra en estado alto, entonces el (Start signal) que establece el microcontrolador es un pulso a nivel bajo con una duración de al menos 1ms(DHT22) o 18ms(DHT11) antes de normalizar el bus(nivel alto). Observe el diagrama de tiempos.
 
El sensor como respuesta(Response signal) colocara el bus en nivel bajo por 80us para luego normalizar esperando otros 80us, partir de este momento el sensor empezara a enviar una trama de 40 bits con la siguiente regla de tiempos:
(Bit 0) Nivel bajo por 50us y normalizar por un tiempo entre 25-28us.
(Bit 1) Nivel bajo por 50us y normalizar por un tiempo de 70us.
 
El siguiente código muestra un ejemplo simple para dar inicio al proceso de lectura en el sensor, siguiendo el diagrama de señal que se muestra en la parte superior.

    DHTtris = 0;    //Modo salida
    DHTpin = 0;     //Inicia el pulso bajo
    if(DHTTYPE == 22)
      __delay_ms(4); //Mantiene mínimo 1ms
    if(DHTTYPE == 11)
      __delay_ms(18);//Mantiene mínimo 18ms
    DHTpin = 1;     //Coloca el bus en nivel alto
    __delay_us(20); //Espera mínimo 20uS
    DHTtris = 1;    //Modo entrada
    while(DHTpin);  //Espera pulso bajo(hasta 40uS)
    while(!DHTpin); //Espera pulso alto(hasta 80uS)
    while(DHTpin);  //Espera pulso bajo(hasta 80uS)

 
Para mejorar este procedimiento inicial se deberá considerar limitar el tiempo de espera dentro de cada bucle indefinido, para esto se puede crear un procedimiento que utilice una variable de control que permita determinar la cantidad de tiempo transcurrido durante la espera.

De los 40 bits recibidos del sensor los dos bytes mas significativos representan la medición de humedad, los siguientes dos bytes corresponden a la medición de temperatura, finalmente un ultimo byte se utiliza para la comprobación de errores que se calcula con la suma de todos los bytes recibidos.


El sensor DHT22 representa la medición como un dato entero de 16-bits y como puede medir temperaturas negativas se utiliza el bit mas significativo del tercer byte para indicar el signo.

Ahora mostraremos la lista de los procedimientos que podemos utilizar para hacer uso de este sensor:
  • DHTSetup(): Es un procedimiento simple que coloca el pin de datos como entrada manteniendo un nivel lógico alto en el bus 1-Wire
  • DHTUpdate(dhtstruct *dht): Esta función realiza la lectura de las mediciones que posee el sensor y retorna un valor del tipo char para indicar si el proceso se realizo correctamente(1) o se produjo un error(0), ademas puesto que la lectura contiene información de humedad y temperatura se utiliza como parámetro un puntero a una estructura ya definida en el encabezado.
         typedef struct
         {
           unsigned int rawhum; //16bits para la humedad
           unsigned int rawtem; //16bits para la Temperatura
         } dhtstruct;




Paso 4. Prueba y Simulación.

Ahora escribiremos un pequeño programa para poner en practica y demostrar el uso de este sensor utilizando los procedimientos previamente descritos.

El siguiente código describe un programa que realiza la lectura de un sensor DHT11, muestra el resultado por el puerto USART en su formato de bits original y su conversión final.
  
#pragma config FOSC = INTRC_NOCLKOUT, WDTE = OFF, LVP = OFF
#include <xc.h>
#include <stdio.h>
#define _XTAL_FREQ 4000000
#define DHTTYPE 11  //Modelo DHT11
#define DHTpin PORTBbits.RB0 //Pin de datos RB0
#define DHTtris TRISBbits.TRISB0
#include "dht.h"
dhtstruct dht; //instancia a la estructura dht
unsigned int humedad, temperatura;
void main(void)
{
    char res;
    ANSEL = 0;  //Desactiva las entradas analógicas AN0-AN7
    ANSELH = 0; //Desactiva las entradas analógicas AN7-AN13
    OSCCONbits.IRCF = 6; //Configura el intosc a 4MHz Tcy = 0.5uS
    while(!OSCCONbits.HTS); //Espera estabilidad del oscilador
    DHTSetup();//Configura el pin de lectura para el DHT
   
    //CONFIGURAICON DEL MODULO USART 9600 BAUDIOS
    TXSTAbits.BRGH = 1; //UART en modo alta velocidad
    BAUDCTLbits.BRG16 = 1; //Generador de baudios en modo 16-bits
    SPBRG = 0x67; //X = _XTAL_FREQ/(4*BAUD) - 1;
    SPBRGH = 0x00;//X=[4000000/(4*9600)]-1 = 103 = 0x0067
    TXSTAbits.TXEN = 1; //Habilita la transmisión
    RCSTAbits.CREN = 1; //Habilita la recepción
    RCSTAbits.SPEN = 1; //Habilita el modulo USART
    while(1)
    {
        res = DHTUpdate(&dht); //Inicia la lectura del sensor
        if(res) //Verifica si se realizo correctamente
        {
            humedad = dht.rawhum >> 8; //Lectura la parte entera de la humedad
            temperatura = dht.rawtem >> 8; //Lectura la parte entera de la temp
            printf("Raw Hu=%04X Te=%04X\r\n", dht.rawhum, dht.rawtem);
            printf("Final Hu=%02u Te=%02u\r\n", humedad, temperatura);
        }
        __delay_ms(3000);
    }
}


1. Valores recibidos por la terminal USART


2. Ensayo del programa con un PIC16F886



Paso 5. Conclusiones y Recomendaciones.


Luego de hacer la demostración del programa podemos concluir que podemos implementar fácilmente cualquiera de estos dos sensores.

Para la conversión de lectura del sensor DHT22 deberá hacer una división entre 10, para conseguir el valor entero y si desea precisión con decimales debe guardar el resultado en variables del tipo float.
  float humedad, temperatura;
  humedad = dht.rawhum / 10.0F;
  temperatura = dht.rawtem / 10.0F;
  printf("Raw Hu=%f Te=%f\r\n", humedad, temperatura);
 
Los procedimientos descritos han sido probados a frecuencias igual o superiores a 4MHz en las versiones Free y Pro del compilador XC8; si requiere trabajar con frecuencias menores a 4MHz sera necesario modificar la función DHTReadbyte del archivo dht.c.

Una desventaja con estos sensores es la velocidad de las lecturas y el tiempo de espera para realizar una lectura nueva(2 segundos), aunque en muchos casos no es un factor negativo si consideramos que la Temperatura y Humedad son variables que no cambian de forma rápida.
 
Aquí dejo el enlace para que puedas descargar el proyecto creado en MPLABX para este blog, 
 
Enlace para Descarga -> DHTPIC16F 

Esperando que este ejemplo pueda servirte de ayuda agradezco tu visita al blog, atentamente Pablo Zárate
contacto:pablinza@me.com / pablinzte@gmail.com @pablinzar
Santa Cruz de la Sierra - Bolivia

 

miércoles, 5 de junio de 2019

I2C Utilizar un LCD con PCF8574

Modulo PCF8574x con el PIC16F

Antes que nada quiero agradecer tu visita a este blog relacionado con la programación de micro-controladores PIC, En esta ocasión veremos como utilizar la tan conocida pantalla LCD utilizando únicamente dos lineas para datos gracias al protocolo serial I2C y el chip PCF8574x.

El objetivo de esta sección enviar mensajes a la pantalla LCD utilizando las dos lineas de comunicación serial MSSP que posee el PIC16F887.

La programación se realizara utilizando el compilador XC8 y el entorno
MPLABX disponibles en la pagina de microchip de forma gratuita, ademas también necesitara de los siguientes archivos peripheral.h y peripheral.c que contienen procedimientos y funciones básicas para el PIC16F887, puede prescindir de ellos o incluso modificarlos a su criterio si posee conocimientos sobre los registros SFR y sus respectivo bits.
 
Al finalizar la sección les dejare el enlace para descargar la carpeta del proyecto que contiene todos los archivos citados.

Modulo PCF8574x con el PIC16F
 
Para establecer una comunicación serial con el chip PCF8574xlos microncontroladores PIC cuentan con el modulo MSSP(Master Sync Serial Port),  el cual posibilita implementar dos protocolos muy utilizados que son SPI(Serial Peripheral Interface) e I2C(Inter-Integrated Circuit). En nuestro programa utilizaremos los archivos i2c.h y i2c.c que poseen las funciones y procedimientos necesarios del protocolo I2C, y tambiénlos archivos pcf8574.h y pcf8574.c que incluyen los procedimientos necesarios para comunicarnos con el chip PCF8754.


Paso 1. Sobre el Modulo PCF8574x.

I2C es un protocolo serial desarrollado por Phillips Semiconductors en la década de los 80, y fue concebido principalmente para comunicar dispositivos que están montados en un mismo sistema o circuito impreso.



El chip PCF8574 fabricado por Texas Instrument es un expansor paralelo de 8 bits controlado por I2C que opera a una velocidad estándar de 100KHz, diseñados para proveer monitoreo y control serial de forma fácil y económica. Existen dos variantes de este chip que son el PCF8574 y PCF8574A que se diferencian únicamente por el rango o mapa de dirección que pueden utilizar conforme a los valores lógicos en sus pines A2:A0.
 

Una gran ventaja de este protocolo es que establece una interfaz bidireccional maestro esclavo de hasta 128 dispositivos, aunque en la practica cada dispositivo limita su rango de asignación a un determinado mapa que puede ser configurado de forma externa o mediante instrucciones sobre el mismo protocolo.



Implementar el bus I2C con el chip PCF es bastante simple, si observa la figura solo se requiere utilizar resistencias pull-up por cada pin del microcontrolador, establecer la dirección de cada dispositivo conectado y su respectiva alimentación.

Ahora si lo que se necesita es controlar una pantalla LCD sera necesario implementar el siguiente esquema de circuito.
 


Algo interesante es que este circuito esta disponible como un modulo genérico producido para aplicaciones de prototipado rápido, y goza de popularidad popular por su bajo costo económico($us. 3), incluso planteando comprar los elementos por separados no se justifica el precio por el trabajo y tiempo necesario para el montaje. Salvo que se trate de un producto final.

 

Por defecto este modulo tiene las lineas A2:A0 con nivel lógico alto que define una dirección de 27h (PCF8574) y 3Fh(PCF8574A); Si requiere agregar mas dispositivos o modificar la dirección necesitara soldar o conectar las siguientes lineas o pads en el circuito.


Otro detalle es que el modulo ya cuenta con las resistencias pull-up requeridas por la interfaz y no sera necesario agregar estas resistencias incluso si utiliza otros dispositivos o sensores I2C.


Paso 2. Conectar el Modulo al PIC16F.



Solo necesitara identificar y conectar las cuatro lineas que posee el modulo, dos corresponden a la alimentación(5V/GND), y dos para la comunicación SDA(datos) y SCL(reloj). Muchas pantallas LCD cuentan con una luz de retroiluminacion(Backlight) que puede ser controlador a través de la interfaz siempre que coloque un puente(jumper) en el conector J2. 
 
El modulo también posee un potenciómetro que permite ajustar el contraste que la pantalla, deberá establecer un valor adecuado al momento de ensayar el programa.


Paso 3. Programación y envió de mensajes.

A fin de facilitar la rápida implementación del modulo en nuestra aplicación, se crearon los siguientes procedimientos y funciones incluidas en el archivo pcf8574.h y pcf8574.c. Se deberá incluir la cabecera y las siguientes definiciones al inicio del programa:

  #include "peripheral.h"
  #include "i2c.h"
  #define PCFADDR 0b01001110 //Dirección 27h bit0=R/W
  #include "pcf8574.h"


El campo de dirección PCFADDR dependerá de los valores lógicos en los pines A2:A0 del chip PCF8574, y se obtendrá conforme al siguiente formato de direccionamiento I2C.
 1. Para un Modulo PCF8574A

 2. Para un Modulo PCF8574

Ahora mostraremos la lista de los procedimientos que podemos utilizar para enviar mensajes a nuestra pantalla LCD.
  • PCFSetupLCD(char mode): Inicializa el modulo y la pantalla, como parámetro obligatorio deberá especificar el modo de operación en LINES1(una linea) o LINES2(multi-linea) .
  • PCFSetLCD(char cmd): Este procedimiento sirve para ejecutar las siguientes instrucciones del controlador de pantalla:  
           - DON/DOFF: Enciende y Apaga la Pantalla            
           - CURSOR_ON/CURSOR_OFF: Oculta o muestra el cursor
           - BLINK_ON/BLINK_OFF: Activa o desactiva el destello del puntero
           - CLEAR: Limpia la pantalla
           - HOME: Retorno el cursor a la posición inicial
  • PCFWritecharLCD(char data): Este procedimiento mostrara un dato o carácter en la posición actual del cursor.
  • PCFWriteLCD(char *str): Muestra en la pantalla una cadena o arreglo de caracteres almacenados en la memoria de datos, el mensaje iniciara desde la posición del cursor. Puede utilizar la variante PCFPutsLCD para cadenas almacenadas en la memoria de programa.
  • PCFGotoLCD(char addr): Posiciona el cursor en la dirección GCADDR de la pantalla LCD, puede usar la variante PCFGotolnLCD para indicar la posición inicial de cada linea o fila de la pantalla LCD. 
        Las direcciones iniciales de la pantalla son las siguientes:
        - 0x00: Linea 0 Posición inicial de la primera fila
        - 0x40: Linea 1 Posición inicial de la segunda fila
        - 0x14: Linea 2 Posición inicial de la tercera fila
        - 0x54: Linea 3 Posición inicial de la cuarta fila
Por ejemplo si deseo escribir el mensaje hola al inicio de la segunda linea (0x40 linea 1) puedo codificar lo siguiente:
PCFGotolnLCD(1); //Seg
    PCFWriteLCD("Ḧola");
 
Ahora si deseo escribir el mensaje al centro de la linea, debo    considerar que la palabra "Hola" posee cuatro letras y la pantalla permite visualizar hasta 16 posiciones, entonces podría codificar los siguiente:
    PCFGotoLCD(0x46); //0x40 + 6 posiciones
    PCFWriteLCD("Hola");
  • PCFSetLED(char mode): Permite encender o apagar la luz de fondo de la pantalla, si el parámetro es 1= Activa y si es 0=desactiva.


Paso 4. Prueba y Simulación.


Ahora escribiremos un pequeño programa para poner en practica y demostrar el uso de una pantalla LCD con este modulo utilizando los procedimientos previamente descritos.

El siguiente código describe un programa que muestra un mensaje inicial en la primera linea y el valor de un contador de segundos en la segunda linea.
  #pragma config FOSC = INTRC_NOCLKOUT, WDTE = OFF, LVP = OFF
  #include <xc.h>
  #include <stdio.h>
  #define _XTAL_FREQ 4000000
  #include "peripheral.h"
  #include "i2c.h"
  #define PCFADDR 0b01001110 //Dirección PC8574 27h
  #include "pcf8574.h"
  char seg = 0, msg[16];
  void main()
  {
    OSCSetup();
    ANSEL = 0;
    ANSELH = 0;
    I2CSetup(I2CSTD); //Inicializa el MSSP
    PCFSetupLCD(LINES2); //Inicia el LCD
    PCFSetLED(1); //Enciende la luz de fondo
    PCFGotoLCD(0x02); //Coloca el cursor en la posición 2 de la fila 0
    PCFPutsLCD("** U.E.B **"); //Muestra el mensaje
    PCFSetLCD(DON & CURSOR_ON & BLINK_OFF);  //Muestra el cursor
    while(true)
    {
        __delay_ms(1000);
        seg = seg + 1; //Incrementa el contador de segundos
        if(seg > 59) seg = 0; //Reinicia el contador de segundos
        PCFGotoLCD(0x40); //Coloca el cursor en la posición 0 de la fila 2
        sprintf(msg, "SEG:%02u", seg); //Cadena personalizada
        PCFWriteLCD(msg); //Muestra la cadena con valores del segundo
    }
}

3.Ensayo del programa con un PIC16F886


Paso 5. Conclusiones y Recomendaciones.

Luego de hacer la demostración del programa podemos concluir que podemos implementar fácilmente este modulo I2C para ahorrarnos entre 4 y 6 lineas o pines del PIC. Si bien la librería solo contiene procedimientos básicos para mostrar mensajes, con un mayor conocimiento de las instrucciones del controlador LCD es posible realizar modificaciones que permitan efectuar operaciones menos frecuentes como ser desplazamientos y generación de caracteres personalizados.
 
Este es el enlace para descargar el ejemplo P8574P16F (MPLABX v5.45 compilador XC8 v1.45)

Esperando que este pequeño aporte te pueda servir de algo, agradezco una vez mas tu visita al blog.

Pablo Zárate Arancibia
Ingeniero Electrónico
pablinzte@gmail.com