一、 目的与要求
制作一个温湿度实时显示小系统,响应时间控制在1s左右,探头部分能够放进大约10cm直径的容器中。
二、 工作原理
采用AT89S52单片机作为本系统的控制单元,传感器采用DHT90(即把SHT10封装为4个2.54标准插针的独立结构,大连北方测控代理 的价格为50元/片),显示模块为LCD1602。单片机收到来自DHT90的温湿度数据,再经过软件线性拟合,最后送到1602上显示并没隔1s刷新一 次数据。
三、 原理图
图1 温湿度显示系统原理图 (原文件名:图1.jpg)
四、 硬件设计
电源部分采用+5V开关电源集成模块(实测输出电压5.19V),这符合DHT90和AT89S52单片机的供电要求。本系统电路设计较简单,难点至于软件编写与调试。
五、 软件设计
5.1 流程图
软件流程图 (原文件名:流程图.jpg)
5.2 软件调试过程及经验
对于本系统,我首先调试1602的显示程序,重点理解时序图。对硬件理解清楚后,参照相关程序,编写自己规范的程序。然后,调试 DHT90数据通信显示,在SHT11(DHT90/SHT10也属于同系列)例程中,显示功能是利用51单片机串口向上位机发数据,在上位机上显示温湿 度。所以,这部分在我的程序中就去掉了。同样,DHT90程序部分的关键也是时序图,读明白时序图,就可以很快得进入编程调试阶段。
六、 系统实物图
图2 单片机控制及显示部分 (原文件名:图2.jpg)
图3 LCD1602显示特写 (原文件名:图3.jpg)
图4 传感器部分特写(准备封装为一个小的探头) (原文件名:图4.jpg)
七、 菜鸟的小结
在两个多月的时间里,我从零开始做这个温湿度测量小系统,现在终于有了初步的成果。在这期间,我学到了很多东西,比如说焊接技术(当然还是比较 菜),下载线制作等等。我觉得最重要的一点是锻炼了自己的实践能力。嗯,很多东西难与不难就看你做没做过。呵呵,套用毛主席他老人家的一句话就是:没有实 践就没有发言权。一开始,做下载线,我感觉很难很难,但现在回头一看,那就是小case而已。
通过这个小系统的制作,我从一个“理想的电子爱好者”转变为一个“实践的电子爱好者”。从享受电子成品的快乐到享受电子DIY过程的快乐,这是一直升华,DIY很美妙。最后一句与所有DIY爱好者共勉。
最后,欢迎大家交流学习,给我批评指正!
[code="CSHARP"]
/**
* ;功能说明:DHT90与LCD1602的温湿度显示
* ;文件名称:DHT90.c
*
* ;微处理器:AT89S52
* ;编译环境:Keil uVision V2.38a
*
* ;作 者:
* ;版 权:(c)
* ;创建日期:2008.08.15
* ;版 本:V1.2
* ;修改日期:2008.08.16
* ;修改说明:添加了一个LCD_disp_str()函数
* ;**********************************************************************************
*/
/**
* ************定义接口********************
* P0------DB0~DB7 (LCD1602)
* P2.0------RS (LCD1602)
* P2.1------RW (LCD1602)
* P2.2------E (LCD1602)
* P2.6------SCK (DHT90)
* P2.7------DATA (DHT90)
*/
#include <AT89x51.h>
#include <intrins.h>
#include <math.h> //Keil library
#include <stdio.h> //Keil library
// *********************第一部分LCD1602设置 START****************************************
#define LCD_DB P0
sbit LCD_RS = P2 ^ 0; //P2^0是p2.0的意思;LCD_RS与P2.0等效起来,对LCD_RS 读写,就是对P2.0读写 好处在于LCD_RS含义直接明了,写程序多了就会知道有必要de
sbit LCD_RW = P2 ^ 1; //P2^1是p2.1的意思
sbit LCD_E = P2 ^ 2; //P2^2是p2.2的意思
/**
* *****定义函数***************
*/
// define uchar unsigned char
// define uint unsigned int
void LCD_init(void); //初始化函数
void LCD_write_command(uchar command); //写指令函数
void LCD_write_data(uchar dat); //写数据函数
void LCD_disp_char(uchar x, uchar y, uchar dat); //在某个屏幕位置上显示一个字符,X(0-15),y(1-2)
void LCD_disp_str(uchar x, uchar y, uchar * str); //LCD1602显示字符串函数
void delay_n10us(uint n); //延时函数
/**
* --------------------------------------
* ;模块名称:LCD_init();
* ;功 能:初始化LCD1602
* ;占用资源:--
* ;参数说明:--
* ;创建日期:2008.08.15
* ;版 本:FV1.0(函数版本Function Version)
* ;修改日期:--
* ;修改说明:--
* ;-------------------------------------
*/
void LCD_init(void) {
delay_n10us(10);
LCD_write_command(0x38); //设置8位格式,2行,5x7
delay_n10us(10);
LCD_write_command(0x0c); //整体显示,关光标,不闪烁
delay_n10us(10);
LCD_write_command(0x06); //设定输入方式,增量不移位
delay_n10us(10);
LCD_write_command(0x01); //清除屏幕显示
delay_n10us(100); //延时清屏,延时函数,延时约n个10us
}
/**
* --------------------------------------
* ;模块名称:LCD_write_command();
* ;功 能:LCD1602写指令函数
* ;占用资源: P2.0--RS(LCD_RS),P2.1--RW(LCD_RW),P2.2--E(LCD_E).
* ;参数说明:dat为写命令参数
* ;创建日期:2008.08.15
* ;版 本:FV1.0(函数版本Function Version)
* ;修改日期:--
* ;修改说明:--
* ;-------------------------------------
*/
void LCD_write_command(uchar dat) {
delay_n10us(10);
LCD_RS = 0; //指令
LCD_RW = 0; //写入
LCD_E = 1; //允许
LCD_DB = dat;
delay_n10us(10); //实践证明,我的LCD1602上,用for循环1次就能完成普通写指令。
LCD_E = 0;
delay_n10us(10); //实践证明,我的LCD1602上,用for循环1次就能完成普通写指令。
}
/**
* --------------------------------------
* ;模块名称:LCD_write_data();
* ;功 能:LCD1602写数据函数
* ;占用资源: P2.0--RS(LCD_RS),P2.1--RW(LCD_RW),P2.2--E(LCD_E).
* ;参数说明:dat为写数据参数
* ;创建日期:2008.08.15
* ;版 本:FV1.0(函数版本Function Version)
* ;修改日期:--
* ;修改说明:--
* ;-------------------------------------
*/
void LCD_write_data(uchar dat) {
delay_n10us(10);
LCD_RS = 1; //数据
LCD_RW = 0; //写入
LCD_E = 1; //允许
LCD_DB = dat;
delay_n10us(10);
LCD_E = 0;
delay_n10us(10);
}
/**
* --------------------------------------
* ;模块名称:LCD_disp_char();
* ;功 能:LCD1602显示一个字符函数,在某个屏幕位置上显示一个字符,X(0-15),y(1-2)。
;占用资源:--
* ;参数说明:X为1602的列值(取值范围是0-15),y为1602的行值(取值范围是1-2),dat为所要显示字符对应的地址参数。
;创建日期:2008.08.15
* ;版 本:FV1.0(函数版本Function Version)
* ;修改日期:--
* ;修改说明:--
* ;-------------------------------------
*/
void LCD_disp_char(uchar x, uchar y, uchar dat) {
uchar address;
if (y == 1)
address = 0x80 + x;
else
address = 0xc0 + x;
LCD_write_command(address);
LCD_write_data(dat);
}
/**
* --------------------------------------
* ;模块名称:LCD_disp_str();
* ;功 能:LCD1602显示字符串函数,在某个屏幕起始位置{X(0-15),y(1-2)}上显示一个字符串。
;占用资源:--
* ;参数说明:X为1602的列值(取值范围是0-15),y为1602的行值(取值范围是1-2),str为所要显示字符串对应的指针参数。
;创建日期:2008.08.16
* ;版 本:FV1.0(函数版本Function Version)
* ;修改日期:--
* ;修改说明:--
* ;-------------------------------------
*/
void LCD_disp_str(uchar x, uchar y, uchar * str) {
uchar address;
if (y == 1)
address = 0x80 + x;
else
address = 0xc0 + x;
LCD_write_command(address);
while (* str != '\0') {
LCD_write_data(* str);
str++;
}
}
/**
* --------------------------------------
* ;模块名称:delay_n10us();
* ;功 能:延时函数,延时约n个10us
* ;占用资源:--
* ;参数说明:--
* ;创建日期:2008.08.15
* ;版 本:FV1.1(函数版本Function Version)
* ;修改日期:2008.08.16
* ;修改说明:修改为较精确的延时函数
;-------------------------------------
*/
void delay_n10us(uint n) {
uint i;
for(i = n;i > 0;i--) {
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_(); //延时10us@12M晶振
}
}
// *********************第一部分LCD1602设置 END****************************************
// *********************第二部分DHT90设置 START****************************************
sbit SCK = P2 ^ 6; //定义通讯时钟端口
sbit DATA = P2 ^ 7; //定义通讯数据端口
typedef union {
unsigned int i; //定义了两个共用体
float f;
}
value;
enum {
TEMP, HUMI}; //TEMP=0,HUMI=1
// define noACK 0 //用于判断是否结束通讯
// define ACK 1 //结束数据传输
// adr command r/w
// define STATUS_REG_W 0x06 //000 0011 0
// define STATUS_REG_R 0x07 //000 0011 1
// define MEASURE_TEMP 0x03 //000 0001 1
// define MEASURE_HUMI 0x05 //000 0010 1
// define RESET 0x1e //000 1111 0
/**
* ***************定义函数***************
*/
void s_transstart(void); //启动传输函数
void s_connectionreset(void); //连接复位函数
char s_write_byte(unsigned char value); //DHT90写函数
char s_read_byte(unsigned char ack); //DHT90读函数
char s_measure(unsigned char * p_value, unsigned char * p_checksum, unsigned char mode); //测量温湿度函数
void calc_dht90(float * p_humidity , float * p_temperature); //温湿度补偿
/**
* --------------------------------------
* ;模块名称:s_transstart();
* ;功 能:启动传输函数
* ;占用资源:--
* ;参数说明:--
* ;创建日期:2008.08.15
* ;版 本:FV1.0(函数版本Function Version)
* ;修改日期:--
* ;修改说明:--
* ;-------------------------------------
*/
void s_transstart(void)
// generates a transmission start
// _____ ________
// DATA: |_______|
// ___ ___
{ // SCK : ___| |___| |______
DATA = 1;
SCK = 0; //Initial state
_nop_();
SCK = 1;
_nop_();
DATA = 0;
_nop_();
SCK = 0;
_nop_();
_nop_();
_nop_();
SCK = 1;
_nop_();
DATA = 1;
_nop_();
SCK = 0;
}
/**
* --------------------------------------
* ;模块名称:s_connectionreset();
* ;功 能:连接复位函数
* ;占用资源:--
* ;参数说明:--
* ;创建日期:2008.08.15
* ;版 本:FV1.0(函数版本Function Version)
* ;修改日期:--
* ;修改说明:--
* ;-------------------------------------
*/
void s_connectionreset(void)
// communication reset: DATA-line=1 and at least 9 SCK cycles followed by transstart
// _____________________________________________________ ________
// DATA: |_______|
// _ _ _ _ _ _ _ _ _ ___ ___
{ // SCK : __| |__| |__| |__| |__| |__| |__| |__| |__| |______| |___| |______
unsigned char i;
DATA = 1;
SCK = 0; //Initial state
for(i = 0;i < 9;i++) { // 9 SCK cycles
SCK = 1;
SCK = 0;
}
s_transstart(); //transmission start
}
/**
* --------------------------------------
* ;模块名称:s_write_byte();
* ;功 能:DHT90写函数
;占用资源:--
* ;参数说明:--
* ;创建日期:2008.08.15
* ;版 本:FV1.0(函数版本Function Version)
* ;修改日期:--
* ;修改说明:--
* ;-------------------------------------
*/
char s_write_byte(unsigned char value)
// ----------------------------------------------------------------------------------
{ // writes a byte on the Sensibus and checks the acknowledge
unsigned char i, error = 0;
for (i = 0x80;i > 0;i /= 2) { // shift bit for masking
if (i &value) DATA = 1; //masking value with i , write to SENSI-BUS
else DATA = 0;
SCK = 1; //clk for SENSI-BUS
_nop_();
_nop_();
_nop_(); //pulswith approx. 5 us
SCK = 0;
}
DATA = 1; //release DATA-line
SCK = 1; //clk #9 for ack
error = DATA; //check ack (DATA will be pulled down by DHT90),DATA在第9个上升沿将被DHT90自动下拉为低电平。
_nop_();
_nop_();
_nop_();
SCK = 0;
DATA = 1; //release DATA-line
return error; //error=1 in case of no acknowledge //返回:0成功,1失败
}
/**
* --------------------------------------
* ;模块名称:s_read_byte();
* ;功 能:DHT90读函数
;占用资源:--
* ;参数说明:--
* ;创建日期:2008.08.15
* ;版 本:FV1.0(函数版本Function Version)
* ;修改日期:--
* ;修改说明:--
* ;-------------------------------------
*/
char s_read_byte(unsigned char ack)
{ // reads a byte form the Sensibus and gives an acknowledge in case of "ack=1"
unsigned char i, val = 0;
DATA = 1; //release DATA-line
for (i = 0x80;i > 0;i /= 2) { // shift bit for masking
SCK = 1; //clk for SENSI-BUS
if (DATA) val = (val | i); //read bit
_nop_();
_nop_();
_nop_(); //pulswith approx. 5 us
SCK = 0;
}
if (ack == 1)DATA = 0; //in case of "ack==1" pull down DATA-Line
else DATA = 1; //如果是校验(ack==0),读取完后结束通讯
_nop_();
_nop_();
_nop_(); //pulswith approx. 5 us
SCK = 1; //clk #9 for ack
_nop_();
_nop_();
_nop_(); //pulswith approx. 5 us
SCK = 0;
_nop_();
_nop_();
_nop_(); //pulswith approx. 5 us
DATA = 1; //release DATA-line
return val;
}
/**
* --------------------------------------
* ;模块名称:s_measure();
* ;功 能:测量温湿度函数
;占用资源:--
* ;参数说明:--
* ;创建日期:2008.08.15
* ;版 本:FV1.0(函数版本Function Version)
* ;修改日期:--
* ;修改说明:--
* ;-------------------------------------
*/
char s_measure(unsigned char * p_value, unsigned char * p_checksum, unsigned char mode)
{ // makes a measurement (humidity/temperature) with checksum
unsigned error = 0;
unsigned int i;
s_transstart(); //transmission start
switch (mode) { // send command to sensor
case TEMP : error += s_write_byte(MEASURE_TEMP);
break;
case HUMI : error += s_write_byte(MEASURE_HUMI);
break;
default : break;
}
for (i = 0;i < 65535;i++) if (DATA == 0) break; //wait until sensor has finished the measurement
if (DATA) error += 1; // or timeout (~2 sec.) is reached * (p_value) = s_read_byte(ACK); //read the first byte (MSB) * (p_value + 1) = s_read_byte(ACK); //read the second byte (LSB) * p_checksum = s_read_byte(noACK); //read checksum
return error;
}
/**
* --------------------------------------
* ;模块名称:calc_dht90();
* ;功 能:温湿度补偿函数
;占用资源:--
* ;参数说明:--
* ;创建日期:2008.08.15
* ;版 本:FV1.0(函数版本Function Version)
* ;修改日期:--
* ;修改说明:--
* ;-------------------------------------
*/
void calc_dht90(float * p_humidity , float * p_temperature)
// calculates temperature [C] and humidity [%RH]
// input : humi [Ticks] (12 bit)
// temp [Ticks] (14 bit)
// output: humi [%RH]
{ // temp [C]
const float C1 = -4.0; // for 12 Bit
const float C2 = + 0.0405; // for 12 Bit
const float C3 = -0.0000028; // for 12 Bit
const float T1 = + 0.01; // for 14 Bit @ 5V
const float T2 = + 0.00008; // for 14 Bit @ 5V
float rh = * p_humidity; // rh: Humidity [Ticks] 12 Bit
float t = * p_temperature; // t: Temperature [Ticks] 14 Bit
float rh_lin; // rh_lin: Humidity linear
float rh_true; // rh_true: Temperature compensated humidity
float t_C; // t_C : Temperature [C]
t_C = t * 0.01 - 40; //calc. temperature from ticks to [C]
rh_lin = C3 * rh * rh + C2 * rh + C1; //calc. humidity from ticks to [%RH]
rh_true = (t_C-25) * (T1 + T2 * rh) + rh_lin; //calc. temperature compensated humidity [%RH]
if (rh_true > 100)rh_true = 100; //cut if the value is outside of
if (rh_true < 0.1)rh_true = 0.1; //the physical possible range
* p_temperature = t_C; //return temperature [C] * p_humidity = rh_true; //return humidity[%RH]
}
// *********************第二部分DHT90设置 END****************************************
// *********主函数*****************
void main(void) {
value humi_val, temp_val;
unsigned char error, checksum;
unsigned int wendu, shidu;
LCD_init();
s_connectionreset();
LCD_disp_str(0, 1, "TE");
LCD_disp_str(0, 2, "RH");
// *********初始化温度显示区*********
LCD_disp_str(2, 1, "TTT.TC");
// *********初始化湿度显示区*********
LCD_disp_str(2, 2, "RRR.R%");
delay_n10us(20000); //延时0.2s
while (1) {
error = 0;
error += s_measure((unsigned char *) &humi_val . i, &checksum, HUMI); //measure humidity
error += s_measure((unsigned char *) &temp_val . i, &checksum, TEMP); //measure temperature
if (error != 0) s_connectionreset(); //in case of an error: connection reset
else {
humi_val . f = (float)humi_val . i; //converts integer to float
temp_val . f = (float)temp_val . i; //converts integer to float
calc_dht90(&humi_val . f, &temp_val . f); //calculate humidity, temperature
wendu = 10 * temp_val . f;
LCD_disp_char(2, 1, wendu / 1000 + '0'); //显示温度百位
LCD_disp_char(3, 1, (wendu % 1000) / 100 + '0'); //显示温度十位
LCD_disp_char(4, 1, (wendu % 100) / 10 + '0'); //显示温度个位
LCD_disp_char(6, 1, (wendu % 10) + '0'); //显示温度小数点后第一位
shidu = 10 * humi_val . f;
LCD_disp_char(2, 2, shidu / 1000 + '0'); //显示湿度百位
LCD_disp_char(3, 2, (shidu % 1000) / 100 + '0'); //显示湿度十位
LCD_disp_char(4, 2, (shidu % 100) / 10 + '0'); //显示湿度个位
LCD_disp_char(6, 2, (shidu % 10) + '0'); //显示湿度小数点后第一位
}
// ----------wait approx. 0.8s to avoid heating up SHTxx------------------------------
delay_n10us(80000); //延时约0.8s
}
}
[/code]