Sei sulla pagina 1di 66

DHT11 Sensor Interfacing with PIC18F4550.

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

DHT11 Interfacing with PIC18F4550


The above circuit diagram shows interfacing of PIC18F4550 with the DHT11 sensor.
In that, a DHT11 sensor is connected to RA0 (PORTA).

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"

#define Data_Out LATA0 /* assign Port pin for data*/


#define Data_In PORTAbits.RA0 /* read data from Port pin*/
#define Data_Dir TRISAbits.RA0 /* Port direction */
#define _XTAL_FREQ 8000000 /* define _XTAL_FREQ for using internal delay */

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*/

LCD_Init(); /* initialize LCD16x2 */


ADCON1=0x0F; /* this makes all pins as a digital I/O pins */
while(1)
{
DHT11_Start(); /* send start pulse to DHT11 module */
DHT11_CheckResponse(); /* wait for response from DHT11 module */

/* read 40-bit data from DHT11 module */


RH_Integral = DHT11_ReadData(); /* read Relative Humidity's integral value */
RH_Decimal = DHT11_ReadData(); /* read Relative Humidity's decimal value */
T_Integral = DHT11_ReadData(); /* read Temperature's integral value */
T_Decimal = DHT11_ReadData(); /* read Relative Temperature's decimal value */
Checksum = DHT11_ReadData(); /* read 8-bit checksum value */

/* convert humidity value to ascii and send it to display*/


sprintf(value,"%d",RH_Integral);
LCD_String_xy(0,0,value);
sprintf(value,".%d ",RH_Decimal);
LCD_String(value);
LCD_Char('%');
/* convert temperature value to ascii and send it to display*/
sprintf(value,"%d",T_Integral);
LCD_String_xy(1,0,value);
sprintf(value,".%d",T_Decimal);
LCD_String(value);
LCD_Char(0xdf);
LCD_Char('C');

sprintf(value,"%d ",Checksum);
LCD_String_xy(1,8,value);

/* check addition of humidity and temperature value equals to checksum */


if(Checksum != (RH_Integral + RH_Decimal + T_Integral + T_Decimal))
LCD_String_xy(0,8,"Error");
else
LCD_String_xy(0,8,"No Error");

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.

Pin diagram of DHT11

Pin No. Pin Name Pin Description


Power supply
1 VCC
3.3 to 5.5 Volt DC
2 DATA Digital output pin
3 NC Not in use
4 GND Ground

Communication with Microcontroller


DHT11 uses only one wire for communication. The voltage levels with certain time value defines the logic one
or logic zero on this pin.

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

Start pulse (Request)

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.

Interfacing DHT11 sensor with PIC16F628A

About DHT11 sensor

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.

"Start" and "Response" signals

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.

Timing difference for transmitting "1s" and "0s"

Start, Response and Data signals in sequence

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.

// LCD module connections


sbit LCD_RS at RB2_bit;
sbit LCD_EN at RB3_bit;
sbit LCD_D4 at RB4_bit;
sbit LCD_D5 at RB5_bit;
sbit LCD_D6 at RB6_bit;
sbit LCD_D7 at RB7_bit;
sbit LCD_RS_Direction at TRISB2_bit;
sbit LCD_EN_Direction at TRISB3_bit;
sbit LCD_D4_Direction at TRISB4_bit;
sbit LCD_D5_Direction at TRISB5_bit;
sbit LCD_D6_Direction at TRISB6_bit;
sbit LCD_D7_Direction at TRISB7_bit;

sbit Data at RA0_bit;


sbit DataDir at TRISA0_bit;
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;
void StartSignal(){
DataDir = 0; // Data port is output
Data = 0;
Delay_ms(25); // Low for at least 18us
Data = 1;
Delay_us(30); // High for 20-40 us
DataDir = 1; // Data port is input

unsigned short CheckResponse(){


TOUT = 0;
TMR2 = 0;
T2CON.TMR2ON = 1; // Start TMR2 while waiting for sensor response
while(!Data &amp;&amp; !TOUT); // If there's no response within 256us, the Timer2
overflows
if (TOUT) return 0; // and exit
else {
TMR2 = 0;
while(Data &amp;&amp; !TOUT);
if (TOUT) return 0;
else {
T2CON.TMR2ON = 0;
return 1;
}
}
}

unsigned short ReadByte(){


unsigned short num = 0, t;
DataDir = 1;
for (i=0; i&lt;8; i++){
while(!Data);
TMR2 = 0;
T2CON.TMR2ON = 1; // Start TMR2 from 0 when a low to high data pulse
while(Data); // is detected, and wait until it falls low again.
T2CON.TMR2ON = 0; // Stop the TMR2 when the data pulse falls low.
if(TMR2 &gt; 40) num |= 1&lt;&lt;(7-i); // If time &gt; 40us, Data is 1
}
return num;
}

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) &amp; 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.

Temperature and relative humidity measured by DHT11 sensor

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

a = 6.1121 mb, b = 18.678, c = 257.14 C, d = 234.5 C.

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]

a = 6.112 mb, b = 17.67, c = 243.5 C.

These valuations provide a maximum error of 0.1%, for 30 C T 35C and 1% < RH < 100%. Also
noteworthy is the Sonntag1990,[10]

a = 6.112 mb, b = 17.62, c = 243.12 C; for 45 C T 60 C (error 0.35 C).

Another common set of values originates from the 1974 Psychrometry and Psychrometric Charts, as presented
by Paroscientific,[11]

a = 6.105 mb, b = 17.27, c = 237.7 C; for 0 C T 60 C (error 0.4 C).

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):

a = 6.1121 mb, b = 17.368, c = 238.88 C; for 0 C T 50 C (error 0.05%).

a = 6.1121 mb, b = 17.966, c = 247.15 C; for 40 C T 0 C (error 0.06%).


Simple approximation

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%:

This can be expressed as a simple rule of thumb:

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 temperatures in degrees Fahrenheit, these approximations work out to

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.

DHT11 Humidity Sensor Interface with PIC16F877A


