环境温湿度监控系统(51+DHT11+1602液晶)

一.原理图

Schematic

二.1602显示模块

模块头文件:引脚定义,接口定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef _1602_H_
#define _1602_H_

#include <reg51.h>
#include <intrins.h>
#define LCD_Bus P2 //LCD1602数据总线
sbit RS = P0^7; // LCD数据/命令选择端
sbit RW = P0^6; // LCD读写选择端
sbit EN = P0^5; // LCD使能端,高脉冲有效

sbit busy_led1 = P1^0; //LCD忙指示灯

extern void Lcd_Init(void); //1602初始化函数
extern void Lcd_WriteData(unsigned char Data); //写数据
extern void Lcd_WriteCmd(unsigned char Cmd); //写指令
extern void Lcd_WriteStr(unsigned char *Str); //写一串字符串

#endif

函数实现见GIT仓库或参考SMC1602 Datasheet编写。

三.DHT11模块

模块头文件:引脚定义,接口定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef _DHT11_H_
#define _DHT11_H_

#include <reg51.h>
#include <intrins.h>

sbit DHT11_Data = P0^4; //dht11数据端口

sbit NOresponse_led2 = P1^1;
sbit start_led3 = P1^2;
sbit ERRORREVISE_LED4 = P1^3;
sbit test_led5 = P1^4;
sbit ASK_LED6 = P1^5;
sbit rec_byte_led7 = P1^6;
sbit rec_40_led8 = P1^7;

/* DHT11 测量范围 20~90%RH(5%RH) 0~50C(2C) 工作电压3~ 5.5V*/
extern unsigned char rec_dat[16]; //全局变量 用于显示的接收数据数组
extern unsigned char RH,TH;
//extern void DHT11_delay_us(unsigned char us); //11.0592大概每次循环延时4.3us
extern void DHT11_delay_ms(unsigned int ms); //ms延时
extern void DHT11_rec_40(void); //接收40位全部数据
#endif

函数实现见GIT仓库或参考DHT11 Datasheet编写。

四.功能模块

主函数

1
2
3
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
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
#include <reg51.h>
#include <intrins.h>
#include "1602.h"
#include "DHT11.h"
#define uchar unsigned char
#define uint unsigned int
/*------------按键引脚---------------*/
sbit ok_SW1 = P3^5; //确定-SW1
sbit add_SW2 = P3^4;//增加-SW2
sbit sub_SW3 = P3^3;//减少-SW3
sbit sw_SW4 = P3^2; //选择-SW4
/*------------蜂鸣器引脚---------------*/
sbit bell = P3^6;
/*------------变量声明---------------*/
uchar HTemp,LTemp,HRH,LRH,set;
/*------------函数声明---------------*/
unsigned char Key_Scan();
void Init_Alarm_Timer0();

int main ()
{
uchar i; //rec_dat[]数组显示用
DHT11_delay_ms(1500); //DHT11上电后要等待1S以越过不稳定状态在此期间不能发送任何指令
DHT11_rec_40();
Lcd_Init(); //Lcd初始化
HTemp=35,LTemp=15;HRH=80,LRH=20;set=2;
Init_Alarm_Timer0();
while(1)
{
switch(set)
{
case 1:
Lcd_WriteCmd(0x80);
Lcd_WriteStr("Press S1 to set ");
Lcd_WriteCmd(0x80 + 0x40);//设置数据指针到第二行首
for(i=0;i<16;i++){Lcd_WriteData(rec_dat[i]);} //显示数据
if(Key_Scan() == 1) {set = 2;}
DHT11_delay_ms(2500);
DHT11_rec_40();
break;
case 2:
Lcd_WriteCmd(0x80);
Lcd_WriteStr("Set the max RH ");
Lcd_WriteCmd(0x80 + 0x40);
Lcd_WriteStr("Maximum RH: ");
Lcd_WriteData('0'+(HRH/10));
Lcd_WriteData('0'+(HRH%10));
Lcd_WriteStr("% ");
switch(Key_Scan())
{
case 1:
set = 3;
break;
case 2:
HRH+=1;
break;
case 3:
HRH-=1;
break;
}
break;
case 3:
Lcd_WriteCmd(0x80);
Lcd_WriteStr("Set the min RH ");
Lcd_WriteCmd(0x80 + 0x40);
Lcd_WriteStr("Minimum RH: ");
Lcd_WriteData('0'+(LRH/10));
Lcd_WriteData('0'+(LRH%10));
Lcd_WriteStr("% ");
switch(Key_Scan())
{
case 1:
set = 4;
break;
case 2:
LRH+=1;
break;
case 3:
LRH-=1;
break;
}
break;
case 4:
Lcd_WriteCmd(0x80);
Lcd_WriteStr("Set the max Temp ");
Lcd_WriteCmd(0x80 + 0x40);
Lcd_WriteStr("Max Temp: ");
Lcd_WriteData('0'+(HTemp/10));
Lcd_WriteData('0'+(HTemp%10));
Lcd_WriteData('0'+175);
Lcd_WriteStr("C ");
switch(Key_Scan())
{
case 1:
set = 5;
break;
case 2:
HTemp+=1;
break;
case 3:
HTemp-=1;
break;
}
break;
case 5:
Lcd_WriteCmd(0x80);
Lcd_WriteStr("Set the min Temp ");
Lcd_WriteCmd(0x80 + 0x40);
Lcd_WriteStr("Min Temp: ");
Lcd_WriteData('0'+(LTemp/10));
Lcd_WriteData('0'+(LTemp%10));
Lcd_WriteData('0'+175);
Lcd_WriteStr("C ");
switch(Key_Scan())
{
case 1:
set = 1;
break;
case 2:
LTemp+=1;
break;
case 3:
LTemp-=1;
break;
}
break;
case 6:
Lcd_WriteCmd(0x80);
Lcd_WriteStr("Over Temp alarm ");
bell = ~bell;
if(Key_Scan()) { set = 4; }
break;
case 7:
Lcd_WriteCmd(0x80);
Lcd_WriteStr("Over RH alarm ");
bell = ~bell;
if(Key_Scan()) { set = 2; }
break;
default :
Lcd_WriteCmd(0x80);
Lcd_WriteStr("ERROR 101 ");
Lcd_WriteCmd(0x80 + 0x40);
Lcd_WriteStr("UNKNOW SET VALUE");
if(Key_Scan()) { set = 1; }
break;
}
}

return 0;
}

