基于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
相关产品推荐
相关产品推荐

