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

如果中断使用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);
    }
}

小结

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

相关推荐
yangyanping2010810 小时前
Go语言学习之对象关系映射GORM
jvm·学习·golang
网络工程小王10 小时前
【Transformer架构详解】(学习笔记)
笔记·学习
Heartache boy11 小时前
野火STM32_HAL库版课程笔记-手动建立工程模板与CubeMX后续用法(重要)
笔记·stm32·单片机·嵌入式硬件
倒酒小生12 小时前
今日算法学习小结
学习
醇氧12 小时前
【学习】【说人话版】子网划分
学习
不灭锦鲤13 小时前
网络安全学习(面试)
学习·安全·web安全
世人万千丶14 小时前
Flutter 框架跨平台鸿蒙开发 - 鸿蒙版本五子棋游戏应用
学习·flutter·游戏·华为·harmonyos·鸿蒙
Aktx20FNz14 小时前
一文学习 Spring AOP 源码全过程
java·学习·spring
Jay Kay14 小时前
生成式推荐模型学习记录part1
学习
可乐鸡翅好好吃15 小时前
UUID----私有服务与公有服务
嵌入式硬件