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

这说讲一下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 
#include  
#include 
/*驱动注册的头文件,包含驱动的结构体和注册和卸载的函数*/
#include 
/*注册杂项设备头文件*/
#include 
/*注册设备节点的文件结构体*/
#include 

/*inux中申请GPIO的头文件*/
#include 
/*三星平台的GPIO配置函数头文件*/
/*三星平台EXYNOS系列平台,GPIO配置参数宏定义头文件*/
#include 
#include 
/*三星平台4412平台,GPIO宏定义头文件*/
#include 
#include 

/*在平台文件中定义的宏里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, 一个在  中定义的宏.*/
	.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  
#include  
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 
#include  
#include 

#include 

/*node*/
#include 

/* kmalloc() */
#include 		
/* error codes */
#include 	
/* size_t */
#include 	

/*inux中申请GPIO的头文件*/
#include 
/*三星平台的GPIO配置函数头文件*/
/*三星平台EXYNOS系列平台,GPIO配置参数宏定义头文件*/
#include 
#include 
/*三星平台4412平台,GPIO宏定义头文件*/
#include 

#include 
#include 

#include 
#include 
#include 

/*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;icdev, &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);



你可能感兴趣的:(智能家居)