stm32/sensor/dht11-temp-humidity.md

温湿度传感器(DHT11,单总线)

知识库 · STM32 📅 初版 2026-09-12 ✏️ 更新于 2026-09-20

温湿度传感器(DHT11,单总线)

DHT11 是低成本温湿度模块,用单总线(One-Wire 风格)时序与 MCU 通信,常用于环境监测。本文为 STM32 驱动要点速查 + 洋桃1号实测档案(2026-09-19 OLED 中文显示改造)。

#一、元信息

字段值
芯片/平台STM32F103C8T6(洋桃1号,官方例程 demo39 标准库;通用驱动写法见下,HAL 兼容)
接口类型单总线(One-Wire 时序,1 根数据线)
验证状态✅ 已验证
验证环境洋桃1号 / Keil MDK(AC5)/ 2026-09-19:温湿度读数 + OLED 中文显示 + 串口每秒打印实测通过
驱动方式位 bang 时序(精确延时 + GPIO 翻转)

#二、接线表

信号MCU 引脚模块引脚备注
DATAPA15DHT11 OUT⚠️ 洋桃1号上与舵机信号脚共用,测 DHT11 前必须拔掉舵机排线(否则舵机乱动、DHT11 读不到数据);模块自带板载上拉,裸 DHT11 四针需 4.7kΩ 上拉到 3.3V
VCC3.3VVCC3.3~5.5V
GNDGNDGND必须共地
串口观察(可选)PA9(USART1_TX)USB-TTL RX115200 8N1,GND 共地

#三、通信时序要点

  • 起始信号:主机拉低 DATA ≥ 18ms,再拉高 20~40µs,释放总线。
  • 从机响应:DHT11 拉低 80µs(应答),再拉高 80µs,然后开始发数据。
  • 数据位:每 bit 先拉低 50µs,再拉高——高电平 26~28µs = 0,70µs = 1。靠高电平持续时间判 0/1。
  • 数据帧:40 bit = 湿度整数 8 + 湿度小数 8(恒为 0)+ 温度整数 8 + 温度小数 8(恒为 0)+ 校验和 8。校验 = 前 4 字节之和低 8 位。
  • 读取周期建议 ≥ 1s(两次采样间隔),连续读取过快会不响应——官方例程里两处 delay_ms(1000)(初始化后 + 主循环)都不能删。
  • 温度分辨率只有 1℃、精度 ±2℃:室温稳定时"读数一直不动"通常是正常的,拿到空调风口吹一会儿验证。

#四、实测改造记录(2026-09-19)

在官方 Demo(英文界面)基础上做三处改造,全部上机验证:

① 串口每秒打印:工程没有现成串口,main.c 补 USART1 配置(PA9 AF_PP / PA10 浮空输入,115200),主循环 sprintf(s,"RH=%d%% T=%dC\r\n",...) 后逐字节 USART_SendData 发出。不直接用 printf 的原因见踩坑清单第 11 条(半主机)。

② 单位美化:字库里 % 的点阵圆圈只有 2×2 个点太细 → 重画(圆圈加大到 3×3、斜线加粗);字库没有 °(ASCII 没这个字符)→ 借位:把从来用不到的反引号 ` ` 的字模替换成度符号,代码里写 C ` 屏上显示 °C`。通用技巧:要用字库没有的符号,就找一个用不到的 ASCII 字符借位。

③ 中文化 + 居中:Humidity/Temperature → 汉字「湿度/温度」,两行水平居中。新字用取模软件生成,设置必须与屏驱动约定一致:

选项值
取模方式纵向8点下高位
数据排列从左到右从上到下
字库宋体16.dot
输出格式C51(0x 前缀)

一个汉字 32 字节,直接贴进 CHS_16x16.h。两个显示 API 的差异(易踩):OLED_DISPLAY_8x16 内部有 col+2 偏移(传 X-2 才显示在物理列 X);OLED_DISPLAY_16x16 没有偏移、第三个参数是 GB_16[] 数组索引(不是 ASCII 码),汉字占 page 与 page+1 两页。混排不补偿间距会不均。

