核心原则:中断必须快进快出。把繁重的工作推迟到任务(Task)中去执行。这就是著名的 “底半部 (Bottom Half)” 机制。
1. 为什么要“推迟处理”?
假设我们要处理一个来自 RS485 总线的 Modbus 协议包,长度 256 字节,带 CRC 校验。
错误的做法(直接在 ISR 里干活):
-
进入 UART 中断。
-
读取数据。
-
执行 CRC16 校验(假设耗时 2ms)。
-
解析协议命令(假设耗时 1ms)。
-
退出中断。
后果: 在这 3ms 里,CPU 处于中断上下文,所有比它优先级低的任务(界面、PID控制、按键)全部卡死。更糟糕的是,如果此时来了一个更高优先级的紧急中断(如电机过流保护),虽然它能抢占,但系统的整体中断延迟(Latency)已经变得不可控。
正确的做法(推迟处理):
-
ISR (上半部):只负责收数据,存入缓冲区,发个信号给任务。耗时 5us。
-
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 里做完:
-
清除硬件标志位:这是废话,不清标志位出不去中断。
-
极高频的硬件时序:比如软件模拟 SPI,你需要微秒级的翻转 IO 口。如果在 Task 里做,会被调度器打断,时序就乱了。
-
简单的数据搬运:比如把数据放入 RingBuffer(下一期会讲)。如果不立刻搬走,下一个字节来了就会覆盖上一个(Overrun Error)。
原则:
-
搬运数据 -> ISR 做。
-
处理数据 -> Task 做。
阶段总结:思维跃迁完成
到这里,我们已经完成了从“裸机思维”到“RTOS 思维”的蜕变。
让我们回顾一下过去8 期的核心认知升级:
-
CPU 角色:从“保姆(轮询)”变成了“调度员(阻塞)”。
-
驱动力:从
if(flag)变成了Wait(Event)。 -
延时:从
HAL_Delay(忙等)变成了vTaskDelay(让权)。 -
架构:从 Super Loop 拆分成了 模块化 Task。
-
通讯:从 全局变量 升级到了 消息队列。
-
保护:从 关中断 升级到了 互斥量。
-
中断:懂得了 SysCall 的红线,学会了 Bottom Half 推迟处理。
接下来的旅程: 有了 RTOS 的架构支撑,我们终于可以往系统里填充真正的“硬货”了。 无论你用裸机还是 RTOS,有些通用的数据结构和算法是嵌入式工程师的必修课。 怎么处理串口的高速数据流?怎么写一个优雅的状态机?怎么快速计算 CRC?
/***************************************************
* 本文为作者《嵌入式开发基础与工程实践》系列文章之一。
* 关注即可订阅后续内容更新,翻阅往期信息,采用异步推送机制,无需主动轮询。
* 转发本文可视为一次网络广播,有助于更多节点接收该信息。
***************************************************/
9416

被折叠的 条评论
为什么被折叠?