In the program, the most important function is dht11_measure(). This function takes no arguments and returns
either 0 or 1. Where 1 indicate success and 0 failure. The failure may be due to a sensor not connected properly.
Their are also two important global variable dht11_rh and dht11_temp. Where the former holds the relative
humidity and latter holds the temperature. These two variable contains valid value only after the
dht11_measure() is called and it returns success(1).

Our program simply prints these two variable to LCD screen.

#include <xc.h>
#include <stdint.h>

#include "lcd_hd44780_pic16.h"

#pragma config FOSC = HS // Oscillator Selection bits (HS oscillator)


#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled)
#pragma config BOREN = ON // Brown-out Reset Enable bit (BOR enabled)
#pragma config LVP = OFF // Low-Voltage (Single-Supply) In-Circuit Serial
Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EEPROM Memory Code Protection bit (Data EEPROM
code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write
protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code
protection off)

#define DH11_DATA_BIT PORTDbits.RD5


#define DH11_DATA_TRIS TRISDbits.TRISD5

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;

//wait for falling edge


while(DH11_DATA_BIT)
{
counter++;
__delay_us(1);

if(counter==80)
return 0;
}

//wait for rising edge


while(!DH11_DATA_BIT)
{
counter++;
__delay_us(1);

if(counter==180)
return 0;
}

//wait for falling edge


while(DH11_DATA_BIT)
{
counter++;
__delay_us(1);

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

//wait for falling edge


while((PORTD & (1<<5)));

TMR1ON=0;

/**/
uint16_t time=TMR1L;
time=time | (TMR1H<<8);

time=time*2;

if(time>55 && time <70)


{
//bit is 0
dht11_data[i]=0;
}
else if(time>150)
{
//bit is 1
dht11_data[i]=1;

}
}

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.

How to Use a DHT11 with a PIC16F628A and LCD


How to read humidity and temperature from a DHT11 and display it on a LCD with a PIC microcontroller. In this example
we're going to use a PIC16F628A.

Requirements

To complete this project, you'll need the following:

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 DHT11 to send data to the MCU.

We want the MCU to process the data and display it on the LCD.

We want to be able to program the MCU with ICSP.

Schematic layout

The schematic layout is split into blocks:


Powerblock Regulates the power to 5 volt. It is using the LM7805.

This is a 1x5 pin header, connected to the MCU's programming pins.


ICSP
We use this header to program the MCU.

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.

The following is the parts list, generated from Eagle:

Part Value Device Package Library Sheet

C1 0.1uF C-EU025-050X050 C025-050X050 rcl 1

C2 100uF CPOL-EUE2.5-5 E2,5-5 rcl 1

C3 0.1uF C-EU025-050X050 C025-050X050 rcl 1

C4 100uF CPOL-EUE2.5-5 E2,5-5 rcl 1

C5 0.1uF C-EU025-050X050 C025-050X050 rcl 1

D1 1N4004 1N4004 DO41-10 diode 1

IC2 7805TV 7805TV TO220V linear 1

IC3 PIC16F628 DIL18 DIL18 ic-package 1

JP1 ICSP PINHD-1X5 1X05 pinhead 1

JP2 16x02 LCD PINHD-1X16 1X16 pinhead 1

JP3 DHT11 PINHD-1X3 1X03 pinhead 1

R1 10K R-EU_0204/7 0204/7 rcl 1

R2 5K TRIM_EU-LI10 LI10 pot 1

R3 4K7 R-EU_0204/7 0204/7 rcl 1

S1 Reset TAC_SWITCHPTH TACTILE-PTH SparkFun 1

X1 7-35vDC W237-10 W237-102 con-wago-500 1


Now that the hardware is good, it's time for the software.

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 */

#define RS_PIN PORTAbits.RA1 /* PORT for RS */


#define TRIS_RS TRISAbits.TRISA1 /* TRIS for RS */

#define E_PIN PORTAbits.RA7 /* PORT for D */


#define TRIS_E TRISAbits.TRISA7 /* TRIS for E */

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:

* - The user must provide three delay routines:


* - DelayFor18TCY() provides a 18 Tcy delay
* - DelayPORXLCD() provides at least 15ms delay
* - DelayXLCD() provides at least 5ms delay

We need to add a fourth delayfunction, Delay10KTCYx

Here is the LCD's init function and the delay functions:

// 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
}

void DelayFor18TCY(void){ // as it says in the XLCD.H file


NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP();
NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP();
return;
}

void DelayPORXLCD(void){ // as it says in the XLCD.H file


__delay_ms(15);
}

void DelayXLCD(void){ // as it says in the XLCD.H file


__delay_ms(5);
}

void Delay10KTCYx(unsigned char){ // as it says in the XLCD.H file


__delay_ms(10);
}

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
}

unsigned short ReadByte(){


unsigned short num = 0, t;
data_dir = 1; // Sets TRISA2 to input
for (i=0;i<8;i++){ // Start loop
while(!data); // When data is not valid
TMR2 = 0; // Sets TMR2 to 0
T2CONbits.TMR2ON = 1; // Start TMR2 from 0 when a low to high data
pulse
while(data); // is detected, and wait until it falls low again
T2CONbits.TMR2ON = 0; // Stop the TMR2 when the data pulse falls low
if(TMR2>40) num |= 1 << (7-i); // If time > 40us, data is 1
}
return num; // Return 8-bit = 1-byte
}

unsigned short CheckResponse(){


TOUT = 0;
TMR2 = 0;
T2CONbits.TMR2ON = 1; // Turn on TMR2
while(!data && !TOUT); // While NOT data and NOT TOUT
if (TOUT) return 0; // Return 0 => OK
else {
TMR2 = 0; // Disable Timer 2
while(data && !TOUT); // While data and NOT TOUT
if(TOUT) return 0; // If Tout = 1 then return 0 => OK
else {
T2CONbits.TMR2ON = 0; // Turn off TMR2
return 1; // Return 1 => NOT OK
}
}
}

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
}
}

And in the end we need the main program:


