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

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

小结

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

相关推荐
数智工坊6 小时前
机器人运动控制:采样、优化与学习三大流派深度对比与实战
android·学习·机器人
ZC跨境爬虫6 小时前
跟着 MDN 学JavaScript day_7:数学运算与逻辑判断实战测试
开发语言·前端·javascript·学习·ecmascript
普中科技7 小时前
【普中STM32F1xx开发攻略--标准库版】-- 第 45 章 FSMC-外扩 SRAM 实验
stm32·单片机·嵌入式硬件·fsmc·普中科技·外扩sram·is62wv51216
MartinYeung59 小时前
[论文学习]隐私保护联邦特徵选择与差分隐私的的工程实践框架
学习
qeen879 小时前
【C++】类与对象之类的默认成员函数(二)
android·c语言·开发语言·c++·笔记·学习
xiaoyuchidayuma9 小时前
永磁同步发电机的线电压和直流母线电压的关系
嵌入式硬件
潜创微科技9 小时前
4K60 over IP 方案简介
网络·嵌入式硬件·网络协议·tcp/ip·音视频
rit843249910 小时前
基于C#的USB HID设备读取测试软件
嵌入式硬件
Flandern111110 小时前
Pull Requests(PR)
学习·github·pr
三佛科技-1873661339710 小时前
FT32F103C8AT7兼容GD32F103C8T632 位通用微控制器MCU,替代性分析
单片机·嵌入式硬件