(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; //1000
    while (!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; //1000
    while (!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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HuggingChat macOS 版现已发布

Hugging Face 的开源聊天应用程序 Hugging Chat&#xff0c;现已推出适用于 macOS 的版本。 主要特点 Hugging Chat macOS 版本具有以下亮点: 强大的模型支持: 用户可以一键访问多个顶尖的开源大语言模型&#xff0c;包括 Qwen 2.5 72B、Command R、Phi 3.5、Mistral 12B 等等&…

WebRTC入门

主要参考资料&#xff1a; WebRTC 在 ESP32 系列硬件平台上的实现: https://www.bilibili.com/video/BV1AEHseWEda/?spm_id_from333.337.search-card.all.click&vd_sourcedd284033cd0c4d1f3f59a2cd40ae4ef9 火山 RTC豆包大模型&#xff0c;给用户体验装上银色子弹: https:…

【网络安全】Cookie与ID未强绑定导致账户接管

未经许可,不得转载。 文章目录 前言正文前言 DigiLocker 是一项在线服务,旨在为公民提供一个安全的数字平台,用于存储和访问重要的文档,如 Aadhaar 卡、PAN 卡和成绩单等。DigiLocker 通过多因素身份验证(MFA)来保护用户账户安全,通常包括 6 位数的安全 PIN 和一次性密…

【RabbitMQ】面试题

在本篇文章中&#xff0c;主要是介绍RabbitMQ一些常见的面试题。对于前几篇文章的代码&#xff0c;都已经在码云中给出&#xff0c;链接是mq-test: 学习RabbitMQ的一些简单案例 (gitee.com)&#xff0c;如果存在问题的话欢迎各位提出&#xff0c;望共同进步。 MQ的作用以及应用…

sentinel原理源码分析系列(一)-总述

背景 微服务是目前java主流开发架构&#xff0c;微服务架构技术栈有&#xff0c;服务注册中心&#xff0c;网关&#xff0c;熔断限流&#xff0c;服务同学&#xff0c;配置中心等组件&#xff0c;其中&#xff0c;熔断限流主要3个功能特性&#xff0c;限流&#xff0c;熔断&…

《OpenCV》—— 指纹验证

用两张指纹图片中的其中一张对其验证 完整代码 import cv2def cv_show(name, img):cv2.imshow(name, img)cv2.waitKey(0)def verification(src, model):sift cv2.SIFT_create()kp1, des1 sift.detectAndCompute(src, None)kp2, des2 sift.detectAndCompute(model, None)fl…

使用 Llama 3.1 和 Qdrant 构建多语言医疗保健聊天机器人的步骤

长话短说&#xff1a; 准备好深入研究&#xff1a; 矢量存储的复杂性以及如何利用 Qdrant 进行高效数据摄取。掌握 Qdrant 中的集合管理以获得最佳性能。释放上下文感知响应的相似性搜索的潜力。精心设计复杂的 LangChain 工作流程以增强聊天机器人的功能。将革命性的 Llama …

在线代码编辑器

在线代码编辑器 文章说明前台核心代码后台核心代码效果展示源码下载 文章说明 采用Java结合vue3设计实现的在线代码编辑功能&#xff0c;支持在线编辑代码、运行代码&#xff0c;同时支持导入文件&#xff0c;支持图片识别&#xff0c;支持复制代码&#xff0c;可将代码导出为图…

《RabbitMQ篇》基本概念介绍

MQ功能 解耦 MQ允许不同系统或组件之间松散耦合。发送者和接收者不需要直接连接&#xff0c;从而提高了系统的灵活性和可维护性。异步处理 使用MQ可以实现异步消息传递&#xff0c;发送者可以将消息放入队列后立即返回&#xff0c;不必等待接收者处理。这提高了系统的响应速度…

Kafka学习笔记(一)Kafka基准测试、幂等性和事务、Java编程操作Kafka

文章目录 前言4 Kafka基准测试4.1 基于1个分区1个副本的基准测试4.2 基于3个分区1个副本的基准测试4.3 基于1个分区3个副本的基准测试 5 Java编程操作Kafka5.1 引入依赖5.2 向Kafka发送消息5.3 从Kafka消费消息5.4 异步使用带有回调函数的生产消息 6 幂等性6.1 幂等性介绍6.2 K…

【BurpSuite】SQL注入 | SQL injection(1-2)

&#x1f3d8;️个人主页&#xff1a; 点燃银河尽头的篝火(●’◡’●) 如果文章有帮到你的话记得点赞&#x1f44d;收藏&#x1f497;支持一下哦 【BurpSuite】SQL注入 | SQL injection&#xff08;1-2&#xff09; 实验一 Lab: SQL injection vulnerability in WHERE clause…

基于微信的乐室预约小程序+ssm(lw+演示+源码+运行)

摘 要 随着社会的发展&#xff0c;社会的方方面面都在利用信息化时代的优势。互联网的优势和普及使得各种系统的开发成为必需。 本文以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#xff0c;它主要是采用java语言技术和mysql数据库来完成对系统的设计。整个…

字体文件压缩

技术点 npm、html、font-spider 实现原理 个人理解&#xff1a;先引入原先字体&#xff0c;然后重置字符为空&#xff0c;根据你自己填充文字、字符等重新生成字体文件&#xff0c;因此在引入的时候务必添加自己使用的文字、字符等&#xff01;&#xff01;&#xff01; 实…

TDengine 流计算与窗口机制的深度解析:揭示计数窗口的关键作用

在 TDengine 3.2.3.0 版本中&#xff0c;我们针对流式计算新增了计数窗口&#xff0c;进一步优化了流式数据处理的能力。本文将为大家解读流式计算与几大窗口的关系&#xff0c;并针对新增的计数窗口进行详细的介绍&#xff0c;帮助大家进一步了解 TDengine 流式计算&#xff0…

解决MySQL报Incorrect datetime value错误

目录 一、前言二、问题分析三、解决方法 一、前言 欢迎大家来到权权的博客~欢迎大家对我的博客进行指导&#xff0c;有什么不对的地方&#xff0c;我会及时改进哦~ 博客主页链接点这里–>&#xff1a;权权的博客主页链接 二、问题分析 这个错误通常出现在尝试将一个不…

沂机管理系统/data/Ajax.aspx接口存在SQL注入漏洞

漏洞描述 沂机管理系统/data/Ajax.aspx接口存在SQL注入漏洞&#xff0c;攻击者可以获取服务器权限 漏洞复现 body"后台管理系统演示版" POC GET /data/Ajax.aspx?methodlog_list&page1&limit20&fkey1&fdate12024-10-0100%3A00%3A00&fdate2…

Postgresql源码(136)syscache/relcache 缓存及失效机制

相关 《Postgresql源码&#xff08;45&#xff09;SysCache内存结构与搜索流程分析》 0 总结速查 syscache&#xff1a;缓存系统表的行。通用数据结构&#xff0c;可以缓存一切数据&#xff08;hash dlist&#xff09;。可以分别缓存单行和多行查询。 syscache使用CatCache数…

AI 对话工具汇总

&#x1f423;个人主页 可惜已不在 &#x1f424;这篇在这个专栏AI_可惜已不在的博客-CSDN博客 &#x1f425;有用的话就留下一个三连吧&#x1f63c; 目录 前言: 正文: 前言: 在科技飞速发展的时代&#xff0c;AI 对话正逐渐成为我们获取信息、交流思想的新方式。它以强…

算法笔记(四)——模拟

算法笔记&#xff08;四&#xff09;——模拟 文章目录 算法笔记&#xff08;四&#xff09;——模拟替换所有的问号提莫攻击Z字形变换外观数列数青蛙 模拟算法就是根据题目的要求&#xff0c;题目让干神马就做神马&#xff0c;一步一步来 替换所有的问号 题目&#xff1a;替换…

初识TCP/IP协议

回顾上文 来回顾一下TCP协议的特性&#xff0c;有一道比较经典的题&#xff1a;如何使用UDP实现可靠传输&#xff0c;通过应用程序的代码&#xff0c;完成可靠传输的过程&#xff1f; 原则&#xff0c;TCO有啥就吹啥&#xff0c;引入滑动窗口&#xff0c;引入流量控制&#x…