int main(int argc, char** argv) {
unsigned short check;
TRISB = 0b00000000; // TRISB output
PORTB = 0b00000000; // PORTB low
TRISA = 0b00000001; // TRISA output
PORTA = 0b00000000; // PORTA low

CMCON = 0x07; // Comparators off

// 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;

init_XLCD(); // Initialize the LCD


putrsXLCD(" Hello World."); // Welcome text
SetDDRamAddr(0x40); // Move cursor to line 2
putrsXLCD(" I'm alive.");
__delay_ms(250);
do {
__delay_ms(1000);
StartSignal(); // Send the Startsignal
check = CheckResponse(); // Assign check with 0 = OK, or 1 = NOT OK
if(!check) { // OK check = 1 => NOT OK
WriteCmdXLCD(0x01); // Clear screen, set cursor in 0,0
putrsXLCD("No response."); // Write error message
SetDDRamAddr(0x40);
putrsXLCD("Please check.");
}
else { // IF chack = 0 => OK

RH_Byte1 = ReadByte(); // Read first byte


RH_Byte2 = ReadByte(); // Read second byte
T_Byte1 = ReadByte(); // Read third byte
T_Byte2 = ReadByte(); // Read fourth byte
CheckSum = ReadByte(); // Read checksum
// Checks if all bytes is equal to the checksum
if (CheckSum == ((RH_Byte1 + RH_Byte2 + T_Byte1 + T_Byte2) & 0xFF))
{
message1[7] = T_Byte1/10 + 48; // Extract the tens place
message1[8] = T_Byte1 + 48; // Extract the ones place
message1[10]= T_Byte2/10 + 48; // Extract the decimal
message1[11] = 223; // ASCII code for degree symbol
message2[7] = RH_Byte1/10 + 48; // Extract the tens place
message2[8] = RH_Byte1 + 48; // Extract the ones place
message2[10] = RH_Byte2/10 + 48; // Extract the decimal

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.

Sensor DHT11 con PIC


El DHT11 es un sensor bastante econmico que permite realizar mediciones de temperatura y humedad. En
esta entrada te presentamos nuestro cdigo en C para leer este sensor y un ejemplo prctico con un PIC16F88
que tambin debera adaptarse sin problemas al PIC16F628A y a otros micros similares de 8 bits como el
PIC18F4550. Nuestro programa se comunica con el sensor y muestra las mediciones en una pantalla LCD.
Cabe aclarar que usaremos MPLAB X y el compilador XC8 como plataforma de desarrollo en este ejemplo.

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.

Funcionamiento del DHT11.

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.

En cuanto a los datos que se transmiten, su interpretacin es como sigue:

Se transmiten 40 bits (5 bytes) en total


El primer byte que recibimos es la parte entera de la humedad relativa (RH)
El segundo byte es la parte decimal de la humedad relativa (no se utiliza en el DHT11, siempre es 0)
El tercer byte es la parte entera de la temperatura
El cuarto byte es la parte decimal de la temperatura (no se utiliza en el DHT11, siempre es 0)
El ultimo byte es la suma de comprobacin (checksum), resultante de sumar todos los bytes anteriores
Comunicacin con el DHT11 en el mundo real

La trama del DHT11 se ve as en el osciloscopio, recordemos que cada cuadrito en la pantalla del instrumento
equivale a 100 uS.

De izquierda a derecha se observa lo siguiente:

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. )

Pulsos de respuesta del DHT11 y primeros bits en la trama del DHT11.


Secuencia de inicio de comunicacin con el DHT11. Se muestra el pulso de 20 ms y los datos que van mucho
ms rpido se ven como la barra naranja a la derecha.

Librera para el DHT11


Descarga e instalacin

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

La librera bsica para el DHT11 consiste en 3 archivos de cdigo fuente:

DHTLib.h Contiene la declaracin y documentacin de las funciones (prototipos).


DHTLib.c Implementacin de las funciones para la lectura de los sensores de humedad y temperatura
DHTLibPort.h Contiene los macros de preprocesador de C que permiten acceder a los puertos de entrada /
salida
Config.h Este archivo es incluido por todas las liberas que requieren ajustes en tiempo de compilacin, aqui se
definen los pines de entrada / salida a utilizar con el DHT11 y la velocidad del reloj.

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.

Configuracin de pines en la librera

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"

// Configuracin del dispositivo


// CONFIG1
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (XT oscillator)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function
is MCLR)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB3 is digital I/O, HV on MCLR
must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB0 // CCP1 Pin Selection bit (CCP1 function on RB0)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
// CONFIG2
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)
#pragma config IESO = OFF // Internal External Switchover bit (Internal External Switchover mode
disabled)
// Buffer para trabajar sobre el texto que se enva a la pantalla LCD
char buf[20];

// Prototipos de las funciones definidas en este archivo


void dht11_task(); // Actualiza la pantalla con la temperatura y humedad
void led_task(); // Hace parpadear el led de estado en el pin 4 del puerto A

