Linux下DHT11驱动编程,以及测试程序

来源:互联网 发布:python canny边缘检测 编辑:程序博客网 时间:2024/06/05 18:17

今天搞了一下DHT11的驱动编程,嘿嘿,分享给大家。

官方文档的时序图以及介绍如下:

一次完整的数据传输为40bit,高位先出。
数据格式:8bit湿度整数数据+8bit湿度小数数据
+8bi温度整数数据+8bit温度小数数据
+8bit校验和
数据传送正确时校验和数据等于“8bit湿度整数数据+8bit湿度小数数据
+8bi温度整数数据+8bit温度小数数据”所得结果的末8位。
用户MCU发送一次开始信号后,DHT11从低功耗模式转换到高速模式,等待主机开始信号结束后,DHT11发送响应信号,送出40bit的数据,并触发一次信号采集,用户可选择读取部分数据.从模式下,DHT11接收到开始信号触发一次温湿度采集,如果没有接收到主机发送开始信号,DHT11不会主动进行温湿度采集.采集数据后转换到低速模式


总线空闲状态为高电平,主机把总线拉低等待DHT11响应,主机把总线拉低必须大于18毫秒,保证DHT11能检测到起始信号。DHT11接收到主机的开始信号后,等待主机开始信号结束,然后发送80us低电平响应信号.主机发送开始信号结束后,延时等待20-40us后, 读取DHT11的响应信号,主机发送开始信号后,可以切换到输入模式,或者输出高电平均可, 总线由上拉电阻拉高 。

总线为低电平,说明DHT11发送响应信号,DHT11发送响应信号后,再把总线拉高80us,准备发送数据,每一bit数据都以50us低电平时隙开始,高电平的长短定了数据位是0还是1.格式见下面图示.如果读取响应信号为高电平,则DHT11没有响应,请检查线路是否连接正常.当最后一bit数据传送完毕后,DHT11拉低总线50us,随后总线由上拉电阻拉高进入空闲状态.

然后还有一点要注意,就是两次读取时间要间隔至少1秒:


基本知识说完了,下面直接上一个完整的驱动程序(因为时间有限,就不细讲了):

