来源:百问网_嵌入式Linux wiki_jz2440 新1期视频维基教程 (视频文字版)
作者:韦东山
本文字数:2379,阅读时长:4分钟
这节课开始讲解DH11温湿度传感器的使用,首先查看芯片手册,里面的典型应用电路如下:
MCU通过一条数据线与DH11连接,MCU通过这条线发命令给DH11,DH11再通过这条线把数据发送给MCU。
因此,温湿度模块的核心就是 MCU发给DH11的命令格式和DH11返回的数据格式。
再来先简单看一下通讯的时序:
灰色这条线是由MCU驱动控制的,浅色的部分是由DH11驱动控制的。
首先MCU发送一个开始信号,这个开始信号是一个低脉冲,然后再拉高。
然后,DH11拉低,做出一个响应信号,再拉高。
接着就是DH11返回的数据。
这些数据一共有40bit,高位先出。
数据格式:8bit湿度整数数据 8bit湿度小数数据 8bi温度整数数据 8bit温度小数数据 8bit校验和
且当前小数部分用于以后扩展,现读出为零.
数据传送正确时校验和数据等于“8bit湿度整数数据 8bit湿度小数数据 8bi温度整数数据 8bit温度小数数据”所得结果的末8位。
DH11的难点是前面所说的时序脉冲,需要满足一定的时长.比如开始信号:
MCU必须先拉低至少18ms,然后再拉高20-40us,DH11再拉低80us以响应,最后再拉高80us.
接下来就是传输数据,我们的目的就是读到温湿度的数据,这些数据由DH11提供,那它怎么传回这些数据,怎么表示0和1呢?
可以看到,不管是0还是1,都开始是50us的低电平,
对于0数据,之后是26~28us的高电平;
对于1数据,之后是70us的高电平;
有了上面的知识,加上之前的高精度延时,现在就可以开始写程序了。
复制前面的第二个程序,文件名改为003_dht11_022_003,然后在sensors目录里新建dht11目录,再创建一个dht11.c文件。
我们的目的是,控制GPIO读取DHT11的数据,流程如下:
- 1. 主机发出至少18MS的低脉冲: start信号
- 2. start信号变为高, 20-40us之后, dht11会拉低总线维持80us,然后拉高80us: 回应信号
- 3. 之后就是数据, 逐位发送
bit0 : 50us低脉冲, 26-28us高脉冲
bit1 : 50us低脉冲, 70us高脉冲
- 4. 数据有40bit: 8bit湿度整数数据 8bit湿度小数数据 8bit温度整数数据 8bit温度小数数据 8bit校验和
DH11的DATA引脚连接到了GPG5。
先实现GPIO的基本操作,配置GPIO模式,实现输出、输入引脚的功能:
static void dht11_data_cfg_as_output(void)
{
GPGCON &= ~(3<<10);
GPGCON |= (1<<10);
}
static void dht11_data_cfg_as_input(void)
{
GPGCON &= ~(3<<10);
}
再设置输出电平或读取引脚数据:
static void dht11_data_set(int val)
{
if (val)
GPGDAT |= (1<<5);
else
GPGDAT &= ~(1<<5);
}
static int dht11_data_get(void)
{
if (GPGDAT & (1<<5))
return 1;
else
return 0;
}
再来实现DHT11的读操作。
在芯片手册里介绍说,DH11传感器上电后,要等待1s,以越过不稳定状态,在此期间无需发送任何指令。
因此首先写一个初始化函数,跳过这个不稳定状态:
void dht11_init(void)
{
dht11_data_cfg_as_output();
dht11_data_set(1);
mdelay(2000);
}
根据start时序要求,编写程序,维持一个大于18ms的低电平,然后释放引脚,即设置为输入引脚即可。
因为该引脚接有上拉电阻,一旦MCU设置为输入,引脚电平将由上拉电阻决定。
static void dht11_start(void)
{
dht11_data_set(0);
mdelay(20);
dht11_data_cfg_as_input();
}
然后等待40us以上,再去读取引脚电平,判断是否被拉低,以确定DH11给了响应。
static int dht11_wait_ack(void)
{
udelay(60);
return dht11_data_get();
}
再写个延时函数,用于时序中的,等待响应信号结束:
static int dht11_wait_for_val(int val, int timeout_us)
{
while (timeout_us--)
{
if (dht11_data_get() == val)
return 0; /* ok */
udelay(1);
}
return -1; /* err */
}
后面的数据会有五个字节组成,这里先写出读取一个字节,每个字节要读取8位。
先等待直到高电平,过滤到共同的50us延时,然后延时28us以上,再读取引脚电平,
如果引脚电平是1,则数据是1,反之是0。
然后再直到低电平的到来,循环8次,完成一个字节数据的读取。
static int dht11_recv_byte(void)
{
int i;
int data = 0;
for (i = 0; i < 8; i )
{
if (dht11_wait_for_val(1, 1000))
{
printf("dht11 wait for high data err!\n\r");
return -1;
}
udelay(40);
data <<= 1;
if (dht11_data_get() == 1)
data |= 1;
if (dht11_wait_for_val(0, 1000))
{
printf("dht11 wait for low data err!\n\r");
return -1;
}
}
return data;
}