/**
* 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;

// Preparamos las libreras para su uso


lcd_init(0, 16, 2); // Iniciar el controlador de pantalla
tick_init(); // Iniciar el contador / temporizador del sistema
dhtlib_init(); // Preparar la comunicacin con el sensor DHT11

// Encendemos la pantalla LCD y escribimos titulo


lcd_on();
lcd_puts(" PRUEBA DHT11 ");

// Ciclo principal de nuestra aplicacin


for (;;) {
led_task();
dht11_task();
}
}

/**
* 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;

// La lectura de la temperatura y la humedad se realiza cada 5 segundos


if (tick_get() - ltick >= TICK_SECOND * 5) {
// Recordar el tiempo en el que se toma la lectura
ltick = tick_get();
// Colocar el cursor en la segunda linea
lcd_goto(0, 1);
// EN ESTA LINEA REALIZAMOS LA LECTURA DEL SENSOR
enum dht_status result = dhtlib_read11(&tb, &hb);
// Preparamos la cadena a mostrar
if (result == E_DHTLIB_OK)
sprintf(buf, "T: %02d C H: %02d%% ", tb, hb);
else
sprintf(buf, "Sensor error: %02d ", result);
// Escribimos la cadena a la pantalla
lcd_puts(buf);
}
}

/**
* 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;

if (tick_get() - ltime >= TICK_SECOND / 2) {


ltime = tick_get();
PORTA ^= 0x10;
}
}

/**
* 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:

RA0 RA3 Bus de datos para el display LCD (4 bits)


RA4 Luz led indicadora de actividad del micro (parpadea una vez por segundo).
RB0 Enable del chip controlador del LCD.
RB1 RS (Register Select) del chip controlador del LCD.
RB2 Conexin al pin de datos del sensor DHT11

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.

DHT11 temp/humidity sensor with PIC16f690 in assembly problem

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

#define LCD_RS PORTB,5


#define LCD_RW PORTB,6
#define LCD_E PORTB,7
#define LED PORTB,4

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

;CONVERT BINARY VALUES TO ASCII CHARACTERS TO SEND TO LCD


Integer_RH_to_ASCII
MOVF Integer_RH,W
CALL Convert_to_ASCII
MOVF Dec_Hun,W
MOVWF RH_Hun
MOVF Dec_Ten,W
MOVWF RH_Ten
MOVF Dec_One,W
MOVWF RH_One

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

;SEND VALUES TO LCD


;set interface
MOVLW b'00111000' ;8-bit interface, 2 lines, 5x8 dots
CALL SendCommand
;enable display, cursor
MOVLW b'00001100' ;display on, cursor off, blinking off
CALL SendCommand
;clear and home
MOVLW b'00000001'
CALL SendCommand
;set cursor move direction
MOVLW b'00000110'
CALL SendCommand

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

MOVF TMR2,W ;if TMR2>40, bit=1


SUBLW d'40'
BTFSC STATUS,C ;if TMR2>40, STATUS,C will be set
BSF BYTE,0 ;sets BYTE,0 to 1 if STATUS,C=1
BCF STATUS,C ;clears STATUS,C
RETURN

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

MOVLW h'30' ;convert all digits to ASCII


ADDWF Dec_Hun,f
MOVLW h'30'
ADDWF Dec_Ten,f
MOVLW h'30'
ADDWF Dec_One,f
RETURN

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 Routine - requires in main code -


; #define DHTPORT PORTA,5
; #define DHTTRIS TRISA, 5
; Needs 5k Pullup : Must be set to Input at power up
; Must have at least 1 second between reads

DHT11_module
nop

dhtlp ; call delay1sec ; 2+ sec delay between reads - to esure


its not read too fast
; call delay1sec

MOVLW 0x04 ; temp byte counter


MOVWF DHTBYCOUNT

BCF INTCON, GIE ; Disable Global Interrupts

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

bsf DHTTRIS ; make port


input - RETURN LINE HIGH AND MAKE INPUT
bcf DHTPORT

movlw 0x08 ; load delay factor


40us - LOOK FOR DHT LOW RESPONSE WITHIN 40us
movwf d1 ; SW = 49us
dht40 btfss DHTPORT ; line should go low within 40us
goto dht80L
decfsz d1, f
goto dht40
goto dhterror ; timeout error
dht80L call delay40us ; delayfor 40us
- DHT SHOULD GO HIGH AFTER 80us
movlw 0x08
movwf d1 ; SW = 49us
- CHECK FOR LINE HERE
dht40L btfsc DHTPORT ; then test line goes high within next 40us
- within total of 80us
goto dht80h
decfsz d1, f
goto dht40L
goto dhterror ; timeout error

dht80h call delay40us ; delayfor 40us


- DHT SHOULD GO LOW AFTER 80us
movlw 0x08
movwf d1 ; SW = 49us CHECK FOR LINE
HERE
dht40h btfss DHTPORT ; then test line goes low within next 40us
- within total of 80us
goto dhtdata ; - DTH FIRST DATA 50us LOW
DETECTED - NOW COLECT DATA STREAM
decfsz d1, f
goto dht40h
goto dhterror ; timeout error

dhtdata ; - DATA STREAM


LOOP
movlw 0x08 ; set up bit counter
movwf DHTBITCOUNT

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

dhtdh movlw 0x06 ; load delay factor for 27us - WHEN


DOES DATA HIGH GO LOW ?
movwf d1 ; SW =
37us

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

movlw 0x07 ; delay another


40us - DATA IS ONE IF RECEIVED WITHIN 70us
movwf d1 ; SW = 43us total high time
37+43= 80us
dhtdh1 btfss DHTPORT ; then test line goes LOW within next 30us
goto dhtone ;
- DATA GONE LOW WITHIN 35us SO MUST BE A ZERO
decfsz d1, f
goto dhtdh1
goto dhterror ; timeout error

dhtzero bcf STATUS,C ; add zero to byte


goto dhtaddbit
dhtone bsf STATUS,C ; add one to byte
dhtaddbit
rlcf DHTBYTE,F
decfsz DHTBITCOUNT,F
goto dhtdata_btyeloop

dhtbyte1 ; first data byte received


- copy to dht_hum
movlw 0x04
cpfseq DHTBYCOUNT
goto dhtbyte2
movff DHTBYTE, DHTHUM
decf DHTBYCOUNT,F
goto dhtdata

dhtbyte2 ; second data byte received


- discard - not used
movlw 0x03
cpfseq DHTBYCOUNT
goto dhtbyte3
decf DHTBYCOUNT,F
goto dhtdata

dhtbyte3 ; third data byte received


- copy to dht_temp
movlw 0x02
cpfseq DHTBYCOUNT
goto dhtbyte4
movff DHTBYTE, DHTTEMP
decf DHTBYCOUNT,F
goto dhtdata

dhtbyte4 ; fourth data byte received


- discard -not used
movlw 0x01
cpfseq DHTBYCOUNT
goto dhtbyte5
; movff DHTBYTE, DHTTEMP
decf DHTBYCOUNT,F
goto dhtdata

dhtbyte5 ; fifth data byte received


- copy to dht_chksum
movff DHTBYTE, DHTCHK

BSF INTCON, GIE ; Enable Global Interrupts

dhtdisp movf DHTHUM,W


call ascii
movff DIGITLO,t2 ;DHT_HUM_ones
movff DIGITHI,t1 ;DHT_HUM_tens

; 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

; Delay = 0.018 seconds


; Clock frequency = 4 MHz

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 }

You are using mikroC PRO PIC. TRISB = 0x20;

The below code is wrong.

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.

UART1_Write() is used to write characters. To send strings use UART1_Write_Text(message1)

There is the code:


I'm using PIC16F877A with 8MHz external X-Tal. And DHT11 sensor.
Code:
/*
Project: Temperature and humidity measurements using DHT11
MCU: PIC16F887
Clock: 10.0MHz external crystal
Board: UNI-DS6 board
Date: Jan 10, 2012
Written by: Rajendra Bhatt (www.embedded-lab.com)
*/

