您的位置:首页 > 其它

自己DIY一个智能家居模型框架--DHT11驱动

2016-10-01 17:41 295 查看
这说讲一下DHT11驱动,一共有3个引脚分别是VCC , GND , DATA。

使用起来也是非常的简单,手册里有非常详细的时序图(还是中文的)。

步骤一:

DHT11上电后(DHT11上电后要等待 1S 以越过不稳定状态在此期间不能发送任何指令),测试环境温湿度数据,并记录数据,同时 DHT11的DATA数据线由上拉电阻拉高一直保持高电平;此时 DHT11的 DATA 引脚处于输入状态,时刻检测外部信号。

步骤二:

微处理器的I/O设置为输出同时输出低电平,且低电平保持时间不能小于18ms,然后微处理器的I/O设置为输入状态,由于上拉电阻,微处理器的I/O即DHT11的DATA数据线也随之变高,等待DHT11作出回答信号,发送信号如图所示:

步骤三:

DHT11的DATA引脚检测到外部信号有低电平时,等待外部信号低电平结束,延迟后DHT11的DATA引脚处于输出状态,输出 80微秒的低电平作为应答信号,紧接着输出 80 微秒的高电平通知外设准备接收数据,微处理器的 I/O 此时处于输入状态,检测到 I/O 有低电平(DHT11回应信号)后,等待80微秒的高电平后的数据接收,发送信号如图所示:

步骤四:

由DHT11的DATA引脚输出40位数据,微处理器根据I/O电平的变化接收40位数据,位数据“0”的格式为: 50 微秒的低电平和 26-28 微秒的高电平,位数据“1”的格式为: 50 微秒的低电平加70微秒的高电平。位数据“0”、“1”格式信号如图所示:

结束信号:

DHT11的DATA引脚输出40位数据后,继续输出低电平50微秒后转为输入状态,由于上拉电阻随之变为高电平。但DHT11内部重测环境温湿度数据,并记录数据,等待外部信号的到来。

以上就是整体的时序操作方法,还要多说一句就是DHT11对是需要求很严格,我最开始怎么调也出不来数据,最后经过网友的博客才想起中断影响时间延时的问题,因此需要屏蔽掉中断在进行操作,就没有问题了。

驱动是直接用我以前写的18b20改的,因此里面有一些忘记改的名称或函数,说明一下不要被迷惑。

/*基本linux头文件,用于定义驱动函数进出口与协议*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
/*驱动注册的头文件,包含驱动的结构体和注册和卸载的函数*/
#include <linux/platform_device.h>
/*注册杂项设备头文件*/
#include <linux/miscdevice.h>
/*注册设备节点的文件结构体*/
#include <linux/fs.h>

/*inux中申请GPIO的头文件*/
#include <linux/gpio.h>
/*三星平台的GPIO配置函数头文件*/
/*三星平台EXYNOS系列平台,GPIO配置参数宏定义头文件*/
#include <plat/gpio-cfg.h>
#include <mach/gpio.h>
/*三星平台4412平台,GPIO宏定义头文件*/
#include <mach/gpio-exynos4.h>
#include <asm/uaccess.h>

/*在平台文件中定义的宏里name的命名*/
#define DRIVER_NAME "18b20_train"
#define DEVICE_NAME "ds18b20_train"
/*1,GPL协议,2,作者名*/
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("C.L.");

static int DHT11_gpios[] = {EXYNOS4_GPX0(1),EXYNOS4212_GPJ1(0)};

static int chose = 0;
unsigned char buff_data[6];
unsigned char check_flag;
#define DHT11_NUM		ARRAY_SIZE(DHT11_gpios)

