4. 优化中断服务程序(ISR)
任务背景与优化目标
中断服务程序(ISR)的核心要求是“短而快”。若在ISR中执行耗时操作(如浮点运算、延时等待、复杂协议解析),会阻塞其他中断或导致主循环任务饥饿,破坏系统实时性。本案例以一个典型的串口接收中断为例,演示如何将ISR中的耗时解析逻辑移至主循环,使ISR仅完成数据搬运与标志置位。
优化前:ISR中直接处理数据
// 优化前:ISR内完成协议解析(耗时操作)
volatile uint8_t rx_buffer[64];
volatile uint8_t rx_index = 0;
volatile uint8_t frame_ready = 0;
void USART_IRQHandler(void) {
uint8_t byte = USART->DR; // 读取硬件数据寄存器
// 以下解析逻辑耗时约50~100us(含浮点运算与循环)
if (byte == 0xAA) { // 帧头
rx_index = 0;
rx_buffer[rx_index++] = byte;
} else if (rx_index > 0) {
rx_buffer[rx_index++] = byte;
if (rx_index >= 8) { // 假设帧长8字节
// 浮点运算:将4字节负载转换为float
float value = *(float*)&rx_buffer[3];
if (value > 100.0f) { // 阈值判断
// 执行耗时的滤波算法(约200us)
value = value * 0.8f + 20.0f;
}
frame_ready = 1;
rx_index = 0;
}
}
}
优化后:ISR仅做数据缓存
// 优化后:ISR只负责接收字节与置位标志
#define RX_FIFO_SIZE 128
volatile uint8_t rx_fifo[RX_FIFO_SIZE];
volatile uint8_t fifo_head = 0;
volatile uint8_t fifo_tail = 0;
volatile uint8_t data_available = 0;
void USART_IRQHandler(void) {
uint8_t byte = USART->DR;
uint8_t next = (fifo_head + 1) % RX_FIFO_SIZE;
if (next != fifo_tail) { // FIFO未满
rx_fifo[fifo_head] = byte;
fifo_head = next;
data_available = 1; // 通知主循环
}
// FIFO满时丢弃新字节(可在此置溢出标志)
}
// 主循环中处理协议解析(非中断上下文)
void main_loop_process(void) {
static uint8_t parse_buffer[8];
static uint8_t parse_index = 0;
while (fifo_tail != fifo_head) {
uint8_t byte = rx_fifo[fifo_tail];
fifo_tail = (fifo_tail + 1) % RX_FIFO_SIZE;
// 以下为原ISR中的耗时逻辑,现移至主循环
if (byte == 0xAA) {
parse_index = 0;
parse_buffer[parse_index++] = byte;
} else if (parse_index > 0) {
parse_buffer[parse_index++] = byte;
if (parse_index >= 8) {
float value = *(float*)&parse_buffer[3];
if (value > 100.0f) {
value = value * 0.8f + 20.0f;
}
// 此处可安全调用耗时算法或外设操作
parse_index = 0;
}
}
}
if (fifo_head == fifo_tail) {
data_available = 0; // 所有数据已处理完
}
}
关键优化点
- ISR职责最小化:仅执行寄存器读取、FIFO写入和指针更新,耗时从数百微秒降至约1~2微秒(取决于总线时钟)。
- 数据缓冲机制:使用环形FIFO(容量128字节)暂存原始数据,避免覆盖。主循环通过
data_available标志或直接比较fifo_head与fifo_tail判断是否有待处理数据。 - 上下文切换:耗时解析、浮点运算、算法调用全部移至主循环的
main_loop_process()中执行,不阻塞中断响应。
验证方法
- 功能验证:向串口发送一帧数据
AA 01 02 00 00 C8 42 00(其中字节3~6为float小端序,值100.5),观察主循环中解析出的value是否等于100.5(经滤波后为100.4)。 - 实时性验证:在ISR入口和出口各置一个GPIO翻转,用示波器测量高电平持续时间。优化前应观察到明显脉冲(>50us),优化后脉冲宽度应小于3us(具体取决于MCU主频与指令周期)。
- 压力测试:以1ms间隔连续发送1000帧数据,确认
frame_ready(或data_available)标志无丢失,且主循环能完整处理所有帧。
注意事项
- 若主循环处理速度跟不上中断接收速率,FIFO会溢出。需根据最坏情况计算:
FIFO大小 > 最大中断间隔内到达的字节数。 - 当系统存在多个优先级中断时,应确保高优先级ISR不被本ISR阻塞——优化后本ISR已满足此要求。
- 若使用RTOS,可将
main_loop_process()放入独立任务,并通过信号量或消息队列与ISR通信,效果相同。