// LCD module connections


sbit LCD_RS at RB2_bit;
sbit LCD_EN at RB3_bit;
sbit LCD_D4 at RB4_bit;
sbit LCD_D5 at RB5_bit;
sbit LCD_D6 at RB6_bit;
sbit LCD_D7 at RB7_bit;

sbit LCD_RS_Direction at TRISB2_bit;


sbit LCD_EN_Direction at TRISB3_bit;
sbit LCD_D4_Direction at TRISB4_bit;
sbit LCD_D5_Direction at TRISB5_bit;
sbit LCD_D6_Direction at TRISB6_bit;
sbit LCD_D7_Direction at TRISB7_bit;
// End LCD module connections

sbit Data at RA0_bit;


sbit DataDir at TRISA0_bit;
char message1[] = "Temp = 00.0 C";
char message2[] = "RH = 00.0 %";
unsigned short TOUT = 0, CheckSum, i,check;
unsigned short T_Byte1, T_Byte2, RH_Byte1, RH_Byte2;

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();

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;
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 ...");
}
}
}
}
PIC Tutorial Three - LCD Modules

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.

Download zipped tutorial files.

LCD Command Control Codes


Binary
Command Hex
D7 D6 D5 D4 D3 D2 D1 D0
Clear Display 0 0 0 0 0 0 0 1 01
Display and Cursor Home 0 0 0 0 0 0 1 x 02 or 03
Character Entry Mode 0 0 0 0 0 1 I/D S 01 to 07
Display On/Off and Cursor 0 0 0 0 1 D U B 08 to 0F
Display/Cursor Shift 0 0 0 1 D/C R/L x x 10 to 1F
Function Set 0 0 1 8/4 2/1 10/7 x x 20 to 3F
Set CGRAM Address 0 1 A A A A A A 40 to 7F
Set Display Address 1 A A A A A A A 80 to FF
I/D: 1=Increment* 0=Decrement R/L: 1=Right Shift 0=Left Shift
S: 1=Display Shift On 0=Display Shift off* 8/4: 1=8 bit interface* 0=4 bit interface
D: 1=Display On 0=Display Off* 2/1: 1=2 line mode 0=1 line mode*
U: 1=Cursor Underline On 0=Cursor Underline Off* 10/7: 1=5x10 dot format 0=5x7 dot format*
B: 1=Cursor Blink On 0=Cursor Blink Off*
D/C: 1=Display Shift 0=Cursor Move *=initialisation setting x=don't care

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!).

Tutorial 3.1 - requires Main Board and LCD Board.

This program displays a text message on the LCD module, it consists mostly of subroutines for using the
LCD module.

;LCD text demo - 4 bit mode


;Nigel Goodwin 2002

LIST p=16F628 ;tell assembler what chip we are using


include "P16F628.inc" ;include the defaults for the chip
ERRORLEVEL 0, -302 ;suppress bank selection messages
__config 0x3D18 ;sets the configuration settings (oscillator type
etc.)

cblock 0x20 ;start of general purpose registers


count ;used in looping routines
count1 ;used in delay routine
counta ;used in delay routine
countb ;used in delay routine
tmp1 ;temporary storage
tmp2
templcd ;temp store for 4 bit mode
templcd2
endc

LCD_PORT Equ PORTA


LCD_TRIS Equ TRISA
LCD_RS Equ 0x04 ;LCD handshake lines
LCD_RW Equ 0x06
LCD_E Equ 0x07

org 0x0000

movlw 0x07
movwf CMCON ;turn comparators off (make it like a 16F84)

Initialise clrf count


clrf PORTA
clrf PORTB

SetPorts bsf STATUS, RP0 ;select bank 1


movlw 0x00 ;make all pins outputs
movwf LCD_TRIS
bcf STATUS, RP0 ;select bank 0

call Delay100 ;wait for LCD to settle

call LCD_Init ;setup LCD

clrf count ;set counter register to zero


Message movf count, w ;put counter value in W
call Text ;get a character from the text table
xorlw 0x00 ;is it a zero?
btfsc STATUS, Z
goto NextMessage
call LCD_Char
call Delay255
incf count, f
goto Message

NextMessage call LCD_Line2 ;move to 2nd row, first column

clrf count ;set counter register to zero


Message2 movf count, w ;put counter value in W
call Text2 ;get a character from the text table
xorlw 0x00 ;is it a zero?
btfsc STATUS, Z
goto EndMessage
call LCD_Char
incf count, f
goto Message2

EndMessage

Stop goto Stop ;endless loop

;Subroutines and text tables

;LCD routines

;Initialise LCD
LCD_Init movlw 0x20 ;Set 4 bit mode
call LCD_Cmd

movlw 0x28 ;Set display shift


call LCD_Cmd

movlw 0x06 ;Set display character mode


call LCD_Cmd

movlw 0x0d ;Set display on/off and cursor command


call LCD_Cmd

call LCD_Clr ;clear display

retlw 0x00

; command set routine


LCD_Cmd movwf templcd
swapf templcd, w ;send upper nibble
andlw 0x0f ;clear upper 4 bits of W
movwf LCD_PORT
bcf LCD_PORT, LCD_RS ;RS line to 0
call Pulse_e ;Pulse the E line high

movf templcd, w ;send lower nibble


andlw 0x0f ;clear upper 4 bits of W
movwf LCD_PORT
bcf LCD_PORT, LCD_RS ;RS line to 0
call Pulse_e ;Pulse the E line high
call Delay5
retlw 0x00

LCD_CharD addlw 0x30


LCD_Char movwf templcd
swapf templcd, w ;send upper nibble
andlw 0x0f ;clear upper 4 bits of W
movwf LCD_PORT
bsf LCD_PORT, LCD_RS ;RS line to 1
call Pulse_e ;Pulse the E line high

