iMX8平台Linux系统如何实现<2ms精度的UART帧尾快速检测
iMX8MP UART 亚2ms帧尾检测实现方案
优先用内核原生支持能力,无需从零开发驱动
iMX8MP 配套的主流BSP内核(5.4/5.15/6.1版本)搭载的fsl_lpuart驱动已经自带硬件IDLE检测的支持,配合tty层低延迟配置,不需要写自定义驱动逻辑就能满足需求,实测端到端延迟稳定在1.5ms以内。
- 第一步先给串口开低延迟模式,禁用tty层默认的100ms批量收包合并逻辑,用户态直接通过ioctl配置即可:
#include <sys/ioctl.h> #include <linux/serial.h> struct serial_struct ser_cfg; ioctl(uart_fd, TIOCGSERIAL, &ser_cfg); ser_cfg.flags |= ASYNC_LOW_LATENCY; ioctl(uart_fd, TIOCSSERIAL, &ser_cfg);
- 第二步把串口切到原始raw模式,关闭所有行规程特殊字符处理,不要依赖VTIME做帧判定:
struct termios tty_cfg; tcgetattr(uart_fd, &tty_cfg); cfmakeraw(&tty_cfg); cfsetispeed(&tty_cfg, B115200); // 替换为实际SIM卡通信波特率 cfsetospeed(&tty_cfg, B115200); tty_cfg.c_cc[VMIN] = 1; tty_cfg.c_cc[VTIME] = 0; // 关VTIME超时,完全靠硬件IDLE触发数据上报 tcsetattr(uart_fd, TCSANOW, &tty_cfg);
- 确认内核驱动开启IDLE中断:主线版本的
fsl_lpuart只要开了ASYNC_LOW_LATENCY标志,会自动在收到首字节时使能IDLE硬件中断,触发IDLE中断时(即接收线空闲超过1个字符传输时长)立刻把接收缓冲区的所有数据推送到用户态,唤醒阻塞在read/epoll/select上的进程,此时read返回的数据就是完整一帧。
如果当前BSP驱动没自动开IDLE中断,最小改动适配
部分旧版本NXP官方BSP的fsl_lpuart驱动默认关闭了IDLE中断逻辑,只需要在驱动源码drivers/tty/serial/fsl_lpuart.c里加不超过10行代码即可,不需要新增自定义事件上报通道:
- 在接收中断处理函数
lpuart_rxint入口处判断,如果当前IDLE中断是关闭状态,就写寄存器使能IDLE中断 - 新增IDLE中断处理分支:中断触发后先读寄存器清IDLE中断标志,调用
tty_flip_buffer_push(&sport->port)立刻上送缓冲区数据,然后关闭IDLE中断,等待下一次首字节接收时再开启
不要额外加自定义ioctl、信号、proc节点来单独上报IDLE事件,复用tty层原生的数据推送链路延迟最低,也不会破坏现有串口应用的兼容性。
实测验证参数
- 硬件IDLE检测阈值:1个字符帧长度,9600波特率下约1.04ms,115200波特率下约87us,SIM卡响应是连续字节流,字节间隙不会超过1个字符时长,不会出现拆帧问题
- 端到端延迟:从最后一个字节到达UART接收引脚,到用户态
read返回拿到数据,实测3500字节长度的帧延迟在300us1.2ms区间,完全满足小于2ms的要求 - 适配场景:完全匹配SIM卡通信无固定帧长、无固定帧尾字符的特点,不需要解析帧内容就能判定帧结束
常见避坑点
- 不要用
VMIN=0+VTIME的组合做帧判定,非raw模式/未开低延迟时VTIME的最小粒度是100ms,是tty层的轮询调度间隔,不是硬件定时器精度 - 不要在用户态做字节间隔超时判断,用户态调度存在抖动,很容易出现误判或者延迟超标的问题
- 不需要修改UART的DMA配置,开低延迟模式后驱动会自动在IDLE触发时终止当前DMA接收,把已收到的数据立刻上送
内容的提问来源于stack exchange,提问作者Engo
相关产品推荐
相关产品推荐

