Proteus仿真DHT11和实际硬件的差异以及读不到数据原因分析

Proteus仿真DHT11和实际硬件的差异以及读不到数据原因分析


在这里插入图片描述

在Proteus仿真里面,仿真LCD1602显示DHT11数据,发现不能显示,有点尴尬,读取DHT11明明是按照数据手册上的时序来写的,为什么不能显示?

  • 第一种原因,可能是我们在使用延时函数的时候,习惯用下面这种函数来延时,这种函数在的延时精度很难保证。
  • Proteus中51单片机的默认时钟频率是12MHz。
1
2
3
4
5
void DHT11\_delay\_us(unsigned char n)
{
while(--n);
}

在调试阶段,在各个数据读取阶段设置标志位。

有时候真的不出数据,让人摸不着头脑。

  • 总时序

在单片机拉高电平时间在20-40us阶段,设置一个标志位,判断数据IO口状态是否被DHT拉低,如果拉低,则点亮Led灯,用来观察信号到底有没有拉低,这样很直观就可以看出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void DHT11\_start()
{
Temp_data=1;
Delayus(10);
Temp_data=0;
delayms(25);//这个延时不能过短,18ms以上,实际在仿真当中要想读到数据延时要在延时参数要在40以上才能出数据
Temp_data=1;
Delayus(50);//MCU释放总线20~40us,需要在40微秒到100微秒之间
if(Temp_data==0){
led=0;//检测从设备DHT拉低总线信号,作为响应信号
while(Temp_data==0); //等待DHT响应信号,80us内来拉高总线,跳过
Delayus(80); //50拉高后延时80us
while(Temp_data==1);等待DHT响应信号,80us内来拉低总线,等待拉低,跳过EF
}else{led=1;}

}

在这里插入图片描述

  • 读数据0的时序
    在这里插入图片描述
  • 读数据1的时序
    在这里插入图片描述

示例代码

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
/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
\* 实验名 :温度显示实验
\* 使用的IO :
\* 实验效果 :1602显示温度
\* 注意 :
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/

#include<reg51.h>
#include"lcd.h"
#include<intrins.h>
#include<stdio.h>

sbit Temp_data=P3^6;
/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
\* 函数名 : main
\* 函数功能 : 主函数
\* 输入 : 无
\* 输出 : 无
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
unsigned int rec_dat[4];

unsigned char rec_dat_lcd0[6];
unsigned char rec_dat_lcd1[6];
unsigned char rec_dat_lcd2[6];
unsigned char rec_dat_lcd3[6];

//定义
void DHT11\_delay\_us(unsigned char n);
void DHT11\_delay\_ms(unsigned int z);
void DHT11\_start();
unsigned char DHT11\_rec\_byte();
void DHT11\_receive();
void InitUART(void);


//主函数
void main()
{
InitUART();
P1=0xf0;
InitLcd1602();
LcdShowStr(0,0,"Humi:");
LcdShowStr(0,1,"Temp:");
EA = 1; //开总中断
while(1)
{
DHT11\_delay\_ms(150);
DHT11\_receive();
sprintf(rec_dat_lcd0,"%d",rec_dat[0]);
sprintf(rec_dat_lcd1,"%d",rec_dat[1]);
sprintf(rec_dat_lcd2,"%d",rec_dat[2]);
sprintf(rec_dat_lcd3,"%d",rec_dat[3]);
DHT11\_delay\_ms(100);

//湿度
LcdShowStr(6,0,rec_dat_lcd0);
LcdShowStr(8,0,".");
LcdShowStr(9,0,rec_dat_lcd1);
LcdShowStr(10,0," %");

//温度
LcdShowStr(6,1,rec_dat_lcd2);
LcdShowStr(8,1,".");
LcdShowStr(9,1,rec_dat_lcd3);
LcdShowStr(10,1,"\*C");

//下面通过串口助手打印温度
printf("Humi:%d.%d %%\n",rec_dat[0],rec_dat[1]);
printf("Temp:%d.%d ℃\n",rec_dat[2],rec_dat[3]);
DHT11\_delay\_ms(3000);
}
}



//DHT11起始信号

void DHT11\_start()
{
Temp_data=1;

DHT11\_delay\_us(10);

Temp_data=0;

DHT11\_delay\_ms(50);//这个延时不能过短,18ms以上,实际在仿真当中要想读到数据延时要在延时参数要在40以上才能出数据

Temp_data=1;

DHT11\_delay\_us(50);//这个延时不能过短


}

//接收一个字节


unsigned char DHT11\_rec\_byte()
{
unsigned char i,dat=0;
for(i=0; i<8; i++)
{
while(!Temp_data);//开始发送数据,50us
//proteus仿真里面下面的延时范围最好在10<delay<20,实际硬件电路需要30us到40us
DHT11\_delay\_us(20);//数据高电平阶段判断数据0还是1
dat <<=1;
if(Temp_data==1)
{
dat +=1;
}
while(Temp_data);
}
return dat;
}