movf templcd, w ;send lower nibble


andlw 0x0f ;clear upper 4 bits of W
movwf LCD_PORT
bsf LCD_PORT, LCD_RS ;RS line to 1
call Pulse_e ;Pulse the E line high
call Delay5
retlw 0x00

LCD_Line1 movlw 0x80 ;move to 1st row, first column


call LCD_Cmd
retlw 0x00

LCD_Line2 movlw 0xc0 ;move to 2nd row, first column


call LCD_Cmd
retlw 0x00

LCD_Line1W addlw 0x80 ;move to 1st row, column W


call LCD_Cmd
retlw 0x00

LCD_Line2W addlw 0xc0 ;move to 2nd row, column W


call LCD_Cmd
retlw 0x00

LCD_CurOn movlw 0x0d ;Set display on/off and cursor command


call LCD_Cmd
retlw 0x00

LCD_CurOff movlw 0x0c ;Set display on/off and cursor command


call LCD_Cmd
retlw 0x00

LCD_Clr movlw 0x01 ;Clear display


call LCD_Cmd
retlw 0x00

LCD_HEX movwf tmp1


swapf tmp1, w
andlw 0x0f
call HEX_Table
call LCD_Char
movf tmp1, w
andlw 0x0f
call HEX_Table
call LCD_Char
retlw 0x00

Delay255 movlw 0xff ;delay 255 mS


goto d0
Delay100 movlw d'100' ;delay 100mS
goto d0
Delay50 movlw d'50' ;delay 50mS
goto d0
Delay20 movlw d'20' ;delay 20mS
goto d0
Delay5 movlw 0x05 ;delay 5.000 ms (4 MHz clock)
d0 movwf count1
d1 movlw 0xC7 ;delay 1mS
movwf counta
movlw 0x01
movwf countb
Delay_0
decfsz counta, f
goto $+2
decfsz countb, f
goto Delay_0

decfsz count1 ,f
goto d1
retlw 0x00

Pulse_e bsf LCD_PORT, LCD_E


nop
bcf LCD_PORT, LCD_E
retlw 0x00

;end of LCD routines

HEX_Table ADDWF PCL , f


RETLW 0x30
RETLW 0x31
RETLW 0x32
RETLW 0x33
RETLW 0x34
RETLW 0x35
RETLW 0x36
RETLW 0x37
RETLW 0x38
RETLW 0x39
RETLW 0x41
RETLW 0x42
RETLW 0x43
RETLW 0x44
RETLW 0x45
RETLW 0x46

Text addwf PCL, f


retlw 'H'
retlw 'e'
retlw 'l'
retlw 'l'
retlw 'o'
retlw 0x00

Text2 ADDWF PCL, f


RETLW 'R'
RETLW 'e'
RETLW 'a'
RETLW 'd'
RETLW 'y'
RETLW '.'
RETLW '.'
RETLW '.'
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

Tutorial 3.2 - requires Main Board and LCD Board.

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.

;LCD 16 bit counter


;Nigel Goodwin 2002

LIST p=16F628 ;tell assembler what chip we are using


include "P16F628.inc" ;include the defaults for the chip
ERRORLEVEL 0, -302 ;suppress bank selection messages
__config 0x3D18 ;sets the configuration settings (oscillator type
etc.)

cblock 0x20 ;start of general purpose registers


count ;used in looping routines
count1 ;used in delay routine
counta ;used in delay routine
countb ;used in delay routine
tmp1 ;temporary storage
tmp2
templcd ;temp store for 4 bit mode
templcd2

NumL ;Binary inputs for decimal convert routine


NumH

TenK ;Decimal outputs from convert routine


Thou
Hund
Tens
Ones
endc

LCD_PORT Equ PORTA


LCD_TRIS Equ TRISA
LCD_RS Equ 0x04 ;LCD handshake lines
LCD_RW Equ 0x06
LCD_E Equ 0x07

org 0x0000

movlw 0x07
movwf CMCON ;turn comparators off (make it like a 16F84)

Initialise clrf count


clrf PORTA
clrf PORTB
clrf NumL
clrf NumH

SetPorts bsf STATUS, RP0 ;select bank 1


movlw 0x00 ;make all pins outputs
movwf LCD_TRIS
bcf STATUS, RP0 ;select bank 0

call LCD_Init ;setup LCD

clrf count ;set counter register to zero


Message movf count, w ;put counter value in W
call Text ;get a character from the text table
xorlw 0x00 ;is it a zero?
btfsc STATUS, Z
goto NextMessage
call LCD_Char
incf count, f
goto Message

NextMessage call LCD_Line2 ;move to 2nd row, first column

call Convert ;convert to decimal


movf TenK, w ;display decimal characters
call LCD_CharD ;using LCD_CharD to convert to ASCII
movf Thou, w
call LCD_CharD
movf Hund, w
call LCD_CharD
movf Tens, w
call LCD_CharD
movf Ones, w
call LCD_CharD
movlw ' ' ;display a 'space'
call LCD_Char
movf NumH, w ;and counter in hexadecimal
call LCD_HEX
movf NumL, w
call LCD_HEX
incfsz NumL, f
goto Next
incf NumH, f
Next call Delay255 ;wait so you can see the digits change
goto NextMessage

;Subroutines and text tables

;LCD routines

;Initialise LCD
LCD_Init call Delay100 ;wait for LCD to settle

movlw 0x20 ;Set 4 bit mode


call LCD_Cmd

movlw 0x28 ;Set display shift


call LCD_Cmd

movlw 0x06 ;Set display character mode


call LCD_Cmd

movlw 0x0c ;Set display on/off and cursor command


call LCD_Cmd ;Set cursor off

call LCD_Clr ;clear display

retlw 0x00

; command set routine


LCD_Cmd movwf templcd
swapf templcd, w ;send upper nibble
andlw 0x0f ;clear upper 4 bits of W
movwf LCD_PORT
bcf LCD_PORT, LCD_RS ;RS line to 0
call Pulse_e ;Pulse the E line high

