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);
本文来自博客园,作者:杨旭0324,转载请注明原文链接:https://www.cnblogs.com/allalonewithyou/p/23013494,个人邮箱:yaonie4444@foxmail.com

浙公网安备 33010602011771号