【全志T113-S3_100ask】11-2编写驱动采集dht11数据(cdev、中断、锁)

背景

在文章 https://blog.csdn.net/qq_46079439/article/details/126131667 中已经实现了一种方法采集dht11的数据,但是经反馈说采集不够稳定,经常读不到数据、DHT11无响应的现象,再次展开研究,参考 https://www.pudn.com/news/623fa4a92cfc38172120c913.html 并进行了改进。

(一)修改设备树

本文的设备树与上一节不完全一致,使用的依然是PD14,但是更加易读。
1、在根节点下添加:

/*添加DHT11的设备树文件*/
	dht11 {
	compatible = "dht-11";
	pinctrl-names = "default";
	pinctrl-1 = <&dht11_pin>;
	dht11-gpios = <&pio PD 14 GPIO_ACTIVE_HIGH>; 
	status = "okay"; 

在这里插入图片描述
2、在pio下添加:

&pio {
	/*增加dht_11的pinctrl控制*/
	dht11_pin: dht11_pin{
				allwinner,pins = "PD14"; /*dht11的时钟和数据线接PD14上面*/
			};

};

在这里插入图片描述

(二)编写驱动程序

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>

#include <linux/of_gpio.h>

typedef unsigned char uint8_t;

#define DEV_DTS_NODE_PATH "/dht11"     /* 设备树节点的路径,在根节点下 */
#define DEV_PIN_DTS_NAME "dht11-gpios" /* GPIO引脚的属性名 */
#define DEV_NAME "dht11"               /* 设备名  /dev/dht11 */
#define DEV_DTS_COMPATIBLE "dht-11"    /* 设备匹配属性 compatible */

#define DHT11_PIN dht11_dev.gpio
#define DHT11_IO_OUT() gpio_direction_output(DHT11_PIN, 1);
#define DHT11_IO_IN() gpio_direction_input(DHT11_PIN)
#define DHT11_WRITE(bit) gpio_set_value(DHT11_PIN, bit)
#define DHT11_READ() gpio_get_value(DHT11_PIN)

struct dht11
{
    int gpio; /* gpio */
    int irq;
    dev_t dev_no; /* 设备号 */
    struct cdev chrdev;
    struct class *class;
    spinlock_t lock;
};

struct dht11 dht11_dev;

static int dht11_wait_for_ready(void)
{
    int timeout = 400;
    while (DHT11_READ() && timeout) /* 等待低电平到来 */
    {
        udelay(1);
        --timeout;
    }
    if (!timeout)
    {
        printk("[failed1] timeout %d\n", __LINE__);
        return -1; /* 超时 */
    }

    timeout = 1000;
    while (!DHT11_READ() && timeout) /* 等待高电平到来    */
    {
        udelay(1);
        --timeout;
    }
    if (!timeout)
    {
        printk("[failed2] timeout %d\n", __LINE__);
        return -1; /* 超时 */
    }

    timeout = 1000;
    while (DHT11_READ() && timeout) /* 等待高电平结束 */
    {
        udelay(1);
        --timeout;
    }
    if (!timeout)
    {
        printk("[failed3] timeout %d\n", __LINE__);
        return -1; /* 超时 */
    }

    return 0;
}

static int dht11_start(void)
{
    DHT11_IO_OUT();
    DHT11_WRITE(0);
    mdelay(20);
    DHT11_WRITE(1);
    udelay(30);
    DHT11_IO_IN(); /* 设置为输入 */
    udelay(2);

    if (dht11_wait_for_ready())
        return -1;
    return 0;
}

static int dht11_read_byte(unsigned char *byte)
{
    unsigned char i;
    unsigned char bit = 0;
    unsigned char data = 0;
    int timeout = 0;

    for (i = 0; i < 8; i++)
    {
        timeout = 1000;
        while (DHT11_READ() && timeout) /* 等待变为低电平 */
        {
            udelay(1);
            --timeout;
        }
        if (!timeout)
        {
            printk("[failed] timeout %d\n", __LINE__);
            return -1; /* 超时 */
        }

        timeout = 1000;
        while (!DHT11_READ() && timeout) /* 等待变为高电平 */
        {
            udelay(1);
            --timeout;
        }
        if (!timeout)
        {
            printk("[failed] timeout %d\n", __LINE__);
            return -1; /* 超时 */
        }
        udelay(40);

        bit = DHT11_READ();

        data <<= 1;
        if (bit)
        {
            data |= 0x01;
#if 0
            timeout = 1000;
            while (DHT11_READ() && timeout)    /* 等待高电平结束 */
            {
                udelay(1);
                --timeout;
            }
            if (!timeout) 
            {
                printk("timeout %d\n", __LINE__);
                return -1;           /* 超时 */
            }
#endif
        }
        // data <<= 1;          /* 导致错误的原因 : 移位要放前面,不能放在这里,若放在后面一旦获取最后一个位就会多移动一位导致数据不对 */
    }

    *byte = data;
    return 0;
}

/* 使设备只能被一个进程打开 */
static int _drv_open(struct inode *node, struct file *file)
{
    printk("[success] dht11 open\n");
    return 0;
}

static ssize_t _drv_read(struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
    int ret;
    int i;
    unsigned char data[5] = {0};
    unsigned long flags;

    if (size != 5)
        return -EINVAL;

    /* 关闭中断,防止时序被中断破坏 */
    spin_lock_irqsave(&dht11_dev.lock, flags);

    /* 启动信号 */
    if (dht11_start() != 0)
    {
        printk("[failed] dht11 start failed\n");
        ret = -EFAULT;
        goto failed1;
    }

    /* 读出5字节数据 */
    for (i = 0; i < 5; i++)
    {
        if (dht11_read_byte(&data[i]))
        {
            printk("[failed] data err\n");
            ret = -EAGAIN;
            goto failed1;
        }
    }

    /* 打开中断 */
    spin_unlock_irqrestore(&dht11_dev.lock, flags);
    /* 校验数据 */
    if (data[4] != (data[0] + data[1] + data[2] + data[3]))
    {
        printk("[failed] check data failed\n");
        ret = -EAGAIN;
        goto failed1;
    }

    /* 将数据拷贝回用户空间 */
    if (copy_to_user(buf, data, 5))
    {
        ret = -EFAULT;
        goto failed1;
    }
    else
    {
        ret = 5;
    }

    return ret;
failed1:
    spin_unlock_irqrestore(&dht11_dev.lock, flags);
    return ret;
}

static int _drv_release(struct inode *node, struct file *file)
{
    printk("[success] dht11 release\n");
    return 0;
}

static struct file_operations drv_file_ops = {
    .owner = THIS_MODULE,
    .open = _drv_open,
    .read = _drv_read,
    .release = _drv_release,
};

/* 设备树的匹配列表 */
static struct of_device_id dts_match_table[] = {
    {
        .compatible = DEV_DTS_COMPATIBLE,
    }, /* 通过设备树来匹配 */
};

static int _driver_probe(struct platform_device *pdev)
{
    int err;
    struct device *ds_dev;
    struct device_node *dev_node;

    struct device_node *node = pdev->dev.of_node;

    if (!node)
    {
        printk("dts node can not found!\r\n");
        return -EINVAL;
    }

    dev_node = of_find_node_by_path(DEV_DTS_NODE_PATH); /* 找到dht11的设备树节点  */
    if (IS_ERR(dev_node))
    {
        printk("dht11 DTS Node not found!\r\n");
        return PTR_ERR(dev_node);
    }

    dht11_dev.gpio = of_get_named_gpio(dev_node, DEV_PIN_DTS_NAME, 0); /* 获取dht11的gpio编号 */
    if (dht11_dev.gpio < 0)
    {
        printk("dht11-gpio not found!\r\n");
        return -EINVAL;
    }

    err = gpio_request(dht11_dev.gpio, DEV_PIN_DTS_NAME);
    if (err)
    {
        printk("gpio_request gpio is failed!\n");
        return -EINVAL;
    }

    printk("dht11 gpio %d\n", dht11_dev.gpio);

    /* 内核自动分配设备号 */
    err = alloc_chrdev_region(&dht11_dev.dev_no, 0, 1, DEV_NAME);
    if (err < 0)
    {
        pr_err("Error: failed to register mbochs_dev, err: %d\n", err);
        goto failed3;
    }

    cdev_init(&dht11_dev.chrdev, &drv_file_ops);

    cdev_add(&dht11_dev.chrdev, dht11_dev.dev_no, 1);

    dht11_dev.class = class_create(THIS_MODULE, DEV_NAME);
    if (IS_ERR(dht11_dev.class))
    {
        err = PTR_ERR(dht11_dev.class);
        goto failed1;
    }

    /* 创建设备节点 */
    ds_dev = device_create(dht11_dev.class, NULL, dht11_dev.dev_no, NULL, DEV_NAME);
    if (IS_ERR(ds_dev))
    { /* 判断指针是否合法 */
        err = PTR_ERR(ds_dev);
        goto failed2;
    }

    spin_lock_init(&dht11_dev.lock); /* 初始化自旋锁 */
    printk("[success] dht11 probe success\r\n");
    return 0;
failed2:
    device_destroy(dht11_dev.class, dht11_dev.dev_no);
    class_destroy(dht11_dev.class);
failed1:
    unregister_chrdev_region(dht11_dev.dev_no, 1);
    cdev_del(&dht11_dev.chrdev);
failed3:
    gpio_free(dht11_dev.gpio);
    return err;
}

static int _driver_remove(struct platform_device *pdev)
{
    device_destroy(dht11_dev.class, dht11_dev.dev_no);
    class_destroy(dht11_dev.class);
    unregister_chrdev_region(dht11_dev.dev_no, 1);
    cdev_del(&dht11_dev.chrdev);
    gpio_free(dht11_dev.gpio);

    printk(KERN_INFO "[success] dht11 remove success\n");

    return 0;
}

static struct platform_driver _platform_driver = {
    .probe = _driver_probe,
    .remove = _driver_remove,
    .driver = {
        .name = DEV_DTS_COMPATIBLE,
        .owner = THIS_MODULE,
        .of_match_table = dts_match_table, /* 通过设备树匹配 */
    },
};

/* 入口函数 */
static int __init _driver_init(void)
{
    int ret;
    printk("dht11 %s\n", __FUNCTION__);

    ret = platform_driver_register(&_platform_driver); //注册platform驱动

    return ret;
}

/*  出口函数 */
static void __exit _driver_exit(void)
{
    printk("dht11  %s\n", __FUNCTION__);
    platform_driver_unregister(&_platform_driver);
}

module_init(_driver_init);
module_exit(_driver_exit);

MODULE_AUTHOR("Fourth");
MODULE_LICENSE("GPL");

(三)编写测试应用程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <stdint.h>

#define DEV_NAME "/dev/dht11"

typedef struct dht11
{
    float temperature;
    float humidity;
} DHT11;

void sleep_ms(unsigned int ms)
{
    struct timeval delay;
    delay.tv_sec = 0;
    delay.tv_usec = ms * 1000;
    select(0, NULL, NULL, NULL, &delay);
}

int getDht11(DHT11 *dht11_data)
{
    int fd;
    int ret;

    /* 2. 打开文件 */
    fd = open(DEV_NAME, O_RDONLY); // | O_NONBLOCK

    if (fd < 0)
    {
        printf("[failed] can not open file %s, %d\n", DEV_NAME, fd);
        return -1;
    }

    uint8_t dht11_temp_data[5];
    int timeout = 5;
    while (timeout)
    {
        ret = read(fd, dht11_temp_data, sizeof(dht11_temp_data)) == sizeof(dht11_temp_data);
        if (ret)
        {
            sleep_ms(500);
            ret = read(fd, dht11_temp_data, sizeof(dht11_temp_data)) == sizeof(dht11_temp_data);
            if (ret)
            {
                dht11_data->temperature = dht11_temp_data[2] + (float)dht11_temp_data[3] / 10.00;
                dht11_data->humidity = dht11_temp_data[0] + dht11_temp_data[1] / 10.00;
                printf("[success] temperture %d.%d  humi %d.%d\r\n", dht11_temp_data[2],
                       dht11_temp_data[3], dht11_temp_data[0], dht11_temp_data[1]);
                close(fd);
                return 0;
            }
            else
                continue;
        }
        else
        {
            printf("[failed] tempget temp err %d\n", ret);
            timeout--;
        }
        sleep_ms(500);
    }
    close(fd);
    return -1;
}

int main(int argc, char **argv)
{
    DHT11 dht11_data;
    if (!getDht11(&dht11_data))
    {
        printf("!!! temp %.1f  humi %.1f\n", dht11_data.temperature, dht11_data.humidity);
    }
    else
        printf("read dht11 error\n");
    return 0;
}

(四)参考Makefile

KERN_DIR = /disk/buildroot806/buildroot-100ask_t113-pro/buildroot/output/build/linux-origin_master


all:
	make -C $(KERN_DIR) M=`pwd` modules 
	$(CROSS_COMPILE)gcc -o dht11_drv_test dht11_drv_test.c 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order
	rm -f dht11_drv_test

obj-m	+= dht11_drv.o

(五)测试结果

# ls
dht11_drv.ko    dht11_drv_test
# chmod 777 dht11_drv_test
# insmod dht11_drv.ko
[ 2004.596630] dht11 _driver_init
[ 2004.600557] dht11 gpio 110
[ 2004.604304] [success] dht11 probe success
# ./dht11_drv_test
[ 2008.430245] [success] dht11 open
[ 2008.982787] [success] dht11 release

temp 30.0  humi 61.0

在这里插入图片描述
至此,测试完毕,但是驱动程序中有bug待优化,比如读取数据的时候会存在数据的滞后性,导致第一次读取数据会发生错误。为保证调用时返回给函数是准确的数据,读取了两次,在一定程度上缓解了滞后性,但是根本问题未解决,有待优化。

End.

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

第四维度4

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值