(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 …...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
