登录发现更多内容
首页
分类
发帖
账号
自动登录
找回密码
密码
登录
立即注册
立即登录
立即注册
其他登录
QQ
微信
首页
Portal
求助问答
Xiuno资源
Xiuno教程
Xiuno插件
Xiuno主题
休闲茶馆
定制主题
产品教程
BBS
求助问答
Xiuno资源
Xiuno教程
Xiuno插件
Xiuno主题
休闲茶馆
定制主题
开发资料
求助问答
Xiuno资源
Xiuno教程
Xiuno插件
Xiuno主题
休闲茶馆
定制主题
样品购买
求助问答
Xiuno资源
Xiuno教程
Xiuno插件
Xiuno主题
休闲茶馆
定制主题
IoT云平台
求助问答
Xiuno资源
Xiuno教程
Xiuno插件
Xiuno主题
休闲茶馆
定制主题
GitHub
求助问答
Xiuno资源
Xiuno教程
Xiuno插件
Xiuno主题
休闲茶馆
定制主题
技术博客
求助问答
Xiuno资源
Xiuno教程
Xiuno插件
Xiuno主题
休闲茶馆
定制主题
搜索
搜索
热搜:
LoRa
ESP8266
安信可
本版
帖子
用户
请
登录
后使用快捷导航
没有账号?
立即注册
每日签到
任务
广播
导读
排行榜
设置
我的收藏
退出
6
1
1
首页
技术杂谈
›
经验分享—— Linux 内核驱动 DHT11 温湿度传感器 ...
返回列表
经验分享—— Linux 内核驱动 DHT11 温湿度传感器
[ 复制链接 ]
发布帖子
paopiu
高级会员
9
主题
10
回帖
503
积分
高级会员
高级会员, 积分 503, 距离下一级还需 497 积分
高级会员, 积分 503, 距离下一级还需 497 积分
积分
503
私信
6人留言
楼主
技术杂谈
2857
6
2023-11-8 08:46:02
[i=s] 本帖最后由 paopiu 于 2023-11-8 08:47 编辑 [/i]
**Linux** 内核中自带的 **DHT11** 驱动使用IIO子系统实现。今天给大家介绍直接使用默认 **DHT11** 驱动的方式读取温湿度。本文以 **LuckFox Pico** 为例,撰写教程。 ### 1. 修改内核 切换到内核目录下,并修改内核配置 ``` cd /home/luckfox/Luckfox-Pico/luckfox-pico/sysdrv/source/kernel cp ./arch/arm/configs/luckfox_rv1106_linux_defconfig .config make ARCH=arm menuconfig ``` 在打开的页面中搜索 **DHT11** 或者也可以按 **↑ ↓ \
** 依次选择 **> Device Drivers > Industrial I/O support > Humidity sensors > DHT11 (and compatible sensors) driver** ) 按下 `/` 搜索,输入 **DHT11** ) 回车,就跳到 **DHT11** 简介页面了 ) 按下数字 **1** ,跳到使能 **DHT11** 页面 ) 按下 **Y** ,打开使能。 选择 **
** 保存配置文件 ) ) 复制配置 ``` make ARCH=arm savedefconfig cp defconfig ./arch/arm/configs/luckfox_rv1106_linux_defconfig ``` 其实这个驱动源代码可以在 **SDK** 里找到 ``` cat /home/luckfox/Luckfox-Pico/luckfox-pico/sysdrv/source/kernel/drivers/iio/humidity/dht11.c ``` 完整的内核可以在 **GitHub** 上找到 [dht11.c]([https://github.com/torvalds/linux/blob/master/drivers/iio/humidity/dht11.c](https://github.com/torvalds/linux/blob/master/drivers/iio/humidity/dht11.c)) ###### dht11.c ``` // SPDX-License-Identifier: GPL-2.0-or-later /* * DHT11/DHT22 bit banging GPIO driver * * Copyright (c) Harald Geyer <[email]harald@ccbib.org[/email]> */ #include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DRIVER_NAME "dht11" #define DHT11_DATA_VALID_TIME 2000000000 /* 2s in ns */ #define DHT11_EDGES_PREAMBLE 2 #define DHT11_BITS_PER_READ 40 /* * Note that when reading the sensor actually 84 edges are detected, but * since the last edge is not significant, we only store 83: */ #define DHT11_EDGES_PER_READ (2 * DHT11_BITS_PER_READ + \ DHT11_EDGES_PREAMBLE + 1) /* * Data transmission timing: * Data bits are encoded as pulse length (high time) on the data line. * 0-bit: 22-30uS -- typically 26uS (AM2302) * 1-bit: 68-75uS -- typically 70uS (AM2302) * The acutal timings also depend on the properties of the cable, with * longer cables typically making pulses shorter. * * Our decoding depends on the time resolution of the system: * timeres > 34uS ... don't know what a 1-tick pulse is * 34uS > timeres > 30uS ... no problem (30kHz and 32kHz clocks) * 30uS > timeres > 23uS ... don't know what a 2-tick pulse is * timeres < 23uS ... no problem * * Luckily clocks in the 33-44kHz range are quite uncommon, so we can * support most systems if the threshold for decoding a pulse as 1-bit * is chosen carefully. If somebody really wants to support clocks around * 40kHz, where this driver is most unreliable, there are two options. * a) select an implementation using busy loop polling on those systems * b) use the checksum to do some probabilistic decoding */ #define DHT11_START_TRANSMISSION_MIN 18000 /* us */ #define DHT11_START_TRANSMISSION_MAX 20000 /* us */ #define DHT11_MIN_TIMERES 34000 /* ns */ #define DHT11_THRESHOLD 49000 /* ns */ #define DHT11_AMBIG_LOW 23000 /* ns */ #define DHT11_AMBIG_HIGH 30000 /* ns */ struct dht11 { struct device *dev; struct gpio_desc *gpiod; int irq; struct completion completion; /* The iio sysfs interface doesn't prevent concurrent reads: */ struct mutex lock; s64 timestamp; int temperature; int humidity; /* num_edges: -1 means "no transmission in progress" */ int num_edges; struct {s64 ts; int value; } edges[DHT11_EDGES_PER_READ]; }; #ifdef CONFIG_DYNAMIC_DEBUG /* * dht11_edges_print: show the data as actually received by the * driver. */ static void dht11_edges_print(struct dht11 *dht11) { int i; dev_dbg(dht11->dev, "%d edges detected:\n", dht11->num_edges); for (i = 1; i < dht11->num_edges; ++i) { dev_dbg(dht11->dev, "%d: %lld ns %s\n", i, dht11->edges[i].ts - dht11->edges[i - 1].ts, dht11->edges[i - 1].value ? "high" : "low"); } } #endif /* CONFIG_DYNAMIC_DEBUG */ static unsigned char dht11_decode_byte(char *bits) { unsigned char ret = 0; int i; for (i = 0; i < 8; ++i) { ret <<= 1; if (bits[i]) ++ret; } return ret; } static int dht11_decode(struct dht11 *dht11, int offset) { int i, t; char bits[DHT11_BITS_PER_READ]; unsigned char temp_int, temp_dec, hum_int, hum_dec, checksum; for (i = 0; i < DHT11_BITS_PER_READ; ++i) { t = dht11->edges[offset + 2 * i + 2].ts - dht11->edges[offset + 2 * i + 1].ts; if (!dht11->edges[offset + 2 * i + 1].value) { dev_dbg(dht11->dev, "lost synchronisation at edge %d\n", offset + 2 * i + 1); return -EIO; } bits[i] = t > DHT11_THRESHOLD; } hum_int = dht11_decode_byte(bits); hum_dec = dht11_decode_byte(&bits[8]); temp_int = dht11_decode_byte(&bits[16]); temp_dec = dht11_decode_byte(&bits[24]); checksum = dht11_decode_byte(&bits[32]); if (((hum_int + hum_dec + temp_int + temp_dec) & 0xff) != checksum) { dev_dbg(dht11->dev, "invalid checksum\n"); return -EIO; } dht11->timestamp = ktime_get_boottime_ns(); if (hum_int < 4) { /* DHT22: 100000 = (3*256+232)*100 */ dht11->temperature = (((temp_int & 0x7f) << 8) + temp_dec) * ((temp_int & 0x80) ? -100 : 100); dht11->humidity = ((hum_int << 8) + hum_dec) * 100; } else if (temp_dec == 0 && hum_dec == 0) { /* DHT11 */ dht11->temperature = temp_int * 1000; dht11->humidity = hum_int * 1000; } else { dev_err(dht11->dev, "Don't know how to decode data: %d %d %d %d\n", hum_int, hum_dec, temp_int, temp_dec); return -EIO; } return 0; } /* * IRQ handler called on GPIO edges */ static irqreturn_t dht11_handle_irq(int irq, void *data) { struct iio_dev *iio = data; struct dht11 *dht11 = iio_priv(iio); if (dht11->num_edges < DHT11_EDGES_PER_READ && dht11->num_edges >= 0) { dht11->edges[dht11->num_edges].ts = ktime_get_boottime_ns(); dht11->edges[dht11->num_edges++].value = gpiod_get_value(dht11->gpiod); if (dht11->num_edges >= DHT11_EDGES_PER_READ) complete(&dht11->completion); } return IRQ_HANDLED; } static int dht11_read_raw(struct iio_dev *iio_dev, const struct iio_chan_spec *chan, int *val, int *val2, long m) { struct dht11 *dht11 = iio_priv(iio_dev); int ret, timeres, offset; mutex_lock(&dht11->lock); if (dht11->timestamp + DHT11_DATA_VALID_TIME < ktime_get_boottime_ns()) { timeres = ktime_get_resolution_ns(); dev_dbg(dht11->dev, "current timeresolution: %dns\n", timeres); if (timeres > DHT11_MIN_TIMERES) { dev_err(dht11->dev, "timeresolution %dns too low\n", timeres); /* In theory a better clock could become available * at some point ... and there is no error code * that really fits better. */ ret = -EAGAIN; goto err; } if (timeres > DHT11_AMBIG_LOW && timeres < DHT11_AMBIG_HIGH) dev_warn(dht11->dev, "timeresolution: %dns - decoding ambiguous\n", timeres); reinit_completion(&dht11->completion); dht11->num_edges = 0; ret = gpiod_direction_output(dht11->gpiod, 0); if (ret) goto err; usleep_range(DHT11_START_TRANSMISSION_MIN, DHT11_START_TRANSMISSION_MAX); ret = gpiod_direction_input(dht11->gpiod); if (ret) goto err; ret = request_irq(dht11->irq, dht11_handle_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, iio_dev->name, iio_dev); if (ret) goto err; ret = wait_for_completion_killable_timeout(&dht11->completion, HZ); free_irq(dht11->irq, iio_dev); #ifdef CONFIG_DYNAMIC_DEBUG dht11_edges_print(dht11); #endif if (ret == 0 && dht11->num_edges < DHT11_EDGES_PER_READ - 1) { dev_err(dht11->dev, "Only %d signal edges detected\n", dht11->num_edges); ret = -ETIMEDOUT; } if (ret < 0) goto err; offset = DHT11_EDGES_PREAMBLE + dht11->num_edges - DHT11_EDGES_PER_READ; for (; offset >= 0; --offset) { ret = dht11_decode(dht11, offset); if (!ret) break; } if (ret) goto err; } ret = IIO_VAL_INT; if (chan->type == IIO_TEMP) *val = dht11->temperature; else if (chan->type == IIO_HUMIDITYRELATIVE) *val = dht11->humidity; else ret = -EINVAL; err: dht11->num_edges = -1; mutex_unlock(&dht11->lock); return ret; } static const struct iio_info dht11_iio_info = { .read_raw = dht11_read_raw, }; static const struct iio_chan_spec dht11_chan_spec[] = { { .type = IIO_TEMP, .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), }, { .type = IIO_HUMIDITYRELATIVE, .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), } }; static const struct of_device_id dht11_dt_ids[] = { { .compatible = "dht11", }, { } }; MODULE_DEVICE_TABLE(of, dht11_dt_ids); static int dht11_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct dht11 *dht11; struct iio_dev *iio; iio = devm_iio_device_alloc(dev, sizeof(*dht11)); if (!iio) { dev_err(dev, "Failed to allocate IIO device\n"); return -ENOMEM; } dht11 = iio_priv(iio); dht11->dev = dev; dht11->gpiod = devm_gpiod_get(dev, NULL, GPIOD_IN); if (IS_ERR(dht11->gpiod)) return PTR_ERR(dht11->gpiod); dht11->irq = gpiod_to_irq(dht11->gpiod); if (dht11->irq < 0) { dev_err(dev, "GPIO %d has no interrupt\n", desc_to_gpio(dht11->gpiod)); return -EINVAL; } dht11->timestamp = ktime_get_boottime_ns() - DHT11_DATA_VALID_TIME - 1; dht11->num_edges = -1; platform_set_drvdata(pdev, iio); init_completion(&dht11->completion); mutex_init(&dht11->lock); iio->name = pdev->name; iio->info = &dht11_iio_info; iio->modes = INDIO_DIRECT_MODE; iio->channels = dht11_chan_spec; iio->num_channels = ARRAY_SIZE(dht11_chan_spec); return devm_iio_device_register(dev, iio); } static struct platform_driver dht11_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = dht11_dt_ids, }, .probe = dht11_probe, }; module_platform_driver(dht11_driver); MODULE_AUTHOR("Harald Geyer <[email]harald@ccbib.org[/email]>"); MODULE_DESCRIPTION("DHT11 humidity/temperature sensor driver"); MODULE_LICENSE("GPL v2"); ``` ### 2. 修改设备树 设备树的修改也很简单。其他 **Linux** 内核的设备树引脚可能略有不同。**Linux** 完整设备树可以参考 **Github** [设备树]([https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/iio/humidity/dht11.yaml](https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/iio/humidity/dht11.yaml)) ``` vi /home/luckfox/Luckfox-Pico/luckfox-pico/sysdrv/source/kernel/arch/arm/boot/dts/rv1103g-luckfox-pico.dts ``` 在 `compatible = "rockchip,rv1103g-38x38-ipc-v10", "rockchip,rv1103";` 这一行下面新增 `humidity_sensor` ,关于这个 `gpios` ,可以看官方引脚图, **RK_P** 是固定的。比如你想使用 **58** 号引脚,那就是 **RK_PD2** 。如果有其他设备树描述涉及到了这个引脚,记得注释掉,防止冲突。 ) ###### rv1103g-luckfox-pico.dts ``` humidity_sensor { compatible = "dht11"; gpios = <&gpio1 RK_PC7 GPIO_ACTIVE_HIGH>; // GPIO pin for DHT11 data }; ``` ### 3. 编译内核 切换到编译目录 ``` cd /home/luckfox/Luckfox-Pico/luckfox-pico ``` 选择编译分支, **LuckFox Pico** 按 **0** ``` ./build.sh lunch ``` 最后编译内核 ``` ./build.sh kernel ``` 内核编译成功后生成的文件在 `
output/image` 目录下,替换原固件中的 `boot.image` 与 `env.txt` 文件。使用 SocToolKit 重新创建SD。 ### 4. 编译传感器读取代码 ###### Makefile ``` # 指定架构 ARCH=arm # 指定编译工具链 CROSS_COMPILE=/home/luckfox/Luckfox-Pico/luckfox-pico/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf- export ARCH CROSS_COMPILE name=humidity_sensor PWD?=$(shell pwd) all: $(CROSS_COMPILE)gcc $(name).c -o $(name) echo $(PWD) clean: rm -f *.ko *.o *.mod *.mod.o *.mod.c *.symvers *.order *.cmd obj-m += $(name).o ``` ###### humidity_sensor.c ``` #include
#include
#include
#include
#include
#include
int main(void) { int temp_fd, humidity_fd; char temp_buf[32], humidity_buf[32]; int temp, humidity; temp_fd = open("/sys/bus/iio/devices/iio:device1/in_temp_input", O_RDONLY); humidity_fd = open("/sys/bus/iio/devices/iio:device1/in_humidityrelative_input", O_RDONLY); if (temp_fd == -1 || humidity_fd == -1) { perror("Failed to open sensor data files"); exit(-1); } while (1) { // Read temperature data lseek(temp_fd, 0, SEEK_SET); ssize_t temp_size = read(temp_fd, temp_buf, sizeof(temp_buf) - 1); if (temp_size == -1) { perror("Failed to read temperature data"); exit(-1); } temp_buf[temp_size] = '\0'; temp = atoi(temp_buf); // Read humidity data lseek(humidity_fd, 0, SEEK_SET); ssize_t humidity_size = read(humidity_fd, humidity_buf, sizeof(humidity_buf) - 1); if (humidity_size == -1) { perror("Failed to read humidity data"); exit(-1); } humidity_buf[humidity_size] = '\0'; humidity = atoi(humidity_buf); // Convert to float float temp_float = (float)temp / 1000; float humidity_float = (float)humidity / 1000; printf("Temperature: %.1f C, Humidity: %.1f %%RH\n", temp_float, humidity_float); sleep(1); } close(temp_fd); close(humidity_fd); return 0; } ``` 使用 `make` 命令编译代码,并将编译好的 `humidity_sensor` 上传到开发板。 在开发板中,执行 `humidity_sensor` ``` chmod +x humidity_sensor ./humidity_sensor ``` ) 如果你的结果和我一样,说明这个 **DHT11** 已经不准了,温度小数位也返回了值。可以执行 `dmesg` 查看日志。 ``` dmesg ``` ) 如果对精度要求不高,可以试着修改 `dht.c` 驱动的第 **156** 行,判断小数位的代码,并重新编译内核并烧录。
点赞
1
收藏
1
淘帖
0
────
1
人觉得很赞
────
回复
使用道具
举报
6 回复
电梯直达
正序浏览
倒序浏览
正序浏览
沙发
bzhou830
回复
使用道具
举报
2023-11-8 08:57:40
学习一下
回复
评论
使用道具
举报
选择去发光,而不是被照亮
板凳
WangChong
回复
使用道具
举报
2023-11-8 08:58:02
学习了,以后我就是老师的枪,老师让我打哪我就打哪
回复
评论
使用道具
举报
地板
爱笑
回复
使用道具
举报
2023-11-8 09:44:33
打卡学习~
回复
评论
使用道具
举报
用心做好保姆工作
5
#
ckdsx.cn
回复
使用道具
举报
2023-11-8 09:57:00
就要这样多元化发展
回复
评论
使用道具
举报
6
#
流水源
回复
使用道具
举报
2023-11-8 14:04:46
学习学习
回复
评论
使用道具
举报
7
#
干簧管
回复
使用道具
举报
2023-11-10 11:12:44
大佬牛,学习一下
回复
评论
使用道具
举报
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
立即登录
手机登录
点评
高级模式
本版积分规则
回帖并转播
回帖后跳转到最后一页
返回
今日推荐
AiPi-PalChatV1_“湾湾小何”提示音测试固件V2.9_UART-MCP
[WB2] 实现自动发现局域网下的设备
热帖排行
[AiPi-PalchatV1] [Windows] 克隆仓库和在线烧录遇到的一些小问
AT+MQTTPUBRAW指令所支持的最大数据传输量是多少
AiPi-PalChatV1_UART-MCP_v2.8 UART-MCP 协议配置 问题
Ai-WB2蓝牙怎么连接打印机
AI-WB2-12F电脑蓝牙连接秒断,手机搜索不到
AiPi-PalChatV1_“湾湾小何”提示音测试固件V2.9_UART-MCP
从零搭建 Ai-WB2 开发板的 Linux 开发环境—— 基于 VMware 虚拟
【公告】第九期电子DIY活动中奖通知
统计信息
会员数: 30471 个
话题数: 44643 篇
首页
分类
我的