movf templcd, w ;send lower nibble


andlw 0x0f ;clear upper 4 bits of W
movwf LCD_PORT
bcf LCD_PORT, LCD_RS ;RS line to 0
call Pulse_e ;Pulse the E line high
call Delay5
retlw 0x00

LCD_CharD addlw 0x30 ;add 0x30 to convert to ASCII


LCD_Char movwf templcd
swapf templcd, w ;send upper nibble
andlw 0x0f ;clear upper 4 bits of W
movwf LCD_PORT
bsf LCD_PORT, LCD_RS ;RS line to 1
call Pulse_e ;Pulse the E line high
movf templcd, w ;send lower nibble
andlw 0x0f ;clear upper 4 bits of W
movwf LCD_PORT
bsf LCD_PORT, LCD_RS ;RS line to 1
call Pulse_e ;Pulse the E line high
call Delay5
retlw 0x00

LCD_Line1 movlw 0x80 ;move to 1st row, first column


call LCD_Cmd
retlw 0x00

LCD_Line2 movlw 0xc0 ;move to 2nd row, first column


call LCD_Cmd
retlw 0x00

LCD_Line1W addlw 0x80 ;move to 1st row, column W


call LCD_Cmd
retlw 0x00

LCD_Line2W addlw 0xc0 ;move to 2nd row, column W


call LCD_Cmd
retlw 0x00

LCD_CurOn movlw 0x0d ;Set display on/off and cursor command


call LCD_Cmd
retlw 0x00

LCD_CurOff movlw 0x0c ;Set display on/off and cursor command


call LCD_Cmd
retlw 0x00

LCD_Clr movlw 0x01 ;Clear display


call LCD_Cmd
retlw 0x00

LCD_HEX movwf tmp1


swapf tmp1, w
andlw 0x0f
call HEX_Table
call LCD_Char
movf tmp1, w
andlw 0x0f
call HEX_Table
call LCD_Char
retlw 0x00

Delay255 movlw 0xff ;delay 255 mS


goto d0
Delay100 movlw d'100' ;delay 100mS
goto d0
Delay50 movlw d'50' ;delay 50mS
goto d0
Delay20 movlw d'20' ;delay 20mS
goto d0
Delay5 movlw 0x05 ;delay 5.000 ms (4 MHz clock)
d0 movwf count1
d1 movlw 0xC7 ;delay 1mS
movwf counta
movlw 0x01
movwf countb
Delay_0
decfsz counta, f
goto $+2
decfsz countb, f
goto Delay_0

decfsz count1 ,f
goto d1
retlw 0x00

Pulse_e bsf LCD_PORT, LCD_E


nop
bcf LCD_PORT, LCD_E
retlw 0x00

;end of LCD routines

HEX_Table ADDWF PCL , f


RETLW 0x30
RETLW 0x31
RETLW 0x32
RETLW 0x33
RETLW 0x34
RETLW 0x35
RETLW 0x36
RETLW 0x37
RETLW 0x38
RETLW 0x39
RETLW 0x41
RETLW 0x42
RETLW 0x43
RETLW 0x44
RETLW 0x45
RETLW 0x46

Text addwf PCL, f


retlw '1'
retlw '6'
retlw ' '
retlw 'B'
retlw 'i'
retlw 't'
retlw ' '
retlw 'C'
retlw 'o'
retlw 'u'
retlw 'n'
retlw 't'
retlw 'e'
retlw 'r'
retlw '.'
retlw 0x00

;This routine downloaded from http://www.piclist.com


Convert: ; Takes number in NumH:NumL
; Returns decimal in
; TenK:Thou:Hund:Tens:Ones
swapf NumH, w
iorlw B'11110000'
movwf Thou
addwf Thou,f
addlw 0XE2
movwf Hund
addlw 0X32
movwf Ones

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

; At this point, the original number is


; equal to
; TenK*10000+Thou*1000+Hund*100+Tens*10+Ones
; if those entities are regarded as two's
; complement binary. To be precise, all of
; them are negative except TenK. Now the number
; needs to be normalized, but this can all be
; done with simple byte arithmetic.

movlw 0X0A ; Ten


Lb1:
addwf Ones,f
decf Tens,f
btfss 3,0
goto Lb1
Lb2:
addwf Tens,f
decf Hund,f
btfss 3,0
goto Lb2
Lb3:
addwf Hund,f
decf Thou,f
btfss 3,0
goto Lb3
Lb4:
addwf Thou,f
decf TenK,f
btfss 3,0
goto Lb4

retlw 0x00
end

Tutorial 3.3 - requires Main Board and LCD Board.

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.

;LCD 16 bit counter - using LCD Busy line


;Nigel Goodwin 2002

LIST p=16F628 ;tell assembler what chip we are using


include "P16F628.inc" ;include the defaults for the chip
ERRORLEVEL 0, -302 ;suppress bank selection messages
__config 0x3D18 ;sets the configuration settings (oscillator type
etc.)

cblock 0x20 ;start of general purpose registers


count ;used in looping routines
count1 ;used in delay routine
counta ;used in delay routine
countb ;used in delay routine
tmp1 ;temporary storage
tmp2
templcd ;temp store for 4 bit mode
templcd2

NumL ;Binary inputs for decimal convert routine


NumH

TenK ;Decimal outputs from convert routine


Thou
Hund
Tens
Ones
endc

LCD_PORT Equ PORTA


LCD_TRIS Equ TRISA
LCD_RS Equ 0x04 ;LCD handshake lines
LCD_RW Equ 0x06
LCD_E Equ 0x07

org 0x0000
goto Start

HEX_Table ADDWF PCL , f


RETLW 0x30
RETLW 0x31
RETLW 0x32
RETLW 0x33
RETLW 0x34
RETLW 0x35
RETLW 0x36
RETLW 0x37
RETLW 0x38
RETLW 0x39
RETLW 0x41
RETLW 0x42
RETLW 0x43
RETLW 0x44
RETLW 0x45
RETLW 0x46

Text addwf PCL, f