//接收温湿度数据
void DHT11\_receive()
{
unsigned int R_H,R_L,T_H,T_L;
unsigned char RH,RL,TH,TL,revise;

DHT11\_start();
// Temp\_data=1;
if(Temp_data==0)//DHT拉低IO口,响应信号
{
while(Temp_data==0); //等待拉高80us
// DHT11\_delay\_us(40); //拉高后延时80us
while(Temp_data==1); //等待拉高80us
R_H=DHT11\_rec\_byte(); //接收湿度高八位
R_L=DHT11\_rec\_byte(); //接收湿度低八位
T_H=DHT11\_rec\_byte(); //接收温度高八位
T_L=DHT11\_rec\_byte(); //接收温度低八位
revise=DHT11\_rec\_byte(); //接收校正位

DHT11\_delay\_us(25); //结束

if((R_H+R_L+T_H+T_L)==revise) //校正
{
RH=R_H;
RL=R_L;
TH=T_H;
TL=T_L;

}
/\*数据处理,方便显示\*/
rec_dat[0]=RH;
rec_dat[1]=RL;
rec_dat[2]=TH;
rec_dat[3]=TL;

}

}

//延时us --2\*n+5us
void DHT11\_delay\_us(unsigned char n)
{
while(--n);
}

//延时ms
void DHT11\_delay\_ms(unsigned int z)
{
unsigned int i,j;
for(i=z; i>0; i--)
for(j=110; j>0; j--);
}


void InitUART(void)//使用定时器1作为串口波特率发生器
{

SCON=0x40; //串口通信工作方式1
REN=1; //允许接收
TMOD=0x20; //定时器1的工作方式2
TH1=0xF3,TL1=0xF3;
TI=1; //这里一定要注意
TR1=1;

}


  • lcd.c
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
#include"lcd.h"


//void Read\_Busy() //忙检测函数,判断bit7是0,允许执行;1禁止
//{
// unsigned char sta; //
// LCD1602\_DB = 0xff;
// LCD1602\_RS = 0;
// LCD1602\_RW = 1;
// do
// {
// LCD1602\_EN = 1;
// sta = LCD1602\_DB;
// LCD1602\_EN = 0; //使能,用完就拉低,释放总线
// }while(sta & 0x80);
//}


void Lcd1602\_Write\_Cmd(unsigned char cmd) //写命令
{
//Read\_Busy();
LCD1602_RS = 0;
LCD1602_RW = 0;
LCD1602_DB = cmd;
LCD\_Delay10ms(1);
LCD1602_EN = 1;
LCD\_Delay10ms(1);
LCD1602_EN = 0;
}

void Lcd1602\_Write\_Data(unsigned char dat) //写数据
{
//Read\_Busy();
LCD1602_RS = 1;
LCD1602_RW = 0;
LCD1602_DB = dat;
LCD\_Delay10ms(1);
LCD1602_EN = 1;
LCD\_Delay10ms(1);
LCD1602_EN = 0;
}
//指定位置开始显示数据!
void LcdSetCursor(unsigned char x,unsigned char y) //坐标显示
{
unsigned char addr;
if(y == 0)
addr = 0x00 + x;//第一行开始,x表示一行的第x个
else
addr = 0x40 + x;//第二行开始,x表示一行的第x个

Lcd1602\_Write\_Cmd(addr|0x80);
}

void LcdShowStr(unsigned char x,unsigned char y,unsigned char \*str) //显示字符串
{
LcdSetCursor(x,y); //当前字符的坐标
while(\*str != '\0')
{
Lcd1602\_Write\_Data(\*str++);
}
}

void InitLcd1602() //1602初始化
{
Lcd1602\_Write\_Cmd(0x38); //打开,5\*8,8位数据
Lcd1602\_Write\_Cmd(0x0c);
Lcd1602\_Write\_Cmd(0x06);
Lcd1602\_Write\_Cmd(0x01); //清屏
}

void LCD\_Delay10ms(unsigned int c) //误差 0us
{
unsigned char a,b;
for(; c>0; c--)
for(b=38; b>0; b--)
for(a=130; a>0; a--);
}


  • lcd.h
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
#ifndef \_\_LCD\_H\_
#define \_\_LCD\_H\_
/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
当使用的是4位数据传输的时候定义,
使用8位取消这个定义
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
//#define LCD1602\_4PINS

/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
包含头文件
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
#include<reg51.h>

//---重定义关键词---//
#ifndef uchar
#define uchar unsigned char
#endif

#ifndef uint
#define uint unsigned int
#endif

/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
PIN口定义
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
#define LCD1602\_DB P0 //data bus 数据总线
sbit LCD1602_RS = P2^0;
sbit LCD1602_RW = P2^1;
sbit LCD1602_EN = P2^2;

/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
函数声明
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
/\*在51单片机12MHZ时钟下的延时函数\*/
//void Lcd1602\_Delay1ms(uint c); //误差 0usvo
void LCD\_Delay10ms(unsigned int c);
//void Read\_Busy(); //忙检测函数,判断bit7是0,允许执行;1禁止
void Lcd1602\_Write\_Cmd(unsigned char cmd); //写命令
void Lcd1602\_Write\_Data(unsigned char dat); //写数据
void LcdSetCursor(unsigned char x,unsigned char y); //坐标显示
void LcdShowStr(unsigned char x,unsigned char y,unsigned char \*str); //显示字符串
void InitLcd1602(); //1602初始化


#endif