/*io口控制,接受由上层传递的数据cmd、arg*/
#define Delay_us(x)		udelay(x)
#define Delay_ms(x)		mdelay(x)
/* #define SetDS18B20IOout()		s3c_gpio_cfgpin(DHT11_gpios[0], S3C_GPIO_OUTPUT)
#define SetDS18B20IOin()		s3c_gpio_cfgpin(DHT11_gpios[0], S3C_GPIO_INPUT)
#define WriteDS18B20IO(data)	gpio_set_value(DHT11_gpios[0], data)
#define ReadDS18B20IO()			gpio_get_value(DHT11_gpios[0]) */

void SetDS18B20IOout(void){
s3c_gpio_cfgpin(DHT11_gpios[chose], S3C_GPIO_OUTPUT);
}

void SetDS18B20IOin(void){
s3c_gpio_cfgpin(DHT11_gpios[chose], S3C_GPIO_INPUT);
}

void WriteDS18B20IO(int data){
gpio_set_value(DHT11_gpios[chose], data);
}

int ReadDS18B20IO(void){
return (gpio_get_value(DHT11_gpios[chose]));
}

static unsigned char DHT11ReadByte(void)
{
int time,i;
unsigned char data;
unsigned char flag;
data = 0x0;
flag = 0x0;
for (i = 0; i < 8; i++)
{
time = 0;

while(!ReadDS18B20IO())
{
time++;
if (time >20)
{
return -1;
printk("Read error1!\n");
}
udelay(5);
}
udelay(30);
flag=0x0;
if(ReadDS18B20IO())
{
flag=0x01;
}
time = 0;
while(ReadDS18B20IO())
{
time++;
if (time > 100)
{
return -1;
printk(">70 error2!!\n");
}
udelay(5);
}

data <<= 1;

data |= flag;

}

return (data);
}
static unsigned char DHT11ReadDate(void)
{
int time, i ;
char sum;

SetDS18B20IOout();
WriteDS18B20IO(0);
Delay_ms(30);
//	msleep(16);//msleep是忙等待时间会超过16ms达到18ms
WriteDS18B20IO(1);
Delay_us(30);
SetDS18B20IOin();

time = 0;
if(ReadDS18B20IO()== 0)
{
while (!ReadDS18B20IO())
{
time++;
if (time > 20)
{
printk("DHT11_read_data %d err!\n",__LINE__);
return -2;
}
Delay_us(5);
}
time = 0;
while (ReadDS18B20IO())
{
time++;
if (time > 20)
{
printk("DHT11_read_data %d err!\n",__LINE__);
return -2;
}
udelay(5);
}

for (i = 0; i < 5; i++)
{
buff_data[i] = DHT11ReadByte();
if (buff_data[i] == -1)
{
return -3;
}
}
sum = 0;

for (i = 0; i < 4; i++)
{
sum += buff_data[i];
}

if (sum != buff_data[i])
{
check_flag=0x0;
printk("humidity check fail\n");
return -4;
}else{
check_flag=0xff;
printk("humidity check pass\n");
printk("humidity=[%d],temp=[%d]\n",buff_data[0],buff_data[2]);
}

SetDS18B20IOout();
WriteDS18B20IO(1);
for (i = 0; i < 5; i++)
{
printk(KERN_EMERG "DHdata[%d] = %d\n",i,buff_data[i]);
}
}
return 0;
}

static ssize_t DHT11_Read(struct file *file, char  *buff, size_t count, loff_t *f_pos)
{
int ret;
local_irq_disable(); //屏蔽外部中断
DHT11ReadDate();
local_irq_enable(); //矢能外部中断
if(check_flag==0xff){

ret=copy_to_user(buff,(char *)buff_data, count);
if(ret<0){
printk("copy to user err\n");
return -EAGAIN;
}else{
printk("copy to user success\n");
}

return  0;
}
return count;
}

/*io口控制,接受由上层传递的数据cmd、arg*/
static long DHT11_ioctl( struct file *files, unsigned int cmd, unsigned long arg){

printk("cmd is %lu,arg is %lu\n",cmd,arg);
if(cmd > 1){
printk(KERN_EMERG "cmd is 0 or 1\n");
}
if(arg > 2){
printk(KERN_EMERG "arg is 0~2\n");
}

chose = cmd;

return 0;
}

