ATmega328P控制LED异常求助:HC-SR04与按键失效问题
ATmega328P HC-SR04与按键控制LED异常问题分析及修复
故障原因
- 传感器测量函数的阻塞式死循环:
measure_distance中等待ECHO引脚拉高的while (!(PIND & (1 << ECHO_PIN)))无超时处理。若HC-SR04未返回ECHO信号(如检测范围内无物体、接线故障),程序会永久卡在该循环,导致按键及后续逻辑完全停止响应,这是按下按键后功能失效的核心原因。 - 逻辑优先级冲突:按键切换LED状态后,传感器检测到10cm内物体时会强制调用
turn_on_led点亮LED,违背按键手动控制的意图。 - 按键无消抖处理:机械按键按下/释放时的抖动可能导致
toggle_led多次触发,影响功能稳定性。
修改方案
- 给传感器测量添加超时机制:在等待ECHO信号的循环中加入计数限制,避免无限阻塞。
- 增加模式控制变量:区分「传感器自动控制」和「按键手动控制」模式,解决逻辑冲突。
- 添加按键消抖:通过延时和状态判断过滤按键抖动。
修改后的代码
#include <avr/io.h> #include <util/delay.h> #define LED_PIN PD2 #define BUTTON_PIN PD3 #define TRIGGER_PIN PD5 #define ECHO_PIN PD6 #define MEASURE_TIMEOUT 10000 // 超时阈值,避免无限等待 void toggle_led() { PORTD ^= (1 << LED_PIN); } void turn_on_led() { if (!(PORTD & (1 << LED_PIN))) { PORTD |= (1 << LED_PIN); } } void turn_off_led() { if (PORTD & (1 << LED_PIN)) { PORTD &= ~(1 << LED_PIN); } } uint16_t measure_distance() { uint16_t duration = 0; uint16_t timeout = MEASURE_TIMEOUT; // 发送触发信号 PORTD |= (1 << TRIGGER_PIN); _delay_us(10); PORTD &= ~(1 << TRIGGER_PIN); // 等待ECHO拉高,带超时 while (!(PIND & (1 << ECHO_PIN)) && timeout--) { _delay_us(1); } if (timeout == 0) { return 0; // 返回0表示超时,无有效测量 } // 测量ECHO高电平持续时间,带超时 timeout = MEASURE_TIMEOUT; while ((PIND & (1 << ECHO_PIN)) && timeout--) { duration++; _delay_us(1); } // 计算距离(cm),58是声波往返时间转距离的系数 uint16_t distance = (duration / 58); return distance; } int main(void) { // 配置引脚方向 DDRD |= (1 << LED_PIN) | (1 << TRIGGER_PIN); DDRD &= ~((1 << BUTTON_PIN) | (1 << ECHO_PIN)); // 启用按键引脚内部上拉电阻 PORTD |= (1 << BUTTON_PIN); uint8_t button_state = 1; // 初始为释放状态(上拉高电平) uint8_t last_button_state = 1; uint8_t manual_mode = 0; // 0:传感器自动控制,1:手动控制 while (1) { // 按键处理:消抖+切换模式 button_state = (PIND & (1 << BUTTON_PIN)) ? 1 : 0; if (button_state != last_button_state) { _delay_ms(20); // 消抖延时 button_state = (PIND & (1 << BUTTON_PIN)) ? 1 : 0; if (button_state != last_button_state) { if (!button_state) { // 按键按下(低电平) manual_mode ^= 1; if (manual_mode) { toggle_led(); // 进入手动模式时切换当前LED状态 } } last_button_state = button_state; } } // 传感器控制逻辑(仅自动模式下生效) if (!manual_mode) { uint16_t distance = measure_distance(); if (distance <= 10 && distance != 0) { turn_on_led(); } else { turn_off_led(); } } _delay_ms(50); // 主循环延时,降低CPU占用 } return 0; }
关键修改说明
- 超时机制:在等待ECHO信号的两个循环中加入
timeout计数,超时后返回0,避免程序卡死。 - 模式切换:新增
manual_mode变量,按键切换模式而非直接控制LED。自动模式下传感器正常控制LED;手动模式下传感器逻辑失效,进入时切换一次LED状态。 - 按键消抖:通过两次读取按键状态并加入20ms延时,过滤机械抖动。
- 内部上拉电阻:启用按键引脚的内部上拉,无需额外外接上拉电阻,简化电路。
内容的提问来源于stack exchange,提问作者Igor Abrunhosa
相关产品推荐
相关产品推荐

