拓冰建站拓冰建站
首页 / 资讯中心 / 正文

STM32驱动DHT11温湿度传感器:单总线时序、代码实现与排错指南

1. 为什么STM32入门第一个传感器十有八九是DHT11做嵌入式这行久了你会发现一个很有意思的现象凡是学STM32的人调通的第一个外部传感器大概率都是DHT11温湿度传感器。不是因为这东西精度多高、性能多强恰恰相反——它精度一般、响应一般、还是个单总线时序通信怎么看都不像个“现代化”的传感器。但它的价值恰恰藏在“原始”里。DHT11通信协议本质上就是一根IO线上的电平拉高拉低用脉冲宽度编码数据。你必须自己控制微秒级别的延时、自己切换GPIO方向、自己卡时序窗口这个过程把STM32的GPIO操作、时钟概念、延时精度、中断对时序的干扰这些基本功全练了一遍。等你把DHT11调得稳稳当当再去看I2C、SPI这类带标准协议的传感器感觉就像从手动挡切换到自动挡轻松不少。我这些年帮人排查过不少DHT11的问题也见过各种花样翻新的坑卡死在读取函数里、数据永远是0、湿度跳变成负数、换一块板子就读不到、用CubeMX生成的代码一上电就HardFault。这些问题绝大多数不是传感器坏了而是时序、上下拉、GPIO模式、延时精度这些基础细节没处理好。这篇文章就基于STM32F103系列把DHT11从硬件接线、通信协议、代码实现到常见问题排查完整过一遍。标准库和HAL库两条路线都会覆盖你手里是什么环境都能直接照着操作。我不光写代码还会把我踩过的坑和排查思路一并交代清楚这些东西比单纯的代码值钱得多。2. DHT11到底是个什么器件为什么时序这么讲究2.1 先搞清楚DHT11的家底DHT11内部集成了一个电阻式感湿元件和一个NTC测温元件通过一个8位单片机把温湿度数据打包成串行数据输出。外部只引出三个引脚VCC、GND、DATA数据脚用单总线协议和主机通信。下面是它的核心参数做项目之前心里有个数项目参数工作电压3.3V ~ 5.5V温度测量范围0°C ~ 50°C温度精度±2°C湿度测量范围20%RH ~ 90%RH湿度精度±5%RH分辨率1整数输出采样周期≥1秒重点看这个采样周期DHT11要求两次读取之间至少间隔1秒。很多新手读不到正确数据就是因为读得太频繁——你连续调用读取函数传感器根本没准备好新数据返回的不是旧数据就是错误电平。后面代码里我会专门加这个间隔判断。DHT11还有个“表亲”DHT22也叫AM2302分辨率和精度都更好温度能到0.1°C精度、湿度到0.1%RH测量范围也大得多。代价是价格贵两三倍、时序要求更严格。如果你做的是环境监测、农业大棚这类对精度有要求的项目直接上DHT22更合适。但本文讲DHT11因为它的时序容错范围更宽松适合学习。2.2 单总线协议的核心思想用时间说话DHT11的数据线只有一根既要传数据又要传时钟所以它采用了一种“脉冲宽度调制”的方式用高电平持续的时间长短来表示逻辑0和逻辑1。这个方法其实很朴素。想象两个人用一根绳子传信号一个人拉一下代表0拉两下代表1——接收方必须掐着秒表看对方拉绳子的时间长度才能判断。DHT11就是这么干的只不过它用的是电信号时间是微秒级的。具体来看一帧完整的数据交互过程主机发送起始信号 - DHT11响应 - DHT11发送40位数据 - 总线释放主机先拉低数据线至少18微秒一般建议18~30微秒然后释放并拉高等待DHT11响应。DHT11检测到这个起始信号后会先拉低80微秒再拉高80微秒这组电平组合就是它的“应答信号”。应答之后DHT11开始连续发送40位数据每一位都由一个低电平脉冲和一个高电平脉冲组成。关键区别在后面的高电平逻辑0低电平约50微秒高电平约26~28微秒逻辑1低电平约50微秒高电平约70微秒所以判断一位是0还是1核心是测量高电平的持续时间。这个测量窗口就是DHT11时序的难点所在如果你的延时函数不够准、读取过程中被中断打断就可能把28微秒误判成70微秒或者干脆测不到。40位数据的分配是这样的湿度整数位(8bit) 湿度小数位(8bit) 温度整数位(8bit) 温度小数位(8bit) 校验和(8bit)DHT11的分辨率是1所以小数位在标准库实现里通常是0但协议里依旧保留这个字节。校验和的算法是钱四个字节相加取低8位等于第五个字节则校验通过。这一点在代码里一定不能省它能帮你挡住绝大多数通信错误。2.3 为什么你的延时函数会让DHT11“失灵”把DHT11接上之后很多人第一步就栽在延时上。DHT11的时序是微秒级别的你拉低起始信号、测量高电平脉宽全都依赖微秒级的延时。用STM32标准库写一个延时函数其实有讲究。早期网上流传的写法是用SysTick做一个简单的循环延时但SysTick一旦被中断服务函数占用延时精度就会受到影响。更常见的问题是主频配置不对——明明芯片跑72MHz延时函数却按8MHz来算那逻辑0的28微秒高电平就可能被算成逻辑1。一个稳的方案是用纯循环做粗略延时再通过逻辑分析仪或示波器校准。比如这样void delay_us(uint32_t us) { uint32_t i; for (i 0; i us * 8; i) { __NOP(); } }这里的系数8不是凭空来的它是基于72MHz主频、NOP指令周期约1/72微秒估算出来的。但不同编译优化级别下循环体耗时会有差异复制别人的延时函数之前一定要先确认自己芯片的实际主频和优化设置。更稳妥的做法是使用DWTData Watchpoint and Trace单元做精确延时ST的Cortex-M3内核基本都带这个外设而且不占SysTick不干扰RTOS心跳。后面代码部分我会给一个DWT延时模块实测比普通循环延时稳定得多。3. 硬件接线和原理图这里面藏着一个影响稳定性的关键3.1 引脚选择、上下拉电阻和供电的讲究DHT11的接线看起来简单得不能再简单VCC接3.3V或者5VGND接地DATA接一个GPIO。但就这么三根线我能给你讲出一堆坑。先说引脚选择。DHT11的数据脚建议选带有FTFive-volt tolerant标识的引脚也就是5V容忍引脚。虽然STM32F103的GPIO大多标注FT但有些引脚不是你要是把DHT11的VCC接到5V数据脚接到非FT引脚轻则数据读不稳定重则烧引脚。保险起见直接用3.3V给DHT11供电数据线电平也就在3.3V跟STM32的GPIO完全匹配这是我最推荐的接法。再说上拉电阻这是新手漏得最多的东西。DHT11的数据线在空闲状态下要保持高电平这是由外部上拉电阻实现的。数据线内部虽然也有上拉但很弱几十千欧级别抗干扰能力差长线或者环境有电磁干扰时容易误码。标准做法是在DATA和VCC之间接一个4.7kΩ到10kΩ的上拉电阻。我自己习惯用4.7kΩ。这个值不是随手选的——上拉电阻太小灌电流大DHT11拉低时功耗高上拉电阻太大电平翻转沿变缓微秒级时序容易失真。4.7kΩ在功耗和信号边沿之间是平衡点。供电方面DHT11的VCC和GND之间建议加一个100nF的陶瓷电容靠近传感器引脚放置。这个电容的作用是滤波——DHT11工作时电流是跳变的供电线上的纹波如果太大可能把数据线上的信号搞乱。这也是数据手册里没细讲、但实测很有用的一个细节。3.2 一个可以直接抄的参考接线表以STM32F103C8T6蓝板最小系统板为例我常用的接法如下DHT11引脚接STM32引脚说明VCC3.3V不要接5V省去电平匹配问题GNDGND共地必须一致DATAPA0任意GPIO建议选容易接线的引脚上拉电阻4.7kΩDATA到VCC不要省略关于DATA引脚PA0本身自带ADC功能但你只把它当普通GPIO用完全没问题。唯一要注意的是如果用CubeMX配置这个引脚记得把GPIO初始模式设置成开漏输出或者推挽输出加外部上拉并且用软件在初始化时把引脚拉高保证总线空闲状态是高电平。有一回我帮一个朋友排查问题他的板子接线、代码都跟我一样就是读不到数据。后来发现他的DHT11模块是那种已经集成上拉电阻的成品模块他的代码里又把GPIO配置成了开漏输出而模块上拉电阻接到的是5V——STM32的IO在开漏输出且没有内部上拉的情况下会被外部5V上拉拉高到5V电平。这不会立刻烧芯片但长期跑下去引脚老化快而且外部中断配置不当可能触发错误。所以如果你用的是成品模块建议先查清模块的原理图再决定GPIO模式。4. DHT11的完整驱动实现标准库和HAL库两套代码4.1 标准库实现GPIO模拟时序的完整代码我先把标准库的完整驱动贴出来这是最适合学习用的版本因为每一行都看得清清楚楚。头文件 dht11.h#ifndef __DHT11_H #define __DHT11_H #include stm32f10x.h #define DHT11_GPIO_PORT GPIOA #define DHT11_GPIO_PIN GPIO_Pin_0 #define DHT11_GPIO_CLK RCC_APB2Periph_GPIOA // 方向控制宏1为输入0为输出 #define DHT11_Dir_Input() { GPIOA-CRL 0xFFFFFFF0; GPIOA-CRL | 0x4; } #define DHT11_Dir_Output() { GPIOA-CRL 0xFFFFFFF0; GPIOA-CRL | 0x3; } // 电平操作宏 #define DHT11_Data_High() GPIO_SetBits(DHT11_GPIO_PORT, DHT11_GPIO_PIN) #define DHT11_Data_Low() GPIO_ResetBits(DHT11_GPIO_PORT, DHT11_GPIO_PIN) #define DHT11_Data_Read() GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_GPIO_PIN) typedef struct { uint8_t humi_int; uint8_t humi_deci; uint8_t temp_int; uint8_t temp_deci; uint8_t checksum; } DHT11_Data_TypeDef; uint8_t DHT11_Init(void); uint8_t DHT11_ReadData(DHT11_Data_TypeDef *dht11_data); #endif这里的方向控制宏直接操作CRL寄存器。STM32F103的PA0对应CRL寄存器的最低4位MODE位设为0x3表示输出模式50MHzCNF位设为0x0表示通用推挽输出输入模式下CNF设为0x1表示浮空输入所以整体CRL低4位写0x4。现在看源文件 dht11.c 的核心部分#include dht11.h #include delay.h static void DHT11_Start(void) { DHT11_Dir_Output(); DHT11_Data_High(); delay_us(2); DHT11_Data_Low(); delay_us(20); // 主机拉低至少18us DHT11_Data_High(); delay_us(20); // 释放总线等待响应 } static uint8_t DHT11_CheckResponse(void) { uint8_t retry 0; DHT11_Dir_Input(); while (DHT11_Data_Read() ! 0 retry 100) { retry; delay_us(1); } if (retry 100) return 1; // 没有低电平响应 retry 0; while (DHT11_Data_Read() ! 1 retry 100) { retry; delay_us(1); } if (retry 100) return 1; // 低电平后没有回到高电平 retry 0; while (DHT11_Data_Read() ! 0 retry 100) { retry; delay_us(1); } return 0; // 高电平80us后回到低电平应答成功 } static uint8_t DHT11_ReadByte(void) { uint8_t i; uint8_t data 0; for (i 0; i 8; i) { uint8_t retry 0; // 等待低电平结束每一位开始的50us低电平 while (DHT11_Data_Read() ! 1 retry 100) { retry; delay_us(1); } retry 0; // 等待高电平结束同时计算高电平持续时间 while (DHT11_Data_Read() ! 0 retry 100) { retry; delay_us(1); } // 根据高电平的持续时间判断0/1 // retry大约28us左右是070us左右是1 if (retry 40) data | (0x80 i); } return data; } uint8_t DHT11_ReadData(DHT11_Data_TypeDef *dht11_data) { uint8_t buf[5]; uint8_t i; if (dht11_data NULL) return 1; DHT11_Start(); if (DHT11_CheckResponse() ! 0) return 2; for (i 0; i 5; i) { buf[i] DHT11_ReadByte(); } // 校验和验证 if (((buf[0] buf[1] buf[2] buf[3]) 0xFF) ! buf[4]) return 3; dht11_data-humi_int buf[0]; dht11_data-humi_deci buf[1]; dht11_data-temp_int buf[2]; dht11_data-temp_deci buf[3]; dht11_data-checksum buf[4]; return 0; }这套代码的核心逻辑就是通过计数循环等待高电平结束用循环次数近似判断高低电平的脉宽。只要你的delay_us基本准确读出来的数据就是稳的。4.2 HAL库实现用CubeMX生成工程主程序更干净如果你用的是HAL库代码结构会更规整。CubeMX里把PA0配置为GPIO_Output初始电平设为High速度设为High然后在代码里动态切换输入输出模式。HAL库版本的关键区别在于GPIO方向切换方式。标准库直接操作CRL寄存器HAL库则用GPIO_InitTypeDef结构体重新初始化。void DHT11_Set_Pin_Output(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin DHT11_PIN; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(DHT11_PORT, GPIO_InitStruct); } void DHT11_Set_Pin_Input(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin DHT11_PIN; GPIO_InitStruct.Mode GPIO_MODE_INPUT; GPIO_InitStruct.Pull GPIO_NOPULL; HAL_GPIO_Init(DHT11_PORT, GPIO_InitStruct); }每次切换方向都调用HAL_GPIO_Init这个函数内部会重新配置寄存器性能上肯定比直接操作寄存器慢一点但胜在语义清晰、不容易出错。对DHT11这种微秒级时序来说多几微秒的耗时完全在可接受范围内只要你不是在时间敏感的代码路径里反复切换。HAL版本的微秒延时我建议用DWT来实现避免依赖SysTick。SysTick在HAL库里被用于HAL_Delay的时基你再拿它做精确定时容易冲突。下面是DWT延时模块直接复制到你的工程里就能用void DWT_Delay_Init(void) { CoreDebug-DEMCR | CoreDebug_DEMCR_TRCENA_Msk; DWT-CYCCNT 0; DWT-CTRL | DWT_CTRL_CYCCNTENA_Msk; } void DWT_Delay_us(uint32_t us) { uint32_t start DWT-CYCCNT; uint32_t ticks us * (SystemCoreClock / 1000000); while ((DWT-CYCCNT - start) ticks); }DWT的CYCCNT计数器每个内核时钟周期加172MHz主频下延时1微秒就是72个周期。这个方法不抢占SysTick也不会被HAL_GetTick干扰实测在HAL库工程里是最省心的延时方案。4.3 主程序怎么调用读取间隔怎么控制无论标准库还是HAL库主程序的调用逻辑都是一样的。这里我特别强调读取间隔控制——DHT11的采样周期是1秒所以每次读取之间至少间隔1秒。这个间隔可以用系统滴答计时也可以简单粗暴地用延时。int main(void) { DHT11_Data_TypeDef dht11_data; uint32_t last_read_time 0; // 初始化省略 while (1) { // 控制读取间隔不小于1秒 if (HAL_GetTick() - last_read_time 1000) { uint8_t ret DHT11_ReadData(dht11_data); if (ret 0) { printf(Humi: %d.%d%% Temp: %d.%dC\r\n, dht11_data.humi_int, dht11_data.humi_deci, dht11_data.temp_int, dht11_data.temp_deci); } else { printf(DHT11 read error: %d\r\n, ret); } last_read_time HAL_GetTick(); } // 其他任务代码 } }这个写法的好处是读取失败时程序不会卡死主循环继续跑到下一个周期再重试。千万不要在读取DHT11失败时用while(1)死等那会让整个系统卡死。实际项目里时序稍有干扰就会偶发读取失败重试机制是必须的。5. DHT11温湿度数据应用扩展从读数据到小系统5.1 显示、预警、记录DHT11能撑起一个完整Demo读到了温湿度数据只通过串口打印出来属实浪费。拿DHT11当核心配合OLED显示屏、蜂鸣器和按键半小时就能搭出一个桌面温湿度监视器。我常用的组合是DHT11 SSD1306 OLED 有源蜂鸣器。OLED用I2C接口只占两个IO蜂鸣器随便接一个GPIO。逻辑是这样定时1秒读取一次温湿度刷新OLED显示温度超过30°C或湿度低于30%时蜂鸣器报警按键切换显示界面当前值/历史最大值/历史最小值这个项目的价值在于打通“采集-处理-显示-告警”的完整链路。学生在毕业设计里最喜欢这种组合它技术栈覆盖了传感器驱动、显示驱动、中断、定时器、状态机改一改就能套到智能家居、花盆助手、环境监测的题目里。OLED显示部分SSD1306的驱动网上有现成的库但如果你自己写过I2C写寄存器的代码会发现它本质就是往显存填数据然后把显存刷到屏幕。跟DHT11的单总线时序相比I2C带标准时序异常好调。蜂鸣器这块有源蜂鸣器最简单给高电平就响给低电平就停。注意STM32的GPIO驱动能力有限蜂鸣器最好通过三极管或MOS管驱动别直接接IO。5.2 用定时器做“非阻塞”读取解放CPU从DHT11的时序你也能看出一次完整读取大约耗时20到30毫秒期间CPU一直在忙等。如果你的系统还需要处理串口数据、按键扫描、显示刷新那这几十毫秒的阻塞是可以接受的。但如果你的主循环有其他高实时性任务就必须把DHT11读取放进一个定时中断里或者直接用状态机方式非阻塞处理。状态机的思路是把DHT11的读取过程拆成几个阶段发送起始信号、等待应答、读取40位数据、解析校验。每个阶段在周期性定时器中断里推进中断每100微秒触发一次检查当前状态并跳转。这样主循环永远不会因为等待DHT11而卡住。这个方法难度不大但代码量会翻几倍。实际项目里我更推荐的做法是如果系统有RTOS把DHT11读取放到一个低优先级任务里用信号量控制读取周期如果没有RTOS就用定时器中断标志位把DHT11当成一个“慢设备”处理。我见过有人用DMA定时器输入捕获去解码DHT11的时序理论上可以但实属大炮打蚊子。DHT11本身精度就一般花大力气去做高精度解码意义不大GPIO模拟时序的性能已经完全够用。6. 排错指南DHT11读不到数据时的排查顺序6.1 从硬件到软件一套可以反复使用的排查流程DHT11出问题90%都集中在几个点。我按排查顺序整理了一份速查表遇到问题你可以从第一行开始逐步检查现象检查项解决办法读取函数一直卡死数据线上拉是否接好检查4.7kΩ上拉电阻到VCC的连接读取函数卡死GPIO方向是否成功切换确认代码里在发送起始信号后切到了输入模式返回校验和错误供电电压是否过低用万用表测VCC保证3.3V±5%返回校验和错误接线过长数据线尽量控制在20cm以内数据全部是0上拉电阻缺失补上4.7kΩ上拉电阻湿度跳变成负数读取间隔小于1秒控制读取周期≥1秒换一块板子读不到引脚是否选错确认GPIO时钟已使能引脚没错偶发读取失败读取过程中被中断打断读取期间PRIMASK屏蔽中断或提高中断优先级管理这里面最容易被忽略的是“读取过程中被中断打断”。DHT11的一位高电平时长只有几十微秒如果你的系统有串口接收中断、定时器中断恰好在这几十微秒里触发并且中断服务函数耗时较长就会导致判断出错。解决思路有两个一是提升读取优先级在读取DHT11时暂时屏蔽所有中断临界区保护二是降低中断频率和耗时把耗时操作从中断服务函数里挪出来。标准库里屏蔽中断很简单__disable_irq(); DHT11_ReadData(dht11_data); __enable_irq();但前提是你的系统里没有其他对实时性要求极高的中断。如果系统里有无线通信这类需要快速响应的外设不能这么做那就得把DHT11读取放到带优先级的任务里确保它不被其他任务挤掉。6.2 用示波器和逻辑分析仪“看”时序这是排查时序问题最有效的手段。把逻辑分析仪的通道夹在DHT11的数据线上设置采样率20MHz以上触发方式设为下降沿然后跑一次读取你就能把整个通信过程看得明明白白。最关键的是看应答信号。示波器上应该能看到这样一段波形主机拉低约20us - 释放拉高 - DHT11拉低约80us - DHT11拉高约80us - 数据位开始如果应答信号缺失说明DHT11根本没检测到有效起始信号问题在主机这端——延时不够、方向没切换对、上拉电阻有问题。如果应答信号正常但后面的数据位波形不对比如高电平时长普遍偏短或偏长那多半是延时函数不准。这时候可以用逻辑分析仪的测量功能量一下每个高电平脉宽对比一下逻辑0的26~28us和逻辑1的70us你就知道自己的延时偏了多少。我有一个快速校准延时函数的方法写一段代码翻转一个GPIO延时1us再翻转用示波器量翻转周期就能算出实际延时和理论值的偏差。比如你写delay_us(1)两次翻转间隔如果是1.3us说明延时偏大30%那读时序时判断0/1的阈值也要相应调整。6.3 几个容易被忽视的细节坑说几个我实际遇到、但网上教程很少提到的坑。第一个是VCC和DATA之间的电容问题。有些成品DHT11模块上自带了一个0.1uF的滤波电容这个电容在VCC和GND之间没问题但如果设计不当电容跨接在DATA和GND之间会严重拖慢数据线上的电平翻转速度导致微秒级时序完全走样。遇到读不到数据的情况可以量一下DATA到GND之间有没有容值较大的电容。第二个是STM32的GPIO输出速度配置。HAL库初始化GPIO时要把Speed设为GPIO_SPEED_FREQ_HIGH如果设成LOWGPIO的翻转速率跟不上起始信号的拉低时间就不够DHT11可能无法识别。我曾在低功耗模式下测试时发现GPIO翻转速度自动降低后DHT11时常读失败排查了半天才意识到是功耗优化策略把GPIO速度降了。第三个是大批量生产时的一致性隐患。DHT11本身参数离散性不小同批次甚至不同批次的传感器时序参数会有差异。你的代码在开发板上测试没问题不代表在所有板子上都能稳定运行。如果是做产品建议读取超时和校验失败的代码路径多做几层保护比如连续读取三次都失败才判定传感器故障避免偶发误差触发误报警。第四个是关于读数据时DFT的坑。如果你在STM32上启用了浮点运算单元或者复杂的数学库在DHT11读取过程中可能会产生较长的指令执行时间在某些极端调度下卡断时序窗口。这个概率很小但我确实遇到过。解决办法是读取DHT11时临时关掉全局中断如上文所说或者把DHT11读取放到一个高优先级中断里。7. 从DHT11出发你还能往哪几个方向继续深入DHT11是个起点不是终点。把它的驱动调通之后顺着几个方向深入下去你的嵌入式水平会有质的提升。第一个方向是换更高精度的传感器。DHT22、SHT30、AHT20都是不错的升级选择。SHT30是I2C接口温度精度能做到±0.3°C湿度精度±2%RH功耗还低非常适合电池供电设备。你用过DHT11之后再上手SHT30会明显感觉到“带标准接口的传感器有多省心”。第二个方向是给自己的驱动加一层“设备抽象”。你写一个dht11传感器驱动能不能设计成通用的温湿度传感器接口上层代码只调用温度读取和湿度读取两个函数跟底层是DHT11还是SHT30完全解耦。以后更换传感器时只需要实现新传感器的驱动接口。这个思路在嵌入式项目里非常重要尤其是做产品时传感器选型经常会变驱动层跟业务层解耦能省大量返工时间。第三个方向是学会用逻辑分析仪和示波器做时序分析。你在调DHT11时积累的这套“量脉宽、查波形”的方法到了I2C、SPI、UART、CAN的调试里依然适用。嵌入式调试的核心能力之一就是“看得见信号”别只会printf。第四个方向是往系统集成上走。把DHT11采集到的温湿度数据通过ESP8266、ESP32或NB-IoT上传到云平台做成远程监控或者跟FreeRTOS结合用队列把传感器数据从采集任务传给显示任务。这些都是把DHT11从“裸机小demo”升级成“物联网节点”的必经之路。最后说一个我个人的经验。DHT11这个传感器网上代码一大把参数手册也写得清清楚楚但真正把它用好的人不多。原因在于很多人只是复制粘贴代码跑通了就完事从不去想时序为什么是这样、延时为什么是这些值、失败时该怎么定位。等你把这些“为什么”搞明白了你学的就不只是DHT11而是嵌入式底层的那一套通用方法论。我在实际调试中最深的一个体会是DHT11看起来简单但它能干教你三件事——第一传感器通信的时序敏感度究竟有多强第二GPIO方向切换和电平控制是怎么影响外部器件的第三系统其他地方的一点点干扰如何通过一根数据线传导到传感器读取结果里。这三件事不做DHT11这种单总线器件你很难有切肤的感受。所以耐心把它调稳你后面学其他传感器会顺很多。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门