基于Tiny4412的DHT11温湿度传感器的Linux设备驱动的简单实现

论坛 期权论坛 脚本     
匿名网站用户   2020-12-21 03:38   35   0

本文的主要内容目录:

一、DHT11的工作原理

二、编写DHT11的驱动程序


一、DHT11的工作原理

1、DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传 感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性与卓越的长期稳定性。传感器包括一个电阻式感湿元件和一个NTC测 温元件,并与一个高性能8位单片机相连接。因此该产品具有品质卓越、超快 响应、抗干扰能力强、性价比极高等优点。每个DHT11传感器都在极为精确的 湿度校验室中进行校准。校准系数以程序的形式储存在OTP内存中,传感器内部在检测信号的处理过程中要调用这些校准系数。单线制串行接口,使系统集成变得简易快捷。超小的体积、极低的功耗,信号传输距离可达20米以上,使 其成为各类应用甚至最为苛刻的应用场合的最佳选则。如下图所示:


2、接口说明,建议连接线长度短于20米时用5K上拉电阻,大于20米时根据实际情况使 用合适的上拉电阻。典型接法如下图:


3、串行接口(单线双向)

数据传输格式:8bit湿度整数数据+8bit湿度小数数据++8bi温度整数数据+8bit温度小数数据+8bit校验和

通信时序图如下:


数据传输开始信号表示图如下:


数据传输中高电平的表示方法如下图:


数据传输中低电平的表示方法:


4、DHT11引脚说明

Pin 名称 注释
1 VDD 供电 3-5.5VDC
2 DATA 串行数据,单总线
3 NC 空脚,请悬空
4 GND 接地,电源负极


二、编写DHT11的驱动程序

1、开发环境

交叉编译环境 :Ubuntu12.04

交叉编译工具链版本 :4.5.1

内核版本: Linux-3.0.86

开发板:Tiny4412(Cortex-A9)

2、DHT11连接

用于开发板本身并没有DHT11芯片,所以需要外接,将芯片接到底板的没有被注册使用的扩展引脚即可,我这里用的是GPX1_7引脚。

3、Linux杂项设备

为了简单,将DHT11注册为杂项设备,这样就省去了为设备创造设备节点,分配主次设备号的步骤。

杂项设备的使用一般步骤:

a、定义一个杂项设备的结构体

b、初始化、设置这个杂项设备:比如名字,此设备号,操作函数等。

c、注册这个杂项设备

4、实现DHT11的设备驱动编写

4.1 定义、并设置这个杂项设备

/* 定义设备杂项设备结构体 */
static struct miscdevice dht11_miscdev = {
 .minor = MISC_DYNAMIC_MINOR, /* 自动分配次设备号 */
 .name = "dht11", /* 杂项设备的名字 */
 .fops = &dht11_fops /* 杂项设备操作的结构体 */
};
4.2 注册这个杂项设备

ret = misc_register(&dht11_miscdev);

4.3 实现DHT11的操作函数

/* 定义杂项设备操作的结构体并初始化成员函数指针 */
static const struct file_operations dht11_fops = {
 .owner    = THIS_MODULE,
 .open     = dht11_open,
 .read     = dht11_read,
 .release  = dht11_release,
};
4.4 DHT11的读时序

/* 杂项设备的读取函数 */
static ssize_t dht11_read (struct file *file, char __user *buffer, size_t size, loff_t *offset)
{
 int ret; 
 unsigned long flags;

 /* 因为DHT11的时序要求很高,所以在读温湿度的时候要让代码进入临界区,防止内核调度和抢占 */
    local_irq_save(flags);  
     dht11_read_data();  
     local_irq_restore(flags);  
 
 if(check_flag == 0xff)  
 {  
  /* 将读取的温湿度数据拷贝到用户空间 */
  ret = copy_to_user(buffer, dht11_data_buf, sizeof(dht11_data_buf));  
  if(ret < 0)
  {  
   printk("copy to user err\n");  
   return -EAGAIN;  
  }  
  else
  {
   return 0;  
  }
 }  
 else 
 {
   return -EAGAIN;
 }
}
读函数当中的核心函数是:dht11_read_data,它的实现如下:

/** read data format :
  *  8bit : humidity integer
  *  8bit : humidity decimal
  *  8bit : temperature integer
  *  8bit : temperature decimal
  *  8bit : check code
  * buf store data :
  *  dht11_data_buf[0] : humidity integer
  *  dht11_data_buf[1] : humidity decimal
  *  dht11_data_buf[2] : temperature integer
  *  dht11_data_buf[3]  : temperature decimal
  *  dht11_data_buf[4] : check code
  *  dht11_data_buf[5] : self calculate check code
  */
