(Linux驱动学习 - 4).Linux 下 DHT11 温湿度传感器驱动编写
DHT11的通信协议是单总线协议,可以用之前学习的pinctl和gpio子系统完成某IO引脚上数据的读与写。
一.在设备树下添加dht11的设备结点
1.流程图

2.设备树代码
(1).在设备树的 iomuxc结点下添加 pinctl_dht11

(2).在根节点下添加 dht11 结点

(3).在内核源码根目录下重新编译设备树文件
linux@ubuntu:~/IMX6ULL/my_linux_kernel$ make dtbs
(4).将新的 dtb 文件更新到开发板上,检查是否有 dht11 这个结点
启动后在/proc/device-tree/目录中查看是否有 dht11 这个节点。

二.编写 dht11 时序代码
#define DHT11_DelayMs(t) mdelay(t)
#define DHT11_DelayUs(t) udelay(t) #define DHT11_PIN_HIGH 1
#define DHT11_PIN_LOW 0 #define DHT11_IO_OUT() gpio_direction_output(dht11.dht11_gpio, 1);
#define DHT11_IO_IN() gpio_direction_input(dht11.dht11_gpio)
#define DHT11_WRITE(bit) gpio_set_value(dht11.dht11_gpio, bit)
#define DHT11_READ() gpio_get_value(dht11.dht11_gpio)/*** @description: 等待响应
*/
//等待响应
static int dht11_wait_for_ready(void)
{ int timeout;timeout = 400;while (DHT11_READ() && timeout) // 等待低电平到来 {udelay(1);--timeout;}if (!timeout) {printk("dht11_wait_for_ready timeout1 %d\n", __LINE__);return -1; // 超时 }timeout = 1000; //1000while (!DHT11_READ() && timeout) // 等待高电平到来 {udelay(1);--timeout;}if (!timeout) {printk("dht11_wait_for_ready timeout2 %d\n", __LINE__);return -1; // 超时 }timeout = 1000;while (DHT11_READ() && timeout) // 等待高电平结束{udelay(1);--timeout;}if (!timeout) {printk("dht11_wait_for_ready timeout3 %d\n", __LINE__);return -1; // 超时 }return 0;
}/*** @description: 起始信号
*/
static int dht11_start(void)
{DHT11_IO_OUT();DHT11_WRITE(0);mdelay(25);DHT11_WRITE(1);udelay(35);DHT11_IO_IN(); // 设置为输入 udelay(2);if (dht11_wait_for_ready()) return -1;return 0;
}/*** @description: 读取一个字节
*///读取数据
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("dht11_read_byte timeout1 %d\n", __LINE__); return -1; // 超时 }timeout = 1000;while (!DHT11_READ() && timeout) // 等待变为高电平 {udelay(1);--timeout;}if (!timeout) {printk("dht11_read_byte timeout2 %d\n", __LINE__);return -1; // 超时 }udelay(40);bit = DHT11_READ();data <<= 1; if (bit) {data |= 0x01;}}*byte = data;return 0;
}//从DHT11读取一次数据
//temp:温度值(范围:0~50°)
//humi:湿度值(范围:20%~90%)
//返回值:0,正常;1,读取失败
static int dht11_read_data(void)
{ unsigned char data[5] = {0};int i = 0,ret = 0;// 启动信号 if (dht11_start() != 0){printk("dht11 start failed\n");ret = -EFAULT;}// 读出5字节数据for (i = 0; i < 5; i++) {if (dht11_read_byte(&data[i])){printk("read data err\n");ret = -EAGAIN;}}if (data[4] != (data[0]+data[1]+data[2]+data[3])){printk("check data failed\n");ret = -EAGAIN;}dht11.humidity = data[0];dht11.temperature = data[2];return 0;
}
三.总的驱动代码
1.流程图

