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

        DHT11的通信协议是单总线协议,可以用之前学习的pinctlgpio子系统完成某IO引脚上数据的读与写。

目录

一.在设备树下添加dht11的设备结点

1.流程图

2.设备树代码

(1).在设备树的 iomuxc结点下添加 pinctl_dht11

(2).在根节点下添加 dht11 结点

(3).在内核源码根目录下重新编译设备树文件

(4).将新的 dtb 文件更新到开发板上,检查是否有 dht11 这个结点

二.编写 dht11 时序代码

三.总的驱动代码

1.流程图

2.代码


一.在设备树下添加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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值