赞
踩
若该文为原创文章,转载请注明原文出处。
记录开发单总线,读取DHT11温湿度
DHT11是串行接口(单线双向)DATA 用于微处理器与 DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分小数部分和整数部分。
DHT11 采用单总线协议与单片机通信,单片机发送一次复位信号后,DHT11 从低功耗模式转换到高速模式,等待主机复位结束后,DHT11 发送响应信号,并拉高总线准备传输数据。一次完整的数据为 40bit,按照高位在前,低位在后的顺序传输。
数据格式为:8bit 湿度整数数据+8bit 湿度小数数据+8bit 温度整数数据+8bit 温度小数数据+8bit 校验和,一共 5 字节(40bit)数据。由于 DHT11 分辨率只能精确到个位,所以小数部分是数据全为 0。校验和为前 4 个字节数据相加,校验的目的是为了保证数据传输的准确性。
根据时序读取DHT11流程为:
1、主机发送复位信号
2、等待DHT11 发送响应信号
3、读取数据(40bit,5个字节)
前面4字节为数据位,第5字节为校验和位
使用的是前面的IO口,GPIO3_PC4。IO口可以跟换,设备的节点需要对应修改。
修改/home/alientek/rk3568_linux_sdk/kernel/arch/arm64/boot/dts/rockchip/目录下的rk3568-atk-evb1-ddr4-v10.dtsi文件,添加gpiodht11节点
- gpiodht11 {
- compatible = "alientek,dht11";
- pinctrl-names = "alientek,dht11";
- pinctrl-0 = <&dht11_gpio>;
- dht11-gpio = <&gpio3 RK_PC4 GPIO_ACTIVE_HIGH>;
- status = "okay";
- };
- dht11-gpios{
- /omit-if-no-ref/
- dht11_gpio: dht11-pin {
- rockchip,pins =
- <3 RK_PC4 RK_FUNC_GPIO &pcfg_pull_none>;
- };
- };
修改后,重新编译内核,生成boot.img文件,重新烧写即可。
重启后会在/proc/device-tree下找到gpiodht11节点.
- /*
- dht11-gpios{
- /omit-if-no-ref/
- dht11_gpio: dht11-pin {
- rockchip,pins =
- <3 RK_PC4 RK_FUNC_GPIO &pcfg_pull_none>;
- };
- };
-
- gpiodht11 {
- compatible = "alientek,dht11";
- pinctrl-names = "alientek,dht11";
- pinctrl-0 = <&dht11_gpio>;
- dht11-gpio = <&gpio3 RK_PC4 GPIO_ACTIVE_HIGH>;
- status = "okay";
- };
- */
-
-
- #include <linux/types.h>
- #include <linux/kernel.h>
- #include <linux/delay.h>
- #include <linux/ide.h>
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/errno.h>
- #include <linux/gpio.h>
- #include <linux/cdev.h>
- #include <linux/device.h>
- #include <linux/of_gpio.h>
- #include <linux/semaphore.h>
- #include <linux/timer.h>
- #include <linux/irq.h>
- #include <linux/wait.h>
- #include <linux/poll.h>
- #include <linux/fs.h>
- #include <linux/fcntl.h>
- #include <linux/platform_device.h>
- //#include <asm/mach/map.h>
- #include <asm/uaccess.h>
- #include <asm/io.h>
-
-
- #define DHT11DEV_CNT 1 /* 设备号长度 */
- #define DHT11DEV_NAME "dtsplatdht11" /* 设备名字 */
-
-
- /* dht11dev 设备结构体 */
- struct dht11dev_dev{
- dev_t devid; /* 设备号 */
- struct cdev cdev; /* cdev */
- struct class *class; /* 类 */
- struct device *device; /* 设备 */
- struct device_node *node; /* dht11dev 设备节点 */
- int gpio_dht11; /* dht11dev 灯 GPIO 标号 */
- };
-
- struct dht11dev_dev dht11dev; /* dht11d ev 设备 */
-
- unsigned char getdata[4] = {0xff, 0xff, 0xff, 0xff};
-
- static void dht11_release( void )
- {
- gpio_direction_output(dht11dev.gpio_dht11, 1);
- }
-
- static void dht11_start(void)
- {
- gpio_direction_output(dht11dev.gpio_dht11, 1);
- mdelay(30);
-
- gpio_set_value( dht11dev.gpio_dht11, 0);
- mdelay(20);
-
- gpio_set_value(dht11dev.gpio_dht11, 1);
- udelay(40);
-
- gpio_direction_input(dht11dev.gpio_dht11);
- }
-
-
- static int dht11_wait_ack(void)
- {
- long timeout_us = 50000;
-
- /* 等待低电平 */
- while (gpio_get_value(dht11dev.gpio_dht11) && --timeout_us)
- {
- udelay(1);
- }
- if (!timeout_us)
- {
- return -1;
- }
-
- /* 现在是低电平 */
- /* 等待高电平 */
- timeout_us = 50000;
-
- while (!gpio_get_value(dht11dev.gpio_dht11) && --timeout_us)
- {
- udelay(1);
- }
-
- if (!timeout_us)
- {
- return -1;
- }
-
- /* 现在是高电平 */
- /* 等待低电平 */
- timeout_us = 20000;
- while (gpio_get_value(dht11dev.gpio_dht11) && --timeout_us)
- {
- udelay(1);
- }
-
- if (!timeout_us)
- {
- return -1;
- }
-
-
- return 0;
- }
-
-
- static int dht11_read_byte( unsigned char datalist )
- {
- int i;
- unsigned char data = 0;
- int timeout_us = 20000;
- u64 pre = 0, last = 0;
-
- for (i = 0; i < 8; i++)
- {
- /* 现在是低电平 */
- /* 等待高电平 */
- timeout_us = 20000;
- while (!gpio_get_value(dht11dev.gpio_dht11) && --timeout_us)
- {
- udelay(1);
- }
-
- if (!timeout_us)
- {
- return -1;
- }
-
- /* 现在是高电平 */
- /* 等待低电平,累加高电平的时间 */
- timeout_us = 20000000;
-
- /* set another gpio low */
- pre = ktime_get_boot_ns();
- while (1)
- {
- last = ktime_get_boot_ns();
- if (last - pre >= 40000)
- break;
- }
-
- if (gpio_get_value(dht11dev.gpio_dht11))
- {
- /* get bit 1 */
- data = (data << 1) | 1;
- /* 当前位的高电平未结束, 等待 */
- timeout_us = 20000;
- while (gpio_get_value(dht11dev.gpio_dht11) && --timeout_us)
- {
- udelay(1);
- }
- if (!timeout_us)
- {
- return -1;
- }
- }
- else
- {
- /* get bit 0 */
- data = (data << 1) | 0;
- }
- }
-
- getdata[datalist] = data;
-
- return 0;
- }
-
-
- static int dht11_get_value(void)
- {
- unsigned long flags;
- int i;
-
-
- local_irq_save(flags); // 关中断
-
- /* 1. 发送高脉冲启动DHT11 */
- dht11_start();
-
- /* 2. 等待DHT11就绪 */
- if (dht11_wait_ack())
- {
- local_irq_restore(flags); // 恢复中断
- return -EAGAIN;
- }
-
- /* 3. 读5字节数据 */
- for (i = 0; i < 5; i++)
- {
- dht11_read_byte(i);
- }
-
- /* 4. 释放总线 */
- dht11_release();
-
- local_irq_restore(flags); // 恢复中断
-
- /* 5. 根据校验码验证数据 */
- if (getdata[4] != (getdata[0] + getdata[1] + getdata[2] + getdata[3]))
- {
- getdata[0] = 0xff;
- getdata[1] = 0xff;
- getdata[2] = 0xff;
- getdata[3] = 0xff;
-
- return -1;
- }
-
- return 0;
- }
-
-
- static int dht11_gpio_init(struct device_node *nd)
- {
- int ret;
-
- /* 从设备树中获取 GPIO */
- dht11dev.gpio_dht11 = of_get_named_gpio(nd, "dht11-gpio", 0);
- if(!gpio_is_valid(dht11dev.gpio_dht11)) {
- printk(KERN_ERR "dht11dev: Failed to get dht11-gpio\n");
- return -EINVAL;
- }
-
- /* 申请使用 GPIO */
- ret = gpio_request(dht11dev.gpio_dht11, "DHT11");
- if (ret) {
- printk(KERN_ERR "dht11: Failed to request dht11-gpio\n");
- return ret;
- }
-
- /* 将 GPIO 设置为输出模式并设置 GPIO 初始电平状态 */
- gpio_direction_output(dht11dev.gpio_dht11,0);
-
- printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
-
- return 0;
- }
-
- /*
- * @description : 打开设备
- * @param – inode : 传递给驱动的 inode
- * @param - filp : 设备文件,file 结构体有个叫做 private_data 的成员变量
- * 一般在 open 的时候将 private_data 指向设备结构体。
- * @return : 0 成功;其他 失败
- */
- static int dht11_open(struct inode *inode, struct file *filp)
- {
- printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
-
- return 0;
- }
-
-
- /*
- linux driver 驱动接口:
- 实现对应的open/read/write等函数,填入file_operations结构体
- */
- static ssize_t dht11_drv_read ( struct file *file, char __user *buf,
- size_t size, loff_t *offset)
- {
- int ret =0;
-
-
- if( !dht11_get_value() )
- {
- //printk("data[0] = %d, data[1] = %d, data[2] = %d, data[3] = %d\n", getdata[0], getdata[1], getdata[2], getdata[3]);
-
- ret = copy_to_user(buf, getdata, sizeof(getdata));
-
- return ret;
- }
-
- return ret;
-
- }
-
-
-
- /*
- * @description : 向设备写数据
- * @param - filp : 设备文件,表示打开的文件描述符
- * @param - buf : 要写给设备写入的数据
- * @param - cnt : 要写入的数据长度
- * @param - offt : 相对于文件首地址的偏移
- * @return : 写入的字节数,如果为负值,表示写入失败
- */
- static ssize_t dht11_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
- {
- printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
-
- return 0;
- }
-
- static int dht11_drv_close(struct inode *node, struct file *file)
- {
- printk(" %s line %d \r\n", __FUNCTION__, __LINE__);
-
- dht11_get_value();
-
- return 0;
- }
-
-
- /* 设备操作函数 */
- static struct file_operations dht11_fops = {
- .owner = THIS_MODULE,
- .open = dht11_open,
- .read = dht11_drv_read,
- .write = dht11_write,
- .release = dht11_drv_close,
- };
-
- /*
- * @description : flatform 驱动的 probe 函数,当驱动与
- * 设备匹配以后此函数就会执行
- * @param - dev : platform 设备
- * @return : 0,成功;其他负值,失败
- */
- static int dht11_probe(struct platform_device *pdev)
- {
- int ret;
-
- printk("dht11 driver and device was matched!\r\n");
-
- /* 初始化 DHT11 */
- ret = dht11_gpio_init(pdev->dev.of_node);
- if(ret < 0)
- return ret;
-
- /* 1、设置设备号 */
- ret = alloc_chrdev_region(&dht11dev.devid, 0, DHT11DEV_CNT, DHT11DEV_NAME);
- if(ret < 0) {
- pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", DHT11DEV_NAME, ret);
- goto free_gpio;
- }
-
- /* 2、初始化 cdev */
- dht11dev.cdev.owner = THIS_MODULE;
- cdev_init(&dht11dev.cdev, &dht11_fops);
-
- /* 3、添加一个 cdev */
- ret = cdev_add(&dht11dev.cdev, dht11dev.devid, DHT11DEV_CNT);
- if(ret < 0)
- goto del_unregister;
-
- /* 4、创建类 */
- dht11dev.class = class_create(THIS_MODULE, DHT11DEV_NAME);
- if (IS_ERR(dht11dev.class)) {
- goto del_cdev;
- }
-
- /* 5、创建设备 */
- dht11dev.device = device_create(dht11dev.class, NULL, dht11dev.devid, NULL, DHT11DEV_NAME);
- if (IS_ERR(dht11dev.device)) {
- goto destroy_class;
- }
-
- return 0;
-
- destroy_class:
- class_destroy(dht11dev.class);
- del_cdev:
- cdev_del(&dht11dev.cdev);
- del_unregister:
- unregister_chrdev_region(dht11dev.devid, DHT11DEV_CNT);
- free_gpio:
- gpio_free(dht11dev.gpio_dht11);
-
- return -EIO;
- }
-
- /*
- * @description : platform 驱动的 remove 函数
- * @param - dev : platform 设备
- * @return : 0,成功;其他负值,失败
- */
- static int dht11_remove(struct platform_device *dev)
- {
- gpio_set_value(dht11dev.gpio_dht11, 0);/* 卸载驱动的时候 GPIO置0 */
- gpio_free(dht11dev.gpio_dht11); /* 注销 GPIO */
- cdev_del(&dht11dev.cdev); /* 删除 cdev */
- unregister_chrdev_region(dht11dev.devid, DHT11DEV_CNT);
- device_destroy(dht11dev.class, dht11dev.devid); /* 注销设备 */
- class_destroy(dht11dev.class); /* 注销类 */
-
- return 0;
- }
-
- /* 匹配列表 */
- static const struct of_device_id dht11_of_match[] = {
- {
- .compatible = "alientek,dht11" },
- { /* Sentinel */ }
- };
-
- MODULE_DEVICE_TABLE(of, dht11_of_match);
-
- /* platform 驱动结构体 */
- static struct platform_driver dht11_driver = {
- .driver = {
- .name = "rk3568-dht11", /* 驱动名字,用于和设备匹配 */
- .of_match_table = dht11_of_match, /* 设备树匹配表 */
- },
-
- .probe = dht11_probe,
- .remove = dht11_remove,
- };
-
- /*
- * @description : 驱动模块加载函数
- * @param : 无
- * @return : 无
- */
- static int __init dht11driver_init(void)
- {
- return platform_driver_register(&dht11_driver);
- }
-
- /*
- * @description : 驱动模块卸载函数
- * @param : 无
- * @return : 无
- */
- static void __exit dht11driver_exit(void)
- {
- platform_driver_unregister(&dht11_driver);
- }
-
- module_init(dht11driver_init);
- module_exit(dht11driver_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("yifeng");
- MODULE_INFO(intree, "Y");
-
-
-

程序是根据正点原子的注册platform平台驱动点亮LED修改,增加读功能. 这里有点需要注意,内存的使用,使用指针,不清楚为什么,会出错。
- KERNELDIR := /home/alientek/rk3568_linux_sdk/kernel
- ARCH=arm64
- CROSS_COMPILE=/opt/atk-dlrk356x-toolchain/usr/bin/aarch64-buildroot-linux-gnu-
-
- export ARCH CROSS_COMPILE
-
- CURRENT_PATH := $(shell pwd)
- obj-m := drv_dht11.o
-
- build: kernel_modules
-
- kernel_modules:
- $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
- clean:
- $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
直接make编译,生成ko文件,拷贝到开发板。
- /***************************************************************
- Copyright 2024-2029. All rights reserved.
- 文件名 : dht11App.c
- 作者 : yifeng
- 版本 : V1.0
- 描述 : 测试dth11驱动程序
- 其他 : 无
- 日志 : 初版V1.0 2024/07/18
- ***************************************************************/
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <linux/types.h>
- #include <linux/i2c-dev.h>
- #include <linux/i2c.h>
- #include <sys/ioctl.h>
- #include <unistd.h>
- #include <stdio.h>
- #include <string.h>
- #include <fcntl.h>
- #include <stdlib.h>
- #include <linux/fs.h>
- #include <errno.h>
- #include <assert.h>
- #include <string.h>
- #include <time.h>
-
- #define DEV_FILE "/dev/dtsplatdht11"
-
-
- int main(void)
- {
- int fd;
- int count_run = 0;
- int ret = 0;
- unsigned char data[4];
-
- fd = open(DEV_FILE, 0);
- if (fd == -1){
- printf("can not open file: %s \n", DEV_FILE);
- return -1;
- }
-
- while( count_run < 50000)
- {
- count_run++;
-
- if (read(fd, &data[0], 4) == 0)
- {
- printf("get humidity : %d.%d\n", data[0], data[1]);
- printf("get temprature: %d.%d\n", data[2], data[3]);
- }
- else
- {
- printf("read dht11 device fail!\n");
- }
-
- sleep(1); // 1毫秒
- }
-
- close(fd);
-
- return 0;
- }

程序比较简单,5秒读一次数据,并打印。
编译:
/opt/atk-dlrk356x-toolchain/bin/aarch64-buildroot-linux-gnu-gcc dht11App.c -o dht11App
测试使用的是正点原子的ATK-DLRK3568板子
加载驱动
insmod drv_dht11.ko
测试
./dht11App
测试虽然有读取到温湿度,也有变化,但驱动不是很完美,只限测试使用。
如有侵权,或需要完整代码,请及时联系博主。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。