无OS微控制器UART中断共享环形缓冲区是否需采用无锁设计
裸机UART缓冲区共享保护方案解答
方案选型结论
你当前采用的关中断保护临界区的方案,是单核心裸机场景下最简单可靠的共享资源保护手段,完全不需要额外实现基于CAS的无锁环形缓冲区:
- 单核心场景下,中断是唯一的异步执行流,关中断即可完全避免共享资源的并发访问冲突
- 优化后的临界区仅包含指针交换、位置变量修改等几个操作,耗时最多十几个CPU周期,哪怕是Cortex-M0跑10MHz也仅耗时1us多,远低于UART单字节传输时长(1Mbps波特率下单比特传输耗时1us,1字节至少要10us),完全不会导致串口丢包
- 自行实现无锁队列容易出现内存序、原子性相关的隐性BUG,调试难度远高于关中断方案
当前代码存在的BUG与优化点
你的现有实现存在几个明显的设计缺陷,需要优先修正:
- 临界区包含耗时过长的操作
swapBuffers函数中把512字节缓冲区清零的操作放在了关中断的区间内,清零操作需要消耗数百个CPU周期,高波特率场景下会直接导致串口中断响应不及时丢数据,需要把清零操作移到开中断之后执行。 - 用0值判断缓冲区为空的逻辑错误
串口传输可能收到合法的0x00数据(比如二进制传输、字符串结束符),用c==0判断无数据会直接丢弃合法数据,应该新增独立的变量记录缓冲区有效数据长度。 - 未做缓冲区溢出保护
中断写入逻辑没有判断缓冲区是否已满,缓冲区满后继续写入会直接覆盖未读取的旧数据,需要新增满判断逻辑。 - readPos重置逻辑错误
缓冲区交换后,readPos应该重置为0,从新的读缓冲区起始位置开始读取,现有逻辑会导致读取位置偏移。
修正后的核心代码示例
新增变量定义
// 记录写缓冲区已写入的有效数据长度,中断中写、swap中读 volatile int writeLen[MAX_CHANNEL_COUNT] = {0}; // 记录读缓冲区的总有效数据长度,仅主循环中访问 int readLen[MAX_CHANNEL_COUNT] = {0};
优化后的swapBuffers函数
void swapBuffers(int channel) { char *old_write_buf; __disable_irq(); // 临界区仅做指针和状态变量交换,耗时极短 old_write_buf = writeBuffers[channel]; writeBuffers[channel] = readBuffers[channel]; readBuffers[channel] = old_write_buf; // 转移有效数据长度,重置写状态 readLen[channel] = writeLen[channel]; writeLen[channel] = 0; writePos[channel] = 0; readPos[channel] = 0; __enable_irq(); // 清零操作移到开中断后执行,不影响中断响应 memset(writeBuffers[channel], 0, UART_BUFSIZE); }
优化后的中断写入逻辑
if (IIR & USART_FLAG_RXNE) { USARTx->SR &= ~USART_FLAG_RXNE; c = USART_ReceiveData(USARTx); // 缓冲区满时直接丢弃新数据,可根据需求修改为覆盖旧数据 if(writeLen[Channel] < UART_BUFSIZE) { writeBuffers[Channel][writePos[Channel]] = c; writePos[Channel]++; writeLen[Channel]++; if(writePos[Channel]>=UART_BUFSIZE) writePos[Channel]=0; } }
优化后的读字符函数
int GetCharUART (char Channel) { // 用有效长度判断是否读完,避免0值误判 if (readPos[Channel] >= readLen[Channel]) { swapBuffers(Channel); return EMPTY; } int c = readBuffers[Channel][readPos[Channel]++]; return c; }
可选的无锁方案说明
如果你对中断延迟要求极高,完全不能接受关中断的开销,可以采用单生产者单消费者无锁环形缓冲区,你的场景刚好匹配该模型的适用条件(UART中断是唯一生产者,主循环是唯一消费者):
- ARM平台下32位整型变量的读写本身就是原子操作,不需要额外CAS指令
- 仅需要将读写位置声明为volatile,避免编译器优化导致的逻辑错误
- 不需要关中断,也不需要任何锁操作,开销比关中断方案更低
内容的提问来源于stack exchange,提问作者Andre Ahmed
相关产品推荐
相关产品推荐

