当前位置: 首页 > 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 …...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

EtherNet/IP转DeviceNet协议网关详解

一&#xff0c;设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络&#xff0c;本网关连接到EtherNet/IP总线中做为从站使用&#xff0c;连接到DeviceNet总线中做为从站使用。 在自动…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

算法:模拟

1.替换所有的问号 1576. 替换所有的问号 - 力扣&#xff08;LeetCode&#xff09; ​遍历字符串​&#xff1a;通过外层循环逐一检查每个字符。​遇到 ? 时处理​&#xff1a; 内层循环遍历小写字母&#xff08;a 到 z&#xff09;。对每个字母检查是否满足&#xff1a; ​与…...

vulnyx Blogger writeup

信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面&#xff0c;gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress&#xff0c;说明目标所使用的cms是wordpress&#xff0c;访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...

vue3 daterange正则踩坑

<el-form-item label"空置时间" prop"vacantTime"> <el-date-picker v-model"form.vacantTime" type"daterange" start-placeholder"开始日期" end-placeholder"结束日期" clearable :editable"fal…...

FFmpeg avformat_open_input函数分析

函数内部的总体流程如下&#xff1a; avformat_open_input 精简后的代码如下&#xff1a; int avformat_open_input(AVFormatContext **ps, const char *filename,ff_const59 AVInputFormat *fmt, AVDictionary **options) {AVFormatContext *s *ps;int i, ret 0;AVDictio…...

Pydantic + Function Calling的结合

1、Pydantic Pydantic 是一个 Python 库&#xff0c;用于数据验证和设置管理&#xff0c;通过 Python 类型注解强制执行数据类型。它广泛用于 API 开发&#xff08;如 FastAPI&#xff09;、配置管理和数据解析&#xff0c;核心功能包括&#xff1a; 数据验证&#xff1a;通过…...

土建施工员考试:建筑施工技术重点知识有哪些?

《管理实务》是土建施工员考试中侧重实操应用与管理能力的科目&#xff0c;核心考查施工组织、质量安全、进度成本等现场管理要点。以下是结合考试大纲与高频考点整理的重点内容&#xff0c;附学习方向和应试技巧&#xff1a; 一、施工组织与进度管理 核心目标&#xff1a; 规…...