#include <linux/module.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/slab.h>#include <linux/device.h>#include <linux/cdev.h>#include <linux/delay.h>#include <mach/gpio.h>#include <asm-generic/uaccess.h>#include <asm/io.h>#define DHT_MAJOR 0typedef struct{uint8_t  humi_int; //湿度的整数部分uint8_t  humi_deci; //湿度的小数部分uint8_t  temp_int; //温度的整数部分uint8_t  temp_deci; //温度的小数部分uint8_t  check_sum; //校验和                 } DHT11_Data_TypeDef;struct s5pv210_dht11{dev_t devno;struct cdev *cdev;struct class *cls;int value;};struct s5pv210_dht11 *dht_dev;DHT11_Data_TypeDef DHT11_Data;volatile unsigned long *GPH3_CONF;volatile unsigned long *GPH3_DAT;//从DHT11读取1byte数据,MSB先行uint8_t DHT11_ReadByte(void){uint8_t i, temp=0;//gpio_request(S5PV210_GPH3(5), "DQ_IN");//gpio_direction_input(S5PV210_GPH3(5));for(i=0;i<8;i++)    { /*每bit以50us低电平标置开始,轮询直到从机发出 的50us 低电平 结束*/while(gpio_get_value(S5PV210_GPH3(5)) == 0);/*DHT11 以26~28us的高电平表示“0”,以70us高电平表示“1”, *通过检测 x us后的电平即可区别这两个状 ,x 即下面的延时  */udelay(40); //延时x us 这个延时需要大于数据0持续的时间即可     if(gpio_get_value(S5PV210_GPH3(5)))/* x us后仍为高电平表示数据“1” */{/* 等待数据1的高电平结束 */while(gpio_get_value(S5PV210_GPH3(5)));temp|=(uint8_t)(0x01<<(7-i));  //把第7-i位置1,MSB先行 }else // x us后为低电平表示数据“0”{   temp&=(uint8_t)~(0x01<<(7-i)); //把第7-i位置0,MSB先行}}//gpio_free(S5PV210_GPH3(5));return temp;}/** * 一次完整的数据传输为40bit,高位先出 * 8bit 湿度整数 + 8bit 湿度小数 + 8bit 温度整数 + 8bit 温度小数 + 8bit 校验和的末8位 */uint8_t DHT11_Read_TempAndHumidity(DHT11_Data_TypeDef *DHT11_Data){gpio_request(S5PV210_GPH3(5), "DQ_OUT");/*主机拉低*/gpio_direction_output(S5PV210_GPH3(5), 0);/*延时18ms,(>=18ms)*/mdelay(18);/*总线拉高 主机延时30us*/gpio_direction_output(S5PV210_GPH3(5), 1);udelay(30);   //延时30us,(20~40us)/*主机设为输入 判断从机响应信号*/ gpio_direction_input(S5PV210_GPH3(5));/*判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行*/   if(gpio_get_value(S5PV210_GPH3(5)) == 0)     {/*轮询直到从机发出 的80us 低电平 响应信号结束*/  while(gpio_get_value(S5PV210_GPH3(5)) == 0);/*轮询直到从机发出的 80us 高电平 标置信号结束*/while(gpio_get_value(S5PV210_GPH3(5)));/*开始接收数据*/   DHT11_Data->humi_int= DHT11_ReadByte();DHT11_Data->humi_deci= DHT11_ReadByte();DHT11_Data->temp_int= DHT11_ReadByte();DHT11_Data->temp_deci= DHT11_ReadByte();DHT11_Data->check_sum= DHT11_ReadByte();/*读取结束,引脚改为输出模式,主机拉高*/gpio_direction_output(S5PV210_GPH3(5), 1);gpio_free(S5PV210_GPH3(5));/*检查读取的数据是否正确*///DHT11_Data->check_sum的正确的结果是温湿度总和的末8位,结构体也有定义check_sum为uint8_t类型if(DHT11_Data->check_sum == DHT11_Data->humi_int + DHT11_Data->humi_deci + DHT11_Data->temp_int+ DHT11_Data->temp_deci)return 0;else return -1;}elsereturn -1;}int dht_open(struct inode *inode, struct file *filp){printk("--------%s---------\n",__func__);// 初始化IO,输出*GPH3_CONF &= ~(0xf << 20);*GPH3_DAT |= (0x1 << 20);return 0;}ssize_t dht_read(struct file *filp, char __user *buf, size_t count, loff_t *fops){printk("--------%s---------\n",__func__);/*调用DHT11_Read_TempAndHumidity读取温湿度,若成功则输出该信息*/if( DHT11_Read_TempAndHumidity ( & DHT11_Data ) == 0){//由于DHT11的温湿度小数部分属于保留的,所以在这里加不加都一样printk("\r\n read ok!\r\n\r\n humi: %d.%d RH ,temp: %d.%d C \r\n",\DHT11_Data.humi_int,DHT11_Data.humi_deci,DHT11_Data.temp_int,DHT11_Data.temp_deci);copy_to_user(buf, &DHT11_Data, count);}elseprintk("Read DHT11 ERROR!\r\n");return count;}long dht_ioctl(struct file *filp, unsigned int cmd, unsigned long args){printk("--------%s---------\n",__func__);/*调用DHT11_Read_TempAndHumidity读取温湿度,若成功则输出该信息*/if( DHT11_Read_TempAndHumidity ( & DHT11_Data ) == 0){//由于DHT11的温湿度小数部分属于保留的,所以在这里加不加都一样printk("\r\n read ok!\r\n\r\n humi: %d.%d RH ,temp: %d.%d C \r\n",\DHT11_Data.humi_int,DHT11_Data.humi_deci,DHT11_Data.temp_int,DHT11_Data.temp_deci);}else{printk("Read DHT11 ERROR!\r\n");}// 把用户空间传递过来的args地址后的整数转化为指针void __user *argp = (void __user *)args;switch(cmd){case 0: // 温度copy_to_user(argp, &DHT11_Data.temp_int, sizeof(DHT11_Data.temp_int));break;case 1:// 湿度copy_to_user(argp, &DHT11_Data.humi_int, sizeof(DHT11_Data.humi_int));break;default:break;}return 0;}int dht_close(struct inode *inode, struct file *filp){printk("--------%s---------\n",__func__);*GPH3_CONF &= ~(0xf << 20);return 0;}const struct file_operations dht_fops = {.open = dht_open,.read = dht_read,.unlocked_ioctl = dht_ioctl,.release = dht_close,};static void __exit dht11_exit(void){printk("--------%s---------\n",__func__);//iounmap(0xE0200C60);device_destroy(dht_dev->cls, dht_dev->devno);class_destroy(dht_dev->cls);cdev_del(dht_dev->cdev);unregister_chrdev_region(dht_dev->devno, 1);kfree(dht_dev);}// 字符设备模块加载函数static int __init dht11_init(void){printk("--------%s---------\n",__func__);int ret = -1;// 分配一个全局的设备对象dht_dev = kzalloc(sizeof(struct s5pv210_dht11), GFP_KERNEL);if(NULL == dht_dev){printk("kzalloc failed !\n");return -ENOMEM;}// 申请设备号if(DHT_MAJOR){dht_dev->devno = MKDEV(DHT_MAJOR, 0);ret = register_chrdev_region(dht_dev->devno, 1, "DHT11_drv");}else {// 动态申请ret = alloc_chrdev_region(&dht_dev->devno, 16, 1, "DHT11_drv");}if(ret < 0){printk("register failed !\n");ret = -EINVAL;goto err;}// 分配一个cdevdht_dev->cdev = cdev_alloc();// 初始化cdevcdev_init(dht_dev->cdev, &dht_fops);// 注册cdevcdev_add(dht_dev->cdev, dht_dev->devno, 1);// 创建设备节点dht_dev->cls = class_create(THIS_MODULE, "dht_cls");device_create(dht_dev->cls, NULL, dht_dev->devno, NULL, "dht1");// 硬件初始化GPH3_CONF = ioremap(0xE0200C60, 8);GPH3_DAT = GPH3_CONF + 1;return 0;err:dht11_exit();return ret;}module_init(dht11_init);module_exit(dht11_exit);MODULE_LICENSE("GPL");

