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

news2024/12/23 15:07:38

        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.coloradmin.cn/o/2183456.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决Excel时出现“被保护单元格不支持此功能“的解决办法,详细喂饭级教程

今天有个朋友发过来一个excel文件&#xff0c;本来想修改表格的内容&#xff0c;但是提示&#xff0c;被保护单元格不支持此功能&#xff0c;对于这个问题&#xff0c;找到一个解决方法&#xff0c;现记录下来&#xff0c;分享给有需要的朋友。 表格文件名为aaa.xls,以WPS为例。…

十一、磁盘的结构

1.磁盘的结构 磁盘 磁盘由表面涂有磁性物质的圆形盘片组成 磁道 每个盘片被划分为一个个磁道 扇区 每个磁道又划分为一个个扇区&#xff0c;每个扇区就是“磁盘块”&#xff0c;由于其容量相等&#xff0c;内磁道扇区面积小&#xff0c;故密度大。 盘面 磁盘有多个盘片“…

【AIGC】ChatGPT提示词解析:如何打造个人IP、CSDN爆款技术文案与高效教案设计

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;打造个人IP爆款文案提示词使用方法 &#x1f4af;CSDN爆款技术文案提示词使用方法 &#x1f4af;高效教案设计提示词使用方法 &#x1f4af;小结 &#x1f4af;前言 在这…

时序必读论文14|VLDB24 TFB:全面且公平的时间序列预测方法框架

论文标题&#xff1a;TFB: Towards Comprehensive and Fair Benchmarking of Time Series Forecasting Methods 论文链接&#xff1a;https://arxiv.org/pdf/2403.20150.pdf 代码链接&#xff1a;https://github.com/decisionintelligence/TFB 前言 五一过后读的第一篇文章…

MySQL 大数据量导入与导出全攻略

《MySQL 大数据量导入与导出全攻略》 在实际的数据库应用中&#xff0c;我们经常会遇到需要处理大数据量的导入和导出的情况。无论是数据迁移、备份恢复&#xff0c;还是数据共享&#xff0c;高效地处理大数据量都是至关重要的。那么&#xff0c;MySQL 是如何应对大数据量的导…

深度学习 Transformer 的标签平滑(Label Smoothing)

01 引言 标签平滑&#xff08;Label Smoothing&#xff09;是一种正则化技术&#xff0c;用于深度学习中的分类任务&#xff0c;尤其是在Transformer模型中。它的目的是减少模型对于训练数据中硬标签&#xff08;hard labels&#xff0c;即标准的one-hot编码&#xff09;的过…

期权卖方怎么选择权利金高的品种,期货VIX高低对行情有什么影响

VIX指数——全称为芝加哥期权交易所市场波动率指数&#xff0c;俗称恐慌指数。 是衡量波动性的重要指标。VIX指数上升&#xff0c;预期未来市场波动性会增加。VIX指数下降&#xff0c;预期未来市场波动性会降低。 期货VIX指数最新价格排序 期权卖方尽量选择期货VIX指数在25以…

【亲测】windows快捷键冲突检测(可删除)-OpenArk

官方下载链接&#xff1a;Releases BlackINT3/OpenArk (github.com) CSDN下载链接&#xff1a;【免费】windows快捷键冲突检测&#xff1a;OpenArk-v1.3.6.zip资源-CSDN文库 内核 -> 系统热键 -> 进入内核模式

10.2 Linux_并发_进程相关函数

创建子进程 函数声明如下&#xff1a; pid_t fork(void); 返回值&#xff1a;失败返回-1&#xff0c;成功返回两次&#xff0c;子进程获得0(系统分配)&#xff0c;父进程获得子进程的pid 注意&#xff1a;fork创建子进程&#xff0c;实际上就是将父进程复制一遍作为子进程&…

深度学习500问——Chapter17:模型压缩及移动端部署(3)

文章目录 17.7 压缩和加速方法如何选择 17.8 改变网络结构设计为什么会实现模型压缩、加速 17.8.1 Group convolution 17.8.2 Depthwise separable convolution 17.8.3 输入输出的channel相同时&#xff0c;MAC最小 17.8.4 减少组卷积的数量 17.8.5 减少网络碎片化程度&#xf…

【Vue】vue2项目打包后部署刷新404,配置publicPath ./ 不生效问题

Vue Router mode&#xff0c;为 history 无效&#xff0c;建议使用默认值 hash&#xff1b;

C++语言学习(2): name lookup 的概念

何谓 name lookup C 中很重要的一个概念&#xff1a;name lookup。 当编译器在遇到一个 name 的时候&#xff0c; 会做查找&#xff08;lookup&#xff09;&#xff0c;会把引入这个 name 的声明和它关联起来&#xff0c;具体来说&#xff0c;又包含两种类型的 lookup&#xf…

深蕾半导体Astra™ SL1620详细介绍,嵌入式物联网处理器

一&#xff0c;SL1620是什么 Astra™ SL系列是深蕾半导体推出的高度集成的嵌入式物联网处理器SoC&#xff08;System on Chip&#xff09;系列产品&#xff0c;专为多模式消费者、企业和工业物联网工作负载而设计。SL1620是Astra™ SL系列中的一款成本和功耗优化的安全嵌入式So…

数据结构-3.8.栈在括号匹配中的应用

一.括号匹配问题&#xff1a; 1.例一&#xff1a; 把左括号依次压入栈中&#xff0c;越往后压入栈的左括号越先被匹配即被弹出栈->先进后出&#xff0c;后进先出 2.例二&#xff1a; 当遇到左括号就压入栈中&#xff0c;当遇到右括号就把栈顶的左括号弹出&#xff0c;检查…

计算机毕业设计 基于协同过滤算法的个性化音乐推荐系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

Linux系统命令:用于改变用户的登录 Shell 的命令chsh命令详解

目录 一、概述 二、用法 1、基本用法 2、常用选项 3、获取帮助 三、示例 1. 更改当前用户的登录 Shell 2. 更改其他用户的登录 Shell 3、列出所有可用的 shell 四、 注意事项 1、已经安装好 2、权限 3、密码验证 4、shell 路径 5、生效时间 五、示例输出 一、概…

mfc140u.dll缺失?快速解决方法全解析,解决mfc140u.dll错误

当你的电脑出现找不到mfc140u.dll的问题&#xff0c;不少用户在使用电脑时陷入了困扰。这个错误提示就像一道屏障&#xff0c;阻挡了用户正常使用某些软件。无论是办公软件、游戏还是专业的设计工具&#xff0c;一旦出现这个问题&#xff0c;都会导致软件无法正常运行。如果您也…

复数表示的电场

Exm加是复振幅&#xff0c;这是用复数表示电场&#xff0c;并提取只与空间有关的项复振幅就是复数表示电场&#xff0c;且把与空间xyz有关的量提取出来 经过验证实数E0cos&#xff08;wtδx&#xff09;对t求导&#xff0c;等于E0e^j(wtδx)对t求导再取实部 实数表示电磁波cos…

Windows11系统下SkyWalking环境搭建教程

目录 前言SkyWalking简介SkyWalking下载Agent监控实现启动配置SkyWalking启动Java应用程序启动Elasticsearch安装总结 前言 本文为博主在项目环境搭建时记录的SkyWalking安装流程&#xff0c;希望对大家能够有所帮助&#xff0c;不足之处欢迎批评指正&#x1f91d;&#x1f91…