static void dht11_read_data(void)  
{  
    int i = 0;  
 
    dht11_gpio_out(0);  
    mdelay(30);  
    dht11_gpio_out(1);  
    udelay(20);  
 
    if(dht11_read_one_bit() == 0)  
    {   
        while(!gpio_get_value(DHT11_PIN))  /* 等待IO口变为高电平 */
        {  
           udelay(5);  
           i++;  
           if(i > 20)  
           {  
    printk("dht11_read_data %d err!\n",  __LINE__);  
    break;  
           }  
        }  
        i = 0;  
        while(gpio_get_value(DHT11_PIN))  /* 等待IO口变为低电平 */
        {  
           udelay(5);  
           i++;  
           if(i > 20)  
           {  
              printk("dht11_read_data %d err!\n", __LINE__);  
              break;  
           }  
        }  
        for(i = 0; i < 5; i++)  /* 读取温湿度数据 */
         dht11_data_buf[i] = dht11_read_byte();    

  /* 对读取到的数据进行校验 */
        dht11_data_buf[5] = dht11_data_buf[0]+dht11_data_buf[1]+dht11_data_buf[2]+dht11_data_buf[3];  

    /* 判断读到的校验值和计算的校验值是否相同 */
        if(dht11_data_buf[4] == dht11_data_buf[5]) /* 相同则把标志值设为0xff */  
        {  
           check_flag = 0xff;   
        }  
        else if(dht11_data_buf[4] != dht11_data_buf[5]) /* 不相同则把标志值设为0 */
        {  
           check_flag = 0x00;  
           printk("dht11 check fail\n");             
        }                     
    }  
}  
5、测试驱动

将驱动和测试程序(完整的驱动和测试程序见附录部分)进行编译,在开发板上安装驱动和执行测试函数,结果如下图:




附录:DHT11驱动程序和测试程序的代码如下。

驱动程序:

#include <asm/cacheflush.h>
#include <linux/fdtable.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/nsproxy.h>
#include <linux/poll.h>
#include <linux/debugfs.h>
#include <linux/rbtree.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/delay.h>

#include <asm/io.h>
#include <asm/irq.h>
#include <asm/gpio.h>

/* 定义DHT11接入的GPIO口 */
#define DHT11_PIN EXYNOS4_GPX1(7)

/* 定义检测标志,用来判断接收到的温湿度数据是否正确 */
static unsigned char check_flag; 
static unsigned char dht11_data_buf[6]; 

/* 
 * 读取一个bit的数据
 */
static int dht11_read_one_bit(void)          
{  
    gpio_direction_input(DHT11_PIN);  
    return gpio_get_value(DHT11_PIN);  
}  

/*set pin is out and set value of this pin*/
static void dht11_gpio_out(int value)   
{  
    gpio_direction_output(DHT11_PIN, value);  
}  

/*read a byte data from dht11*/
static unsigned char dht11_read_byte(void)  
{      
    int i = 0;  
    int num;   
    unsigned char flag = 0;  
    unsigned char data = 0;  
  
    for(num = 0; num < 8; num++)  
    {                
  i = 0;  
  /* 等待DHT11的引脚变为高电平 */
  while(!gpio_get_value(DHT11_PIN))  
  {  
   udelay(10);  
   i++;  
   if(i > 10)  
    break;  
  }  
  flag = 0x00;  
  udelay(28);              
  if(gpio_get_value(DHT11_PIN))  
  {  
   flag = 0x01;            
  }  
  i = 0;  
  while(gpio_get_value(DHT11_PIN))  
  {  
   udelay(10);  
   i++;  
   if(i > 12)  
    break;  
  }  
  data <<= 1;  
  data |= flag;  
    }    
    return data;  
}  

/** read data format :
  *  8bit : humidity integer
  *  8bit : humidity decimal
  *  8bit : temperature integer
  *  8bit : temperature decimal
  *  8bit : check code
  * buf store data :
  *  dht11_data_buf[0] : humidity integer
  *  dht11_data_buf[1] : humidity decimal
  *  dht11_data_buf[2] : temperature integer
  *  dht11_data_buf[3]  : temperature decimal
  *  dht11_data_buf[4] : check code
  *  dht11_data_buf[5] : self calculate check code
  */
