3. GPIO控制LED与按键中断——为机器人关节使能与限位开关做准备

做机器人运动学调试,第一步不是算正逆解,而是让关节动起来。怎么动?靠使能信号。怎么停?靠限位开关。这两个信号,在STM32上就是GPIO的输入输出。

我见过不少新手,一上来就搞CAN总线、搞EtherCAT,结果关节没使能,电机干烧。说白了,GPIO是嵌入式世界的“手”和“眼”。今天咱们就把这只手练利索。

3.1 从LED开始:关节使能信号的“Hello World”

关节使能信号,本质上就是一个高电平或低电平。你给驱动器一个ENABLE引脚拉高,电机就通电;拉低,就断电。跟LED亮灭一个道理。

先看最简代码,用STM32CubeMX生成工程后,在main.c里写:

// 使能关节(假设PA0接使能引脚,高电平有效)
void Joint_Enable(void)
{
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);
}

// 关闭关节
void Joint_Disable(void)
{
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
}

// 翻转使能状态(调试时常用)
void Joint_Toggle(void)
{
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0);
}

嗯,就这么简单。但我在项目里吃过亏——使能瞬间的电流冲击。驱动器使能瞬间,母线电容会猛充电,如果电源功率不够,STM32会掉电复位。后来我加了个延时:

void Joint_Enable_Safe(void)
{
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);
    HAL_Delay(50);  // 等50ms,让驱动器缓启动
}
我的习惯:使能引脚不要直接连STM32,中间串一个100Ω电阻。万一驱动器那边短路,电阻能保命。我烧过两个IO口才长记性。

3.2 按键中断:限位开关的“急停”逻辑

限位开关,就是机器人的“墙壁传感器”。关节转到极限位置,限位开关被触发,必须立刻停。用轮询?不行。万一主循环在算矩阵,关节就撞上去了。

必须用中断。看配置:

参数 配置值 说明
GPIO Mode External Interrupt Mode with Rising/Falling edge trigger 双边沿触发,适应常开/常闭两种限位开关
GPIO Pull-up/Pull-down Pull-up 内部上拉,防止悬空误触发
NVIC Priority Preemption Priority = 0, Sub Priority = 0 最高优先级,必须立刻响应
GPIO Speed High 限位信号变化快,用高速模式

中断服务函数怎么写?

// 假设限位开关接PB0,低电平触发(常闭型限位开关被压开时变高)
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if(GPIO_Pin == GPIO_PIN_0)
    {
        // 读取当前电平
        if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == GPIO_PIN_RESET)
        {
            // 限位触发!立刻关闭关节使能
            Joint_Disable();
            // 设置故障标志
            g_LimitSwitchTriggered = 1;
        }
        else
        {
            // 限位释放,可以重新使能
            g_LimitSwitchReleased = 1;
        }
    }
}
我曾经踩过的坑:中断里调用HAL_Delay()会导致死锁。因为HAL_Delay依赖SysTick中断,而SysTick中断优先级比EXTI低,如果EXTI不退出,SysTick永远进不来。中断里只做最轻量的操作——置标志位、清中断、关使能。其他事交给主循环。

3.3 硬件去抖:别让开关“抖”掉你的关节

机械开关都有抖动。按下一次,电平会跳变几十毫秒。如果你不做处理,一次限位触发可能产生几十次中断。

软件去抖最简单:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if(GPIO_Pin == GPIO_PIN_0)
    {
        // 先关中断,防止重复触发
        HAL_NVIC_DisableIRQ(EXTI0_IRQn);
        
        // 启动定时器,20ms后重新采样
        HAL_TIM_Base_Start_IT(&htim6);
        g_DebounceTimerPin = GPIO_PIN_0;
    }
}

// 定时器回调中重新采样
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM6)
    {
        HAL_TIM_Base_Stop_IT(&htim6);
        
        // 重新读取电平
        uint8_t level = HAL_GPIO_ReadPin(GPIOB, g_DebounceTimerPin);
        // 根据电平做相应处理
        ProcessLimitSwitch(level);
        
        // 重新使能中断
        HAL_NVIC_EnableIRQ(EXTI0_IRQn);
    }
}

硬件去抖更可靠——在GPIO引脚对地并一个0.1μF电容,再串一个10kΩ电阻。RC时间常数≈1ms,能滤掉大部分抖动。

实战建议:量产产品用硬件去抖+软件去抖双重保险。我见过电容虚焊导致整批机器限位失灵的事故,软件去抖是最后的防线。

3.4 本章知识体系

下面这张图把GPIO在机器人关节控制中的角色串起来了:

GPIO在机器人关节控制中的知识体系 STM32 GPIO 输出模式 输入模式 关节使能信号 LED状态指示 PWM使能控制 限位开关检测 急停按钮输入 编码器索引信号 核心要点 输出模式控制关节使能 → 输入模式检测限位开关 → 中断保证实时响应 → 去抖保证信号可靠

3.5 实战:搭建一个关节使能+限位保护的最小系统

把上面所有知识点串起来,写一个完整示例:

// 头文件
#include "main.h"
#include "gpio.h"
#include "tim.h"

// 全局变量
volatile uint8_t g_JointEnabled = 0;
volatile uint8_t g_LimitSwitchHit = 0;

// 初始化
void JointControl_Init(void)
{
    // 默认关闭关节
    Joint_Disable();
    g_JointEnabled = 0;
    g_LimitSwitchHit = 0;
    
    // 使能限位开关中断
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}

// 主循环中的状态机
void JointControl_Task(void)
{
    if(g_LimitSwitchHit)
    {
        // 限位触发,保持关节关闭
        Joint_Disable();
        // 点亮报警LED
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);
    }
    else
    {
        // 正常运行时,根据指令使能
        if(g_JointEnabled)
        {
            Joint_Enable_Safe();
            // 熄灭报警LED
            HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
        }
    }
}

// 中断回调
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if(GPIO_Pin == LIMIT_SWITCH_PIN)
    {
        // 硬件去抖已经做了,这里直接读
        if(HAL_GPIO_ReadPin(LIMIT_SWITCH_PORT, LIMIT_SWITCH_PIN) == GPIO_PIN_RESET)
        {
            g_LimitSwitchHit = 1;
            Joint_Disable();  // 立刻断电
        }
        else
        {
            g_LimitSwitchHit = 0;
        }
    }
}
调试技巧:用逻辑分析仪抓GPIO波形。使能信号上升沿到电机实际转动,中间有驱动器响应延迟。我测过某款国产驱动器,延迟高达120ms。这个参数在运动学控制里必须补偿。

3.6 常见故障与定位

故障现象 可能原因 排查方法
关节无法使能 GPIO配置为开漏输出 检查GPIO模式,改为推挽输出
限位触发后关节不停 中断优先级不够高 检查NVIC配置,设为最高抢占优先级
限位误触发 未做去抖处理 加硬件RC或软件延时去抖
使能瞬间MCU复位 电源功率不足 使能后加延时,或增加电源电容
中断不响应 GPIO时钟未使能 检查RCC配置,确保GPIOB时钟已开启

GPIO看似简单,但它是机器人控制的基石。使能信号给不对,后面所有运动学计算都是白搭。限位开关处理不好,轻则撞限位,重则损坏机械结构。

我个人习惯,每做一个新项目,先把GPIO这一层测透——用示波器看波形,用逻辑分析仪看时序,确保每个信号都干净可靠。这一步省不了,也急不得。