2.代码
#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.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <asm/mach/map.h>
#include <linux/timer.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define DHT11_CNT 1
#define DHT11_NAME "dht11"#define DHT11_DelayMs(t) mdelay(t)
#define DHT11_DelayUs(t) udelay(t) #define DHT11_PIN_HIGH 1
#define DHT11_PIN_LOW 0 #define DHT11_IO_OUT() gpio_direction_output(dht11.dht11_gpio, 1);
#define DHT11_IO_IN() gpio_direction_input(dht11.dht11_gpio)
#define DHT11_WRITE(bit) gpio_set_value(dht11.dht11_gpio, bit)
#define DHT11_READ() gpio_get_value(dht11.dht11_gpio)/* dht11设备结构体 */
struct dht11_dev
{dev_t devid;struct cdev cdev;struct class *class;struct device *device;int major;int minor;struct device_node *nd;int dht11_gpio;uint16_t humidity, temperature; //检测到的温湿度数据
};struct dht11_dev dht11;/*** @description: 等待响应
*/
//等待响应
static int dht11_wait_for_ready(void)
{ int timeout;timeout = 400;while (DHT11_READ() && timeout) // 等待低电平到来 {udelay(1);--timeout;}if (!timeout) {printk("dht11_wait_for_ready timeout1 %d\n", __LINE__);return -1; // 超时 }timeout = 1000; //1000while (!DHT11_READ() && timeout) // 等待高电平到来 {udelay(1);--timeout;}if (!timeout) {printk("dht11_wait_for_ready timeout2 %d\n", __LINE__);return -1; // 超时 }timeout = 1000;while (DHT11_READ() && timeout) // 等待高电平结束{udelay(1);--timeout;}if (!timeout) {printk("dht11_wait_for_ready timeout3 %d\n", __LINE__);return -1; // 超时 }return 0;
}/*** @description: 起始信号
*/
static int dht11_start(void)
{DHT11_IO_OUT();DHT11_WRITE(0);mdelay(25);DHT11_WRITE(1);udelay(35);DHT11_IO_IN(); // 设置为输入 udelay(2);if (dht11_wait_for_ready()) return -1;return 0;
}/*** @description: 读取一个字节
*///读取数据
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("dht11_read_byte timeout1 %d\n", __LINE__); return -1; // 超时 }timeout = 1000;while (!DHT11_READ() && timeout) // 等待变为高电平 {udelay(1);--timeout;}if (!timeout) {printk("dht11_read_byte timeout2 %d\n", __LINE__);return -1; // 超时 }udelay(40);bit = DHT11_READ();data <<= 1; if (bit) {data |= 0x01;}}*byte = data;return 0;
}//从DHT11读取一次数据
//temp:温度值(范围:0~50°)
//humi:湿度值(范围:20%~90%)
//返回值:0,正常;1,读取失败
static int dht11_read_data(void)
{ unsigned char data[5] = {0};int i = 0,ret = 0;// 启动信号 if (dht11_start() != 0){printk("dht11 start failed\n");ret = -EFAULT;}// 读出5字节数据for (i = 0; i < 5; i++) {if (dht11_read_byte(&data[i])){printk("read data err\n");ret = -EAGAIN;}}if (data[4] != (data[0]+data[1]+data[2]+data[3])){printk("check data failed\n");ret = -EAGAIN;}dht11.humidity = data[0];dht11.temperature = data[2];return 0;
}/*** @description: DHT11初始化函数
*/
static int dht11io_init(void)
{/* 找到设备树中的结点 */dht11.nd = of_find_node_by_path("/dht11");if(NULL == dht11.nd){return -EINVAL;}/* 获取io编号 */dht11.dht11_gpio = of_get_named_gpio(dht11.nd,"dht11-gpio",0);if(0 > dht11.dht11_gpio){printk("can not get dht11 io\r\n");return -EINVAL;}printk("dht11 gpio num = %d \r\n",dht11.dht11_gpio);/* 初始化io */gpio_request(dht11.dht11_gpio,"dht11a");gpio_direction_output(dht11.dht11_gpio,1); //初始化为输出高电平return 0;
}/*** @description: 打开DHT11设备* @param - inode : 传递给驱动的inode* @param - filp : 设备文件* @return : 0 成功,其他 失败
*/
static int dht11_open(struct inode *inode,struct file *filp)
{int ret = 0;filp->private_data = &dht11;ret = dht11io_init();if(0 > ret){return ret;}return 0;
}/*** @description: 读取dht11的数据* @param - filp : 文件描述符* @param - buf : 传递给用户空间的缓冲区* @param - cnt : 要读取的字节数* @param - offt : 相对于文件首地址的偏移量* @return : 读取到的字节数
*/
static ssize_t dht11_read(struct file *filp,char __user *buf,size_t cnt,loff_t *offt)
{int ret = 0;uint16_t databuf[2] = {0,0};dht11_read_data();databuf[0] = dht11.humidity;databuf[1] = dht11.temperature;ret = copy_to_user(buf,databuf,sizeof(databuf));return ret;
}/*** @description: 释放设备* @param - filp : 设备文件* @return : 0 成功,其他 失败
*/
static int dht11_release(struct inode *inode,struct file *filp)
{return 0;
}/* 绑定操作函数 */
struct file_operations dht11_fops =
{.owner = THIS_MODULE,.open = dht11_open,.read = dht11_read,.release = dht11_release,
};/*** @description: 驱动入口函数* @param - : 无* @return : 无
*/
static int __init dht11_init(void)
{/* 注册字符设备驱动 *//* 1.创建设备号 */if(dht11.major){dht11.devid = MKDEV(dht11.major,0);register_chrdev_region(dht11.devid,DHT11_CNT,DHT11_NAME);}else{alloc_chrdev_region(&dht11.devid,0,DHT11_CNT,DHT11_NAME);dht11.major = MAJOR(dht11.devid);dht11.minor = MINOR(dht11.devid);}printk("dht11 major = %d,minor = %d\r\n",dht11.major,dht11.minor);/* 2.初始化cdev */dht11.cdev.owner = THIS_MODULE;cdev_init(&dht11.cdev,&dht11_fops);/* 3.添加一个cdev */cdev_add(&dht11.cdev,dht11.devid,DHT11_CNT);/* 4.创建类 */dht11.class = class_create(THIS_MODULE,DHT11_NAME);if(IS_ERR(dht11.class)){return PTR_ERR(dht11.class);}/* 5.创建设备 */dht11.device = device_create(dht11.class,NULL,dht11.devid,NULL,DHT11_NAME);if(IS_ERR(dht11.device)){return PTR_ERR(dht11.device);}return 0;
}/*** @description: 驱动出口函数
*/
static void __exit dht11_exit(void)
{/* 注销字符设备驱动 */gpio_free(dht11.dht11_gpio);cdev_del(&dht11.cdev);unregister_chrdev_region(dht11.devid,DHT11_CNT);device_destroy(dht11.class,dht11.devid);class_destroy(dht11.class);
}module_init(dht11_init);
module_exit(dht11_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("kaneki");
Makefile:
KERNELDIR := /home/linux/IMX6ULL/my_linux_kernel
CURRENT_PATH :=$(shell pwd)
obj-m := dht11.o
build: kernel_modules
kernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
相关文章:
(Linux驱动学习 - 4).Linux 下 DHT11 温湿度传感器驱动编写
DHT11的通信协议是单总线协议,可以用之前学习的pinctl和gpio子系统完成某IO引脚上数据的读与写。 一.在设备树下添加dht11的设备结点 1.流程图 2.设备树代码 (1).在设备树的 iomuxc结点下添加 pinctl_dht11 (2).在根…...
前端登录页面验证码
首先,在el-form-item里有两个div,各占一半,左边填验证码,右边生成验证码 <el-form-item prop"code"><div style"display: flex " prop"code"><el-input placeholder"请输入验证…...
【鸿蒙】HarmonyOS NEXT应用开发快速入门教程之布局篇(上)
系列文章目录 【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器(上) 【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器(下) 【鸿蒙】HarmonyOS NEXT应用开发快速入门教程之布局篇(上) 文…...
使用 Nginx 和 Gunicorn 部署 Flask 项目详细教程
使用 Nginx 和 Gunicorn 部署 Flask 项目详细教程 在这篇文章中,我们将介绍如何使用 Nginx 和 Gunicorn 来部署一个 Flask 项目。这种部署方式非常适合在生产环境中使用,因为它能够提供更好的性能和更高的稳定性。 目录 Flask 项目简介环境准备Gunico…...
linux中bashrc和profile环境变量在Shell编程变量的传递作用
在 Linux 系统中,.bashrc文件和.profile文件都是用于配置用户环境的重要文件,它们之间有以下关联: 一、作用相似性 环境设置:两者都用于设置用户的环境变量和启动应用程序的配置。例如,它们可以定义路径变量…...
数据结构-4.2.串的定义和基本操作
一.串的定义: 1.单/双引号不是字符串里的内容,他只是一个边界符,用来表示字符串的头和尾; 2.空串也是字符串的子串,空串长度为0; 3.字符的编号是从1开始,不是0; 4.空格也是字符&a…...
fastzdp_redis第一次开发, 2024年9月26日, Python操作Redis零基础快速入门
提供完整录播课 安装 pip install fastzdp_redisPython连接Redis import redis# 建立链接 r redis.Redis(hostlocalhost, port6379, db0)# 设置key r.set(foo, bar)# 获取key的值 print(r.get(foo))RESP3 支持 简单的理解: 支持更丰富的数据类型 参考文档: https://blog.c…...
文件名:\\?\C:\Windows\system32\inetsrv\config\applicationHost.config错误:无法写入配置文件
文件名: \\?\C:\Windows\system32\inetsrv\config\applicationHost.config 错误:无法写入配置文件 解决办法: 到C:\inetpub\history中找到最近一次的【CFGHISTORY_00000000XX】文件,点击进去找到applicationHost.config文件,用其覆盖C:\Win…...
Optiver股票大赛Top2开源!
Optiver股票大赛Top2开源! ↑↑↑关注后"星标"kaggle竞赛宝典 作者:杰少 Optiver第二名方案解读 简介 Optiver竞赛已经于今天结束了,竞赛也出现了极端情况,中间断崖式的情况,在Kaggle过往的竞赛中&#…...
Maven 实现依赖统一管理
Maven 实现依赖统一管理主要是通过两个关键机制:pom.xml 文件中的 <dependencies> 节点用于声明项目依赖,以及通过继承(Inheritance)和聚合(Aggregation)功能来统一管理和组织这些依赖。此外…...
【最新】微信小程序连接onenet——stm32+esp8266+onenet实现查看温湿度,控制单片机
微信小程序——stm32esp8266onenet实现查看温湿度,控制单片机 (最新已验证)stm32 新版 onenet dht11esp8266/01s mqtt物联网上报温湿度和控制单片机(保姆级教程) :↓↓👇 👇 👇 👇…...
差分(续前缀和)(含一维二维)
题目引入 开发商小 Q 买下了一条街,他想在这条街的一边盖房子。 街道可以抽象为一条数轴,而小 Q 只会在坐标在 1~n 的范围内盖房子。 首先,小 Q 将街上坐标在 1∼ 𝑛1∼ n 范围内的物体全部铲平。也就是说,在正式动工盖…...
【STM32-HAL库】自发电型风速传感器(使用STM32F407ZGT6)(附带工程下载链接)
一、自发电型风速传感器介绍 自发电型风速传感器,也称为风力发电型风速传感器或无源风速传感器,是一种不需要外部电源即可工作的风速测量设备。这种传感器通常利用风力来驱动内部的发电机构,从而产生电能来供电测量风速的传感器部分。以下是自…...
【计算机毕业设计】springboot就业信息管理系统
就业信息管理系统 摘 要 随着信息化时代的到来,管理系统都趋向于智能化、系统化,就业信息管理系统也不例外,但目前国内仍都使用人工管理,市场规模越来越大,同时信息量也越来越庞大,人工管理显然已无法应对时…...
实用工具推荐---- PDF 转换
直接上链接:爱PDF |面向 PDF 爱好者的在线 PDF 工具 (ilovepdf.com) 主要功能如下: 全免费!!!!...
安宝特案例 | 某知名日系汽车制造厂,借助AR实现智慧化转型
案例介绍 在全球制造业加速数字化的背景下,工厂的生产管理与设备维护效率愈发重要。 某知名日系汽车制造厂当前面临着设备的实时监控、故障维护,以及跨地域的管理协作等挑战,由于场地分散和突发状况的不可预知性,传统方式已无法…...
RabbitMQ基本原理
一、基本结构 所有中间件技术都是基于 TCP/IP 协议基础之上进行构建新的协议规范,RabbitMQ遵循的是AMQP协议(Advanced Message Queuing Protocol - 高级消息队列协议)。 生产者发送消息流程: 1、生产者和Broker建立TCP连接&#…...
【NodeJS】npm、yarn、pnpm当前项目设置国内镜像源
全局设置镜像源,可以参考下这篇文章,还挺详细:《npm、yarn、pnpm 最新国内镜像源设置和常见问题解决》 临时设置镜像源:《npm永久或临时切换源》 有时候可能要同时多个开发项目,又不想修改全局的镜像源(具体场景…自行…...
25考研咨询周开启,西安电子科技大学是否改考408??
学长这几天帮大家问了西安电子科技大学是否会从833、834、953改考为408? 西电老师回复:根据上级文件要求,招生简章以及专业目录会在网上报名开始前公布,专业课不会又大变动! 因为大家安心复习即可,保证今…...
git(1) -- 环境配置
1. 配置文件 编辑~/.gitconfig文件,内容如下。 [user]email xflming163.comname xflm [core]editor vim [color]diff autostatus autobranch autoui true [commit]template /home/xflm/configuser/git-commit.template [diff]tool bc4 [difftool]prompt …...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
vue3 daterange正则踩坑
<el-form-item label"空置时间" prop"vacantTime"> <el-date-picker v-model"form.vacantTime" type"daterange" start-placeholder"开始日期" end-placeholder"结束日期" clearable :editable"fal…...