以上就是完整的驱动程序,下面在发一个测试程序:

#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <fcntl.h>#include <stdlib.h>#include <stdint.h>struct DHT_structure{    uint8_t humi_int;    uint8_t humi_deci;    uint8_t temp_int;    uint8_t temp_deci;    uint8_t check_sum;};struct DHT_structure DHT11_data;int main(void){int ret = -1,    fd = -1;fd = open("/dev/dht1", O_RDWR);if(fd < 0){    perror("open failed !\n");    exit(1);}bzero(&DHT11_data, sizeof(DHT11_data));while(1){#if 0    ret = read(fd, &DHT11_data, sizeof(DHT11_data));    if(ret < 0){perror("read failed !\n");exit(1);    }#else#if 1//注意这两条ioctl不要同一时间调用,因为同时调用就相当于读两次,而读取第二次至少需要间隔1sret = ioctl(fd,1,&DHT11_data.humi_int);#elseret = ioctl(fd,0,&DHT11_data.temp_int);#endif#endif    printf("humi: %d.%d, temp: %d.%d\n",DHT11_data.humi_int,\    DHT11_data.humi_deci,DHT11_data.temp_int,DHT11_data.temp_deci);    sleep(1);}if(close(fd) < 0){    perror("close failed !\n");    exit(1);}return 0;}

就这样吧,写作业去了,回见各位!

原创粉丝点击