当前位置: 首页 > news >正文

(Linux驱动学习 - 4).Linux 下 DHT11 温湿度传感器驱动编写

        DHT11的通信协议是单总线协议,可以用之前学习的pinctlgpio子系统完成某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的通信协议是单总线协议&#xff0c;可以用之前学习的pinctl和gpio子系统完成某IO引脚上数据的读与写。 一.在设备树下添加dht11的设备结点 1.流程图 2.设备树代码 &#xff08;1&#xff09;.在设备树的 iomuxc结点下添加 pinctl_dht11 &#xff08;2&#xff09;.在根…...

前端登录页面验证码

首先&#xff0c;在el-form-item里有两个div&#xff0c;各占一半&#xff0c;左边填验证码&#xff0c;右边生成验证码 <el-form-item prop"code"><div style"display: flex " prop"code"><el-input placeholder"请输入验证…...

【鸿蒙】HarmonyOS NEXT应用开发快速入门教程之布局篇(上)

系列文章目录 【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器&#xff08;上&#xff09; 【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器&#xff08;下&#xff09; 【鸿蒙】HarmonyOS NEXT应用开发快速入门教程之布局篇&#xff08;上&#xff09; 文…...

使用 Nginx 和 Gunicorn 部署 Flask 项目详细教程

使用 Nginx 和 Gunicorn 部署 Flask 项目详细教程 在这篇文章中&#xff0c;我们将介绍如何使用 Nginx 和 Gunicorn 来部署一个 Flask 项目。这种部署方式非常适合在生产环境中使用&#xff0c;因为它能够提供更好的性能和更高的稳定性。 目录 Flask 项目简介环境准备Gunico…...

linux中bashrc和profile环境变量在Shell编程变量的传递作用

在 Linux 系统中&#xff0c;.bashrc文件和.profile文件都是用于配置用户环境的重要文件&#xff0c;它们之间有以下关联&#xff1a; 一、作用相似性 环境设置&#xff1a;两者都用于设置用户的环境变量和启动应用程序的配置。例如&#xff0c;它们可以定义路径变量&#xf…...

数据结构-4.2.串的定义和基本操作

一.串的定义&#xff1a; 1.单/双引号不是字符串里的内容&#xff0c;他只是一个边界符&#xff0c;用来表示字符串的头和尾&#xff1b; 2.空串也是字符串的子串&#xff0c;空串长度为0&#xff1b; 3.字符的编号是从1开始&#xff0c;不是0&#xff1b; 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 错误:无法写入配置文件 解决办法&#xff1a; 到C:\inetpub\history中找到最近一次的【CFGHISTORY_00000000XX】文件&#xff0c;点击进去找到applicationHost.config文件&#xff0c;用其覆盖C:\Win…...

Optiver股票大赛Top2开源!

Optiver股票大赛Top2开源&#xff01; ↑↑↑关注后"星标"kaggle竞赛宝典 作者&#xff1a;杰少 Optiver第二名方案解读 简介 Optiver竞赛已经于今天结束了&#xff0c;竞赛也出现了极端情况&#xff0c;中间断崖式的情况&#xff0c;在Kaggle过往的竞赛中&#…...

Maven 实现依赖统一管理

Maven 实现依赖统一管理主要是通过两个关键机制&#xff1a;pom.xml 文件中的 <dependencies> 节点用于声明项目依赖&#xff0c;以及通过继承&#xff08;Inheritance&#xff09;和聚合&#xff08;Aggregation&#xff09;功能来统一管理和组织这些依赖。此外&#xf…...

【最新】微信小程序连接onenet——stm32+esp8266+onenet实现查看温湿度,控制单片机

微信小程序——stm32esp8266onenet实现查看温湿度&#xff0c;控制单片机 &#xff08;最新已验证&#xff09;stm32 新版 onenet dht11esp8266/01s mqtt物联网上报温湿度和控制单片机(保姆级教程) &#xff1a;↓↓&#x1f447; &#x1f447; &#x1f447; &#x1f447…...

差分(续前缀和)(含一维二维)

题目引入 开发商小 Q 买下了一条街&#xff0c;他想在这条街的一边盖房子。 街道可以抽象为一条数轴&#xff0c;而小 Q 只会在坐标在 1~n 的范围内盖房子。 首先&#xff0c;小 Q 将街上坐标在 1∼ &#x1d45b;1∼ n 范围内的物体全部铲平。也就是说&#xff0c;在正式动工盖…...

【STM32-HAL库】自发电型风速传感器(使用STM32F407ZGT6)(附带工程下载链接)

一、自发电型风速传感器介绍 自发电型风速传感器&#xff0c;也称为风力发电型风速传感器或无源风速传感器&#xff0c;是一种不需要外部电源即可工作的风速测量设备。这种传感器通常利用风力来驱动内部的发电机构&#xff0c;从而产生电能来供电测量风速的传感器部分。以下是自…...

【计算机毕业设计】springboot就业信息管理系统

就业信息管理系统 摘 要 随着信息化时代的到来&#xff0c;管理系统都趋向于智能化、系统化&#xff0c;就业信息管理系统也不例外&#xff0c;但目前国内仍都使用人工管理&#xff0c;市场规模越来越大&#xff0c;同时信息量也越来越庞大&#xff0c;人工管理显然已无法应对时…...

实用工具推荐---- PDF 转换

直接上链接&#xff1a;爱PDF |面向 PDF 爱好者的在线 PDF 工具 (ilovepdf.com) 主要功能如下&#xff1a; 全免费&#xff01;&#xff01;&#xff01;&#xff01;...

安宝特案例 | 某知名日系汽车制造厂,借助AR实现智慧化转型

案例介绍 在全球制造业加速数字化的背景下&#xff0c;工厂的生产管理与设备维护效率愈发重要。 某知名日系汽车制造厂当前面临着设备的实时监控、故障维护&#xff0c;以及跨地域的管理协作等挑战&#xff0c;由于场地分散和突发状况的不可预知性&#xff0c;传统方式已无法…...

RabbitMQ基本原理

一、基本结构 所有中间件技术都是基于 TCP/IP 协议基础之上进行构建新的协议规范&#xff0c;RabbitMQ遵循的是AMQP协议&#xff08;Advanced Message Queuing Protocol - 高级消息队列协议&#xff09;。 生产者发送消息流程&#xff1a; 1、生产者和Broker建立TCP连接&#…...

【NodeJS】npm、yarn、pnpm当前项目设置国内镜像源

全局设置镜像源&#xff0c;可以参考下这篇文章&#xff0c;还挺详细&#xff1a;《npm、yarn、pnpm 最新国内镜像源设置和常见问题解决》 临时设置镜像源&#xff1a;《npm永久或临时切换源》 有时候可能要同时多个开发项目&#xff0c;又不想修改全局的镜像源(具体场景…自行…...

25考研咨询周开启,西安电子科技大学是否改考408??

学长这几天帮大家问了西安电子科技大学是否会从833、834、953改考为408&#xff1f; 西电老师回复&#xff1a;根据上级文件要求&#xff0c;招生简章以及专业目录会在网上报名开始前公布&#xff0c;专业课不会又大变动&#xff01; 因为大家安心复习即可&#xff0c;保证今…...

git(1) -- 环境配置

1. 配置文件 编辑~/.gitconfig文件&#xff0c;内容如下。 [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 …...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...