|
本文的主要内容目录:
一、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;
}
|