RK3568笔记四十一:DHT11驱动开发测试

若该文为原创文章,转载请注明原文出处。

记录开发单总线,读取DHT11温湿度

一、DHT11介绍

DHT11是串行接口(单线双向)DATA 用于微处理器与 DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分小数部分和整数部分。

1、协议及数据格式

DHT11 采用单总线协议与单片机通信,单片机发送一次复位信号后,DHT11 从低功耗模式转换到高速模式,等待主机复位结束后,DHT11 发送响应信号,并拉高总线准备传输数据。一次完整的数据为 40bit,按照高位在前,低位在后的顺序传输。

数据格式为:8bit 湿度整数数据+8bit 湿度小数数据+8bit 温度整数数据+8bit 温度小数数据+8bit 校验和,一共 5 字节(40bit)数据。由于 DHT11 分辨率只能精确到个位,所以小数部分是数据全为 0。校验和为前 4 个字节数据相加,校验的目的是为了保证数据传输的准确性。
 

2、时序

根据时序读取DHT11流程为:

1、主机发送复位信号

2、等待DHT11 发送响应信号

3、读取数据(40bit,5个字节)

前面4字节为数据位,第5字节为校验和位

二、原理图

使用的是前面的IO口,GPIO3_PC4。IO口可以跟换,设备的节点需要对应修改。

三、创建节点

1、在设备树中创建设备节点

修改/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";
};

2、创建设备的 pinctrl 节点

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节点.

四、驱动编写

1、drv_dht11.c

