You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于FreeRTOS的ATSAMD21 SERCOM(USART)非阻塞连续读写实现问题

ATSAMD21 UART非阻塞连续读写解决方案

问题根因

你将驱动中等待RXC接收标志位的while循环改为if判断后,原设计读取N字节的接口每次仅会读取1字节就直接返回,剩余未读取的字节会滞留在接收寄存器中,多次调用读接口时会重复读取寄存器内未更新的旧数据,最终出现多字节发送时仅返回最后一个字节重复值的问题。

推荐实现方案:驱动层新增环形缓冲区适配FreeRTOS

该方案通过中断+环形缓冲区缓存接收数据,完全避免应用层阻塞,同时不会丢数,适配FreeRTOS多任务场景。

实现步骤

  • 第一步:在SERCOM3 UART驱动中新增静态环形缓冲区、读写指针变量,大小可根据实际波特率调整,常规场景设为32~128字节即可
  • 第二步:修改UART接收中断服务函数,每次触发RXC接收中断时,直接将接收寄存器的数据写入环形缓冲区并更新写指针,无需应用层等待标志位
  • 第三步:新增非阻塞读接口,调用时直接从环形缓冲区读取已缓存的数据,缓冲区数据不足时直接返回实际读到的字节数,不阻塞
  • 第四步:若需要写操作也非阻塞,按同样逻辑给发送链路新增环形缓冲区,发送中断触发时从缓冲区取数据写入发送寄存器即可

示例代码

驱动层修改代码

// 驱动文件新增全局变量定义
#define UART_RX_BUF_SIZE 64
static uint8_t uart_rx_buf[UART_RX_BUF_SIZE];
static volatile uint8_t rx_wr_ptr = 0;
static volatile uint8_t rx_rd_ptr = 0;

// 原有SERCOM3接收中断服务函数修改
void SERCOM3_USART_InterruptHandler(void)
{
    // 原有其他中断处理逻辑保留
    if(SERCOM3->USART.INTFLAG.bit.RXC) {
        uint8_t recv_data = SERCOM3->USART.DATA.reg;
        uint8_t next_wr = (rx_wr_ptr + 1) % UART_RX_BUF_SIZE;
        // 缓冲区未满时写入数据
        if(next_wr != rx_rd_ptr) {
            uart_rx_buf[rx_wr_ptr] = recv_data;
            rx_wr_ptr = next_wr;
        }
        // 清除中断标志位
        SERCOM3->USART.INTFLAG.reg = SERCOM_USART_INTFLAG_RXC;
    }
}

// 新增非阻塞读接口,返回实际读取的字节数
uint8_t SERCOM3_USART_Read_Noblock(uint8_t *out_buf, uint8_t req_len)
{
    uint8_t read_cnt = 0;
    // 进入临界区防止中断修改指针引发竞争
    taskENTER_CRITICAL();
    while((rx_rd_ptr != rx_wr_ptr) && (read_cnt < req_len)) {
        out_buf[read_cnt++] = uart_rx_buf[rx_rd_ptr];
        rx_rd_ptr = (rx_rd_ptr + 1) % UART_RX_BUF_SIZE;
    }
    taskEXIT_CRITICAL();
    return read_cnt;
}

应用层代码修改

while(1)
{
    memset(ch, 0, 2);
    // 非阻塞读,无需等待,直接返回实际读到的字节数
    uint8_t recv_len = SERCOM3_USART_Read_Noblock(ch, 2);
    // 可在这里判断recv_len == 2后再执行接收数据的处理逻辑
    SERCOM3_USART_Write(th, 2);
    // 可选:增加1ms延时让出CPU,避免该任务占满处理器资源
    vTaskDelay(pdMS_TO_TICKS(1));
}

临时验证方案:不改驱动的应用层实现

仅用于快速验证功能,波特率较高时容易丢包,不建议量产使用:

uint8_t recv_cnt = 0;
while(1)
{
    // 直接查询接收标志位,有数据再读
    if(SERCOM3->USART.INTFLAG.bit.RXC) {
        ch[recv_cnt++] = SERCOM3->USART.DATA.reg;
        if(recv_cnt >= 2) {
            recv_cnt = 0;
            // 收到2字节后的处理逻辑写在这里
        }
    }
    SERCOM3_USART_Write(th, 2);
    vTaskDelay(pdMS_TO_TICKS(1));
}

注意:配置UART中断优先级时需保证其优先级高于普通应用任务,避免高负载场景下中断响应不及时丢数;如果写操作也存在阻塞问题,按照接收环形缓冲区的逻辑给发送链路也加一套缓存即可。

内容的提问来源于stack exchange,提问作者Murlidhar Roy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 05:36:05