static int DHT11_release(struct inode *inode, struct file *file){
printk(KERN_EMERG "DHT11 release\n");
return 0;
}

static int DHT11_open(struct inode *inode, struct file *file){
printk(KERN_EMERG "DHT11 open\n");
return 0;
}

/*注册设备节点文件结构体*/
static struct file_operations DHT11_ops = {
/*它是一个指向拥有这个结构的模块的指针. 这个成员用来在它的操作还在被使用时阻止模块被卸载. 几乎所有时间中, 它被简单初始化为 THIS_MODULE, 一个在 <linux/module.h> 中定义的宏.*/
.owner = THIS_MODULE,//必选
.open = DHT11_open,//必选  打开文件
.read  =DHT11_Read,
.unlocked_ioctl = DHT11_ioctl,
.release = DHT11_release,//必选  关闭文件

};

/*Linux内核使用struct miscdeivce来描述一个混杂设备*/
static  struct miscdevice DHT11_dev = {
.minor = MISC_DYNAMIC_MINOR,//minor是这个混杂设备的次设备号,若由系统自动配置,则可以设置为MISC_DYNANIC_MINOR
.name = DEVICE_NAME,//name是设备名
.fops = &DHT11_ops,
};

static int DHT11_probe(struct platform_device *pdv){
int ret,i,init;
printk(KERN_EMERG "\tinitialized\n");
for(i = 0; i< DHT11_NUM;i++){
ret = gpio_request(DHT11_gpios[i],"GPIO");
if(ret){
printk("%s: request GPIO %d for DHT11 failed, ret = %d\n", DRIVER_NAME,
i, ret);
}
else{

printk(" request DHT11'S GPIO  success\n");
}
}
init = misc_register(&DHT11_dev);
//生成设备节点,在probe函数中调用,源函数在/*注册杂项设备头文件*/中
printk("DHT11_dev's driver init = %d\n",init);
return 0;

}

/*DHT11_driver init's child*/
static int DHT11_remove(struct platform_device *pdv){
int init;
printk(KERN_EMERG "\tremove\n");
init = misc_deregister(&DHT11_dev);/****************/
//解除设备节点,在remove中调用,源函数在/*注册杂项设备头文件*/中
printk("DHT11's driver exit init = %d\n",init);
return 0;
}

struct platform_driver DHT11_linux_driver = {
.probe = DHT11_probe,
.remove = DHT11_remove,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
}
};

/*init the DHT11_driver*/
static int DHT11_linux_driver_init(void)
{
int DriverState;
printk(KERN_EMERG "DHT11's driver success enter!\n");
DriverState = platform_driver_register(&DHT11_linux_driver);//在入口函数中添加的初始化的函数。
printk(KERN_EMERG "tDriverState is %d\n",DriverState);//打印出返回值
return 0;
}
/*exit DHT11_driver*/
static void DHT11_linux_driver_exit(void)
{
gpio_free(DHT11_gpios[0]);
gpio_free(DHT11_gpios[1]);
printk(KERN_EMERG "driver success exit!\n");
platform_driver_unregister(&DHT11_linux_driver);//卸载时释放函数占用
}

module_init(DHT11_linux_driver_init);
module_exit(DHT11_linux_driver_exit);


调用驱动的应用代码:

#include<stdio.h>
#include<sys/types.h>
int main(int argc,char *argv[])
{
int humidityfd;
int ret;
char buf[5];
unsigned char  tempz = 0;
unsigned char  tempx = 0;
unsigned char  humidiyz = 0;
unsigned char  humidiyx = 0;
humidityfd = open("/dev/ds18b20_train",0);
if(humidityfd<0){
printf("/dev/humidiy open fail\n");
return 0;
}
printf("argv = %d\n",argv[1]);
ret = ioctl(humidityfd,atoi(argv[1]),0);
if(ret<0){
printf("ioctl  fail\n");
return 0;
}
while(1){
ret=read(humidityfd,buf,sizeof(buf));
if(ret<0)
printf("read err!\n");
else{
humidiyz  =buf[0];
humidiyx  =buf[1];
tempz     =buf[2];
tempx     =buf[3];
printf("humidity = %d.%d%%\n", humidiyz, humidiyx);
printf("temp = %d.%d\n",tempz,tempx);
}
sleep(2);
}
close(humidityfd);
return 0;
}


