距离第六篇发布差不多两周时间,这期间一直再捣腾DHT11,真的很痛苦(加之孩子开学,本人开学),试了各种方法,从相对简单的python,到驱动模块,再到GPIO直接驱动,一直没有成功,直到潜下心来,仔细看了看源码以及相关的设备树,总算解决了。距离总目标又进一步,现在总结一下。
源码里面已经编制好了DHT11相应的驱动(我之前按照stm32思路编写了相应驱动,一直没有加载成功),用户只需要确认一下即可,如图1所示。
图1 选择DTH11模块
在设备树里,修改相应说明,增加DHT11的说明(如图2),因为这里用到了引脚61(该引脚默认是SPI的,所以注释掉使其作为GPIO引脚使用,如图3所示)。
图2 增加设备树中DHT11的说明代码
图3 注释掉设备树中SPI的说明代码
对应源码中DHT11代码。经验证,发现运行时,会有错误发生,不像裸机那样稳定。其实,这个也是我自行编写相应DHT11驱动时一直没有成功的原因之一--为了保证这个单总线方式通信的成功率,尤其像这种有时序要求的通信,在基于OS运行机制下进行通信时需要添加开关中断操作(过去在UCOS-II做项目时被类似相应模块折磨过,记忆犹新),但在蜂鸟板对应源码中没有找到(我将尝试对应添加),错误如图4所示。
图4 运行错误提示
测试用例,具体运行情况,如视频所示。这里我同时还用stm32裸机跑了一个程序,来对比查看和蜂鸟板上运行的情况。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
int main(void) {
int fd;
char buf[5];
int j;
fd = open("/dev/dht11", O_RDONLY);
while(1)
{
temp = read(fd, buf, sizeof(buf));
if( buf[4] == (buf[0]+buf[1]+buf[2]+buf[3]) )
{
printf("read sucessful!\\n");
printf("humi is %d.%d, temp is %d.%d \\n", buf[0],buf[1], buf[2],buf[3]);
}
else
{
j++;
if(j == 10)
{
close(fd);
return -1;
}
printf("read failed!\\n");
}
sleep(1);
}
return 0;
}
今天抽空按照我的理解对内核的dht11.c代码进行了修改,并进行了测试(测试代码还是沿用之前的,没有修改),明显效果要好得多,运行了近10分钟,没有出现error的情况。
主要修改的就是下面dht11_read函数
ssize_t dht11_read(struct file *file, char __user * user_buf, size_t size, loff_t * loff)
{
volatile int max_wait;
int err;
unsigned long flags;
unsigned char dht11_data[5];
local_irq_save(flags); // 关中断
//mdelay(5); //拉低5ms 以免过快检测导致读取数据失败
gpio_set_value(dht11_GPIO, LOW);
mdelay(20); //拉低20ms
/*拉高20-40us*/
gpio_set_value(dht11_GPIO, HIGH);
udelay(30); //拉高30us
//设置为输入
gpio_direction_input(dht11_GPIO);
/*DHT11响应信号*/
udelay(10);
if (gpio_get_value(dht11_GPIO) == LOW) {
//80us 低电平
while(gpio_get_value(dht11_GPIO) == LOW);
}
if (gpio_get_value(dht11_GPIO) == HIGH) {
//80us 高电平
err = 0;
while(gpio_get_value(dht11_GPIO) == HIGH);
}
dht11_data[0] = get_dht11_value(); //湿度整数数据
dht11_data[1] = get_dht11_value(); //湿度小数数据
dht11_data[2] = get_dht11_value(); //温度整数数据
dht11_data[3] = get_dht11_value(); //温度小数数据
dht11_data[4] = get_dht11_value(); //校验和
if (copy_to_user(user_buf, dht11_data, size) != 0)
ERROR_PRINT("dht11 date copy_to_user failed");
//恢复为输出模式高电平
end();
local_irq_restore(flags); // 恢复中断
return 0;
}
更多回帖