零、概述
记录在linxu上不同的驱动DHT11的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| static const struct of_device_id dht11_of_match[] = { { .compatible = "alientek,dht11" }, { /* Sentinel */ } };
static struct platform_driver dht11_driver = { .driver = { .owner = THIS_MODULE, .name = "dht11", .of_match_table = dht11_of_match, }, .probe = dht11_probe, .remove = dht11_remove, };
module_platform_driver(dht11_driver);
MODULE_LICENSE("GPL");
|
二、完成probe和remove函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| /* * @description : 驱动的probe函数,当驱动与 * 设备匹配以后此函数就会执行 * @param - pdev : pdev设备 * @return : 0表示转换成功,其它值表示转换失败 */ static int dht11_probe(struct platform_device *pdev) { struct miscdevice *mdev; int ret;
dev_info(&pdev->dev, "dht11 device and driver matched successfully!\n");
ret = dht11_request_gpio(pdev); if (ret) return ret;
/* 初始化 MISG设备 */ mdev = &dht11_device.mdev; mdev->name = "dht11"; mdev->minor = MISC_DYNAMIC_MINOR; mdev->fops = &dht11_fops; /* 初始化定时器 */ timer_setup(&dht11_device.timer, dht11_timer_callback, 0); dht11_device.timer.expires=jiffies + msecs_to_jiffies(1500); add_timer(&dht11_device.timer); /* 初始化工作队列 */ INIT_WORK(&dht11_device.work, dht11_work_callback); /* MISG 设备注册 */ return misc_register(mdev); }
/* * @description : 驱动的remove函数,移除驱动的时候此函数会执行 * @param - pdev : pdev设备 * @return : 0,成功;其他负值,失败 */ static int dht11_remove(struct platform_device *pdev) { gpio_set_value(dht11_device.gpio, 0);
/* 卸载MISG设备 */ misc_deregister(&dht11_device.mdev); /* 卸载定时器 */ del_timer(&dht11_device.timer); /* 卸载工作队列 */ cancel_work_sync(&dht11_device.work);
dev_info(&pdev->dev, "DHT11 driver has been removed!\n"); return 0; }
|
三、dht11_request_gpio中是去设备树获取节点并request请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| /* * @description : GPIO的初始化函数 * @param pdev : platform设备 * @return : 0表示转换成功,其它值表示转换失败 */ static int dht11_request_gpio(struct platform_device *pdev) { struct device *dev = &pdev->dev; int ret;
dht11_device.gpio = of_get_named_gpio(dev->of_node, "dht11-gpio", 0); if (!gpio_is_valid(dht11_device.gpio)) { dev_err(dev, "Failed to get gpio"); return -EINVAL; }
ret = devm_gpio_request(dev, dht11_device.gpio, "DHT11 Gpio"); if (ret) { dev_err(dev, "Failed to request gpio"); return ret; }
return 0; }
|
四、初始化定时器和工作队列,使用定时器定时去调度工作队列(schedule_work)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| /* 初始化定时器 */ timer_setup(&dht11_device.timer, dht11_timer_callback, 0); dht11_device.timer.expires=jiffies + msecs_to_jiffies(1500); add_timer(&dht11_device.timer);
/* 初始化工作队列 */ INIT_WORK(&dht11_device.work, dht11_work_callback); /* * @description : 定时器的操作函数,每1s去获取一次数据 * @param - asg : 定时器的结构体 * @return : 无 */ static void dht11_timer_callback(struct timer_list *arg) { schedule_work(&dht11_device.work); mod_timer(&dht11_device.timer, jiffies + (1500 * HZ/1000)); }
/* * @description : GPIO的初始化函数 * @param pdev : platform设备 * @return : 0表示转换成功,其它值表示转换失败 */ static int dht11_request_gpio(struct platform_device *pdev) { struct device *dev = &pdev->dev; int ret;
dht11_device.gpio = of_get_named_gpio(dev->of_node, "dht11-gpio", 0); if (!gpio_is_valid(dht11_device.gpio)) { dev_err(dev, "Failed to get gpio"); return -EINVAL; }
ret = devm_gpio_request(dev, dht11_device.gpio, "DHT11 Gpio"); if (ret) { dev_err(dev, "Failed to request gpio"); return ret; }
return 0; }
|
五、其中dht11_init是初始化函数dht11_read_byte就是主要的dht11读取函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| /* * @description : DHT11的初始化 * @param : 无 * @return : 0,初始化成功; 其它表示失败 */ static int dht11_init(void) { dht11_set_output(HIGH); /* 把拉高Duot */ udelay(30); /* 拉高30us */
dht11_set_output(LOW); /* 把拉低Duot */ mdelay(20); /* 拉低20us */
dht11_set_output(HIGH); /* 把拉高Duot */ udelay(30); /* 拉高30us */
dht11_set_input(); /* 设置Duot为输入模式 */ udelay(200); /* 延时200us */ if(!dht11_get_io()) { /* 不是高电平,DHT11就没有响应 */ return -ENODEV; } return 0; }
//读取函数 /* * @description : 读取一个字节的数据 * @param : 无 * @return : 读取到的数据 */ static unsigned char dht11_read_byte(void) { unsigned char i, time = 0, data = 0; local_irq_disable();
for(i = 0; i < 8; i++) { time = 0;
while(dht11_get_io() == 0) { udelay(1); time++; if (time > 100) { return -EINVAL; } } udelay(45); /* 延时45us */ if (dht11_get_io() == 1) { /* 获取到高电平,数据就为1,否则就是0 */ data |= 1<<(7 - i); time = 0; while(dht11_get_io() == 1) { udelay(1); time++; if (time > 100) return -EINVAL; } } } local_irq_enable();
return data; }
|

首先主机发送开始信号,即:拉低数据线,保持 t1(至少 18ms)时间,然后拉高数据线 t2(20~ 40us)时间,然后读取 DHT11 的响应,正常的话,DHT11 会拉低数据线,保持 t3(40~50us)时间,作为响应信号,然后 DHT11 拉高数据线,保持 t4(40 ~50us)时间后,开始输出数据。
主机需要先发送一个开始信号给DHT11才可以开始采集数据
开始信号=18ms低电平+20-40us高电平
然后将GPIO设置成输入,等待DHT11的响应信号
DHT11响应信号=40-50us低电平+40-50us高电平
DHT11发出数据信号:
数据为0的一位信号 = 一个低脉冲 + 一个高脉冲。低脉冲持续50us,高脉冲持续26~28us。
数据为1的一位信号 = 一个低脉冲 + 一个高脉冲。低脉冲持续50us,高脉冲持续70us。
DHT11发出结束信号:
最后1bit数据传送完毕后,DHT11拉低总线50us,然后释放总线,总线由上拉电阻拉高进入空闲状态。
读到的数据格式是5个字节数据
8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据+8bit校验和
校验和=前4个字节相加