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

如果中断使用delay函数:

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

这更像是:

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


所以应该怎么办?

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

不要在中断里做:

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

那业务逻辑写在哪里?

一般分两层:

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

比如:

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

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


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

比如:

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

所以大部分情况下,真正业务逻辑更推荐写在 mainwhile(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);
    }
}

小结

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

相关推荐
AI的探索之旅12 分钟前
AI + LLM Wiki 高效学习 OpenCV:我的 8 步闭环方法论
人工智能·opencv·学习
Dr.kangder13 分钟前
嵌入式处理器虚拟化仿真技术(九)——SimpleScalar原理与应用
嵌入式硬件·架构·嵌入式·虚拟化·仿真
weixin_4316004422 分钟前
前端对接 SSE 的两种常见方式
前端·后端·学习·ai·sse·nest.js
芯片设计-曾工10 小时前
当精准脉冲遇上清晰视界——CH2205E模块与YL1621的“黄金搭档“
驱动开发·stm32·单片机·51单片机·硬件工程
圆奋奋14 小时前
FreeRTOS学习(三)- 任务调度模块
学习·开源·freertos
吃着火锅x唱着歌15 小时前
Effective C++ 学习笔记 条款40 明智而审慎地使用多重继承
c++·笔记·学习
LCG元15 小时前
STM32 硬件 SPI + DMA 驱动 OLED 显示屏(SSD1306):从原理到 112fps 高速刷新全流程
stm32·单片机·嵌入式硬件
xian_wwq16 小时前
【学习笔记】Prompt Engineering 没死,只是它不再够用了-2/16
笔记·学习·prompt
天吾cc16 小时前
RTT-ADC
单片机·嵌入式硬件·学习
kaixin_啊啊16 小时前
test_机器学习算法学习
学习·算法·机器学习