static void dht11_read_data(void)  
{  
    int i = 0;  
 
    dht11_gpio_out(0);  
    mdelay(30);  
    dht11_gpio_out(1);  
    udelay(20);  
 
    if(dht11_read_one_bit() == 0)  
    {   
        while(!gpio_get_value(DHT11_PIN))  /* 等待IO口变为高电平 */
        {  
           udelay(5);  
           i++;  
           if(i > 20)  
           {  
    printk("dht11_read_data %d err!\n",  __LINE__);  
    break;  
           }  
        }  
        i = 0;  
        while(gpio_get_value(DHT11_PIN))  /* 等待IO口变为低电平 */
        {  
           udelay(5);  
           i++;  
           if(i > 20)  
           {  
              printk("dht11_read_data %d err!\n", __LINE__);  
              break;  
           }  
        }  
        for(i = 0; i < 5; i++)  /* 读取温湿度数据 */
         dht11_data_buf[i] = dht11_read_byte();    

  /* 对读取到的数据进行校验 */
        dht11_data_buf[5] = dht11_data_buf[0]+dht11_data_buf[1]+dht11_data_buf[2]+dht11_data_buf[3];  

    /* 判断读到的校验值和计算的校验值是否相同 */
        if(dht11_data_buf[4] == dht11_data_buf[5]) /* 相同则把标志值设为0xff */  
        {  
           check_flag = 0xff;   
        }  
        else if(dht11_data_buf[4] != dht11_data_buf[5]) /* 不相同则把标志值设为0 */
        {  
           check_flag = 0x00;  
           printk("dht11 check fail\n");             
        }                     
    }  
}  

/* 打开杂项设备,这里写为空函数 */
static int dht11_open (struct inode *inode, struct file *file)
{
 return 0;
} 

/* 杂项设备的读取函数 */
static ssize_t dht11_read (struct file *file, char __user *buffer, size_t size, loff_t *offset)
{
 int ret; 
 unsigned long flags;

 /* 因为DHT11的时序要求很高,所以在读温湿度的时候要让代码进入临界区,防止内核调度和抢占 */
    local_irq_save(flags);  
    dht11_read_data();  
    local_irq_restore(flags);  
 
 if(check_flag == 0xff)  
 {  
  /* 将读取的温湿度数据拷贝到用户空间 */
  ret = copy_to_user(buffer, dht11_data_buf, sizeof(dht11_data_buf));  
  if(ret < 0)
  {  
   printk("copy to user err\n");  
   return -EAGAIN;  
  }  
  else
  {
   return 0;  
  }
 }  
 else 
 {
   return -EAGAIN;
 }
}

/* 释放函数,这里写为空函数 */
static int dht11_release (struct inode *inode, struct file *file)
{
 return 0;
}

/* 定义杂项设备操作的结构体并初始化成员函数指针 */
static const struct file_operations dht11_fops = {
 .owner    = THIS_MODULE,
 .open     = dht11_open,
 .read     = dht11_read,
 .release  = dht11_release,
};

/* 定义设备杂项设备结构体 */
static struct miscdevice dht11_miscdev = {
 .minor = MISC_DYNAMIC_MINOR, /* 自动分配次设备号 */
 .name = "dht11", /* 杂项设备的名字 */
 .fops = &dht11_fops /* 杂项设备操作的结构体 */
};

/* 入口函数 */
static int yl_dht11_init(void)
{
 int ret;

 /* 申请对应的GPIO引脚资源,只有引脚资源没有被使用才可以申请成功 */
 ret = gpio_request(DHT11_PIN , "dht11");  
 if(ret)
 {
  printk("gpio_request for dht11 is failed!\n");
  return ret;
 }

 /* 注册杂项设备 */
 ret = misc_register(&dht11_miscdev);
 return ret;
}

/* 函数出口 */
static void yl_dht11_exit(void)
{
 /* 释放注册的杂项设备和GPIO资源 */
 misc_deregister(&dht11_miscdev);
 gpio_free(DHT11_PIN);
}

module_init(yl_dht11_init);
module_exit(yl_dht11_exit);

MODULE_LICENSE("GPL");

测试程序:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

/* 程序的入口函数 */
int main(int argc, char *argv[])
{
 int fd;
 unsigned char buf[6]; /* 定义存放数据的数组 */
 int length;

 /* 以只读方式打开设备节点 */
 fd = open("/dev/dht11", O_RDONLY);
 if(fd == -1)
 {
  printf("open failed!\n");
  return -1;
 }

 while(1)
 {
  length = read(fd, buf, 6); /* 读取温湿度数据 */
  if(length == -1)
  {
   printf("read error!\n");
   return -1;
  }
  /* 将数据从终端打印出来 */
  printf("Temp : %d, Humi : %d\n", buf[2], buf[0]);
  sleep(2);
 }

 /* 关闭DHT11设备节点 */
 close(fd);
 return 0;
}






分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:1136255
帖子:227251
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP