IMX9x的串口9600波特率的分包DMA配置

前言

IMX93&IMX91的串口在9600的情况下会有 数据分包 不对的情况。

问题描述

fsl-lpuart 串口驱动开启了硬件 eDMA,导致modbus字节不满足块传输要求,就会有100多ms的超时,导致采集速度变慢,看改成PIO中断模式有没有问题,好处是一旦485总线空闲就会把数据直接返回给应用层,可以把串口改成PIO模式,实时性要求比较高。

分析

IMX93在9600波特率 使用DMA接收的模式下发现尾部数据会分包, 看代码大概是因为DMA尾部数据只能通过uart总线空闲中断去上报到ttys中, 而总线空闲中断触发时间为128位的空闲时长。 当用户将串口使用到modbus协议中这个尾包的间隔让在modbus帧检测出错。

正常的DMA传输过程中的是超过半水位就发送中断给CPU,就是传输中间有数据有传输完成但是没有到半水位的情况,这个时候就需要等待DMA的空闲中断了,然后这个空闲中断的等待时间比较长,波特率越低等待相同的位数时间越长,9600波特率就差不多要等待133ms,导致应用层等不到数据从而产生异常。

patch

--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c

@@ -240,7 +240,12 @@

#define DMA_RX_TIMEOUT        (10)

-#define DMA_RX_IDLE_CHARS    8
+#define DMA_RX_IDLE_CHARS    2

+
+/*
+ * IDLECFG value for DMA idle interrupt mode (dma_idle_int=true).
+ * 0x1 = 2 idle character times. At 9600 baud this is ~2.08ms, well
+ * below the Modbus RTU 3.5T inter-frame gap (~3.65ms).
+ * RXIDEN must be <= IDLECFG (in char times) so FIFO is flushed to
+ * DMA ring before the IDLE interrupt handler reads ring position.
+ */
+#define LPUART32_DMA_IDLECFG    0x1  /* 2 idle chars */
+#define LPUART32_DMA_RXIDEN    0x2  /* 2 idle chars (FIFO flush trigger) */

@@ -1670,7 +1670,7 @@ static void lpuart32_setup_watermark(struct lpuart_port *sport)
     val |= UARTFIFO_TXFE | UARTFIFO_RXFE;
     val |= UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH;
-    val |= FIELD_PREP(UARTFIFO_RXIDEN, 0x3);
+    val |= FIELD_PREP(UARTFIFO_RXIDEN, LPUART32_DMA_RXIDEN);
 
@@ -1700,7 +1700,7 @@ static void lpuart32_setup_watermark_enable(struct lpuart_port *sport)
     ctrl = lpuart32_read(&sport->port, UARTCTRL);
     ctrl |= UARTCTRL_RE | UARTCTRL_TE;
-    ctrl |= FIELD_PREP(UARTCTRL_IDLECFG, 0x7);
+    ctrl &= ~UARTCTRL_IDLECFG;
+    ctrl |= FIELD_PREP(UARTCTRL_IDLECFG, LPUART32_DMA_IDLECFG);
     lpuart32_write(&sport->port, ctrl, UARTCTRL);

 
@@ -2370,6 +2370,13 @@ lpuart32_set_termios(...)
+    /*
+     * Recalculate IDLECFG: set_termios reads old ctrl which carries
+     * the stale IDLECFG bits. Clear and re-apply the DMA idle config
+     * so baud-rate changes do not silently restore IDLECFG=0x7.
+     */
+    if (sport->dma_idle_int) {
+        ctrl &= ~UARTCTRL_IDLECFG;
+        ctrl |= FIELD_PREP(UARTCTRL_IDLECFG, LPUART32_DMA_IDLECFG);
+    }

     lpuart32_write(port, ctrl, UARTCTRL);
posted @ 2026-09-17 15:46  杨旭0324  阅读(12)  评论(0)    收藏  举报