【第25期】中断处理策略:即时处理 vs 推迟处理 (Deferred)

核心原则:中断必须快进快出。把繁重的工作推迟到任务(Task)中去执行。这就是著名的 “底半部 (Bottom Half)” 机制。

1. 为什么要“推迟处理”?

假设我们要处理一个来自 RS485 总线的 Modbus 协议包,长度 256 字节,带 CRC 校验。

错误的做法(直接在 ISR 里干活):

  1. 进入 UART 中断。

  2. 读取数据。

  3. 执行 CRC16 校验(假设耗时 2ms)。

  4. 解析协议命令(假设耗时 1ms)。

  5. 退出中断。

后果: 在这 3ms 里,CPU 处于中断上下文,所有比它优先级低的任务(界面、PID控制、按键)全部卡死。更糟糕的是,如果此时来了一个更高优先级的紧急中断(如电机过流保护),虽然它能抢占,但系统的整体中断延迟(Latency)已经变得不可控。

正确的做法(推迟处理):

  1. ISR (上半部):只负责收数据,存入缓冲区,发个信号给任务。耗时 5us。

  2. Task (底半部):收到信号醒来,慢慢做 CRC 校验和解析。耗时 3ms。

由于 Task 是受 RTOS 调度的,如果此时有更紧急的任务,Task 会被抢占。这样就保证了系统的高响应性。

2. 实现机制:信号量与任务通知

在 RTOS 中,实现“推迟处理”最经典的模式是 “ISR 发信号 -> 高优先级任务处理”

方案 A:二值信号量 (Binary Semaphore)

这是最传统的做法。

// 定义一个信号量
SemaphoreHandle_t xSemPacketReceived;

// 1. 上半部:中断服务函数 (ISR)
void USART_IRQHandler(void) {
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    
    // 硬件操作:只把数据从寄存器搬到缓冲区
    Buffer[Index++] = USART->DR;
    
    // 如果收到了包尾
    if (Is_Packet_End()) {
        // 发送信号量:告诉任务“货到了”
        xSemaphoreGiveFromISR(xSemPacketReceived, &xHigherPriorityTaskWoken);
        
        // 核心:如果有高优先级任务在等这个信号,立刻触发调度!
        // 这样 ISR 一退出,CPU 直接跳到处理任务,而不是回原来的低级任务。
        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    }
}

// 2. 底半部:处理任务 (Task)
void Task_Protocol(void *pvParam) {
    // 这是一个高优先级任务
    while(1) {
        // 死等信号量 (阻塞)
        xSemaphoreTake(xSemPacketReceived, portMAX_DELAY);
        
        // 醒来后,开始干重活
        // 此时中断已经开启,其他中断可以随时打断这里,系统很安全
        if (CRC16(Buffer) == OK) {
            Process_Command(Buffer);
        }
    }
}

方案 B:直接任务通知 (Direct Task Notification)

这是 FreeRTOS 特有的黑科技。比信号量更快,更省内存。

  • ISR 里用:vTaskNotifyGiveFromISR(...)

  • Task 里用:ulTaskNotifyTake(...)

  • 效果:速度比信号量快 45%,RAM 消耗更少。推荐优先使用。

3. 特殊情况:什么时候必须“即时处理”?

虽然我们提倡推迟,但并不是所有事情都能推迟。 有些事情必须在 ISR 里做完:

  1. 清除硬件标志位:这是废话,不清标志位出不去中断。

  2. 极高频的硬件时序:比如软件模拟 SPI,你需要微秒级的翻转 IO 口。如果在 Task 里做,会被调度器打断,时序就乱了。

  3. 简单的数据搬运:比如把数据放入 RingBuffer(下一期会讲)。如果不立刻搬走,下一个字节来了就会覆盖上一个(Overrun Error)。

原则

  • 搬运数据 -> ISR 做。

  • 处理数据 -> Task 做。


阶段总结:思维跃迁完成

到这里,我们已经完成了从“裸机思维”到“RTOS 思维”的蜕变。

让我们回顾一下过去8 期的核心认知升级:

  1. CPU 角色:从“保姆(轮询)”变成了“调度员(阻塞)”。

  2. 驱动力:从 if(flag) 变成了 Wait(Event)

  3. 延时:从 HAL_Delay(忙等)变成了 vTaskDelay(让权)。

  4. 架构:从 Super Loop 拆分成了 模块化 Task。

  5. 通讯:从 全局变量 升级到了 消息队列。

  6. 保护:从 关中断 升级到了 互斥量。

  7. 中断:懂得了 SysCall 的红线,学会了 Bottom Half 推迟处理。

接下来的旅程: 有了 RTOS 的架构支撑,我们终于可以往系统里填充真正的“硬货”了。 无论你用裸机还是 RTOS,有些通用的数据结构和算法是嵌入式工程师的必修课。 怎么处理串口的高速数据流?怎么写一个优雅的状态机?怎么快速计算 CRC?

/***************************************************
 * 本文为作者《嵌入式开发基础与工程实践》系列文章之一。
 * 关注即可订阅后续内容更新,翻阅往期信息,采用异步推送机制,无需主动轮询。
 * 转发本文可视为一次网络广播,有助于更多节点接收该信息。
 ***************************************************/

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值