Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Introduction
DHT11 is a single wire digital humidity and temperature sensor, which provides humidity and temperature
values serially.
It can measure relative humidity in percentage (20 to 90% RH) and temperature in degree Celsius in the range
of 0 to 50C.
It has 4 pins of which 2 pins are used for supply, 1 is not used and the last one is used for data.
The data is the only pin used for communication. Pulses of different TON and TOFF are decoded as logic 1 or
logic 0 or start pulse or end of a frame.
Interfacing Diagram
Programming Steps
First, initialize the LCD16x2_8-bit library.
Define pin no. to interface DHT11 sensor, in our program we define RA0 (Pin no.2)
Send the start pulse to the DHT11 sensor by making low to high on data pin.
Receive the response pulse from the DHT11 sensor.
After receiving the response, receive 40-bit data serially from the DHT11 sensor.
Display this received data on LCD16x2 along with error indication.
/*
* DHT11 Interfacing with PIC18F4550
* http://www.electronicwings.com
*/
#include <pic18f4550.h>
#include <xc.h>
#include <stdio.h>
#include "Configuration_Header_File.h"
#include "LCD_16x2_8-bit_Header_File.h"
void DHT11_Start();
void DHT11_CheckResponse();
char DHT11_ReadData();
void main()
{
char RH_Decimal,RH_Integral,T_Decimal,T_Integral;
char Checksum;
char value[10];
OSCCON = 0x72; /* set internal oscillator with frequency 8 MHz*/
sprintf(value,"%d ",Checksum);
LCD_String_xy(1,8,value);
MSdelay(500);
}
}
char DHT11_ReadData()
{
char i,data = 0;
for(i=0;i<8;i++)
{
while(!(Data_In & 1)); /* wait till 0 pulse, this is start of data pulse */
__delay_us(30);
if(Data_In & 1) /* check whether data is 1 or 0 */
data = ((data<<1) | 1);
else
data = (data<<1);
while(Data_In & 1);
}
return data;
}
void DHT11_Start()
{
Data_Dir = 0; /* set as output port */
Data_Out = 0; /* send low pulse of min. 18 ms width */
__delay_ms(18);
Data_Out = 1; /* pull data bus high */
__delay_us(20);
Data_Dir = 1; /* set as input port */
}
void DHT11_CheckResponse()
{
while(Data_In & 1); /* wait till bus is High */
while(!(Data_In & 1)); /* wait till bus is Low */
while(Data_In & 1); /* wait till bus is High */
}
DHT11 Humidity and Temperature Sensor Library for mikroC pro for PIC
DHT11_init();
DHT11_Start();
DHT11_Read();
This library has 3 funcitons and reads 5 bytes from the sensor. (2 bytes humidity, 2 bytes temp. and 1 byte checksum)
1420571970_dht11_humidity_a_mikroc_pic.rar [64.56KB] mikroC PRO for PIC
DHT11 - Introduction
The DHT11 is a basic, low cost digital temperature and humidity sensor.
DHT11 is a single wire digital humidity and temperature sensor, which provides humidity and temperature
values serially with one-wire protocol.
DHT11 sensor provides relative humidity value in percentage (20 to 90% RH) and temperature values in degree
Celsius (0 to 50 C).
DHT11 sensor uses resistive humidity measurement component, and NTC temperature measurement
component.
Pin Description:
DHT11 is a 4-pin sensor, these pins are VCC, DATA, GND and one pin is not in use shown in fig below.
The communication process is divided in three steps, first is to send request to DHT11 sensor then sensor will
send response pulse and then it starts sending data of total 40 bits to the microcontroller.
Communication process
To start communication with DHT11, first we should send the start pulse to the DHT11 sensor.
To provide start pulse, pull down (low) the data pin minimum 18ms and then pull up, as shown in diag.
Response
After getting start pulse from, DHT11 sensor sends the response pulse which indicates that DHT11 received start
pulse.
The response pulse is low for 54us and then goes high for 80us.
Data
After sending the response pulse, DHT11 sensor sends the data, which contains humidity and temperature value
along with checksum.
The data frame is of total 40 bits long, it contains 5 segments (byte) and each segment is 8-bit long.
In these 5 segments, first two segments contain humidity value in decimal integer form. This value gives us
Relative Percentage Humidity. 1st 8-bits are integer part and next 8 bits are fractional part.
Next two segments contain temperature value in decimal integer form. This value gives us temperature in
Celsius form.
Last segment is the checksum which holds checksum of first four segments.
Here checksum byte is direct addition of humidity and temperature value. And we can verify it, whether it is
same as checksum value or not. If it is not equal, then there is some error in the received data.
Once data received, DHT11 pin goes in low power consumption mode till next start pulse.
End of frame
After sending 40-bit data, DHT11 sensor sends 54us low level and then goes high. After this DHT11 goes in sleep
mode.
DHT11 vs DHT22
Two versions of the DHT sensor, they look a bit similar and have the same pinout, but have different
characteristics and specifications:
DHT11
Ultra-low cost
3 to 5V power and I/O
2.5mA max current use during conversion (while requesting data)
Good for 20-80% humidity readings with 5% accuracy
Good for 0-50C temperature readings 2C accuracy
No more than 1 Hz sampling rate (once every second)
Body size 15.5mm x 12mm x 5.5mm
4 pins with 0.1" spacing
DHT22
Low cost
3 to 5V power and I/O
2.5mA max current use during conversion (while requesting data)
Good for 0-100% humidity readings with 2-5% accuracy
Good for -40 to 125C temperature readings 0.5C accuracy
No more than 0.5 Hz sampling rate (once every 2 seconds)
Body size 15.1mm x 25mm x 7.7mm
4 pins with 0.1" spacing
Measurement of temperature & relative humidity using DHT11 sensor & PIC micro
Measurement and control of temperature and relative humidity finds applications in numerous areas. These days
devices are available which have both temperature and humidity sensors with signal conditioning, ADC,
calibration and communication interface all built inside them. The use of such smart sensors greatly simplify the
design and reduces the overall cost. We discussed in past about Humidity and temperature measurements with
Sensirions SHT1x/SHT7x sensors. These sensors are capable of measuring both temperature and relative
humidity and provide fully calibrated digital outputs. While SHT1x/SHT7x are very accurate sensors, they are
still expensive for hobbyists use. This articles discusses the DHT11 sensor which also provides calibrated
digital outputs for temperature and humidity but is relatively lot cheaper than the Sensirion sensors. The DHT11
sensor uses a proprietary 1-wire protocol which we will be exploring here and implementing with the
PIC16F628A microcontroller that will receive the temperature and humidity values from the sensor and display
them on a 162 character LCD.
The DHT11 sensor comes in a single row 4-pin package and operates from 3.5 to 5.5V power supply. It can
measure temperature from 0-50 C with an accuracy of 2C and relative humidity ranging from 20-95% with
an accuracy of 5%. The sensor provides fully calibrated digital outputs for the two measurements. It has got
its own proprietary 1-wire protocol, and therefore, the communication between the sensor and a microcontroller
is not possible through a direct interface with any of its peripherals. The protocol must be implemented in the
firmware of the MCU with precise timing required by the sensor.
DHT11 sensor comes in a single row 4-pin package
The following timing diagrams describe the data transfer protocol between a MCU and the DHT11 sensor. The
MCU initiates data transmission by issuing a Start signal. The MCU pin must be configured as output for this
purpose. The MCU first pulls the data line low for at least 18 ms and then pulls it high for next 20-40 ?s before
it releases it. Next, the sensor responds to the MCU Start signal by pulling the line low for 80 ?s followed by
a logic high signal that also lasts for 80 ?s. Remember that the MCU pin must be configured to input after
finishing the Start signal. Once detecting the response signal from the sensor, the MCU should be ready to
receive data from the sensor. The sensor then sends 40 bits (5 bytes) of data continuously in the data line. Note
that while transmitting bytes, the sensor sends the most significant bit first.
The 40-bit data from the sensor has the following structure.
Data (40-bit) = Integer Byte of RH + Decimal Byte of RH + Integer Byte of Temp. + Decimal Byte of Temp.
+ Checksum Byte
For DHT11 sensor, the decimal bytes of temperature and humidity measurements are always zero. Therefore,
the first and third bytes of received data actually give the numeric values of the measured relative humidity (%)
and temperature (C). The last byte is the checksum byte which is used to make sure that the data transfer has
happened without any error. If all the five bytes are transferred successfully then the checksum byte must be
equal to the last 8 bits of the sum of the first four bytes, i.e.,
Checksum = Last 8 bits of (Integer Byte of RH + Decimal Byte of RH + Integer Byte of Temp. + Decimal Byte
of Temp.)
Now lets talk about the most important thing, which is signalling for transmitting 0 and 1. In order to send
a bit of data, the sensor first pulls the line low for 50 ?s. Then it raises the line to high for 26-28 ?s if it has to
send 0, or for 70 ?s if the bit to be transmitted is 1. So it is the width of the positive pulse that carries
information about 1 and 0.
At the end of the last transmitted bit, the sensor pulls the data line low for 50 ?s and then releases it. The
DHT11 sensor requires an external pull-up resistor to be connected between its Vcc and the data line so that
under idle condition, the data line is always pulled high. After finishing the data transmission and releasing the
data line, the DHT11 sensor goes to the low-power consumption mode until a new Start signal arrives from
the MCU.
Circuit diagram
Here is the circuit diagram showing the DHT11 sensor and a HD44780-based character LCD interfaced to the
PIC16F628A microcontroller. The microcontroller runs at 4.0 MHz clock using an external resonator connected
between OSC1 (16) and OSC2 (15) pins. The use of 4.0 MHz clock makes the timing calculation easier as 1
machine cycle becomes 1 ?s. The timing information will be used to calculate the width of the received data
pulse from the sensor so that we could identify if it is carrying a 1 or 0.
Circuit connections for PIC16F628A and DHT11 sensor
The following pictures show the circuit setup on a breadboard. Dont get confused with the four LEDs and tact
switches shown on the perforated board. They have nothing to do with this project. They are there because I am
using my DIY Experimenters I/O board for the LCD part of this project. Similarly, I am using my 18-pin
PIC16F board for easy prototyping with the PIC16F628A microcontroller.
Complete setup of the circuit
PIC16F628A module and the DHT11 sensor are plugged into the breadboard
Software
Writing a software for DHT11 sensor is little more challenging than the hardware part because of the timing
conditions for 1s and 0s. I have written sub-routines in mikroC Pro for PIC for initializing the DHT11 sensor
and reading the 40-bit of data in sequence. I have used Timer2 module to keep track of the width of the received
data pulse, which is required to identify if the received bit is 1 or 0. When a low-to-high pulse is detected at
the beginning of any data bit, TMR2 is cleared and turned ON. Since the clock frequency used here is 4.0 MHz,
the TMR2 increments by 1 in every 1 ?s. The TMR2 is stopped whenever the data pulse is low again. The value
of the TMR2 register gives you the the width of the data pulse in ?s. I am using 40 ?s as the threshold for
identifying 0 and 1. If the TMR2 is greater than 40, it means the received bit is 1, else it is 0. Here is the
complete source code written in mikroC Pro for PIC. It can be easily adapted to any other platform, but
remember that if you are using a different clock frequency you should have to modify the timer operation
accordingly.
void interrupt(){
if(PIR1.TMR2IF){
TOUT = 1;
T2CON.TMR2ON = 0; // stop timer
PIR1.TMR2IF = 0; // Clear TMR0 interrupt flag
}
}
void main() {
unsigned short check;
TRISB = 0b00000000;
PORTB = 0;
TRISA = 0b00100001;
CMCON = 7;
INTCON.GIE = 1; //Enable global interrupt
INTCON.PEIE = 1; //Enable peripheral interrupt
// Configure Timer2 module
PIE1.TMR2IE = 1; // Enable Timer2 interrupt
T2CON = 0; // Prescaler 1:1, and Timer2 is off initially
PIR1.TMR2IF =0; // Clear TMR INT Flag bit
TMR2 = 0;
Lcd_Init();
Lcd_Cmd(_Lcd_Clear);
Lcd_Cmd(_LCD_CURSOR_OFF);
do {
Delay_ms(1000);
StartSignal();
check = CheckResponse();
if (!check) {
Lcd_Cmd(_Lcd_Clear);
Lcd_Out(1, 1, "No response");
Lcd_Out(2, 1, "from the sensor");
}
else{
RH_Byte1 = ReadByte();
RH_Byte2 = ReadByte();
T_Byte1 = ReadByte();
T_Byte2 = ReadByte();
CheckSum = ReadByte();
// Check for error in Data reception
if (CheckSum == ((RH_Byte1 + RH_Byte2 + T_Byte1 + T_Byte2) & 0xFF))
{
message1[7] = T_Byte1/10 + 48;
message1[8] = T_Byte1%10 + 48;
message1[10] = T_Byte2/10 + 48;
message2[7] = RH_Byte1/10 + 48;
message2[8] = RH_Byte1%10 + 48;
message2[10] = RH_Byte2/10 + 48;
message1[11] = 223; // Degree symbol
Lcd_Cmd(_Lcd_Clear);
Lcd_Out(1, 1, message1);
Lcd_Out(2, 1, message2);
}
else{
Lcd_Cmd(_Lcd_Clear);
Lcd_Out(1, 1, "Checksum Error!");
Lcd_Out(2, 1, "Trying Again ...");
}
}
}while(1);
}
You can also simplify the ReadByte subroutine without using the Timer2 module. The following version of ReadByte
subroutine works equally well. Once the data pin is detected high, wait for 40 ?s and check the data line again. If it is still
high, it is 1, else 0.
unsigned short ReadByte(){
unsigned short num = 0, t;
DataDir = 1;
for (i=0; i<8; i++){
while(!Data);
Delay_us(40);
if(Data) num |= 1<<(7-i);
while(Data);
}
return num;
}
Output
The accuracy of DHT11 is not as good as Sensirions SHT1X/7X series sensors, but it provides an easy and
cheap solution to hobbyists for measuring relative humidity and temperature in parallel using a single device,
which is sometime required in certain applications such as calculating the dew point.
Dew point is the temperature to which air must be cooled to become saturated with water vapor. When further cooled,
the airborne water vapor will condense to form liquid water (dew). When air cools to its dew point through contact with
a surface that is colder than the air, water will condense on the surface. When the temperature is below the freezing
point of water, the dew point is called the frost point, as frost is formed rather than dew. The measurement of the dew
point is related to humidity. A higher dew point means there will be more moisture in the air.
A well-known approximation used to calculate the dew point, Tdp, given just the actual ("dry bulb") air
temperature, T (in degrees Celsius) and relative humidity (in percent), RH, is the Magnus formula:
The more complete formulation and origin of this approximation involves the interrelated saturated water vapor
pressure (in units of millibars, also called hectopascals) at T, Ps(T), and the actual vapor pressure (also in units
of millibars), Pa(T), which can be either found with RH or approximated with the barometric pressure (in
millibars), BPmb, and "wet-bulb" temperature, Tw is (unless declared otherwise, all temperatures are expressed in
degrees Celsius):
For greater accuracy, Ps(T) (and therefore (T, RH)) can be enhanced, using part of the Bgel modification, also
known as the Arden Buck equation, which adds a fourth constant d:
where
There are several different constant sets in use. The ones used in NOAA's presentation[8] are taken from a 1980
paper by David Bolton in the Monthly Weather Review:[9]
These valuations provide a maximum error of 0.1%, for 30 C T 35C and 1% < RH < 100%. Also
noteworthy is the Sonntag1990,[10]
Another common set of values originates from the 1974 Psychrometry and Psychrometric Charts, as presented
by Paroscientific,[11]
Also, in the Journal of Applied Meteorology and Climatology,[12] Arden Buck presents several different
valuation sets, with different minimum accuracies for different temperature ranges. Two particular sets provide
a range of 40 C to +50 C between the two, with even greater minimum accuracy than all of the other, above
sets (maximum error at extremes of temperature range):
There is also a very simple approximation that allows conversion between the dew point, temperature, and
relative humidity. This approach is accurate to within about 1 C as long as the relative humidity is above
50%:
For every 1 C difference in the dew point and dry bulb temperatures, the relative humidity decreases by 5%,
starting with RH = 100% when the dew point equals the dry bulb temperature.
The derivation of this approach, a discussion of its accuracy, comparisons to other approximations, and more
information on the history and applications of the dew point are given in the Bulletin of the American
Meteorological Society.[13]
For example, a relative humidity of 100% means dew point is the same as air temp. For 90% RH, dew point is
3 F lower than air temperature. For every 10 percent lower, dew point drops 3 F.
#include <xc.h>
#include <stdint.h>
#include "lcd_hd44780_pic16.h"
uint8_t dht11_data[40];
uint8_t dht11_rh;
uint8_t dht11_temp;
uint8_t dht11_measure();
void delay()
{
uint16_t i;
for(i=0;i<3000;i++)
{
__delay_ms(1);
}
}
void main()
{
LCDInit(LS_NONE);
__delay_ms(50);
LCDWriteStringXY(0,0,"DHT11 Demo");
LCDWriteStringXY(0,1,"By -Avinash G");
delay();
LCDClear();
while(1)
{
LCDClear();
if(!dht11_measure())
{
LCDClear();
LCDWriteStringXY(0,0,"Error!");
LCDWriteStringXY(0,1,"No Sensor Found");
}
else
{
LCDWriteStringXY(0,0,"Humidity: % ");
LCDWriteIntXY(10,0,dht11_rh,-1);
LCDWriteStringXY(0,1,"Temperat: %0C");
LCDWriteIntXY(10,1,dht11_temp,-1);
__delay_ms(50);
__delay_ms(50);
__delay_ms(50);
__delay_ms(50);
__delay_ms(50);
__delay_ms(50);
}
uint8_t dht11_measure()
{
for(uint8_t i=0;i<40;i++) dht11_data[i]=0;
DH11_DATA_BIT=0;
DH11_DATA_TRIS=0;
__delay_ms(20);
DH11_DATA_TRIS=1;
uint8_t counter=0;
if(counter==80)
return 0;
}
if(counter==180)
return 0;
}
if(counter==80)
return 0;
}
for(uint8_t i=0;i<40;i++)
{
//wait for rising edge
while(!(PORTD & (1<<5)));
//Setup Timer1
T1CKPS0=1; //Prescaller = 1:2
TMR1L=0x00; //Init counter
TMR1H=0x00;
TMR1ON=1; //Stat timer
TMR1ON=0;
/**/
uint16_t time=TMR1L;
time=time | (TMR1H<<8);
time=time*2;
}
}
dht11_rh=dht11_temp=0;
for(uint8_t i=0;i<8;i++)
{
if(dht11_data[i]==1)
dht11_rh|=(1<<(7-i));
}
for(uint8_t i=0;i<8;i++)
{
if(dht11_data[16+i]==1)
dht11_temp|=(1<<(7-i));
}
return 1;
}
Schematic/Circuit Diagram
Make the circuit as per the circuit diagram on a general purpose PCB or a breadboard. After burning the MCU
and placing on the circuit, power on the system using a 12v adapter. Adjust the variable resistor RV1 until you
get clear display on LCD.
Please note that in order to run the program successfully only a 20 MHz crystal oscillator should be used. Using
anyother value crystal will break the code.
Requirements
A computer with Microchip's MPLAB X IDE, with the XC8 v1.34 compiler installed. (I'm using MPLAB X v3.05 and
XC8 v1.34)
PIC16F628 microcontroller
LCD (HD4480 or equivalent)
DHT11 sensor
A way to program the MCU (I'm using a PICkit3)
Parts listed in the Partslist from Eagle.
If you want to breadboard the circuit, you'll need a breadboard and some jumper wires.
Introduction
The DHT11 is a humidity and temperature sensor that uses one wire to send 40-bits of data. The 16 first bits are
the integer and decimal of the humidity, the next 16 bits are the integer and fractions of the temperature, and the
last 8 bits are the checksum.
To get the DHT11 and the MCU to talk together, they need to be synchronized. To synchronize them, the MCU
sends a Start signal that is a 20us high pulse on the data pin. After the pulse, the MCU waits for the data to be
received. In the software, we have to change the direction of the data pin. You can read more about the DHT11
in the datasheet. You can get the sensor in both 4-pin and 3-pin layout, but we're using the 3-pin version.
There's no difference in the performance of the two, and the extra pin is not connected to anything.
Hardware
The first smart thing to do when making a gadget is to make a block diagram. This way you will get an
overlook of what you want and how you want it. This is a block diagram of our gadget:
(It might be a bit overkill to make a block diagram of every little gadget you make, but I find it very useful.)
We want the MCU to process the data and display it on the LCD.
Schematic layout
This is a 1x3 pin header, which holds the sensor. The middle pin is
DHT11
connected to the MCU, for data transfer.
This is the PIC16F628A, which receives data from the DHT11 and
MCU
displays it on the LCD
Display This is a 16x02 LCD, that displays the humidity and temperature.
We are using the MCU's internal 4MHz oscillator. Therefore there are no crystals or ceramic resonators in the
circuit.
Software
When you installed the XC8 compiler, you also installed some header and source files. In this How-To we're
using the LCD files that comes with the XC8 compiler: the XLCD.H and a bunch of source files. To make
things a bit easier I've copied all the source files into one file. On my Ubuntu installation I find the XLCD
sourcefiles under:
/opt/microchip/xc8/v1.34/sources/pic18/plib/XLCD
There are 10 files there, bysyxlcd.c, openxlcd.c, putrxlcd.c and so on. I placed all these files in one file, and I
called it my_xlcd.c. This files now hold all the functions. myxlcd.c file and the xlcd.h file is copied to the
project folder. (The xlcd.h file is found here: /opt/microchip/xc8/v1.34/include/plib). The xlcd.h file is a
standard file that needs a little bit of editing. We need to change the connections for the MCU's pins to match
our setup:
/* DATA_PORT defines the port to which the LCD data lines are connected */
#define DATA_PORT PORTB
#define TRIS_DATA_PORT TRISB
/* CTRL_PORT defines the port where the control lines are connected.
* These are just samples, change to match your application.
*/
#define RW_PIN PORTAbits.RA0 /* PORT for RW */
#define TRIS_RW TRISAbits.TRISA0 /* TRIS for RW */
Here, the connection between the LCD and the MCU is defined. There is nothing more to to with these two
files. (my_xlcd.h and my_xlcd.c)
Next is the main program. It starts off with some includes, configuration bits, defines, variables and prototyping
of the functions to come:
// INCLUDES
#include <stdio.h> // Including Standard Input / Outputlibrary
#include <stdlib.h> // Including Standard library function
#include <xc.h> // Including XC8 compiler library
#include "my_xlcd.h" // Including my custom LCD
// CONFIG
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTRC oscillator: I/O
function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = ON // RA5/MCLR pin function select (RA5/MCLR pin function is
MCLR)
#pragma config BOREN = ON // Brown-out Reset Enable bit (BOD Reset enabled)
#pragma config LVP = ON // Low-Voltage Programming Enable bit (RB4/PGM pin has
PGM function, low-voltage programming enabled)
#pragma config CPD = OFF // Data Code Protection bit (Data memory code protection off)
#pragma config CP = OFF // Code Protection bits (Program memory code protection off)
// DEFINES
#define _XTAL_FREQ 4000000 // Telling the compiler, that we are using 4MHz
#define data PORTAbits.RA2 // Defining RA0 as datapin
#define data_dir TRISAbits.TRISA2 // Definig TRISA0 as dataport
// GLOBAL VARIABLES
char message1[] = "Temp = 00.0 c";
char message2[] = "RH = 00.0 %";
unsigned short TOUT = 0, CheckSum, i;
unsigned short T_Byte1, T_Byte2, RH_Byte1, RH_Byte2;
// PROTOTYES
void init_XLCD(void);
void DelayFor18TCY(void);
void DelayPORXLCD(void);
void DelayXLCD(void);
void Delay10KTCYx(unsigned char);
void StartSignal(void);
unsigned short ReadByte();
unsigned short CheckResponse();
Then we have the functions. To get the LCD to work with our MCU, we need to make a few Delay functions. In
the top of your XLCD.H file it is stated that:
// FUNCTIONS
void init_XLCD(void){
OpenXLCD(FOUR_BIT & LINES_5X7); // Sets 4-bit & 5x7 charachters
while(BusyXLCD()); // Checks if LCD is busy
WriteCmdXLCD(0x06); // Moves cursor right
WriteCmdXLCD(0x0C); // Display on, cursor off
}
Next in line are the Start signal, the Readbyte, and the CheckResponse functions:
void StartSignal(){
data_dir = 0; // Sets TRISA2 to output
data = 0; // Set RA2 to LOW
__delay_ms(18); // Waits for 18 ms
data = 1; // Sets RA2 HIGH
__delay_us(20); // Waits for 20 ms
data_dir = 1; // Sets TRISA2 to input
}
To get hold of when the MCU is sending the Start signal, and when the DHT11 is finished with its 40-bit, we need an
interrupt function:
void interrupt tc_int(void){
if(PIR1bits.TMR2IF){ // If TMR2 to PR2 match Interrupt Flag
TOUT = 1;
T2CONbits.TMR2ON = 0; // Stop timer
PIR1bits.TMR2IF = 0; // Clear TMR0 interrupt flag
}
}
// TIMER
INTCONbits.GIE = 1; // Enable global interrupt
INTCONbits.PEIE = 1; // Enable peripheral interrupt
PIE1bits.TMR2IE = 1; // Enable Timer2 interrupt
T2CON = 0; // Prescaler 1:1 and Timer2 is off initially
PIR1bits.TMR2IF = 0; // Clear TMR INT flag bit
TMR2 = 0;
WriteCmdXLCD(0x01);
putrsXLCD(message1); // Write the temp to LCD
SetDDRamAddr(0x40);
putrsXLCD(message2); // Write the humidity to LCD
}
else { // Checksum is not correct
WriteCmdXLCD(0x01);
putrsXLCD("Checksum error!");
SetDDRamAddr(0x40);
putrsXLCD("Please wait.");
}
}
} while (1); // Do it forever.
}
This is one way to use the DHT11 with a PIC and a LCD.
Conclusion
We used a PIC16F628A MCU with this DHT11, and we displayed the temperature and humidity on a LCD.
You can, with some effort, convert/adjust the code to suit your favorite MCU. The program takes up 32% of the
MCU's Data memory and 55% of its Program memory. That is because the IC is small mid-range.
One of the differences between the PIC16F628 and the PIC16F628A is that the "A" version have an internal
oscillator. The version without the "A" needs an external crystal or other RC network. If you buy a PIC16F628,
be sure it is the "A" version. The other one is obsolete.
En la foto de portada vemos un sensor DHT11 conectado a un PIC16F88 que muestra la lectura de
temperatura y humedad en una pantalla LCD de 162.
El DHT11 no utiliza una interfaz serial estndar como I2C, SPI o 1Wire. En cambio requiere su propio
protocolo para comunicarse a travs de un solo hilo. Afortunadamente el protocolo es simple y puede
implementarse tranquilamente usando los pines de I/O en un PIC o cualquier otro microcontrolador (un AVR
vestido de azul y blanco viene a nuestra mente).
Gracias a la amabilidad de la gente de embedded-lab.com tenemos las siguientes imgenes que he traducido y
que explican en detalle la comunicacin. Dicen por ah que una imagen vale ms que mil palabras y para
nuestro caso, llevan razn con este dicho:
Para iniciar la comunicacin con el DHT11 el micro jala la linea de datos a tierra por al menos 18 mS
El microcontrolador debe iniciar la comunicacin con el DHT11 manteniendo la linea de datos en estado bajo
durante al menos 18 ms. Luego el DHT11 enva una respuesta con un pulso a nivel bajo de 80 uS y luego deja
flotar la linea de datos por otros 80 uS. En la figura de arriba, el pulso de inicio enviado por el
microcontrolador esta coloreado en rojo, mientras que la respuesta desde el sensor esta coloreada en azul.
La codificacin de datos esta basada en un esquema de ancho de pulso: Un pulso ancho representa un 1 lgico,
un pulso corto representa un 0 lgico.
Los datos binarios se codifican segn la longitud del pulso alto. Todos los bits comienzan con un pulso bajo de
50 uS. En nuestra librera aprovechamos el pulso bajo en cada bit para sincronizar con la seal del DHT11.
Luego viene un pulso alto que vara segn el estado lgico o el valor del bit que el DHT11 desea transmitir. Se
utilizan pulsos de 26-28 microsegundos para un 0 y pulsos de 70 microsegundos para un 1. Los
pulsos se repiten hasta un total de 40 bits.
Una transmisin completa comienza como ya describimos y se compone de 40 bits (5 bytes) que incluyen todos
los datos que el sensor puede proporcionar.
La trama del DHT11 se ve as en el osciloscopio, recordemos que cada cuadrito en la pantalla del instrumento
equivale a 100 uS.
1. Fin de la seal de inicio seguida de un glitch a nivel alto que ocurre mientras el microcontrolador libera el bus
y el DHT11 toma el control de la linea.
2. Pulso de respuesta bajo del DHT11 de alrededor de 80 uS.
3. Pulso de respuesta alto del DHT11 de alrededor de 80 uS.
4. Primer bit 0 compuesto de un pulso bajo de 50 uS y otro alto de alrededor de 25 uS
5. Segundo bit 0
6. Tercer bit 1 compuesto de un pulso bajo de 50 uS y otro alto de alrededor de 70 uS
7. La secuencia continua (0 0 0 1 etc. )
Nuestra librera para el DHT11 se encuentra alojada en Github, como siempre, agradeceremos cualquier
contribucin, correccin o ejemplo que quieras aportar. Puedes obtener la librera con los ultimos cambios y
novedades desde nuestra pgina en github.com: DHTlib-master.zip
Si no tienes instalado Git o no deseas utilizar esta herramienta, aqu esta el paquete de MPLAB X con el
proyecto completo, listo para compilarse.
Para cambiar los pines asociados al DHT11 o la velocidad de reloj editamos el contenido del archivo Config.h
que se encuentra en la carpeta project_conf dentro del proyecto de MPLAB X. El contenido del archivo en
nuestro ejemplo es el siguiente:
#ifndef CONFIG_H
#define CONFIG_H
/**
* Configures here the processor speed
*/
#define CONFIG_TIMING_MAIN_CLOCK 2000000
/**
* Configure here the registers and pins associated with the DHT11 sensor
*/
#define CONFIG_DHTLIB_OUTPIN PORTBbits.RB2
#define CONFIG_DHTLIB_PORTPIN PORTBbits.RB2
#define CONFIG_DHTLIB_TRIS TRISBbits.TRISB2
#endif
// End of Header file
Uso bsico para leer el sensor
Usar la librera para el DHT11 es bastante sencillo: Solamente debemos incluir el archivo DHTLib.h y luego,
en alguna parte del comienzo de nuestro programa debemos llamar a la funcin dhtlib_init(). Posteriormente
cuando deseemos obtener el valor de la temperatura deberemos llamar a la funcin dhtlib_read(). Esta ultima
acepta como parmetro dos apuntadores a variables de tipo uint8_t donde se guardarn las medidas de
humedad y temperatura. La funcin retorna un valor de tipo enumeracin que nos informa si la comunicacin
con el sensor se pudo llevar a cabo de forma exitosa.
Dejamos aqu el cdigo de la funcin principal de nuestro programa para el PIC que ilustra el uso de la librera
para el DHT11. Hemos destacado las partes que todo programa que use la librera DHTLib debe tener:
#include <xc.h>
#include <stdio.h>
#include "../Tick/Tick.h"
#include "../LCD/LCD.h"
#include "../DHTLib.h"
/**
* Implementacin de la funcionalidad principal del programa
*/
void main()
{
//OSCCON = 0x6C; // Para usar el oscilador interno PIC16F88 4 Mhz
OSCCON = 0x7C; // Para usar el oscilador interno PIC16F88 8 Mhz
ANSEL = 0x00; // Configuracion de pines analogicos y digitales
TRISA = 0xE0;
TRISB = 0xFF;
/**
* Cdigo para leer el sensor de temperatura y mostrar en pantalla
*/
void dht11_task()
{
// Variables locales
static uint32_t ltick = 0;
uint8_t tb = 0, hb = 0;
/**
* Esta funcion alterna el estado del led cada 1/2 segundo
*/
void led_task()
{
// Guarda el valor del tick en el que se alterno el pin del led
static uint32_t ltime = 0;
/**
* Implementacin del vector de interrupcin (nico)
*/
void interrupt isr(void)
{
// Actualizar el conteo del tiempo
tick_update();
// Otras interrupciones deben manejarse aqui
}
Conexiones para el programa de prueba
El programa permite medir y desplegar la temperatura y humedad en una pantalla LCD de 16 x 2. Utilizamos la
pantalla en modo de 4 bits y realizamos las conexiones como sigue:
Podemos personalizar esta configuracin segn nuestras necesidades, e incluso llevarla fcilmente a otros
microcontroladores de esta misma familia como el PIC16F877A o incluso el PIC18F4550 que es mucho ms potente. El
hardware para la pueba nos qued de la siguiente forma utilizando nuestra entrenadora con PIC16F88:
La velocidad de reloj en este caso es
de 8 Mhz y todas las funciones que dependen del tiempo funcionan a esta velocidad. En caso de querer compilar este
programa para un cristal ms veloz o para el PIC18F4550 hay que modificar el archivo Config.h.
Hemos usado el oscilador interno del PIC16F88 a 8 Mhz (2 MIPS) ya que la placa entrenadora tiene soldado un cristal de
4 Mhz y nos result insuficiente para implementar el protocolo requerido para el DHT11.
Conclusin
A fin de cuentas observamos que implementar la comunicacin con el DHT11 es bastante sencillo y nuestra
pequea librera nos puede servir para varios microcontroladores ms. Por ejemplo, nosotros ya la probamos en
un ATMEGA8 y un PIC24HJ128GP504. Para migrar nuestra librera a otro microcontrolador, basta con
escribir de nueva cuenta los macros de preprocesador que se encuentran en el archivo DHT11Port.h y definir
los pines correctos en el archivo Config.h.
En la ultima versin de nuestra librera hemos incluido soporte para el sensor DHT22, que es similar al DHT11
pero ofrece mejor desempeo que este. Las funciones para leer datos desde el DHT22 se encuentran
documentadas en el archivo DHTLib.h y pueden ser usadas de manera similar a las presentadas en este
artculo.
Hello all. I just joined the forum and was hoping you all could help me with some problems I've been having.
I'm trying to interface a DHT11 with a PIC16f690 and display the results on an LCD. I'm not fully done with
the code, but I think I was far enough to display the temperature on the LCD. Anyway, I tested my program out
today and I didn't get anything. I'm pretty sure the problem has to do something with the start signal since I
tracked RA0 (the pin that is connected to the data line of the DHT11) using an oscilloscope and just got a
constant high voltage. The start signal is supposed to make RA0 low for 20ms, then high for 30us, then it gets
configured as an input and waits for the response signal from the DHT11. I'm pretty sure I'm looking over
something fairly obvious in my code, but I just can't figure it out. If you could help me out with problem, or
anything else you see in my code that would be awesome :).
processor 16f690
include <p16f690.inc>
;Codeprotect off, watchdog off, internal high speed osc, powerup timer on, clear
reset off, brown out off
__CONFIG _CP_OFF & _WDT_OFF & _INTOSCIO & _PWRTE_ON & _MCLRE_OFF & _BOR_OFF
CBLOCK 0x20
BYTE
Counter
Integer_RH
Decimal_RH
Integer_T
Decimal_T
Checksum
D1
D2
Dec_Hun
Dec_Ten
Dec_One
RH_Hun
RH_Ten
RH_One
T_Hun
T_Ten
T_One
ENDC
org 0
Main
;Initialize Oscillator, 4MHz internal
BANKSEL OSCCON ;Bank 1
MOVLW b'01100000' ;Set internal oscillator frequency to 4 MHz
MOVWF OSCCON
CLRF TRISB ;port B as output
CLRF TRISC ;port C as output
BANKSEL ANSEL
CLRF ANSEL ;configure pins as digital
CLRF ANSELH
Start
BANKSEL TRISA
BCF TRISA,0 ;RA0 as output
BANKSEL PORTA
BCF PORTA,0 ;RA0 low
CALL Delay_20ms ;RA0 low for 20ms
BSF PORTA,0 ;RA0 high
CALL Delay_30us ;RA0 high for 30us
BANKSEL TRISA
BSF TRISA,0 ;RA0 as input
BANKSEL PORTA
CALL Delay_160us ;Wait for DHT11 response signal
Read_Integer_RH
CALL Read_BYTE
MOVF BYTE,W
MOVWF Integer_RH ;Move BYTE to Integer_RH
Read_Decimal_RH
CALL Read_BYTE
MOVF BYTE,W
MOVWF Decimal_RH ;Move BYTE to Decimal_RH
Read_Integer_T
CALL Read_BYTE
MOVF BYTE,W
MOVWF Integer_T ;Move BYTE to Integer_T
Read_Decimal_T
CALL Read_BYTE
MOVF BYTE,W
MOVWF Decimal_T ;Move BYTE to Decimal_T
Read_Checksum
CALL Read_BYTE
MOVF BYTE,W
MOVWF Checksum ;Move BYTE to Checksum
;CHECKSUM
Integer_T_to_ASCII
MOVF Integer_T,W
CALL Convert_to_ASCII
MOVF Dec_Hun,W
MOVWF T_Hun
MOVF Dec_Ten,W
MOVWF T_Ten
MOVF Dec_One,W
MOVWF T_One
MOVF T_Hun,W
CALL SendCharacter
MOVF T_Ten,W
CALL SendCharacter
MOVF T_One,W
CALL SendCharacter
CALL Delay_20ms
GOTO Start
;********************SUBROUTINES*************************************
Read_BYTE
CLRF BYTE ;BYTE=00000000
MOVLW d'7' ;Check_bit 8 times, each time rotating left and
updating the LSB
MOVWF Counter
RLF BYTE,f
CALL Check_bit
DECFSZ Counter,f
GOTO $-3
RETURN
Check_bit
CLRF TMR2
BTFSS PORTA,0 ;Tests until RA0 is pulled high
GOTO $-1
BSF T2CON,2 ;Starts TMR2
BTFSC PORTA,0 ;Tests until RA0 is pulled low
GOTO $-1
BCF T2CON,2 ;Stops TMR2
Convert_to_ASCII
MOVWF BYTE
CLRF Dec_Hun
CLRF Dec_Ten
CLRF Dec_One
B2D1
MOVLW d'100' ;move 100 to work
SUBWF BYTE,f ;BYTE-100
BTFSS STATUS,C ;check if negative value
goto B2D2 ;goto next tens place if value was negative
INCF Dec_Hun,f ;add 1 to hundreds
GOTO B2D1 ;repeat until negative
B2D2
MOVLW d'100'
ADDWF BYTE,f ;add 100 back to make it positive
B2D3
MOVLW d'10' ;move 10 to work
SUBWF BYTE,f ;BYTE-10
BTFSS STATUS,C ;check if negative value
goto B2D4 ;goto next tens place if value was negative
INCF Dec_Ten,f ;add 1 to tens
GOTO B2D3 ;repeat until negative
B2D4
MOVLW d'10'
ADDWF BYTE,W ;add 10 back to make it positive and store in
work
MOVWF Dec_One ;remainder is the ones place
SendCommand
BANKSEL PORTC
MOVWF PORTC
BCF LCD_RS
BCF LCD_RW
BSF LCD_E
CALL Delay_20ms
BCF LCD_E
return
SendCharacter
BANKSEL PORTC
MOVWF PORTC
BSF LCD_RS
BCF LCD_RW
BSF LCD_E
CALL Delay_20ms
BCF LCD_E
return
Delay_20ms
;19993 cycles
MOVLW 0x9E
MOVWF D1
MOVLW 0x10
MOVWF D2
L0
DECFSZ D1, f
GOTO $+2
DECFSZ D2, f
GOTO L0
;3 cycles
GOTO $+1
NOP
;4 cycles (including call)
RETURN
Delay_30us
MOVLW 0x09
MOVWF D1
L1
DECFSZ D1, f
GOTO L1
RETURN
Delay_160us
MOVLW 0x34
MOVWF D1
L2
DECFSZ D1, f
GOTO L2
NOP
RETURN
END
The code is there in the dht11.rar file, you have to be logged in for it to show up.
I simulated your lcd code and as you say it works fine, though you can use the lcd routine in whats called 4 bit
mode and save yourself 4 i/os.
Some helpfull code here - http://www.winpicprog.co.uk/pic_tutorial3.htm
Edit - I can see that code file is there, but perhaps something not quite right at your end so here it is.
; ***************************************************************************
DHT11_module
nop
bcf DHTTRIS
bcf DHTPORT ; turn off port for
18ms - SEND 18MS START SIGNAL
call delay18ms ; SW = 18.02ms
bsf DHTPORT ; turm on port
nop ; little pause
nop
dhtdata_btyeloop
movlw 0x0A ; load delay factor for 50us
movwf d1 ; SW =
62us
; - DHT
SHOULD GO HIGH AFTER 50us
dht50h btfsc DHTPORT ; then test line goes high within next 50us
goto dhtdh
decfsz d1, f
goto dht50h
goto dhterror ; timeout error
dhtdhz btfss DHTPORT ; then test line goes LOW within next 30us
goto dhtzero ;
- DATA GONE LOW WITHIN 35us SO MUST BE A ZERO
decfsz d1, f
goto dhtdhz
; movff DHTTEMP,BIN
; call ascii3
; movff ones, E15 ;DHT_TEMP_ones
; movff tens, E14 ;DHT_TEMP_tens
; movff DHTCHK,BIN
; call ascii3
return
dhterror
movlw 'E'
movwf t1
movlw 'R'
movwf t2
BSF INTCON, GIE ; Enable Global Interrupts
return
delay40us
movlw 0x0b
movwf d1
dly40us decfsz d1, f
goto dly40us
nop
return
delay18ms
movlw 0x0F
movwf d1
movlw 0x0F
movwf d2
Delay18L
decfsz d1, f
goto dly182
decfsz d2, f
dly182 goto Delay18L
return
Hi eveyone,
I need a help in programming for DHT11 interfacing with PIC16F877A.
I need to transfer my data on Rx, Tx pin of micro controller.
There's a code for on interfacing with LCD but whenever i am modifiying the code for USART my data is not
coming on Rx Tx pin.
Please help me with the code.
Code C - [expand]
1 char message1[] = "Temp = 00.0 C";
2 char message2[] = "RH = 00.0 %";
3 unsigned short TOUT = 0, CheckSum, i, Check;
Code C - [expand]
4 unsigned short T_Byte1, T_Byte2, RH_Byte1, RH_Byte2;
5 unsigned short Data, DataDir, heck ;
6 char usart_rd;
7
8 void StartSignal(){
9 DataDir = 0;
10 Data = 0;
11 Delay_ms(25);
12 Data = 1;
13 Delay_us(30);
14 DataDir = 1;
15 }
16 unsigned short CheckResponse(){
17 TOUT = 0;
18 TMR2 = 0;
19 T2CON.TMR2ON = 1;
20 while(!Data && !TOUT);
21 if (TOUT) return 0;
22 else {
23 TMR2 = 0;
24 while(Data && !TOUT);
25 if (TOUT) return 0;
26 else {
27 T2CON.TMR2ON = 0;
28 return 1;
29 }
30 }
31 }
32 unsigned short ReadByte(){
33 unsigned short num = 0, t, i;
34 DataDir = 1;
35 for(i=0;i<8;i++)
36 {
37 while(!Data);
38 TMR2 = 0;
39 T2CON.TMR2ON = 1;
40 while(Data);
41 T2CON.TMR2ON = 0;
42 if(TMR2 > 40) num |= 1<<(7-i);
43 }
44 return num;
45 }
46
47 void interrupt(){
48 if(PIR1.TMR2IF){
49 TOUT = 1;
50 T2CON.TMR2ON = 0;
51 PIR1.TMR2IF = 0;
52 }
53 }
54
55 void main(){
56 PORTA = 0b11111111;
57 PORTB = 0;
58 TRISB = 0;
59 PORTC = 0;
60 TRISC = 0;
61 PORTD = 0;
62 TRISD = 0;
63 CMCON = 7;
64 INTCON.GIE = 1;
Code C - [expand]
65 INTCON.PEIE = 1;
66
67 PIE1.TMR2IE = 1;
68 T2CON = 0;
69 PIR1.TMR2IF =0;
70 TMR2 = 0;
71 Usart_Init(9600);
72 Delay_ms(100);
73 LCD_Init(&PORTD); // Initialize LCD connected to PORTB
74
75 LCD_Cmd(LCD_CLEAR); // Clear display
76 LCD_Cmd(LCD_CURSOR_OFF); // Turn cursor off
77
78 do {
79 Delay_ms(1000);
80 StartSignal();
81 heck = CheckResponse();
82 if (!check) {
83 Lcd_Cmd(Lcd_CLEAR);
84 Lcd_Out(1, 1, "No response");
85 Lcd_Out(2, 1, "from the sensor");
86 }
87 else{
88
89 RH_Byte1 = ReadByte();
90 RH_Byte2 = ReadByte();
91 T_Byte1 = ReadByte();
92 T_Byte2 = ReadByte();
93 CheckSum = ReadByte();
94
95 if (CheckSum == ((RH_Byte1 + RH_Byte2 + T_Byte1 + T_Byte2) & 0xFF))
96 {
97 message1[7] = T_Byte1/10 + 48;
98 message1[8] = T_Byte1%10 + 48;
99 message1[10] = T_Byte2/10 + 48;
100 message2[7] = RH_Byte1/10 + 48;
101 message2[8] = RH_Byte1%10 + 48;
102 message2[10] = RH_Byte2/10 + 48;
103 message1[11] = 223;
104 Lcd_Cmd(Lcd_CLEAR);
105 Lcd_Out(1, 1, message1);
106 Lcd_Out(2, 1, message2);
107 usart_rd = message1;
108 }
109
110 else{
111 Lcd_Cmd(Lcd_CLEAR);
112 Lcd_Out(1, 1, "Checksum Error!");
113 Lcd_Out(2, 1, "Trying Again ...");
114 }
115 }
116
117 }while(1);
118 TRISC = 0;
119 PORTC = 0;
120 UART1_Init(9600);
121 Delay_ms(100);
122 while(1){
123 if(UART1_Data_Ready()){
124 usart_rd = UART1_Read();
125 UART1_Write(usart_rd);
Code C - [expand]
126 }
Code C - [expand]
1
2 TRISC = 0;
PORTC = 0;
3
4 UART1_Init(9600);
5 Delay_ms(100);
6 while(1){
if(UART1_Data_Ready()){
7
8 usart_rd = UART1_Read();
9 UART1_Write(usart_rd);
}
You should not use UARTx_Init() inside while(1) or do...while(1) loop. UART1_Init() should be before
superloop inside the main() function.
void StartSignal()
{
DataDir = 0;
Data = 0;
Delay_ms(25);
Data = 1;
Delay_us(30);
DataDir = 1;
}
unsigned short CheckResponse()
{
TOUT = 0;
TMR2 = 0;
T2CON.TMR2ON = 1;
while(!Data && !TOUT);
if (TOUT) return 0;
else
{
TMR2 = 0;
while(Data && !TOUT);
if (TOUT) return 0;
else
{
T2CON.TMR2ON = 0;
return 1;
}
}
}
unsigned short ReadByte()
{
unsigned short num = 0, t, i;
DataDir = 1;
for(i=0;i<8;i++)
{
while(!Data);
TMR2 = 0;
T2CON.TMR2ON = 1;
while(Data);
T2CON.TMR2ON = 0;
if(TMR2 > 40) num |= 1<<(7-i);
}
return num;
}
void interrupt()
{
if(PIR1.TMR2IF)
{
TOUT = 1;
T2CON.TMR2ON = 0;
PIR1.TMR2IF = 0;
}
}
void main()
{
PORTA = 0b11111111;
PORTB = 0;
TRISB = 0;
PORTC = 0;
TRISC = 0;
PORTD = 0;
TRISD = 0;
CMCON = 7;
INTCON.GIE = 1;
INTCON.PEIE = 1;
PIE1.TMR2IE = 1;
T2CON = 0;
PIR1.TMR2IF =0;
TMR2 = 0;
Delay_ms(100);
Lcd_Init();
Lcd_Cmd(_Lcd_Clear);
Lcd_Cmd(_LCD_CURSOR_OFF);
while(1)
{
Delay_ms(1000);
StartSignal();
check = CheckResponse();
if (!check)
{
Lcd_Cmd(_Lcd_Clear);
Lcd_Out(1, 1, "No response");
Lcd_Out(2, 1, "from the sensor");
}
else
{
RH_Byte1 = ReadByte();
RH_Byte2 = ReadByte();
T_Byte1 = ReadByte();
T_Byte2 = ReadByte();
CheckSum = ReadByte();
For the first parts of this tutorial you require the Main Board and the LCD Board, the later parts will also use
the Switch Board, as written the tutorials use the LCD Board on PortA and the Switch Board on PortB.
Although the hardware diagram shows a 2x16 LCD, other sizes can be used, I've tested it with a 2x16, 2x20,
and 2x40 - all worked equally well. The intention is to develop a useful set of LCD routines, these will be used
in the later parts of the tutorials to display various information.
This table shows the command codes for the LCD module, it was taken from an excellent LCD tutorial that
was published in the UK magazine 'Everyday Practical Electronics' February 1997 - it can be downloaded as a
PDF file from the EPE website. The following routines are an amalgamation of a number of routines from
various sources (including the previously mentioned tutorial), plus various parts of my own, the result is a set of
reliable, easy to use, routines which work well (at least in my opinion!).
This program displays a text message on the LCD module, it consists mostly of subroutines for using the
LCD module.
org 0x0000
movlw 0x07
movwf CMCON ;turn comparators off (make it like a 16F84)
EndMessage
;LCD routines
;Initialise LCD
LCD_Init movlw 0x20 ;Set 4 bit mode
call LCD_Cmd
retlw 0x00
decfsz count1 ,f
goto d1
retlw 0x00
end
As usual, first we need to set things up, after the normal variable declarations and port setting we reach 'call
LCD_Init', this sets up the LCD module. It first waits for 100mS to give the module plenty of time to settle
down, we then set it to 4 bit mode (0x20) and set the various options how we want them - in this case, Display
Shift is On (0x28), Character Entry Mode is Increment (0x06), and Block Cursor On (0x0D). Once the LCD is
setup, we can then start to send data to it, this is read from a table, exactly the same as the LED sequencer in the
earlier tutorials - except this time we send the data to the LCD module (using LCD_Char) and use a 0x00 to
mark the end of the table, thus removing the need to maintain a count of the characters printed. Once the first
line is displayed we then sent a command to move to the second line (using call LCD_Line2), and then print the
second line from another table. After that we enter an endless loop to leave the display as it is.
This program introduces a new use of the 'goto' command, 'goto $+2' - '$' is an MPASM arithmetic operator,
and uses the current value of the program counter, so 'goto $+2' means jump to the line after the next one - 'goto
$+1' jumps to the next line, and may seem pretty useless (as the program was going to be there next anyway),
but it can be extremely useful. A program branch instruction (like goto) uses two instruction cycles, whereas
other instructions only take one, so if you use a 'nop' in a program it takes 1uS to execute, and carries on from
the next line - however, if you use 'goto $+1' it still carries on from the next line, but now takes 2uS. You'll
notice more use of the 'goto $' construction in later tutorials, if you are checking an input pin and waiting for it
to change state you can use 'goto $-1' to jump back to the previous line, this saves allocating a label to the line
that tests the condition.
This is a table of the LCD subroutines provided in these programs, you can easily add more if you wish - for
instance to set a line cursor rather than a block one, if you find you are using a particular feature a lot you may
as well make a subroutine for it.
LCD Subroutines
LCD_Init Initialise LCD Module
LCD_Cmd Sent a command to the LCD
Add 0x30 to a byte and send to the LCD
LCD_CharD
(to display numbers as ASCII)
LCD_Char Send the character in W to the LCD
LCD_Line1 Go to start of line 1
LCD_Line2 Go to start of line 2
LCD_Line1W Go to line 1 column W
LCD_Line2W Go to line 2 column W
LCD_CurOn Turn block cursor on
LCD_CurOff Turn block cursor off
LCD_Clr Clear the display
LCD_HEX Display the value in W as Hexadecimal
This program displays a text message on the top line and a running 16 bit counter on the bottom line, with
the values displayed in both decimal and hexadecimal , it consists mostly of the previous subroutines for using
the LCD module, plus an extra one for converting from 16 bit hexadecimal to decimal.
org 0x0000
movlw 0x07
movwf CMCON ;turn comparators off (make it like a 16F84)
;LCD routines
;Initialise LCD
LCD_Init call Delay100 ;wait for LCD to settle
retlw 0x00
decfsz count1 ,f
goto d1
retlw 0x00
movf NumH,w
andlw 0X0F
addwf Hund,f
addwf Hund,f
addwf Ones,f
addlw 0XE9
movwf Tens
addwf Tens,f
addwf Tens,f
swapf NumL,w
andlw 0X0F
addwf Tens,f
addwf Ones,f
rlf Tens,f
rlf Ones,f
comf Ones,f
rlf Ones,f
movf NumL,w
andlw 0X0F
addwf Ones,f
rlf Thou,f
movlw 0X07
movwf TenK
retlw 0x00
end
This program displays a text message on the top line and a running 16 bit counter on the bottom line, just as
the last example, however, instead of using the Delay calls this version waits until the LCD Busy flag is clear.
The LCD module takes time to carry out commands, these times vary, and the previous tutorials used a delay
more than long enough to 'make sure' - however, the modules have the capability of signalling when they are
ready, this version uses that facility and avoids any unnecessary delays. I've also used the LCD_Line2W routine
to position the numbers further to the right and demonstrate the use of the routine, another slight change is that
the tables have been moved to the beginning of program memory, this was done because it's important that
tables don't cross a 256 byte boundary, so putting them at the start avoids this.
org 0x0000
goto Start
;LCD routines
;Initialise LCD
LCD_Init call LCD_Busy ;wait for LCD to settle
retlw 0x00
decfsz count1 ,f
goto d1
retlw 0x00
LCD_Busy
bsf STATUS, RP0 ;set bank 1
movlw 0x0f ;set Port for input
movwf LCD_TRIS
bcf STATUS, RP0 ;set bank 0
bcf LCD_PORT, LCD_RS ;set LCD for command mode
bsf LCD_PORT, LCD_RW ;setup to read busy flag
bsf LCD_PORT, LCD_E
swapf LCD_PORT, w ;read upper nibble (busy flag)
bcf LCD_PORT, LCD_E
movwf templcd2
bsf LCD_PORT, LCD_E ;dummy read of lower nibble
bcf LCD_PORT, LCD_E
btfsc templcd2, 7 ;check busy flag, high = busy
goto LCD_Busy ;if busy check again
bcf LCD_PORT, LCD_RW
bsf STATUS, RP0 ;set bank 1
movlw 0x00 ;set Port for output
movwf LCD_TRIS
bcf STATUS, RP0 ;set bank 0
return
swapf NumL,w
andlw 0X0F
addwf Tens,f
addwf Ones,f
rlf Tens,f
rlf Ones,f
comf Ones,f
rlf Ones,f
movf NumL,w
andlw 0X0F
addwf Ones,f
rlf Thou,f
movlw 0X07
movwf TenK
retlw 0x00
end