ATtiny25驱动DHT22时出现异常时序与行为问题排查
项目实现方案
采用ATtiny25作为主控,通过MOSFET为DHT22温湿度传感器、超声波测距传感器供电,MOSFET输出端同时接入供电总线,总线上配有3.3k上拉电阻。主程序绝大多数时间处于休眠状态,到达工作触发时机后执行以下逻辑:
if ((buff[1]<<8) + buff[0] <= powerCutoff) { generalStatus &= wipeMask; // 清除低3位状态 setOutput(signalPin); setLow(signalPin); MOSFETPowerOn(devPower) initSleep(1<<WDP2); measureDHT(); measureDistance(); // sendData(); MOSFETPowerOff(devPower) }
相关宏定义与函数实现
#define MOSFETPowerOn(line) { line ## _port &= ~(1<<line); line ## _ddr |= (1<<line); asm("nop"); } #define MOSFETPowerOff(line) { line ## _ddr &= ~(1<<line); line ## _port |= (1<<line); asm("nop"); } #define ping(bit) { \ bit ## _port &= ~(1<<bit); \ bit ## _ddr |= (1<<bit); \ bit ## _port |= (1<<bit); \ asm("nop"); \ bit ## _port &= ~(1<<bit); \ asm("nop"); } /** 设备供电引脚定义 */ #define devPower PB2 #define devPower_port PORTB #define devPower_ddr DDRB #define devPower_pin PINB /** 调试引脚定义 */ #define debug PB0 #define debug_port PORTB #define debug_ddr DDRB #define debug_pin PINB void initSleep(uint8_t timing) { asm("WDR"); WDTCR = 1<<WDIE | timing; MCUCR &= ~(1<<SM0); MCUCR |= 1<<SE | 1<<SM1; asm("sleep"); } void measureDHT() { generalStatus |= 1<<fDHTMeasurementInProgress; initSleep(1<<WDP2); // 休眠0.25s for (uint8_t i = 2; i < 7; i++) buff[i] = 0; dht22_status.status = dht22_inPresence; dht22_status.bitCounter = 0; dht22_status.byteCounter = 0; setOutput(devBus); _delay_us(1200); // 1ms起始低脉冲 setInput(devBus); _delay_us(50); // ... 其余代码省略
故障现象
- 系统基础配置:时钟为8MHz,电源由稳压器提供,板载小型去耦电容。
initSleep()等模块已在多个其他项目中复用,可正常工作。 - 时序异常表现:根据芯片手册,看门狗配置
1<<WDP2对应0.25s休眠时长。按代码逻辑,MOSFET上电后先执行一次0.25s休眠,进入measureDHT()后几乎第一时间再次执行0.25s休眠,之后拉低通信总线,因此从上电到拉低总线的理论间隔应略大于500ms,但实际测量值为2349ms,其余环节时序均正常。 - 逻辑分析仪通道定义:第一通道为电源波形,第二通道为DHT22数据波形,第三通道为测距传感器波形,异常时序波形如下:

- 临时验证:在MOSFET上电后添加一行
ping(debug)代码,修改片段如下:
// ... setOutput(signalPin); setLow(signalPin); MOSFETPowerOn(devPower) ping(debug) initSleep(1<<WDP2); measureDHT(); // ...
此时时序恢复正常,波形如下:
波形最下方通道可见ping操作产生的窄尖峰脉冲,添加该操作后时序完全正常。
- 开发环境为Linux,avr-gcc版本为v5.4.0,编程器为AVR Dragon,初步排查未定位故障原因。
补充测试结果
- 最小复现验证:将代码精简为最小复现版本,故障仍然存在,代码如下:
#include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #include <stdint.h> #include "macros.inc" #include "pinConfig.h" #define wakeCycle GPIOR0 #define MOSFETPowerOn(line) { line ## _port &= ~(1<<line); line ## _ddr |= (1<<line); asm("nop"); } #define MOSFETPowerOff(line) { line ## _ddr &= ~(1<<line); line ## _port |= (1<<line); asm("nop"); } void initSleep(uint8_t timing) { asm("WDR"); WDTCR = 1<<WDIE | timing; MCUCR &= ~(1<<SM0); MCUCR |= 1<<SE | 1<<SM1; asm("sleep"); } // 看门狗超时中断服务函数 ISR(WDT_vect) { wakeCycle = 12; } void measureDHT() { initSleep(1<<WDP2); // 0.25s setOutput(devBus); _delay_us(1200); // 1ms起始低脉冲 setInput(devBus); _delay_us(50); } int main(void) { MOSFETPowerOff(devPower) // MOSFET驱动的设备默认通过上拉电阻关断 GIMSK |= 1<<PCIE; // 使能引脚变化中断 sei(); while (1) { MOSFETPowerOn(devPower) initSleep(1<<WDP2); measureDHT(); MOSFETPowerOff(devPower) initSleep(1<<WDP2 | 1<<WDP1 | 1<<WDP0); // 休眠2s } }
- 中断修改测试:将看门狗中断修改为如下空实现,此时时序运行正常:
// 看门狗超时中断服务函数 ISR(WDT_vect) { asm("nop"); }
- 硬件移除测试:将电路中的DHT22移除后,精简版本代码与原代码均运行正常。
- 当前待排查问题:无法确定该异常属于硬件电路问题还是代码/编译问题,寻求解决方案。
内容的提问来源于stack exchange,提问作者Gambanishu Habbeba
相关产品推荐
相关产品推荐