/* 
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修改,增加读功能. 这里有点需要注意,内存的使用,使用指针,不清楚为什么,会出错。

2、makefile

KERNELDIR := /home/alientek/rk3568_linux_sdk/kernel
ARCH=arm64
CROSS_COMPILE=/opt/atk-dlrk356x-toolchain/usr/bin/aarch64-buildroot-linux-gnu-export  ARCH  CROSS_COMPILECURRENT_PATH := $(shell pwd)
obj-m := drv_dht11.obuild: kernel_moduleskernel_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

测试虽然有读取到温湿度,也有变化,但驱动不是很完美,只限测试使用。

如有侵权,或需要完整代码,请及时联系博主。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/47542.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

利用一维数组计算今天是今年的第几天

分析&#xff1a; 在一维数组里初始化12个月份&#xff0c;在进行判断是不是闰年&#xff0c;是闰年就把数组的二月的下标改为29&#xff0c;否则不变就按照平年计算&#xff0c;最后把想要计算的月份减1累加到sum里&#xff0c;在进行计算该月份的天也要累加。例如&#xff1a…

神经网络处理器模拟器的一点思考

一 神经网络处理器 通常基于FPGA的神经网络处理器进行部署某种网络&#xff0c;考虑的因素较多&#xff0c;具体包括网络模型的不同&#xff0c;涵盖不同的算子、激活函数、调度策略等等&#xff1b;具体硬件实现&#xff0c;涉及神经网络处理器并行度、硬件资源消耗&#xff0…

java高级——Collection集合之Set探索(底层为HashMap实现)

java高级——Collection集合之Set探索 前情提要文章介绍继承结构底层代码&#xff08;一张图你就悟了&#xff09;下期预告 前情提要 上一篇文章我们探索了HashMap&#xff0c;详细解说了哈希冲突&#xff0c;红黑树以及Map底层到底是怎么实现的&#xff0c;这一篇我们简单说一…

图像生成(Text-to-Image)发展脉络

这篇博客对 图像生成&#xff08;image generation&#xff09; 领域的经典工作发展进行了梳理&#xff0c;包括重要的一些改进&#xff0c;目的是帮助读者对此领域有一个整体的发展方向把握&#xff0c;并非是对每个工作的详细介绍。 脉络发展&#xff08;时间顺序&#xff0…

气膜工业仓储与气膜体育馆的配置区别—轻空间

气膜工业仓储和气膜体育馆在配置上有明显的区别&#xff0c;这主要是由于它们的使用功能和环境不同所导致的。 结构设计 气膜工业仓储&#xff1a; 主要设计为大跨度、大空间&#xff0c;以便容纳大量货物。 气膜体育馆&#xff1a; 设计注重支撑观众席、运动场地和相关设施&…

Golang | Leetcode Golang题解之第274题H指数

题目&#xff1a; 题解&#xff1a; func hIndex(citations []int) int {// 答案最多只能到数组长度left,right:0,len(citations)var mid intfor left<right{// 1 防止死循环mid(leftright1)>>1cnt:0for _,v:range citations{if v>mid{cnt}}if cnt>mid{// 要找…

One-Class SVM

前提知识&#xff1a;支持向量机&#xff08;SVM&#xff09;-CSDN博客 主要思想 找一个超平面将样本中的正例圈出来&#xff0c;预测就是用这个超平面做决策&#xff0c;在圈内的样本就认为是正样本&#xff0c;圈外的是其他样本&#xff0c;如图1所示&#xff1a; 图1 OSVM…

完整创建一个vite前端项目

目录 1.先创建一个vite项目 2.下载第三方依赖 ① 安装路由vue-router ② 安装vuex全局数据管理 ③ 安装element-plus ④ 安装element-plus图标 ⑤ 安装axios发送请求 ⑥ 完整main.js代码模板 3.开发组件 4.登陆页面开发用例 5. 完整项目代码 废话少说&#xff0c;直接…

【C#】| 与 及其相关例子

按位或&#xff08;|&#xff09; 按位或运算符 | 对两个数的每一位进行比较&#xff0c;如果两个数中至少有一个为 1&#xff0c;则结果位为 1&#xff1b;否则&#xff0c;结果位为0。 1010 (10 in decimal) | 1100 (12 in decimal) ------1110 (14 in decimal) 力扣相关…

【MySQL进阶之路 | 高级篇】EXPLAIN的进一步使用

1. EXPLAIN的四种输出格式 EXPLAIN可以输出四种格式&#xff1a;传统格式&#xff0c;JSON格式&#xff0c;TREE格式以及可视化输出。用户可以根据需要选择使用于自己的格式。 1). 传统格式 传统格式简单明了&#xff0c;输出是一个表格形式。 2). JSON格式 第一种格式中介…

面向对象练习题

代码&#xff1a; public class LL {public static void main(String[] args) { Teacher tnew Teacher("Mike",32,"前端页面",20000);Professor P new Professor("Jack",58,"嵌入式开发",20000);pro p new pro("Bob",34,&q…

【规范】小程序发布,『小程序隐私保护指引』填写指南

前言 &#x1f34a;缘由 『小程序隐私保护指引』小程序发布&#xff0c;每次都躲不开&#xff01; &#x1f3c0;事情起因&#xff1a; 最近在帮朋友弄一个小项目&#xff0c;uniappunicloud壁纸小程序。虽然之前做过不少小程序&#xff0c;但是每次发布正式版本时都有一步『…

WSL的安装

&#x1f3bc;个人主页&#xff1a;金灰 &#x1f60e;作者简介:一名简单的大一学生;易编橙终身成长社群的嘉宾.✨ 专注网络空间安全服务,期待与您的交流分享~ 感谢您的点赞、关注、评论、收藏、是对我最大的认可和支持&#xff01;❤️ &#x1f34a;易编橙终身成长社群&#…

网络初识和网络编程(Java版)

前言 网络已经成为我们日常生活中不可分割的一部分&#xff0c;我们每天都会从网络上得到各种各样的信息&#xff0c;我们也会在网络上传播各种各样的信息&#xff0c;可以说我们使用的软件都是依赖于网络的。作为一个程序猿&#xff0c;在我们未来部署的软件中&#xff0c;客…

【存储学习笔记】1:机械硬盘(Hard Drive Disk)结构和寻址方式

目录 HDD的结构HDD的寻址方式CHS寻址&#xff08;不适用于等密度结构磁盘&#xff09;LBA寻址&#xff08;目前普遍使用的线性寻址方式&#xff09; HDD的寻址速度 HDD的结构 盘面&#xff08;Platter&#xff09;&#xff1a;单面或者双面覆盖着用于记录数据的磁性物质&#x…

《昇思25天学习打卡营第25天|第23天》

今天是打卡的第二十三天&#xff0c;今天学习的是应用实践篇中的计算机视觉中FCN图像语义分割。 首先&#xff0c;是对全卷积网络&#xff08;FCN&#xff09;的简介&#xff0c;语义分割的简介&#xff0c;模型简介&#xff08;1、卷积化&#xff0c;2、上采样&#xff0c;3、…

51单片机嵌入式开发:15、STC89C52RC操作蜂鸣器实现一个music音乐播放器的音乐盒

STC89C52RC操作蜂鸣器实现一个music音乐播放器的音乐盒 1 概述2 蜂鸣器操作方法3 蜂鸣器发出音声4 硬件电路5 软件实现6 整体工程&#xff1a;7 总结 1 概述 要实现一个基于STC89C52RC单片机的音乐盒&#xff0c;可以按照以下步骤进行&#xff1a; &#xff08;1&#xff09;硬…

STM32项目分享:智能宠物喂食系统

目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 1.PCB图 五、程序设计 六、实验效果 七、资料内容 项目分享 一、前言 项目成品图片&#xff1a; 哔哩哔哩视频链接&#xff1a; https://www.bilibili.com/video/BV1zy411z7…

关于对CSDN的谴责

关于对CSDN的谴责 如果不是心血来潮登了一次旧帐号我是万万不会想到&#xff0c;所有的文章都被设置成了仅VIP可见。 CSDN你的VIP有多不受人待见您不知道吗&#xff1f;为什么要把我用于你开通VIP刷绩效的工具&#xff1f; 这种东西不应该首先经过同意再开启吗&#xff1f;默认…

JavaWeb day01-HTML入门

Web前端 课程安排 HTML、CSS简介 HTML快速入门 实现标题排版 新闻标题样式