You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

STM32 Nucleo L152RE伺服仅动作一次后控制器无响应问题排查

问题分析与修复方案

核心问题

你的代码存在几个致命问题,直接导致控制器崩溃:

  1. 中断服务函数(ISR)执行耗时操作:ISR里直接调用m_pump(),其中包含大量循环和ThisThread::sleep_for()——ISR的设计原则是尽可能短、绝不阻塞,mbed环境中在ISR里调用线程休眠函数会破坏系统调度逻辑,直接引发死机。
  2. Timer未初始化启动:第一次进入m_pump()时,timer未启动,elapsed_time()返回0,会一直执行正转循环;后续也没有正确启动timer的逻辑,状态永远无法切换。
  3. 按键无消抖处理:外部中断触发未做消抖,容易产生误触发,加重系统负载。

修复后的代码

#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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 01:40:26