Makefile(反正就一起发了)

obj-m += DHT11_driver.o

KDIR := /home/topeet/android4.0/iTop4412_Kernel_3.0

#当前目录变量
PWD ?= $(shell pwd)

all:
make -C $(KDIR) M=$(PWD) modules
rm -rf *.o *.mod.c *.order *.symvers
#make clean执行的操作是删除后缀为o的文件
clean:
rm -rf *.o


最后再说几个我的驱动中问题:

1,GPIO我是直接用得三星的头文件中定义的GPIO头和操作方法,可能与你的不匹配。

2,驱动中我写了2个DHT11的操作。通过ioctl来控制读取哪一个。从app应用代码也可以看出。

3,硬件注册采用的是平台文件注册的,这个需要注意(想要直接用,就复制read相关函数里的代码改一下引脚就OK)。

4,代码写的有点乱,注释也不是很全,有什么问题评论提出吧。

/////////////////////////  更新 2017/5/8 /////////////////////////////////////////////

#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>

#include <linux/platform_device.h>

/*node*/
#include <linux/fs.h>

/* kmalloc() */
#include <linux/slab.h>
/* error codes */
#include <linux/errno.h>
/* size_t */
#include <linux/types.h>

/*inux中申请GPIO的头文件*/
#include <linux/gpio.h>
/*三星平台的GPIO配置函数头文件*/
/*三星平台EXYNOS系列平台,GPIO配置参数宏定义头文件*/
#include <plat/gpio-cfg.h>
#include <mach/gpio.h>
/*三星平台4412平台,GPIO宏定义头文件*/
#include <mach/gpio-exynos4.h>

#include <asm/uaccess.h>
#include <linux/cdev.h>

#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/timer.h>

/*platform_device name*/
#define DRIVER_NAME "18b20_train"

/*platform_driver name*/
#define DEVICE_NAME "DHT11_DEVICE_"

/*设备号 0 自动分配*/
int DHT11_major = 0;
int DHT11_minor = 0;

/*1,GPL协议,2,作者名*/
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("C.L.");

/*资源,这里应该注册到mach文件中,方便修改放到这了*/
static int DHT11_gpios[] = {EXYNOS4_GPX0(1),EXYNOS4212_GPJ1(0)};
#define DHT11_NUM ARRAY_SIZE(DHT11_gpios)

/*驱动结构信息*/
struct DHT11_dev{
int chose;
unsigned char buff_data[6];
unsigned char check_flag;
struct mutex mutex; /* mutual exclusion semaphore */
struct cdev cdev; /* Char device structure */
};

struct DHT11_dev *dht11_devices;

static struct class *myclass;

#define Set_DHT11_IOout(chose) s3c_gpio_cfgpin(DHT11_gpios[chose], S3C_GPIO_OUTPUT)
#define Set_DHT11_IOin(chose) s3c_gpio_cfgpin(DHT11_gpios[chose], S3C_GPIO_INPUT)
#define Write_DHT11_IO(chose,data) gpio_set_value(DHT11_gpios[chose], data)
#define Read_DHT11_IO(chose) gpio_get_value(DHT11_gpios[chose])

#define Delay_us(x) udelay(x)
#define Delay_ms(x) mdelay(x)

