STM32 Nucleo L152RE伺服仅动作一次后控制器无响应问题排查
问题分析与修复方案
核心问题
你的代码存在几个致命问题,直接导致控制器崩溃:
- 中断服务函数(ISR)执行耗时操作:ISR里直接调用
m_pump(),其中包含大量循环和ThisThread::sleep_for()——ISR的设计原则是尽可能短、绝不阻塞,mbed环境中在ISR里调用线程休眠函数会破坏系统调度逻辑,直接引发死机。 - Timer未初始化启动:第一次进入
m_pump()时,timer未启动,elapsed_time()返回0,会一直执行正转循环;后续也没有正确启动timer的逻辑,状态永远无法切换。 - 按键无消抖处理:外部中断触发未做消抖,容易产生误触发,加重系统负载。
修复后的代码
#include "mbed.h" #include <chrono> PwmOut servo(PC_7); InterruptIn s1_pos1(PA_10); InterruptIn s2_pos2(PA_6); InterruptIn s3_pos3(PA_1); PortOut motor(PortC, 0xf00); int m_state[4] = {0x300, 0x700, 0xc00, 0x900}; Timer timer; unsigned int const T_periode = 20; // 标志位:用于主循环触发动作 volatile int target_servo_pos = 0; volatile bool start_pump = false; volatile bool pump_running = false; // 按键消抖定时器 Timeout debounce_timer; volatile bool debounce_active = false; void moveServoToPosition(int position) { printf("Moving servo to position %d\r\n", position); switch (position) { case 1: servo.pulsewidth_us(500); break; case 2: servo.pulsewidth_us(1500); break; case 3: servo.pulsewidth_us(2500); break; default: break; } } void handle_pump() { if (!pump_running) return; auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(timer.elapsed_time()).count(); if (elapsed < 15000) { // 正转步进电机 for (int i = 0; i < 4; i++) { motor = m_state[i]; ThisThread::sleep_for(4ms); } } else { // 反转100圈后停止 for (int j = 0; j < 100; j++) { for (int i = 3; i >= 0; i--) { motor = m_state[i]; ThisThread::sleep_for(4ms); } } motor = 0x000; timer.stop(); timer.reset(); pump_running = false; start_pump = false; printf("Pump stopped\r\n"); } } // 消抖回调:恢复中断触发权限 void debounce_done() { debounce_active = false; } // ISR仅设置标志,不执行耗时操作 void isr_s1_pos1() { if (!debounce_active) { debounce_active = true; debounce_timer.attach(&debounce_done, 50ms); // 50ms消抖 target_servo_pos = 1; start_pump = true; } } void isr_s2_pos2() { if (!debounce_active) { debounce_active = true; debounce_timer.attach(&debounce_done, 50ms); target_servo_pos = 2; start_pump = true; } } void isr_s3_pos3() { if (!debounce_active) { debounce_active = true; debounce_timer.attach(&debounce_done, 50ms); target_servo_pos = 3; start_pump = true; } } int main() { // 初始化按键 s1_pos1.mode(PullDown); s1_pos1.rise(&isr_s1_pos1); s2_pos2.mode(PullDown); s2_pos2.rise(&isr_s2_pos2); s3_pos3.mode(PullDown); s3_pos3.rise(&isr_s3_pos3); // 初始化伺服电机 servo.period_ms(T_periode); while (true) { // 处理伺服电机动作 if (target_servo_pos != 0) { moveServoToPosition(target_servo_pos); target_servo_pos = 0; // 重置标志 } // 启动泵 if (start_pump && !pump_running) { printf("Starting pump\r\n"); timer.start(); pump_running = true; } // 运行泵逻辑 if (pump_running) { handle_pump(); } // 降低CPU占用 ThisThread::sleep_for(10ms); } }
关键修改说明
- ISR轻量化:所有中断服务函数仅设置标志位和启动消抖定时器,符合ISR“短平快”的设计规范,避免阻塞系统。
- 主循环处理业务:伺服动作、泵运行逻辑全部移到主循环执行,由标志位触发,系统调度更稳定。
- Timer正确管理:仅在启动泵时启动timer,状态切换逻辑清晰。
- 按键消抖:添加50ms消抖处理,避免按键抖动导致的多次误触发。
- 状态跟踪:用
pump_running、start_pump等标志位管理系统状态,避免逻辑混乱。
内容的提问来源于stack exchange,提问作者Erazor7
相关产品推荐
相关产品推荐

