【嵌入式】外部中断的学习小坑记录

如果中断使用delay函数:

中断里的 HAL_Delay() 要等系统时基 tick 增加;而这个 tick 往往依赖 SysTick 中断。当前中断没退出时,SysTick 可能进不来,于是 HAL_Delay() 一直等,程序就卡住了。

这更像是:

中断里阻塞等待,结果把给它计时的人也堵住了。


所以应该怎么办?

中断里只做短、小、快的事

不要在中断里做:

  • HAL_Delay()
  • 长时间循环
  • 复杂计算
  • 串口长打印
  • 大量业务逻辑

那业务逻辑写在哪里?

一般分两层:

1)中断里:只做"事件登记"

比如:

  • 置一个标志位
  • 记录当前时间
  • 记录哪个按键来了
  • 极简单地翻一个状态

中断里适合做的是这种"快进快出"的事。


2)主循环 while(1) 里:做真正业务处理

比如:

  • 消抖
  • 判断长按短按
  • 切换 LED
  • 刷界面
  • 发串口
  • 状态机处理

所以大部分情况下,真正业务逻辑更推荐写在 main 的 while(1) 里。


最推荐的模式

这种按键外部中断,标准思路是:

中断里

只告诉主循环:

"有一个按键事件来了。"

比如:

c 复制代码
volatile uint8_t key6_event = 0;

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == GPIO_PIN_5)
    {
        key6_event = 1;
    }
}

主循环里

再真正处理:

c 复制代码
while (1)
{
    if (key6_event == 1)
    {
        key6_event = 0;

        HAL_Delay(20);  // 消抖放这里,不放中断里

        if (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_5) == GPIO_PIN_RESET)
        {
            HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_5);
        }
    }

    HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_9);
    HAL_Delay(500);
}

那中断里能不能直接写业务?

可以,但要满足一个条件:

这个业务必须非常短、非常快、不阻塞。

比如这种就可以:

c 复制代码
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == GPIO_PIN_5)
    {
        HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_5);
    }
}

因为它只是翻转一个引脚,动作非常快。


所以到底怎么选?

情况1:逻辑特别简单

比如:

  • 按一下翻转一个 LED

可以直接写在中断里。


情况2:逻辑稍微复杂

比如:

  • 要消抖
  • 要判断长按短按
  • 要做延时
  • 要串口输出
  • 要修改多个状态

就不要全塞中断里,应该:

  • 中断里置标志
  • 主循环里处理业务

可以记一个经验法则

中断里做:

"通知"

while(1) 里做:

"处理"


为什么主循环更适合做业务?

因为主循环里:

  • 可以安全用 HAL_Delay
  • 可以慢慢判断状态
  • 不会长时间占住中断
  • 不容易影响别的外设响应

而中断里如果写太多,会带来这些问题:

  • 卡住别的中断
  • 响应变差
  • 调试困难
  • 出现这次这种"看起来死锁"的现象

例子1

c 复制代码
volatile uint8_t key6_pressed = 0;

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == GPIO_PIN_5)
    {
        key6_pressed = 1;
    }
}

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();

    while (1)
    {
        if (key6_pressed == 1)
        {
            key6_pressed = 0;

            HAL_Delay(20);  // 消抖放主循环

            if (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_5) == GPIO_PIN_RESET)
            {
                HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_5);
            }
        }

        HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_9);
        HAL_Delay(500);
    }
}

小结

中断里不做阻塞延时,不做重业务;中断里只置标志,主循环里处理业务。

相关推荐
云半S一15 分钟前
Agent学习总结二
经验分享·笔记·学习·agent
彧azz15 分钟前
B-树学习总结:从结构特性到核心操作的系统复习
数据结构·笔记·b树·学习
燕落南枝15 分钟前
HTML 学习笔记:从基础到音频标签
笔记·学习·html
我是伪码农10 小时前
百蝶杯学习
学习
国科安芯10 小时前
立方星与微纳卫星综合电子微型化趋势下的抗辐射MCU选型综述
人工智能·单片机·嵌入式硬件·mcu·risc-v·抗辐射
xunyan623410 小时前
3-1 独立按键控制LED亮灭(SDCC + Notepad++ + STC-ISP/普中ISP)
单片机·嵌入式硬件·接口隔离原则
keyipatience10 小时前
JavaScript学习(1)书写位置,变量、数据类型、运算符
开发语言·前端·javascript·css·学习
WD1372980155711 小时前
WD5030L,为充电桩、工业设备、5G 基站、车载系统、消费电子提供电源,7-30V 的宽压输入
stm32·单片机·嵌入式硬件·5g·车载系统·51单片机
xian_wwq12 小时前
【学习笔记】Milvus 如何完成一次向量相似度检索,Milvus 索引到底应该怎么选?-15
笔记·学习·milvus
Hhuhkt12 小时前
3D 云养宠小游戏
学习