dht11传感器总结
设备树规定GPIO
在设备树规定使用的IO口
/*DT11 gpio4 23*/
/{ dht11 {compatible = "imx6ull,dht11";pinctrl-names = "default";pinctrl-0 = <&pinctrl_dht11>;dht11-gpios = <&gpio4 23 GPIO_ACTIVE_HIGH>; // 新添加的 GPIO,用于 DHT11 数据status = "okay";};
}&iomuxc{pinctrl_dht11: dht11grp {fsl,pins = <MX6UL_PAD_CSI_DATA02__GPIO4_IO23 0x10B0 >;};
}
驱动程序
dth11协议
判断dht11的响应信号(低脉冲持续 80us,高脉冲持续 80us)
static int dht11_wait_for_ready(void)
{int timeout_us = 20000;// 检查 GPIO 描述符是否有效if (!is_dht11_gpio_valid()) {printk(KERN_ERR "DHT11: dht11_gpio invalid in dht11_wait_for_ready (start)\n");return -EFAULT;}// 等待低电平(主机开始信号)while (gpiod_get_value(dht11_gpio) && --timeout_us){udelay(1);}if (!timeout_us){printk("%s %s line %d timeout waiting for low\n", __FILE__, __FUNCTION__, __LINE__);return -1;}// 等待高电平(DHT11响应信号开始)timeout_us = 80; // 高电平持续时间为 80uswhile (!gpiod_get_value(dht11_gpio) && --timeout_us){udelay(1);}if (!timeout_us){printk("%s %s line %d timeout waiting for high\n", __FILE__, __FUNCTION__, __LINE__);return -1;}// 等待低电平(DHT11响应信号结束)timeout_us = 80; // 低电平持续时间为 80uswhile (gpiod_get_value(dht11_gpio) && --timeout_us){udelay(1);}if (!timeout_us){printk("%s %s line %d timeout waiting for low after high\n", __FILE__, __FUNCTION__, __LINE__);return -1;}return 0;
}
判断dht11接受到的字节是0或1
/* 读取 DHT11 一个字节的数据 */
static int dht11_read_byte(unsigned char *buf)
{int i;int us = 0;unsigned char data = 0;// 检查 GPIO 描述符是否有效if (!is_dht11_gpio_valid()) {printk(KERN_ERR "DHT11: dht11_gpio invalid in dht11_read_byte (start)\n");return -EFAULT;}for (i = 0; i < 8; i++){int timeout_us = 400; // 重置超时us = 0;// 等待高电平while (!gpiod_get_value(dht11_gpio) && --timeout_us){udelay(1);us++;}if (!timeout_us){printk("%s %s line %d timeout waiting for high in bit %d\n", __FILE__, __FUNCTION__, __LINE__, i);return -1;}udelay(40); // 高电平持续时间,用于判断位是 0 还是 1// 检查 GPIO 值,判断是 0 还是 1if (gpiod_get_value(dht11_gpio)){data = (data << 1) | 1; // 获取 bit 1timeout_us = 400; // 重置超时us = 0;while (gpiod_get_value(dht11_gpio) && --timeout_us){udelay(1);us++;}if (!timeout_us){printk("%s %s line %d timeout waiting for low after bit 1 in bit %d\n", __FILE__, __FUNCTION__, __LINE__, i);return -1;}}else{data = (data << 1) | 0; // 获取 bit 0}}*buf = data; // 返回读取的字节数据return 0;
}
与用户空间交互
严格执行协议的a到e步骤
static ssize_t dht11_read(struct file *file, char __user *buf, size_t size, loff_t *offset){int i;int re;unsigned char data[5];unsigned long flags;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);// 检查读取的数据大小if(size != 4){printk(KERN_ERR "DHT11: Read size must be 4 bytes, got %zu\n", size);return -EINVAL;}// 检查 dht11_gpio 是否有效if (!is_dht11_gpio_valid()) {printk(KERN_ERR "DHT11: dht11_gpio invalid in dht11_read (top)\n");return -EFAULT;}local_irq_save(flags); // 关中断,保护时序敏感操作// 设置 GPIO 引脚方向为输出并拉高gpiod_direction_output(dht11_gpio, 1); mdelay(30); // 等待传感器准备gpiod_set_value(dht11_gpio, 0); // 拉低总线,发送启动信号mdelay(20); // 延时 20msgpiod_set_value(dht11_gpio, 1); // 拉高总线udelay(40); // 延时 40usgpiod_direction_input(dht11_gpio); // 切换为输入模式udelay(2); // 稍作延时if (dht11_wait_for_ready()) {local_irq_restore(flags); // 恢复中断printk("%s %s line %d dht11_wait_for_ready failed\n", __FILE__, __FUNCTION__, __LINE__);return -EAGAIN;}// 读取 5 字节数据for (i = 0; i < 5; i++){if (dht11_read_byte(&data[i])) {local_irq_restore(flags); // 恢复中断printk("%s %s line %d dht11_read_byte failed for byte %d\n", __FILE__, __FUNCTION__, __LINE__, i);return -EAGAIN;}}// 校验码验证if (data[4] != (data[0] + data[1] + data[2] + data[3])) {printk(KERN_WARNING "DHT11: Checksum mismatch! Data: %d %d %d %d, Checksum: %d, Expected: %d\n",data[0], data[1], data[2], data[3], data[4], (data[0] + data[1] + data[2] + data[3]));}printk("data %d %d\n", data[0], data[2]); // 打印湿度和温度数据// 将数据拷贝到用户空间re = copy_to_user(buf, data, 4);if (re != 0) {printk(KERN_ERR "DHT11: Failed to copy data to user space: %d\n", re);return -EFAULT;}return 4; // 返回读取的字节数
}
probe函数获取硬件资源
/* 5.1 实现 probe 函数 */
static int dht11_probe(struct platform_device *dev)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/* 获取硬件资源 */dht11_gpio = gpiod_get(&dev->dev, "dht11", GPIOD_OUT_HIGH);if (IS_ERR(dht11_gpio)) {int err = PTR_ERR(dht11_gpio);dev_err(&dev->dev, "Failed to get dht11 GPIO: %d\n", err);return err;}dev_info(&dev->dev, "Successfully got dht11 GPIO\n");/* 创建设备节点,用户空间可以访问 */device_create(dht11_class, NULL, MKDEV(major, 0), NULL, "querydht11");return 0;
}
入口函数与出口函数
注册字符设备、创建类、注册平台驱动
注销字符设备、注销类、注销平台驱动
应用函数
open打开设备节点,然后再用read读传感器的信息
#include "dht11.h"
#include <QFile>temper::temper()
{}temper::~temper()
{}void temper::run()
{QFile file;QString fileName = "/dev/querydht11";file.setFileName(fileName);file.open(QIODevice::ReadWrite | QIODevice::Unbuffered); //非缓冲读取while(!stop){file.read(reinterpret_cast<char*>(databuf), sizeof(databuf));if(databuf[0] > 0 && databuf[0] < 100 && databuf[1] > 0 && databuf[1]){ //判断数据是否正确for(int i = 0; i < 100; i++){if(!stop){msleep(100); //每10s读一次数据}}}else{ //数据不对重读msleep(1000);continue;}}file.close();
}void temper::getData(int delay)
{//获取一次数据后退出while(1){QFile file;QString fileName = "/dev/dth11";file.setFileName(fileName);file.open(QIODevice::ReadWrite | QIODevice::Unbuffered);file.read(reinterpret_cast<char*>(databuf), sizeof(databuf));file.close();if(databuf[0] > 0 && databuf[0] < 100 && databuf[1] > 0 && databuf[1]){break;}msleep(delay);}
}void temper::temperStop()
{stop = 1;
}
QT中
启动线程,并且设计定时器定时更新界面
// 启动温湿度读取线程myTemper = new temper(); // 创建温湿度线程对象myTemper->getData(500); // 启动线程进行温湿度数据获取myTemper->start(); // 启动线程ui->lineEdit->setText("湿度:" + QString::number(myTemper->databuf[0]) + "%"); // 显示湿度ui->lineEdit_2->setText("温度:" + QString::number(myTemper->databuf[1]) + "℃"); // 显示温度
// 每60秒更新一次温湿度数据if(timerNums > 600){ // 60s测一次myTimer->stop(); // 停止定时器myTemper->getData(500); // 获取温湿度数据if(myTemper->databuf[0] > 0 && myTemper->databuf[0] < 100 && myTemper->databuf[1] > 0 && myTemper->databuf[1]){ui->lineEdit->setText("湿度:" + QString::number(myTemper->databuf[0]) + "%"); // 更新湿度显示ui->lineEdit_2->setText("温度:" + QString::number(myTemper->databuf[1]) + "℃"); // 更新温度显示}timerNums = 0; // 重置计时器计数myTimer->start(); // 重启定时器