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 50°C. It has 4 pins of which 2 pins p ins are used for supply, 1 is not no t used and the last one is used u sed for data. The data is the only pin used for communication. Pulses of different TON and TOFF are decoded as a s 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 DHT1 1 sensor. Display this received data on LCD16x2 along alon g with error indication.
/* * DHT11 Interfacing with PIC18F4550 * http://www.electronicwings.com */ #include #include #include #include #include #define #define #define #define
"Configuration_Header_File.h" "LCD_16x2_8-bit_Header_File.h" Data_Out LATA0 Data_In PORTAbits.RA0 Data_Dir TRISAbits.RA0 _XTAL_FREQ 8000000
/* /* /* /*
assign Port pin for data*/ read data from Port pin*/ Port direction */ 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(); ADCON1=0x0F; while(1)
/* initialize LCD16x2 */ /* this makes all pins as a digital I/O pins */
{ 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 RH_Decimal = DHT11_ReadData(); /* read T_Integral = DHT11_ReadData(); /* read T_Decimal = DHT11_ReadData(); /* read Checksum = DHT11_ReadData(); /* read
Relative Humidity's integral value */ Relative Humidity's decimal value */ Temperature's integral value */ Relative Temperature's decimal value */ 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('%');
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 DHT1 1 sensor. Display this received data on LCD16x2 along alon g with error indication.
/* * DHT11 Interfacing with PIC18F4550 * http://www.electronicwings.com */ #include #include #include #include #include #define #define #define #define
"Configuration_Header_File.h" "LCD_16x2_8-bit_Header_File.h" Data_Out LATA0 Data_In PORTAbits.RA0 Data_Dir TRISAbits.RA0 _XTAL_FREQ 8000000
/* /* /* /*
assign Port pin for data*/ read data from Port pin*/ Port direction */ 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(); ADCON1=0x0F; while(1)
/* initialize LCD16x2 */ /* this makes all pins as a digital I/O pins */
{ 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 RH_Decimal = DHT11_ReadData(); /* read T_Integral = DHT11_ReadData(); /* read T_Decimal = DHT11_ReadData(); /* read Checksum = DHT11_ReadData(); /* read
Relative Humidity's integral value */ Relative Humidity's decimal value */ Temperature's integral value */ Relative Temperature's decimal value */ 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; /* Data_Out = 0; /* __delay_ms(18); Data_Out = 1; /* __delay_us(20); Data_Dir = 1; /* }
set as output port */ send low pulse of min. 18 ms width */ pull data bus high */ 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 t he 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 se nsor, 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 me asurement 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
1 2 3 4
Pin Description Power supply
VCC DATA NC GND
3.3 to 5.5 Volt DC Digital output pin Not in use 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 se nsor 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 rec eived 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. T his 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, w hether 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 se nds 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-50°C temperature readings ±2°C 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 125°C temperature readings ±0.5°C 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 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 Sensirion’s 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 16×2 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 ±2°C 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 read y 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) = I nteger Byte of RH + Decimal Byte of R H + I nteger B yte 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. Don’t 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 Experimenter’s I/O board for the LCD part of this project. Similarly, I am using m y 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 ha rdware 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 sbit sbit sbit sbit sbit sbit sbit sbit sbit sbit sbit
LCD_RS at RB2_bit; LCD_EN at RB3_bit; LCD_D4 at RB4_bit; LCD_D5 at RB5_bit; LCD_D6 at RB6_bit; LCD_D7 at RB7_bit; LCD_RS_Direction at LCD_EN_Direction at LCD_D4_Direction at LCD_D5_Direction at LCD_D6_Direction at LCD_D7_Direction at
TRISB2_bit; TRISB3_bit; TRISB4_bit; TRISB5_bit; TRISB6_bit; 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 = 0; Delay_ms(25); // Data = 1; Delay_us(30); // DataDir = 1; //
Data port is output Low for at least 18us High for 20-40 us Data port is input
} unsigned short CheckResponse(){ TOUT = 0; TMR2 = 0; T2CON.TMR2ON = 1; // Start TMR2 while waiting for sensor response while(!Data && !TOUT); // If there's no response within 256us, the Timer2 overflows
if (TOUT) return 0; // and exit else { TMR2 = 0; while(Data && !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<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 > 40) num |= 1<<(7-i); // If time > 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) & 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 detecte d high, wait for 40 ?s and chec k 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 Sensirion’s 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, T dp, 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 , P s(T ), and the actual vapor pressure (also in units of millibars), P a(T ), which can be either found with RH or approximated with the barometric pressure (in millibars), BP mb, and "wet-bulb" temperature, T w is (unless declared otherwise, all temperatures are expressed in degrees Celsius):
For greater accuracy, P s(T ) (and therefore γ(T , RH)) can be enhanced, using part of the Bögel 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 ≤ 35°C 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 #include #include "lcd_hd44780_pic16.h" #pragma #pragma #pragma #pragma #pragma
config config config config config
FOSC = HS WDTE = OFF PWRTE = ON BOREN = ON LVP = OFF
// Oscillator Selection bits (HS oscillator) // Watchdog Timer Enable bit (WDT disabled) // Power-up Timer Enable bit (PWRT enabled) // Brown-out Reset Enable bit (BOR enabled) // 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 #define DH11_DATA_TRIS
PORTDbits.RD5 TRISDbits.TRISD5
uint8_t dht11_data[40]; uint8_t dht11_rh; uint8_t dht11_temp;
uint8_t dht11_measure(); delay() void { uint16_t i; for(i=0;i<3000;i++)
{ __delay_ms(1); } } main() void { 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 w ith 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.
Schematiclayout
The schematic layout is split into blocks:
Powerblock Regulates the power to 5 volt. It is using the LM7805. ICSP
This is a 1x5 pin header, connected to the MCU's programming pins. We use this header to program the MCU.
DHT11
This is a 1x3 pin header, which holds t he sensor. The middle pin is connected to the MCU, for data transfer.
MCU
This is the PIC16F628A, which rec eives data from the DHT11 and 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
rcl
1
C3
0.1uF
C-EU025-050X050 C025-050X050 rcl
1
C4
100uF
CPOL-EUE2.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
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
con-wago-500 1
PINHD-1X5
E2,5-5
E2,5-5
W237-102
Now that the hardware is good, it's time for the software. 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 Ubun tu 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, openx lcd.c, putrxlcd.c and so on. 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 x lcd.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 #define TRIS_RS
PORTAbits.RA1 TRISAbits.TRISA1
/* PORT for RS */ /* TRIS for RS */
#define E_PIN #define TRIS_E
PORTAbits.RA7 TRISAbits.TRISA7
/* PORT for D /* 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 #include #include #include "my_xlcd.h"
// Including Standard Input / Outputlibrary // Including Standard library function // Including XC8 compiler library // 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 #define data PORTAbits.RA2 #define data_dir TRISAbits.TRISA2
// Telling the compiler, that we are using 4MHz // Defining RA0 as datapin // 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 d elay functions: // FUNCTIONS void init_XLCD(void){ OpenXLCD(FOUR_BIT & LINES_5X7); while(BusyXLCD()); WriteCmdXLCD(0x06); WriteCmdXLCD(0x0C); }
// // // //
Sets 4-bit & 5x7 charachters Checks if LCD is busy Moves cursor right 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){ __delay_ms(15); }
// as it says in the XLCD.H file
void DelayXLCD(void){ __delay_ms(5); }
// as it says in the XLCD.H file
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; data = 0; __delay_ms(18); data = 1; __delay_us(20); data_dir = 1; }
// // // // // //
Sets TRISA2 to output Set RA2 to LOW Waits for 18 ms Sets RA2 HIGH Waits for 20 ms Sets TRISA2 to input
unsigned short ReadByte(){ unsigned short num = 0, t; data_dir = 1; for (i=0;i<8;i++){ while(!data); TMR2 = 0; T2CONbits.TMR2ON = 1; pulse while(data); T2CONbits.TMR2ON = 0; if(TMR2>40) num |= 1 << (7-i); } return num; } unsigned short CheckResponse(){ TOUT = 0; TMR2 = 0; T2CONbits.TMR2ON = 1; // while(!data && !TOUT); // if (TOUT) return 0; // else { TMR2 = 0; // while(data && !TOUT); // if(TOUT) return 0; // else { T2CONbits.TMR2ON = 0; return 1; } } }
// // // // //
Sets TRISA2 to input Start loop When data is not valid Sets TMR2 to 0 Start TMR2 from 0 when a low to high data
// is detected, and wait until it falls low again // Stop the TMR2 when the data pulse falls low // If time > 40us, data is 1 // Return 8-bit = 1-byte
Turn on TMR2 While NOT data and NOT TOUT Return 0 => OK Disable Timer 2 While data and NOT TOUT If Tout = 1 then return 0 => OK // Turn off TMR2 // 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 nee d an interrupt function: void interrupt tc_int(void){ if(PIR1bits.TMR2IF){ TOUT = 1; T2CONbits.TMR2ON = 0; PIR1bits.TMR2IF = 0; } }
And in the end we need the main program: int main(int argc, char** argv) {
// If TMR2 to PR2 match Interrupt Flag // Stop timer // Clear TMR0 interrupt flag
unsigned short check; TRISB = 0b00000000; PORTB = 0b00000000; TRISA = 0b00000001; PORTA = 0b00000000;
// // // //
CMCON = 0x07;
// Comparators off
// TIMER INTCONbits.GIE = 1; INTCONbits.PEIE = 1; PIE1bits.TMR2IE = 1; T2CON = 0; PIR1bits.TMR2IF = 0; TMR2 = 0;
// // // // //
TRISB PORTB TRISA PORTA
output low output low
Enable global interrupt Enable peripheral interrupt Enable Timer2 interrupt Prescaler 1:1 and Timer2 is off initially Clear TMR INT flag bit
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); SetDDRamAddr(0x40); putrsXLCD(message2);
// Write the temp to LCD // 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 bu y a PIC16F628, be sure it is the "A" version. The other one is obsolete.
Sensor DHT11 con PIC El DHT11 es un sensor bastante económico que permite realizar mediciones de temperatura y humedad. En esta entrada te presentamos nuestro código en C para leer este sensor y un ejemplo práctico con un PIC16F88 que también debería 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 16×2.
Funcionamiento del DHT11. El DHT11 no utiliza una interfaz serial estándar como I2C, SPI o 1Wire. En cambio requiere su propio protocolo para comunicarse a través 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 imágenes que he traducido y que explican en detalle la comunicación. Dicen por ahí que una imagen vale más que mil palabras y para nuestro caso, llevan razón con este dicho:
Para iniciar la comunicación con el DHT11 el micro “jala” la linea de datos a tierra por al menos 18 mS
El microcontrolador debe iniciar la comunicación con el DHT11 manteniendo la linea de datos en estado bajo durante al menos 18 ms. Luego el DHT11 envía 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 codificación de datos esta basada en un esquema de ancho de pulso: Un pulso ancho representa un 1 lógico, un pulso corto representa un 0 lógico. Los datos binarios se codifican según la longitud del pulso alto. Todos los bits comienzan con un pulso bajo de 50 uS. En nuestra librería aprovechamos el pulso bajo en cada bit para sincronizar con la señal del DHT11. Luego viene un pulso alto que varía según el estado lógico 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 transmisión 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 interpretación 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 decim al 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 comprobación (checksum), resultante de sumar todos los bytes anteriores
Comunicación 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 señal 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 comunicación con el DHT11. Se muestra el pulso de 20 ms y los datos que van mucho más rápido se ven como la barra naranja a la derecha.
Librería para el DHT11 Descargaeinstalación
Nuestra librería para el DHT11 se encuentra alojada en Github, como siempre, agradeceremos cualquier contribución, corrección o ejemplo que quieras aportar. Puedes obtener la librería con los ultimos cambios y novedades desde nuestra página en github.com: DHTlib-master.zip La librería básica para el DHT11 consiste en 3 archivos de código fuente:
DHTLib.h – Contiene la declaración y documentación de las funciones (prototipos). DHTLib.c – Implementación 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 liberías que r equieren ajustes en tiempo de compilación, aqui se definen los pines de entrada / salida a utilizar con el DHT11 y la velocidad del re loj.
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. Configuracióndepinesenlalibrería
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 Usobásicoparaleerelsensor
Usar la librería 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 función dhtlib_init(). Posteriormente cuando deseemos obtener el valor de la temperatura deberemos llamar a la función dhtlib_read(). Esta ultima acepta como parámetro dos apuntadores a variables de tipo “uint8_t” donde se guardarán las medidas de humedad y temperatura. La función retorna un valor de tipo enumeración que nos informa si la comunicación con el sensor se pudo llevar a cabo de forma exitosa. Dejamos aquí el código de la función principal de nuestro programa para el PIC que ilustra el uso de la librería para el DHT11. Hemos destacado las partes que todo programa que use la librería DHTLib debe tener: #include #include #include "../Tick/Tick.h" #include "../LCD/LCD.h" #include "../DHTLib.h" // Configuración 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 envía 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 /** * Implementación 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 P IC16F88 8 Mhz ANSEL = 0x00; // Configuracion de pines analogicos y digitales TRISA = 0xE0; TRISB = 0xFF; // Preparamos las librerías 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 comunicación con el sensor DHT11 // Encendemos la pantalla LCD y escribimos titulo lcd_on(); lcd_puts(" PRUEBA DHT11 "); // Ciclo principal de nuestra aplicación for (;;) { led_task(); dht11_task(); } } /** * Código 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; } } /** * Implementación del vector de interrupción (ú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 – Conexión al pin de datos del sensor DHT11
Podemos personalizar esta configuración según nuestras necesidades, e incluso llevarla fácilmente a otros microcontroladores de esta misma familia como el PIC16F877A o incluso el PIC18F4550 que es mucho más 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 depe nden del tiempo funcionan a esta velocidad. En caso de querer compilar este programa para un cristal más veloz o para e l PIC18F4550 hay que modificar e l 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.
Conclusión A fin de cuentas observamos que implementar la comunicación con el DHT11 es bastante sencillo y nuestra pequeña librería nos puede servir para varios microcontroladores más. Por ejemplo, nosotros ya la probamos en un ATMEGA8 y un PIC24HJ128GP504. Para migrar nuestra librería 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 versión de nuestra librería hemos incluido soporte para el sensor DHT22, que es similar al DHT11 pero ofrece mejor desempeño 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 artículo.
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 ;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 #define #define #define org
LCD_RS LCD_RW LCD_E LED
PORTB,5 PORTB,6 PORTB,7 PORTB,4
0
Main ;Initialize BANKSEL MOVLW MOVWF CLRF CLRF
Oscillator, 4MHz internal OSCCON ;Bank 1 b'01100000' ;Set internal oscillator frequency to 4 MHz OSCCON TRISB ;port B as output TRISC ;port C as output
BANKSEL CLRF CLRF
ANSEL ANSEL ANSELH
Start BANKSEL BCF BANKSEL BCF CALL BSF CALL BANKSEL
TRISA TRISA,0 PORTA PORTA,0 Delay_20ms PORTA,0 Delay_30us TRISA
;configure pins as digital
;RA0 as output ;RA0 ;RA0 ;RA0 ;RA0
low low for 20ms high high for 30us
BSF BANKSEL CALL
TRISA,0 PORTA Delay_160us
;RA0 as input ;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 MOVF MOVWF
Read_BYTE BYTE,W Integer_T
;Move BYTE to Integer_T
Read_Decimal_T CALL MOVF MOVWF
Read_BYTE BYTE,W Decimal_T
;Move BYTE to Decimal_T
Read_Checksum CALL MOVF MOVWF
Read_BYTE BYTE,W 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' CALL SendCommand ;enable display, cursor MOVLW b'00001100' CALL SendCommand ;clear and home MOVLW b'00000001' CALL SendCommand
;8-bit interface, 2 lines, 5x8 dots
;display on, cursor off, blinking off
;set cursor move direction MOVLW b'00000110' CALL SendCommand MOVF CALL MOVF CALL MOVF CALL
T_Hun,W SendCharacter T_Ten,W SendCharacter T_One,W SendCharacter
CALL GOTO
Delay_20ms 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 BTFSS GOTO BSF BTFSC GOTO BCF MOVF SUBLW BTFSC BSF BCF RETURN
TMR2 PORTA,0 $-1 T2CON,2 PORTA,0 $-1 T2CON,2 TMR2,W d'40' STATUS,C BYTE,0 STATUS,C
Convert_to_ASCII MOVWF BYTE CLRF Dec_Hun CLRF Dec_Ten CLRF Dec_One B2D1 MOVLW d'100' SUBWF BYTE,f BTFSS STATUS,C goto B2D2 INCF Dec_Hun,f GOTO B2D1 B2D2 MOVLW d'100' ADDWF BYTE,f B2D3 MOVLW d'10' SUBWF BYTE,f BTFSS STATUS,C goto B2D4 INCF Dec_Ten,f
;Tests until RA0 is pulled high ;Starts TMR2 ;Tests until RA0 is pulled low ;Stops TMR2 ;if TMR2>40, bit=1 ;if TMR2>40, STATUS,C will be set ;sets BYTE,0 to 1 if STATUS,C=1 ;clears STATUS,C
;move 100 to work ;BYTE-100 ;check if negative value ;goto next tens place if value was negative ;add 1 to hundreds ;repeat until negative
;add 100 back to make it positive ;move 10 to work ;BYTE-10 ;check if negative value ;goto next tens place if value was negative ;add 1 to tens
GOTO
B2D3
;repeat until negative
MOVLW ADDWF
d'10' BYTE,W
;add 10 back to make it positive and store in
MOVWF
Dec_One
;remainder is the ones place
MOVLW ADDWF MOVLW ADDWF MOVLW ADDWF RETURN
h'30' Dec_Hun,f h'30' Dec_Ten,f h'30' Dec_One,f
;convert all digits to ASCII
B2D4
work
SendCommand BANKSEL MOVWF BCF BCF BSF CALL BCF return
PORTC PORTC LCD_RS LCD_RW LCD_E Delay_20ms LCD_E
SendCharacter BANKSEL MOVWF BSF BCF BSF CALL BCF return
PORTC PORTC LCD_RS LCD_RW LCD_E Delay_20ms LCD_E
Delay_20ms ;19993 cycles MOVLW MOVWF MOVLW MOVWF
0x9E D1 0x10 D2
DECFSZ GOTO DECFSZ GOTO
D1, f $+2 D2, f L0
L0
;3 cycles GOTO NOP
$+1 ;4 cycles (including call)
RETURN Delay_30us MOVLW MOVWF L1 DECFSZ GOTO RETURN Delay_160us MOVLW MOVWF
0x09 D1 D1, f L1
0x34 D1
L2 DECFSZ GOTO NOP RETURN
D1, f L2
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 its not read too fast ; call delay1sec MOVLW MOVWF BCF
0x04 DHTBYCOUNT
DHTPORT SEND 18MS START SIGNAL delay18ms DHTPORT
bsf
DHTTRIS
bcf
DHTPORT
movlw
dht40
btfss
; Disable Global Interrupts
DHTTRIS
input
40us
- to esure
; temp byte counter
INTCON, GIE bcf bcf call bsf nop nop
18ms
; 2+ sec delay between reads
; ;
turn off port for
SW = 18.02ms ; turm on port ; little pause
; make port - RETURN LINE HIGH AND MAKE INPUT
0x08 ; load delay factor - LOOK FOR DHT LOW RESPONSE WITHIN 40us movwf d1 ; SW = 49us DHTPORT ; line should go low within 40us goto dht80L decfsz d1, f goto dht40 goto dhterror ; timeout error
dht80L
call
delay40us
movlw movwf - CHECK FOR LINE HERE dht40L btfsc DHTPORT - within total of 80us goto decfsz goto goto dht80h
call
; delayfor 40us - DHT SHOULD GO HIGH AFTER 80us
0x08 d1
; SW = 49us ; then test line goes high within next 40us
dht80h d1, f dht40L dhterror
delay40us movlw movwf
; timeout error ; delayfor 40us - DHT SHOULD GO LOW AFTER 80us
0x08 d1
; SW = 49us CHECK FOR LINE
HERE dht40h btfss DHTPORT - within total of 80us goto dhtdata DETECTED - NOW COLECT DATA STREAM decfsz d1, f goto dht40h goto dhterror
dhtdata LOOP
; then test line goes low within next 40us ;
- DTH
FIRST DATA 50us LOW
; timeout error
; movlw movwf
dhtdata_btyeloop movlw movwf 62us
- DATA STREAM
0x08 DHTBITCOUNT
; set up bit counter
0x0A d1
; load delay factor for 50us ; SW = ;
SHOULD GO HIGH AFTER 50us dht50h btfsc DHTPORT goto dhtdh decfsz d1, f goto dht50h goto dhterror
dhtdh movlw 0x06 DOES DATA HIGH GO LOW ? movwf d1 37us
dhtdhz
btfss
DHTPORT goto dhtzero decfsz goto
d1, f dhtdhz
movlw
0x07
movwf
d1
40us 37+43= 80us dhtdh1 btfss
DHTPORT
- DHT
; then test line goes high within next 50us
; timeout error
; load delay factor for 27us
- WHEN
; SW =
; then test line goes LOW within next 30us ; - DATA GONE LOW WITHIN 35us SO MUST BE A ZERO
; delay another - DATA IS ONE IF RECEIVED WITHIN 70us ; SW = 43us
total high time
; then test line goes LOW within next 30us
goto
dhtone
decfsz goto goto
d1, f dhtdh1 dhterror
goto
STATUS,C dhtaddbit STATUS,C
dhtzero bcf dhtone bsf dhtaddbit rlcf decfsz goto
dhtbyte1 - copy to dht_hum movlw cpfseq goto movff decf goto
dhtbyte2 - discard - not used movlw cpfseq goto decf goto
dhtbyte3 - copy to dht_temp movlw cpfseq goto movff decf goto
dhtbyte4 - discard -not used movlw cpfseq goto ; movff decf goto
dhtbyte5 - copy to dht_chksum movff BSF dhtdisp movf
; - DATA GONE LOW WITHIN 35us SO MUST BE A ZERO
; timeout error ; add zero to byte ; add one to byte
DHTBYTE,F DHTBITCOUNT,F dhtdata_btyeloop
; first data byte received 0x04 DHTBYCOUNT dhtbyte2 DHTBYTE, DHTHUM DHTBYCOUNT,F dhtdata
; second data byte received 0x03 DHTBYCOUNT dhtbyte3 DHTBYCOUNT,F dhtdata
; third data byte received 0x02 DHTBYCOUNT dhtbyte4 DHTBYTE, DHTTEMP DHTBYCOUNT,F dhtdata
; fourth data byte received 0x01 DHTBYCOUNT dhtbyte5 DHTBYTE, DHTTEMP DHTBYCOUNT,F dhtdata
; fifth data byte received DHTBYTE, DHTCHK INTCON, GIE
DHTHUM,W call ascii movff DIGITLO,t2
; Enable Global Interrupts
;DHT_HUM_ones
movff ; ; ; ;
movff call movff movff
; ;
DIGITHI,t1
;DHT_HUM_tens
DHTTEMP,BIN ascii3 ones, E15 tens, E14
movff call
;DHT_TEMP_ones ;DHT_TEMP_tens
DHTCHK,BIN ascii3
return
dhterror movlw movwf movlw movwf BSF
'E' t1 'R' t2 INTCON, GIE
; Enable Global Interrupts
return
delay40us
dly40us decfsz
movlw movwf d1, f goto nop return
0x0b d1 dly40us
; 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 2 3
char message1[] = "Temp = 00.0 C"; char message2[] = "RH = 00.0 %"; unsigned short TOUT = 0, CheckSum, i, Check;
Code C - [expand] 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
unsigned short T_Byte1, T_Byte2, RH_Byte1, RH_Byte2; unsigned short Data, DataDir, heck ; char usart_rd; 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;
Code C - [expand] 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
INTCON.PEIE = 1; PIE1.TMR2IE = 1; T2CON = 0; PIR1.TMR2IF =0; TMR2 = 0; Usart_Init(9600); Delay_ms(100); LCD_Init(&PORTD); // Initialize LCD connected to PORTB LCD_Cmd(LCD_CLEAR); // Clear display LCD_Cmd(LCD_CURSOR_OFF); // Turn cursor off do { Delay_ms(1000); StartSignal(); heck = 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); usart_rd = message1; } else{ Lcd_Cmd(Lcd_CLEAR); Lcd_Out(1, 1, "Checksum Error!"); Lcd_Out(2, 1, "Trying Again ..."); } } }while(1); TRISC = 0; PORTC = 0; UART1_Init(9600); Delay_ms(100); while(1){ if(UART1_Data_Ready()){ usart_rd = UART1_Read(); 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 3 4 5 6 7 8 9
TRISC = 0; PORTC = 0; UART1_Init(9600); Delay_ms(100); while(1){ if(UART1_Data_Ready()){ usart_rd = UART1_Read(); 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 Command
Binary
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 S: 1=Display Shift On 0=Display Shift off* D: 1=Display On 0=Display Off* U: 1=Cursor Underline On 0=Cursor Underline Off* B: 1=Cursor Blink On 0=Cursor Blink Off* D/C: 1=Display Shift 0=Cursor Move
R/L: 1=Right Shift 8/4: 1=8 bit interface* 2/1: 1=2 line mode 10/7: 1=5x10 dot format *=initialisation setting
0=Left Shift 0=4 bit interface 0=1 line mode* 0=5x7 dot format* 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 include "P16F628.inc" ERRORLEVEL 0,
-302
;tell assembler what chip we are using ;include the defaults for the chip ;suppress bank selection messages
__config 0x3D18
;sets the configuration settings (oscillator type
etc.)
cblock
0x20 count count1 counta countb tmp1 tmp2 templcd templcd2
;start of general purpose registers ;used in looping routines ;used in delay routine ;used in delay routine ;used in delay routine ;temporary storage ;temp store for 4 bit mode
endc LCD_PORT LCD_TRIS LCD_RS LCD_RW LCD_E
Equ Equ Equ Equ Equ
PORTA TRISA 0x04 0x06 0x07
org
0x0000
movlw movwf
0x07 CMCON
Initialise
clrf clrf clrf
count PORTA PORTB
SetPorts
bsf movlw movwf bcf
STATUS, 0x00 LCD_TRIS STATUS,
call
Delay100
;wait for LCD to settle
call
LCD_Init
;setup LCD
clrf movf call xorlw btfsc goto call call incf goto
count count, w Text 0x00 STATUS, Z NextMessage LCD_Char Delay255 count, f Message
;set counter register to zero ;put counter value in W ;get a character from the text table ;is it a zero?
call
LCD_Line2
;move to 2nd row, first column
clrf movf call xorlw btfsc
count count, w Text2 0x00 STATUS, Z
;set counter register to zero ;put counter value in W ;get a character from the text table ;is it a zero?
Message
NextMessage
Message2
;LCD handshake lines
;turn comparators off (make it like a 16F84)
RP0
;select bank 1 ;make all pins outputs
RP0
;select bank 0
goto call incf goto
EndMessage LCD_Char count, f Message2
goto
Stop
EndMessage Stop
;endless loop
;Subroutines and text tables ;LCD routines ;Initialise LCD LCD_Init movlw call
0x20 LCD_Cmd
;Set 4 bit mode
movlw call
0x28 LCD_Cmd
;Set display shift
movlw call
0x06 LCD_Cmd
;Set display character mode
movlw call
0x0d LCD_Cmd
;Set display on/off and cursor command
call
LCD_Clr
;clear display
retlw
0x00
; command set routine LCD_Cmd movwf swapf andlw movwf bcf call
LCD_CharD LCD_Char
templcd templcd, w 0x0f LCD_PORT LCD_PORT, LCD_RS Pulse_e
movf andlw movwf bcf call call retlw
templcd, w 0x0f LCD_PORT LCD_PORT, LCD_RS Pulse_e Delay5 0x00
addlw movwf swapf andlw movwf bsf call
0x30 templcd templcd, w 0x0f LCD_PORT LCD_PORT, LCD_RS Pulse_e
movf andlw movwf bsf call call
templcd, w 0x0f LCD_PORT LCD_PORT, LCD_RS Pulse_e Delay5
;send upper nibble ;clear upper 4 bits of W ;RS line to 0 ;Pulse the E line high ;send lower nibble ;clear upper 4 bits of W ;RS line to 0 ;Pulse the E line high
;send upper nibble ;clear upper 4 bits of W ;RS line to 1 ;Pulse the E line high ;send lower nibble ;clear upper 4 bits of W ;RS line to 1 ;Pulse the E line high
retlw
0x00
LCD_Line1
movlw call retlw
0x80 LCD_Cmd 0x00
;move to 1st row, first column
LCD_Line2
movlw call retlw
0xc0 LCD_Cmd 0x00
;move to 2nd row, first column
LCD_Line1W
addlw call retlw
0x80 LCD_Cmd 0x00
;move to 1st row, column W
LCD_Line2W
addlw call retlw
0xc0 LCD_Cmd 0x00
;move to 2nd row, column W
LCD_CurOn
movlw call retlw
0x0d LCD_Cmd 0x00
;Set display on/off and cursor command
LCD_CurOff
movlw call retlw
0x0c LCD_Cmd 0x00
;Set display on/off and cursor command
LCD_Clr
movlw call retlw
0x01 LCD_Cmd 0x00
;Clear display
LCD_HEX
movwf swapf andlw call call movf andlw call call retlw
tmp1 tmp1, w 0x0f HEX_Table LCD_Char tmp1, w 0x0f HEX_Table LCD_Char 0x00
Delay255
movlw goto movlw goto movlw goto movlw goto movlw movwf movlw movwf movlw movwf
0xff d0 d'100' d0 d'50' d0 d'20' d0 0x05 count1 0xC7 counta 0x01 countb
decfsz goto decfsz goto
counta, f $+2 countb, f Delay_0
decfsz goto
count1 d1
Delay100 Delay50 Delay20 Delay5 d0 d1
;delay 255 mS ;delay 100mS ;delay 50mS ;delay 20mS ;delay 5.000 ms (4 MHz clock) ;delay 1mS
Delay_0
,f
Pulse_e
retlw
0x00
bsf nop bcf retlw
LCD_PORT, LCD_E LCD_PORT, LCD_E 0x00
;end of LCD routines HEX_Table
ADDWF RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW
PCL 0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x41 0x42 0x43 0x44 0x45 0x46
Text
addwf retlw retlw retlw retlw retlw retlw
PCL, f 'H' 'e' 'l' 'l' 'o' 0x00
Text2
ADDWF RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW
PCL, f 'R' 'e' 'a' 'd' 'y' '.' '.' '.' 0x00
, f
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
LCD_CharD
Add 0x30 to a byte and send to the LCD (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 include "P16F628.inc" ERRORLEVEL 0, __config 0x3D18
-302
;tell assembler what chip we are using ;include the defaults for the chip ;suppress bank selection messages ;sets the configuration settings (oscillator type
etc.)
cblock
0x20 count count1 counta
;start of general purpose registers ;used in looping routines ;used in delay routine ;used in delay routine
countb tmp1 tmp2 templcd templcd2
;used in delay routine ;temporary storage
NumL NumH
;Binary inputs for decimal convert routine
TenK Thou Hund Tens Ones
;Decimal outputs from convert routine
;temp store for 4 bit mode
endc LCD_PORT LCD_TRIS LCD_RS LCD_RW LCD_E
Equ Equ Equ Equ Equ
PORTA TRISA 0x04 0x06 0x07
org
0x0000
movlw movwf
0x07 CMCON
Initialise
clrf clrf clrf clrf clrf
count PORTA PORTB NumL NumH
SetPorts
bsf movlw movwf bcf
STATUS, 0x00 LCD_TRIS STATUS,
call
LCD_Init
;setup LCD
clrf movf call xorlw btfsc goto call incf goto
count count, w Text 0x00 STATUS, Z NextMessage LCD_Char count, f Message
;set counter register to zero ;put counter value in W ;get a character from the text table ;is it a zero?
call
LCD_Line2
;move to 2nd row, first column
call movf call movf call movf call movf
Convert TenK, w LCD_CharD Thou, w LCD_CharD Hund, w LCD_CharD Tens, w
;convert to decimal ;display decimal characters ;using LCD_CharD to convert to ASCII
Message
NextMessage
;LCD handshake lines
;turn comparators off (make it like a 16F84)
RP0
;select bank 1 ;make all pins outputs
RP0
;select bank 0
Next
call movf call movlw call movf call movf call incfsz goto incf call goto
LCD_CharD Ones, w LCD_CharD ' ' LCD_Char NumH, w LCD_HEX NumL, w LCD_HEX NumL, f Next NumH, f Delay255 NextMessage
;display a 'space' ;and counter in hexadecimal
;wait so you can see the digits change
;Subroutines and text tables ;LCD routines ;Initialise LCD LCD_Init call
Delay100
;wait for LCD to settle
movlw call
0x20 LCD_Cmd
;Set 4 bit mode
movlw call
0x28 LCD_Cmd
;Set display shift
movlw call
0x06 LCD_Cmd
;Set display character mode
movlw call
0x0c LCD_Cmd
;Set display on/off and cursor command ;Set cursor off
call
LCD_Clr
;clear display
retlw
0x00
; command set routine LCD_Cmd movwf swapf andlw movwf bcf call
LCD_CharD LCD_Char
templcd templcd, w 0x0f LCD_PORT LCD_PORT, LCD_RS Pulse_e
;send upper nibble ;clear upper 4 bits of W ;RS line to 0 ;Pulse the E line high
movf andlw movwf bcf call call retlw
templcd, w 0x0f LCD_PORT LCD_PORT, LCD_RS Pulse_e Delay5 0x00
;send lower nibble ;clear upper 4 bits of W
addlw movwf swapf andlw movwf bsf call
0x30 templcd templcd, w 0x0f LCD_PORT LCD_PORT, LCD_RS Pulse_e
;add 0x30 to convert to ASCII
;RS line to 0 ;Pulse the E line high
;send upper nibble ;clear upper 4 bits of W ;RS line to 1 ;Pulse the E line high
movf andlw movwf bsf call call retlw
templcd, w 0x0f LCD_PORT LCD_PORT, LCD_RS Pulse_e Delay5 0x00
;send lower nibble ;clear upper 4 bits of W
LCD_Line1
movlw call retlw
0x80 LCD_Cmd 0x00
;move to 1st row, first column
LCD_Line2
movlw call retlw
0xc0 LCD_Cmd 0x00
;move to 2nd row, first column
LCD_Line1W
addlw call retlw
0x80 LCD_Cmd 0x00
;move to 1st row, column W
LCD_Line2W
addlw call retlw
0xc0 LCD_Cmd 0x00
;move to 2nd row, column W
LCD_CurOn
movlw call retlw
0x0d LCD_Cmd 0x00
;Set display on/off and cursor command
LCD_CurOff
movlw call retlw
0x0c LCD_Cmd 0x00
;Set display on/off and cursor command
LCD_Clr
movlw call retlw
0x01 LCD_Cmd 0x00
;Clear display
LCD_HEX
movwf swapf andlw call call movf andlw call call retlw
tmp1 tmp1, w 0x0f HEX_Table LCD_Char tmp1, w 0x0f HEX_Table LCD_Char 0x00
Delay255
movlw goto movlw goto movlw goto movlw goto movlw movwf movlw movwf movlw movwf
0xff d0 d'100' d0 d'50' d0 d'20' d0 0x05 count1 0xC7 counta 0x01 countb
decfsz
counta, f
Delay100 Delay50 Delay20 Delay5 d0 d1
Delay_0
;RS line to 1 ;Pulse the E line high
;delay 255 mS ;delay 100mS ;delay 50mS ;delay 20mS ;delay 5.000 ms (4 MHz clock) ;delay 1mS
Pulse_e
goto decfsz goto
$+2 countb, f Delay_0
decfsz goto retlw
count1 d1 0x00
bsf nop bcf retlw
LCD_PORT, LCD_E
,f
LCD_PORT, LCD_E 0x00
;end of LCD routines HEX_Table
ADDWF RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW
PCL 0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x41 0x42 0x43 0x44 0x45 0x46
Text
addwf retlw retlw retlw retlw retlw retlw retlw retlw retlw retlw retlw retlw retlw retlw retlw retlw
PCL, f '1' '6' ' ' 'B' 'i' 't' ' ' 'C' 'o' 'u' 'n' 't' 'e' 'r' '.' 0x00
, f
;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 andlw addwf addwf addwf addlw movwf addwf addwf
NumH,w 0X0F Hund,f Hund,f Ones,f 0XE9 Tens Tens,f Tens,f
swapf andlw addwf addwf
NumL,w 0X0F Tens,f Ones,f
rlf rlf comf rlf
Tens,f Ones,f Ones,f Ones,f
movf andlw addwf rlf
NumL,w 0X0F Ones,f Thou,f
movlw movwf
0X07 TenK ; ; ; ; ; ; ; ;
movlw
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.
0X0A
Lb1: addwf decf btfss goto
Ones,f Tens,f 3,0 Lb1
addwf decf btfss goto
Tens,f Hund,f 3,0 Lb2
addwf decf btfss goto
Hund,f Thou,f 3,0 Lb3
addwf decf btfss goto
Thou,f TenK,f 3,0 Lb4
retlw
0x00
Lb2:
Lb3:
Lb4:
; Ten
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 include "P16F628.inc" ERRORLEVEL 0, __config 0x3D18
-302
;tell assembler what chip we are using ;include the defaults for the chip ;suppress bank selection messages ;sets the configuration settings (oscillator type
etc.)
cblock
0x20 count count1 counta countb tmp1 tmp2 templcd templcd2
;start of general purpose registers ;used in looping routines ;used in delay routine ;used in delay routine ;used in delay routine ;temporary storage
NumL NumH
;Binary inputs for decimal convert routine
TenK Thou Hund Tens Ones
;Decimal outputs from convert routine
;temp store for 4 bit mode
endc LCD_PORT LCD_TRIS LCD_RS LCD_RW LCD_E
HEX_Table
Equ Equ Equ Equ Equ
PORTA TRISA 0x04 0x06 0x07
org goto
0x0000 Start
ADDWF RETLW RETLW RETLW
PCL 0x30 0x31 0x32
;LCD handshake lines
, f
RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW RETLW
0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x41 0x42 0x43 0x44 0x45 0x46
Text
addwf retlw retlw retlw retlw retlw retlw retlw retlw retlw retlw retlw retlw retlw retlw retlw retlw
PCL, f '1' '6' ' ' 'B' 'i' 't' ' ' 'C' 'o' 'u' 'n' 't' 'e' 'r' '.' 0x00
Start
movlw movwf
0x07 CMCON
Initialise
clrf clrf clrf clrf clrf
count PORTA PORTB NumL NumH
SetPorts
bsf movlw movwf movwf bcf
STATUS, 0x00 LCD_TRIS TRISB STATUS,
call
LCD_Init
;setup LCD
clrf movf call xorlw btfsc goto call incf goto
count count, w Text 0x00 STATUS, Z NextMessage LCD_Char count, f Message
;set counter register to zero ;put counter value in W ;get a character from the text table ;is it a zero?
Message
;turn comparators off (make it like a 16F84)
RP0
;select bank 1 ;make all pins outputs
RP0
;select bank 0
NextMessage
Next
movlw call
d'2' LCD_Line2W
call movf call movf call movf call movf call movf call movlw call movf call movf call incfsz goto incf call goto
Convert TenK, w LCD_CharD Thou, w LCD_CharD Hund, w LCD_CharD Tens, w LCD_CharD Ones, w LCD_CharD ' ' LCD_Char NumH, w LCD_HEX NumL, w LCD_HEX NumL, f Next NumH, f Delay255 NextMessage
;move to 2nd row, third column ;convert to decimal ;display decimal characters ;using LCD_CharD to convert to ASCII
;display a 'space' ;and counter in hexadecimal
;wait so you can see the digits change
;Subroutines and text tables ;LCD routines ;Initialise LCD LCD_Init call
LCD_Busy
;wait for LCD to settle
movlw call
0x20 LCD_Cmd
;Set 4 bit mode
movlw call
0x28 LCD_Cmd
;Set display shift
movlw call
0x06 LCD_Cmd
;Set display character mode
movlw call
0x0c LCD_Cmd
;Set display on/off and cursor command ;Set cursor off
call
LCD_Clr
;clear display
retlw
0x00
; command set routine LCD_Cmd movwf swapf andlw movwf bcf call movf andlw movwf bcf call
templcd templcd, w 0x0f LCD_PORT LCD_PORT, LCD_RS Pulse_e templcd, w 0x0f LCD_PORT LCD_PORT, LCD_RS Pulse_e
;send upper nibble ;clear upper 4 bits of W ;RS line to 0 ;Pulse the E line high ;send lower nibble ;clear upper 4 bits of W ;RS line to 0 ;Pulse the E line high
call retlw
LCD_Busy 0x00
addlw movwf swapf andlw movwf bsf call
0x30 templcd templcd, w 0x0f LCD_PORT LCD_PORT, LCD_RS Pulse_e
movf andlw movwf bsf call call retlw
templcd, w 0x0f LCD_PORT LCD_PORT, LCD_RS Pulse_e LCD_Busy 0x00
;send lower nibble ;clear upper 4 bits of W
LCD_Line1
movlw call retlw
0x80 LCD_Cmd 0x00
;move to 1st row, first column
LCD_Line2
movlw call retlw
0xc0 LCD_Cmd 0x00
;move to 2nd row, first column
LCD_Line1W
addlw call retlw
0x80 LCD_Cmd 0x00
;move to 1st row, column W
LCD_Line2W
addlw call retlw
0xc0 LCD_Cmd 0x00
;move to 2nd row, column W
LCD_CurOn
movlw call retlw
0x0d LCD_Cmd 0x00
;Set display on/off and cursor command
LCD_CurOff
movlw call retlw
0x0c LCD_Cmd 0x00
;Set display on/off and cursor command
LCD_Clr
movlw call retlw
0x01 LCD_Cmd 0x00
;Clear display
LCD_HEX
movwf swapf andlw call call movf andlw call call retlw
tmp1 tmp1, w 0x0f HEX_Table LCD_Char tmp1, w 0x0f HEX_Table LCD_Char 0x00
Delay255
movlw goto movlw goto movlw
0xff d0 d'100' d0 d'50'
LCD_CharD LCD_Char
Delay100 Delay50
;add 0x30 to convert to ASCII ;send upper nibble ;clear upper 4 bits of W ;RS line to 1 ;Pulse the E line high
;RS line to 1 ;Pulse the E line high
;delay 255 mS ;delay 100mS ;delay 50mS
Delay20 Delay5 d0 d1
goto movlw goto movlw movwf movlw movwf movlw movwf
d0 d'20' d0 0x05 count1 0xC7 counta 0x01 countb
decfsz goto decfsz goto
counta, f $+2 countb, f Delay_0
decfsz goto retlw
count1 d1 0x00
bsf nop bcf retlw
LCD_PORT, LCD_E
;delay 20mS ;delay 5.000 ms (4 MHz clock) ;delay 1mS
Delay_0
Pulse_e
,f
LCD_PORT, LCD_E 0x00
LCD_Busy bsf movlw movwf bcf bcf bsf bsf swapf bcf movwf bsf bcf btfsc goto bcf bsf movlw movwf bcf return
STATUS, RP0 0x0f LCD_TRIS STATUS, RP0 LCD_PORT, LCD_RS LCD_PORT, LCD_RW LCD_PORT, LCD_E LCD_PORT, w LCD_PORT, LCD_E templcd2 LCD_PORT, LCD_E LCD_PORT, LCD_E templcd2, 7 LCD_Busy LCD_PORT, LCD_RW STATUS, RP0 0x00 LCD_TRIS STATUS, RP0
;set bank 1 ;set Port for input ;set bank 0 ;set LCD for command mode ;setup to read busy flag ;read upper nibble (busy flag)
;dummy read of lower nibble ;check busy flag, high = busy ;if busy check again ;set bank 1 ;set Port for output ;set bank 0
;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 andlw addwf addwf addwf addlw movwf addwf addwf
NumH,w 0X0F Hund,f Hund,f Ones,f 0XE9 Tens Tens,f Tens,f
swapf andlw addwf addwf
NumL,w 0X0F Tens,f Ones,f
rlf rlf comf rlf
Tens,f Ones,f Ones,f Ones,f
movf andlw addwf rlf
NumL,w 0X0F Ones,f Thou,f
movlw movwf
0X07 TenK ; ; ; ; ; ; ; ;
movlw
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.
0X0A
Lb1: addwf decf btfss goto
Ones,f Tens,f 3,0 Lb1
addwf decf btfss goto
Tens,f Hund,f 3,0 Lb2
addwf decf btfss goto
Hund,f Thou,f 3,0 Lb3
addwf decf btfss goto
Thou,f TenK,f 3,0 Lb4
retlw
0x00
Lb2:
Lb3:
Lb4:
end
; Ten