void Init_Alarm_Timer0(void)
{
TMOD |= 0x01; //使用工作模式1,16位定时器,使用"|"或等符号可以在使用多个定时器时不受影响
TH0 = (65535-46082) / 256; //给定初值50ms
TL0 = (65535-46082) % 256;
EA=1; //打开总中断
ET0=1; //定时器中断打开 T0的溢出中断允许位
TR0 = 1; //打开定时器开关
}

void Timer0_Alarm(void) interrupt 1
{
TH0 = (65535-46082) / 256; //重设初值
TL0 = (65535-46082) % 256;
if(set == 1)
{
if(TH<LTemp||TH>HTemp)
{
set = 6;
}else if(RH<LRH||RH>HRH)
{
set = 7;
}
}
}

unsigned char Key_Scan()
{
unsigned char keyValue = 0 , i; //保存键值
//--检测按键SW1--//
if (ok_SW1 != 1)//检测按键'确定-SW1'是否按下 按键被按下VCC上拉电阻接地,输入线线路电压0V
{
DHT11_delay_ms(10);//消除抖动
if (ok_SW1 != 1)//再次检测按键是否按下
{
keyValue = 1;
i = 0;
while ((i<50) && (ok_SW1 != 1)) //检测按键是否松开
{
DHT11_delay_ms(10);
i++;
}
}
}
//--检测按键SW2--//
if (add_SW2 != 1)//检测'增加-SW2'是否按下
{
DHT11_delay_ms(10);//消除抖动
if (add_SW2!= 1)//再次检测按键是否按下
{
keyValue = 2;
i = 0;
while ((i<50) && (add_SW2 != 1)) //检测按键是否松开
{
DHT11_delay_ms(10);
i++;
}
}
}
//--检测按键SW3--//
if (sub_SW3 != 1)//检测'减少-SW3'是否按下
{
DHT11_delay_ms(10);//消除抖动
if (sub_SW3 != 1)//再次检测按键是否按下
{
keyValue = 3;
i = 0;
while ((i<50) && (sub_SW3 != 1)) //检测按键是否松开
{
DHT11_delay_ms(10);
i++;
}
}
}
//--检测按键SW4--//
if (sw_SW4 != 1)//检测'选择-SW4'是否按下
{
DHT11_delay_ms(10);//消除抖动
if (sw_SW4!= 1)//再次检测按键是否按下
{
keyValue = 4;
i = 0;
while ((i<50) && (sw_SW4 != 1)) //检测按键是否松开
{
DHT11_delay_ms(10);
i++;
}
}
}
return keyValue; //将读取到键值的值返回
}

Tips:

nop指令的作用:
1)就是通过nop指令的填充(nop指令一个字节),使指令按字对齐,从而减少取指令时的内存访问次数。(一般用来内存地址偶数对齐,比如有一条指令,占3字节,这时候使用nop指令,cpu 就可以从第四个字节处读取指令了。)
2)通过nop指令产生一定的延迟,但是对于快速的CPU来说效果不明显,可以使用rep前缀,多延迟几个时钟。
3)i/o传输时,也会用一下 nop,等待缓冲区清空,总线恢复;
4)清除由上一个算术逻辑指令设置的flag位;

该函数是在51单片机中用的延时函数,表示执行一条没有什么意义的指令,延时一个指令周期,有的指令周期是两个或两个以上的机械周期,但是_nop_();指令需要的只是一个机械周期也就是12个时钟周期(震荡周期)。
51单片机中,1个机械周期 = 12个时钟周期 = 12 * ( 1 / f)。(f 为晶振频率)。
如果只用的是12MHZ的晶振,那么 一个机械周期就是1us;也就是说:
nop(); 指令的延迟时间为 1us。可以较为精确得控制延迟时间。