static unsigned char DHT11ReadByte(struct DHT11_dev *dev)
{
int time,i;
unsigned char data;
unsigned char flag;
data = 0x0;
flag = 0x0;
for (i = 0; i < 8; i++){
time = 0;

while(!Read_DHT11_IO(dev->chose)){
time++;

if (time >20){
return -1;
printk("Read error1!\n");
}

Delay_us(5);
}

Delay_us(30);
flag=0x0;

if(Read_DHT11_IO(dev->chose)){
flag=0x01;
}

time = 0;

while(Read_DHT11_IO(dev->chose)){
time++;

if (time > 100){
return -1;
printk(">70 error2!!\n");
}

Delay_us(5);
}

data <<= 1;

data |= flag;

}

return (data);
}
static unsigned char DHT11ReadDate(struct DHT11_dev *dev)
{
int time, i ;
char sum;

Set_DHT11_IOout(dev->chose);
Write_DHT11_IO(dev->chose,0);
Delay_ms(30);
Write_DHT11_IO(dev->chose,1);
Delay_us(30);
Set_DHT11_IOin(dev->chose);
//printk("DHT11_read_data %d!\n",__LINE__);

time = 0;
if(Read_DHT11_IO(dev->chose)== 0){

while (!Read_DHT11_IO(dev->chose)){
time++;

if (time > 20){
printk("DHT11_read_data %d err!\n",__LINE__);
return -2;
}

Delay_us(5);
}
time = 0;

while (Read_DHT11_IO(dev->chose)){
time++;

if (time > 20){
printk("DHT11_read_data %d err!\n",__LINE__);
return -2;
}

Delay_us(5);

}

for (i = 0; i < 5; i++){

dev->buff_data[i] = DHT11ReadByte(dev);

if (dev->buff_data[i] == -1){
return -3;
}

}
sum = 0;

for (i = 0; i < 4; i++){
sum += dev->buff_data[i];
}

if (sum != dev->buff_data[i]){
dev->check_flag=0x0;
printk("humidity check fail\n");
return -4;
}else{
dev->check_flag=0xff;
printk("humidity check pass\n");
printk("humidity=[%d],temp=[%d]\n",dev->buff_data[0],dev->buff_data[2]);
}

Set_DHT11_IOout(dev->chose);
Write_DHT11_IO(dev->chose,1);
for (i = 0; i < 5; i++){
printk(KERN_EMERG "DHdata[%d] = %d\n",i,dev->buff_data[i]);
}
}else{
printk("DHT11_fail_read_data %d!\n",__LINE__);
return -5;
}
return 0;
}

static ssize_t DHT11_Read(struct file *filp, char *buff, size_t count, loff_t *f_pos)
{
int ret;
struct DHT11_dev *dev = filp->private_data;
printk("dev-chose = %d\n",dev->chose);

if (mutex_lock_interruptible(&dev->mutex))
return -ERESTARTSYS;

local_irq_disable(); //disable_irq
DHT11ReadDate(dev);
local_irq_enable(); //enable_irq

mutex_unlock(&dev->mutex);

if(dev->check_flag==0xff){
ret=copy_to_user(buff,(char *)(dev->buff_data), count);
if(ret<0){
printk("copy to user err\n");
return -EAGAIN;
}else{
printk("copy to user success\n");
}

return 0;
}
return count;
}

static int DHT11_open(struct inode *inode, struct file *filp){
struct DHT11_dev *dev; /* device information */
dev = container_of(inode->i_cdev, struct DHT11_dev, cdev);

filp->private_data = dev; /* for other methods */

printk(KERN_EMERG "DHT11 open\n");
return 0; /* success */
}

static int DHT11_release(struct inode *inode, struct file *filp){

printk(KERN_EMERG "DHT11 release\n");
return 0;
}

static long DHT11_ioctl( struct file *files, unsigned int cmd, unsigned long arg){

printk("cmd is %u,arg is %lu\n",cmd,arg);

return 0;
}

/*注册设备节点文件结构体*/
static struct file_operations DHT11_ops = {
.owner = THIS_MODULE,
.open = DHT11_open,
.read =DHT11_Read,
.unlocked_ioctl = DHT11_ioctl,
.release = DHT11_release,

};