retlw '1'
retlw '6'
retlw ' '
retlw 'B'
retlw 'i'
retlw 't'
retlw ' '
retlw 'C'
retlw 'o'
retlw 'u'
retlw 'n'
retlw 't'
retlw 'e'
retlw 'r'
retlw '.'
retlw 0x00

Start movlw 0x07


movwf CMCON ;turn comparators off (make it like a 16F84)

Initialise clrf count


clrf PORTA
clrf PORTB
clrf NumL
clrf NumH

SetPorts bsf STATUS, RP0 ;select bank 1


movlw 0x00 ;make all pins outputs
movwf LCD_TRIS
movwf TRISB
bcf STATUS, RP0 ;select bank 0

call LCD_Init ;setup LCD

clrf count ;set counter register to zero


Message movf count, w ;put counter value in W
call Text ;get a character from the text table
xorlw 0x00 ;is it a zero?
btfsc STATUS, Z
goto NextMessage
call LCD_Char
incf count, f
goto Message
NextMessage movlw d'2'
call LCD_Line2W ;move to 2nd row, third column

call Convert ;convert to decimal


movf TenK, w ;display decimal characters
call LCD_CharD ;using LCD_CharD to convert to ASCII
movf Thou, w
call LCD_CharD
movf Hund, w
call LCD_CharD
movf Tens, w
call LCD_CharD
movf Ones, w
call LCD_CharD
movlw ' ' ;display a 'space'
call LCD_Char
movf NumH, w ;and counter in hexadecimal
call LCD_HEX
movf NumL, w
call LCD_HEX
incfsz NumL, f
goto Next
incf NumH, f
Next call Delay255 ;wait so you can see the digits change
goto NextMessage

;Subroutines and text tables

;LCD routines

;Initialise LCD
LCD_Init call LCD_Busy ;wait for LCD to settle

movlw 0x20 ;Set 4 bit mode


call LCD_Cmd

movlw 0x28 ;Set display shift


call LCD_Cmd

movlw 0x06 ;Set display character mode


call LCD_Cmd

movlw 0x0c ;Set display on/off and cursor command


call LCD_Cmd ;Set cursor off

call LCD_Clr ;clear display

retlw 0x00

; command set routine


LCD_Cmd movwf templcd
swapf templcd, w ;send upper nibble
andlw 0x0f ;clear upper 4 bits of W
movwf LCD_PORT
bcf LCD_PORT, LCD_RS ;RS line to 0
call Pulse_e ;Pulse the E line high

movf templcd, w ;send lower nibble


andlw 0x0f ;clear upper 4 bits of W
movwf LCD_PORT
bcf LCD_PORT, LCD_RS ;RS line to 0
call Pulse_e ;Pulse the E line high
call LCD_Busy
retlw 0x00

LCD_CharD addlw 0x30 ;add 0x30 to convert to ASCII


LCD_Char movwf templcd
swapf templcd, w ;send upper nibble
andlw 0x0f ;clear upper 4 bits of W
movwf LCD_PORT
bsf LCD_PORT, LCD_RS ;RS line to 1
call Pulse_e ;Pulse the E line high

movf templcd, w ;send lower nibble


andlw 0x0f ;clear upper 4 bits of W
movwf LCD_PORT
bsf LCD_PORT, LCD_RS ;RS line to 1
call Pulse_e ;Pulse the E line high
call LCD_Busy
retlw 0x00

LCD_Line1 movlw 0x80 ;move to 1st row, first column


call LCD_Cmd
retlw 0x00

LCD_Line2 movlw 0xc0 ;move to 2nd row, first column


call LCD_Cmd
retlw 0x00

LCD_Line1W addlw 0x80 ;move to 1st row, column W


call LCD_Cmd
retlw 0x00

LCD_Line2W addlw 0xc0 ;move to 2nd row, column W


call LCD_Cmd
retlw 0x00

LCD_CurOn movlw 0x0d ;Set display on/off and cursor command


call LCD_Cmd
retlw 0x00

LCD_CurOff movlw 0x0c ;Set display on/off and cursor command


call LCD_Cmd
retlw 0x00

LCD_Clr movlw 0x01 ;Clear display


call LCD_Cmd
retlw 0x00

LCD_HEX movwf tmp1


swapf tmp1, w
andlw 0x0f
call HEX_Table
call LCD_Char
movf tmp1, w
andlw 0x0f
call HEX_Table
call LCD_Char
retlw 0x00

Delay255 movlw 0xff ;delay 255 mS


goto d0
Delay100 movlw d'100' ;delay 100mS
goto d0
Delay50 movlw d'50' ;delay 50mS
goto d0
Delay20 movlw d'20' ;delay 20mS
goto d0
Delay5 movlw 0x05 ;delay 5.000 ms (4 MHz clock)
d0 movwf count1
d1 movlw 0xC7 ;delay 1mS
movwf counta
movlw 0x01
movwf countb
Delay_0
decfsz counta, f
goto $+2
decfsz countb, f
goto Delay_0

decfsz count1 ,f
goto d1
retlw 0x00

Pulse_e bsf LCD_PORT, LCD_E


nop
bcf LCD_PORT, LCD_E
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

;end of LCD routines

;This routine downloaded from http://www.piclist.com


Convert: ; Takes number in NumH:NumL
; Returns decimal in
; TenK:Thou:Hund:Tens:Ones
swapf NumH, w
iorlw B'11110000'
movwf Thou
addwf Thou,f
addlw 0XE2
movwf Hund
addlw 0X32
movwf Ones
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

; At this point, the original number is


; equal to
; TenK*10000+Thou*1000+Hund*100+Tens*10+Ones
; if those entities are regarded as two's
; complement binary. To be precise, all of
; them are negative except TenK. Now the number
; needs to be normalized, but this can all be
; done with simple byte arithmetic.

movlw 0X0A ; Ten


Lb1:
addwf Ones,f
decf Tens,f
btfss 3,0
goto Lb1
Lb2:
addwf Tens,f
decf Hund,f
btfss 3,0
goto Lb2
Lb3:
addwf Hund,f
decf Thou,f
btfss 3,0
goto Lb3
Lb4:
addwf Thou,f
decf TenK,f
btfss 3,0
goto Lb4

retlw 0x00

end

Potrebbero piacerti anche