#五、踩坑清单

  1. 上拉电阻必须有:裸 DHT11 的 DATA 需 4.7kΩ 上拉(模块版自带板载上拉);不上拉读不到稳定波形。
  2. 延时精度是命门:时序 µs 级,微秒延时用定时器/DWT 实现,且采样期间避免中断干扰。
  3. 校验和必查:不校验可能拿到错误数据。
  4. 起始拉低时间要够(≥18ms),太短从机不响应。
  5. 采样间隔 ≥ 1s,否则返回上一次或失败;例程两处 delay_ms(1000) 不能删。
  6. DHT11 精度低(湿度 ±5%RH,温度 ±2℃),要求高换 DHT22/AM2302(协议相同,分辨率更高)。
  7. 手绘字模 → 汉字上下颠倒:本屏取模约定是「纵向8点下高位」——16×16 字 = 上半页 16 字节 + 下半页 16 字节,每字节 bit0 是页内最上面一行(不是 bit7)。写反整个字上下镜像。别手绘,用取模软件设置对了一次成型(可拿原库正常字反推验证)。
  8. 改了 .h 字库,编译"没变化":Keil 依赖跟踪没把 ASCII_8x16.h/CHS_16x16.h 当依赖,增量编译(UV4 -b)根本不重编 oled0561.c,pyocd 报 identical ... programmed 0。改字库/头文件后一律全量重建(UV4.exe -r -j0)。
  9. pyocd 报 identical、写入 0 字节:即使编译正确也可能页比较误判 → pyocd flash --erase chip --pack ... 强制全片擦写,成功标志是日志明确写 programmed N bytes。
  10. 宋体"度"字糊成一团:逐点渲染字模确认是字体固有缺陷——「广」的长撇 +「廿」两竖 = 三条竖线并排再交叉,宋体 1 像素细笔画小屏必然糊("湿/温"右边是「日」框所以清楚)。解法:换黑体(2 像素等粗笔画)或加大字号。
  11. printf 卡死(半主机):工程未勾 MicroLIB 时,printf 走 semihosting 把输出交给调试器,脱机运行卡死在 BKPT。解法 A(推荐):sprintf 到缓冲区 + USART_SendData,不碰工程配置、跨电脑一致;解法 B:勾 MicroLIB 再重定向 fputc。
  12. .md 文档改了"没反应":文件带只读属性,写入被静默拒绝。(Get-Item xxx.md).IsReadOnly 检查,Set-ItemProperty -Name IsReadOnly -Value $false 解除。

#六、验证方法:从 hex 验尸

怀疑"屏上显示不对"时别靠肉眼猜,直接解析 hex 把烧进芯片的字库提出来渲染对比:

  1. 在 .map 文件里找字库符号地址(如 GB_16 0x08002cde Data 224 oled0561.o(.constdata));
  2. 解析 hex 文件(注意先处理 type 04 扩展线性地址记录,否则地址对不上、读出全是 0xFF);
  3. 按地址取字节,按 bit0=顶 的规则画成 #/. 点阵图。

能 100% 确认"芯片里到底是什么字形",把"数据问题"和"显示问题"彻底分开——上面第 10 条(宋体度字)就是这样定性的。

#七、驱动代码(要点,HAL 风格伪代码)

c/* 微秒延时依赖 DWT 或 TIM */
void delay_us(uint32_t us);

uint8_t dht11_read(uint8_t *humid_h, uint8_t *temp_h)
{
    uint8_t buf[5] = {0};
    /* 1. 主机起始:拉低 18ms */
    DHT11_OUTPUT();
    HAL_GPIO_WritePin(DATA_PORT, DATA_PIN, 0);
    delay_us(18000);
    HAL_GPIO_WritePin(DATA_PORT, DATA_PIN, 1);
    delay_us(30);
    /* 2. 切输入,等从机 80us 低 + 80us 高 */
    DHT11_INPUT();
    if (wait_level(0, 100) || wait_level(1, 100)) return 1; /* 超时 */
    /* 3. 读 40 bit */
    for (int i = 0; i < 40; i++)
    {
        wait_level(0, 100);          /* 等下降沿 */
        delay_us(40);                /* 采样点:40us 处判高低 */
        buf[i/8] <<= 1;
        if (HAL_GPIO_ReadPin(DATA_PORT, DATA_PIN)) buf[i/8] |= 1;
    }
    /* 4. 校验 */
    if (buf[4] != (buf[0]+buf[1]+buf[2]+buf[3])) return 2;
    *humid_h = buf[0]; *temp_h = buf[2];
    return 0;
}

#八、调用示例

cuint8_t h, t;
if (dht11_read(&h, &t) == 0)
    sprintf(buf, "RH=%d%% T=%dC\r\n", h, t), uart_send(buf); /* 别用 printf,见坑 11 */
HAL_Delay(1000); /* 间隔 ≥1s */

#九、相关链接

#十、参考库/链接

  • DHT11 数据手册(搜索 "DHT11 datasheet")
  • 洋桃1号 A5 周围电路手册(温湿度传感器章节)

#11、更新日志

📝共 2 条记录
日期类型更新内容
2026-09-12新增首次建档:DHT11 单总线时序 + 踩坑 + 驱动要点
2026-09-20实测更新验证状态 ❌→✅(洋桃1号 2026-09-19 实测);接线改 PA15(补与舵机共用警告);新增"实测改造记录"(串口打印 / 单位美化借位 ° / 中文取模居中);踩坑清单并入 6 个实机坑(手绘字模颠倒、改 .h 未重编、pyocd identical、宋体度字、printf 半主机、只读属性);新增 hex 验尸验证法