dht11传感器总结

发布于:2025-08-29 ⋅ 阅读:(12) ⋅ 点赞:(0)

设备树规定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;  // 高电平持续时间为 80us
    while (!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;  // 低电平持续时间为 80us
    while (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 还是 1
        if (gpiod_get_value(dht11_gpio))
        {
            data = (data << 1) | 1; // 获取 bit 1

            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 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); // 延时 20ms
    gpiod_set_value(dht11_gpio, 1); // 拉高总线
    udelay(40); // 延时 40us

    gpiod_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();  // 重启定时器

网站公告

今日签到

点亮在社区的每一天
去签到