static int DHT11_probe(struct platform_device *pdv){
int ret,i;
printk(KERN_EMERG "\tinitialized\n");
for(i = 0; i< DHT11_NUM;i++){
ret = gpio_request(DHT11_gpios[i],"GPIO");
if(ret){
printk("%s: request GPIO %d for DHT11 failed, ret = %d\n", DRIVER_NAME,
i, ret);
}
else{

printk(" request DHT11'S GPIO success\n");
}
}

return 0;
}

static int DHT11_remove(struct platform_device *pdv){
int i;
printk(KERN_EMERG "\tremove\n");
for(i=0;i<DHT11_NUM;i++){
gpio_free(DHT11_gpios[i]);
}

return 0;
}

struct platform_driver DHT11_linux_driver = {
.probe = DHT11_probe,
.remove = DHT11_remove,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
}
};

/*exit DHT11_driver*/
static void DHT11_linux_driver_exit(void)
{
int i;
dev_t devno = MKDEV(DHT11_major, DHT11_minor);

printk(KERN_EMERG "driver success exit!\n");
platform_driver_unregister(&DHT11_linux_driver);//卸载时释放函数占用

/* Get rid of our char dev entries */
if (dht11_devices) {
for (i = 0; i < DHT11_NUM; i++) {
cdev_del(&dht11_devices[i].cdev);
/*摧毁设备节点函数*/
device_destroy(myclass,MKDEV(DHT11_major, DHT11_minor + i));
}
/*释放设备class*/
class_destroy(myclass);
kfree(dht11_devices);
}

/* cleanup_module is never called if registering failed */
unregister_chrdev_region(devno, DHT11_NUM);
}

/*
* Set up the char_dev structure for this device.
*/
static void dht11_setup_cdev(struct DHT11_dev *dev, int index)
{
int err, devno = MKDEV(DHT11_major, DHT11_minor + index);
cdev_init(&dev->cdev, &DHT11_ops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &DHT11_ops;
err = cdev_add (&dev->cdev, devno, 1);

/*creat dev node */
device_create(myclass, NULL, devno, NULL, DEVICE_NAME"%d",index);

}

/*init the DHT11_driver*/
static int DHT11_linux_driver_init(void)
{
int result,i;
dev_t dev = 0;

printk(KERN_EMERG "DHT11's driver success enter!\n");
/*
* Get a range of minor numbers to work with, asking for a dynamic
* major unless directed otherwise at load time.
*/
if (DHT11_major) {
dev = MKDEV(DHT11_major, DHT11_minor);
result = register_chrdev_region(dev, DHT11_NUM, "DHT11");
} else {
result = alloc_chrdev_region(&dev, DHT11_minor, DHT11_NUM,
"DHT11");
DHT11_major = MAJOR(dev);
}
if (result < 0) {
printk(KERN_WARNING "DHT11: can't get major %d\n", DHT11_major);
return result;
}

dht11_devices = kmalloc(DHT11_NUM * sizeof(struct DHT11_dev), GFP_KERNEL);
if (!dht11_devices) {
result = -ENOMEM;
goto fail; /* Make this more graceful */
}
memset(dht11_devices, 0, DHT11_NUM * sizeof(struct DHT11_dev));

myclass = class_create(THIS_MODULE, "DHT11_DRIVER");

printk(KERN_EMERG "tDriverState is %d\n",platform_driver_register(&DHT11_linux_driver));//打印出返回值
/* Initialize each device. */
for (i = 0; i < DHT11_NUM; i++) {
dht11_devices[i].chose = i;
mutex_init(&dht11_devices[i].mutex);
dht11_setup_cdev(&dht11_devices[i],i);
}

return 0;/* succeed */
fail:
DHT11_linux_driver_exit();
return result;
}

module_init(DHT11_linux_driver_init);
module_exit(DHT11_linux_driver_exit);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: