跳到主要内容

UART FAQ 排查指南

本文档基于瑞芯微官方文档 Rockchip_Developer_Guide_UART_FAQ_CN.pdf(V1.1.0, 2021-12-29)整理,提供 UART 开发和维护过程中常见问题的解决方案。

适用范围
  • 芯片平台:所有采用 Linux 内核的 Rockchip 芯片
  • 读者对象:技术支持工程师、软件开发工程师
前置阅读

使用本文前,请先阅读 UART 开发指南,确保对 Rockchip UART 的功能特点、使用方法、调试手段等内容理解充分。


使用说明​

相关文档​

  • UART 开发指南:Rockchip Linux Kernel UART 开发指南
  • U-Boot UART 开发指南:Rockchip_Developer_Guide_UBoot_Nextdev_CN 中的 UART 章节
提示

UART 根据使用场景和具体软硬件平台的不同,相同的问题在解决方案上会存在差异,请灵活处理。Rockchip 平台 SDK 中 UART 基础功能都是经过测试的。如果使用的 SDK 版本较早,请获取最新 SDK 源码并对比 UART 驱动代码。如有疑问,请通过 Rockchip Redmine 平台联系 FAE 获取技术支持。

开机日志​

根据开机日志中各个阶段启动的标志性打印,可以判断出 UART 问题出现的阶段。以 RK356x 为例,常见的开机日志阶段:

阶段标志打印
DDR 阶段(TPL)DDR Version <version>
Miniloader 阶段(SPL)U-Boot SPL board init / U-Boot SPL <version>
Trust 阶段(BL31)INFO: Preloader serial: 2 / NOTICE: BL31: <version>
OP-TEE 阶段(BL32)I/TC: Rockchip release version: 1.0 / I/TC: OP-TEE version: <version>
U-Boot 阶段U-Boot <version> / Model: Rockchip RK3568 Evaluation Board
Kernel earlyconStarting kernel ... / bootconsole [uart8250] enabled
fiq_debuggerconsole [ttyFIQ0] enabled / bootconsole [uart8250] disabled
普通 UART 初始化Serial: 8250/16550 driver / fe650000.serial: ttyS1 at MMIO ... is a 16550A

io 命令​

Rockchip 平台中通常集成 io 命令用于直接读写寄存器。下面以 RK3568 UART5_M0 为例,说明常用调试操作。

确保 UART5 pclk 打开:

echo 1 > /sys/kernel/debug/clk/pclk_uart5/clk_enable_count

查看 UART5 控制器全部寄存器:

io -4 -l 0x100 0xFE690000

查看 UART5 相关时钟:

cat /sys/kernel/debug/clk/clk_summary | grep uart5          # UART5 相关时钟
cat /sys/kernel/debug/clk/sclk_uart5/clk_rate # UART5 工作时钟频率
echo 24000000 > /sys/kernel/debug/clk/sclk_uart5/clk_rate # 设置工作时钟
cat /sys/kernel/debug/clk/sclk_uart5/clk_enable_count # UART5 工作时钟状态
echo 1 > /sys/kernel/debug/clk/sclk_uart5/clk_enable_count # 使能工作时钟

检查 IOMUX 配置:

io -4 0xFDC60310    # uart5_iomux_sel,选择 iomux group 0 或 group 1
# fdc60310: 00000000 → bit 0 为 0,说明 UART5_M0 配置正确

io -4 0xFDC60020 # gpio2a1_sel 和 gpio2a2_sel,rx 和 tx 的 GPIO function
# fdc60020: 00001331 → bit 7:4 和 bit 11:8 均为 3,说明 UART5_M0 RX 和 TX 配置正确

通过寄存器配置 UART 基础收发功能(115200 波特率):

io -4 0xFE690088 0x00000007   # SRR 寄存器,对 UART 和 FIFO 进行 reset
io -4 0xFE690010 0x00000010 # MCR 寄存器,配置为 loopback 模式
io -4 0xFE69000C 0x00000080 # LCR 寄存器,div_lat_access 置 1
io -4 0xFE690000 0x0000000D # DLL 寄存器,配置波特率分频系数
io -4 0xFE690004 0x00000000 # DLH 寄存器,配置波特率分频系数
io -4 0xFE69000C 0x00000003 # LCR 寄存器,div_lat_access 清 0 并配置协议参数
io -4 0xFE690010 0x00000000 # MCR 寄存器,配置为一般模式
io -4 0xFE690004 0x00000001 # IER 寄存器,打开接收中断
io -4 0xFE690008 0x00000041 # FCR 寄存器,打开 FIFO,触发阈值配置为 1/4
io -4 0xFE69009C # SRT 寄存器,读写 RX FIFO 触发阈值
io -4 0xFE6900A0 # STET 寄存器,读写 TX FIFO 触发阈值
io -4 0xFE690000 0x00000055 # THR 寄存器,发送 "U"(0x55)
io -4 0xFE690000 # RBR 寄存器,读取接收到的数据
注意

配置 DLL 和 DLH 时,需要先将 LCR 寄存器的 bit 7(div_lat_access)置 1。在此操作前,需要先将 MCR 寄存器的 bit 4(loopback)置 1。如果 UART 不在 loopback 模式下,当配置 DLL/DLH 时 RX 引脚受到外部信号干扰,将会触发 UART busy 状态导致 LCR 的 bit 7 无法重新配置为 0。

关闭其它打印干扰​

调试过程中可以使用以下命令关闭其它打印干扰:

su
echo 0 > /proc/sysrq-trigger

波特率相关​

UART 在某个波特率下能否正常工作,主要取决于:

  1. UART 控制器内部分频(DLL 和 DLH 寄存器)
  2. CRU 提供给 UART 控制器的工作时钟分频策略(uart_sclk)

注意事项:

  • 请使用常见的波特率:115200、460800、921600、1.5M、3M、4M 等。Rockchip 对于非典型波特率的支持不做保证
  • Rockchip UART IP 最高稳定支持 4M 波特率,更高波特率需要修改驱动框架层代码
  • 工作时钟 uart_sclk 的后端约束频率 sign_off 值通常设计为 100MHz,特殊场景下也请不要超过 6M 波特率
  • 如果 SDK 版本较为老旧,请联系 Rockchip 更新 UART 相关源码

时钟分频策略​

在 Linux Kernel 4.19 中,主要关注以下驱动文件和函数:

文件函数
drivers/tty/serial/8250/8250_dw.cdw8250_set_termios
drivers/clk/clk-fractional-divider.cclk_fd_set_rate
drivers/clk/rockchip/clk.crockchip_fractional_approximation

在 dw8250_set_termios 函数中:

  • 波特率为 115200 及以下:时钟源选择 24M 晶振
  • 波特率在 115200 之上:时钟源选择 PLL,由 CRU 控制

对于某些特殊波特率,CRU 分频后提供的工作时钟可能误差较大,可以通过再乘以 2(sclk_rate = baud_rate × 16 × 2)来改善。

为了减少 CRU 输入到 UART 控制器的工作时钟的 jitter,CRU 的小数分频器存在一个 20 倍关系的限制。解除 20 倍限制请查看 rockchip_fractional_approximation 函数中的标志 CLK_FRAC_DIVIDER_NO_LIMIT。

案例​

波特率测量方法错误​

出现问题时,波特率测量请一定使用示波器。示波器抓取 UART TX 信号波形才能得到真实的波特率。逻辑分析仪、USB 转 UART 小板等方式的测量结果不一定可靠。

通常获取并对比以下三个值就能定位问题:

  1. 示波器抓取 UART TX 实际输出波形的波特率
  2. 打印对应 UART 的工作时钟频率
  3. 根据时钟分频策略计算出的理论工作时钟频率
  • 时钟频率正常、波特率异常 → 检查测量过程
  • 理论值与实际值有差异 → 尝试修改时钟分频策略或更换其它波特率

使用过程中出错,重启后正常​

使用过程中一段时间后出现数据传输错误,请根据时钟分频策略检查时钟源。如果时钟源选择 CRU 提供的 PLL,查看出现问题前后 PLL 是否出现变化。

某些操作(如休眠唤醒、变频等)会导致 PLL 实际频率发生变化,进而影响 UART 波特率。

波特率由高到低切换后异常​

如果低→高正常,高→低异常,请查看 UART 工作时钟源是否在 CRU(PLL)和晶振之间切换正常。

通常 1.5M 及以下波特率选择 24MHz 晶振,1.5M 以上选择 CRU PLL。打印问题出现前后的 UART 工作时钟信息可以定位。


引脚复用相关​

一个 UART 控制器的信号在引脚分配上通常有多个 iomux。配置 UART iomux 需要同时配置 GRF 中的两处寄存器:

  1. 选择 GPIO 引脚的 function:同一个引脚会与多个模块复用,需要确认已配置成 UART 对应功能
  2. 选择 uart_iomux_sel 寄存器中的 group:一个 UART 控制器需要整体配置 group 才能实现整体切换

案例​

不同 iomux 的 TX 可以同时使用但 RX 不能​

同一个 UART 控制器只能选择一组 iomux group。iomux group 是否成功切换需要检查 UART RX 功能。

原因:UART M0 和 M1 两组 iomux 的 TX 引脚中并未加入选择开关,只要对应 GPIO 的 function 都配置成 UART 功能,两个 TX 都会有数据输出。但 RX 引脚中存在选择开关,不会出现两个 RX 都能接收的情况。

开启其他模块后串口异常​

由于引脚复用,某些模块开启后会重新配置 iomux 寄存器,导致串口传输错误。常见冲突功能:

  • PWM(显示设备中使用的 PWM 背光)
  • SDMMC(系统启动的存储位置)
  • JTAG(force jtag 位需要保持关闭)

尤其是用于 console 的 UART 引脚,其 iomux function 涉及的模块使用时需要特别注意。出现此类问题时,请确认对应引脚的 iomux function 寄存器值。


引脚电平相关​

UART 引脚电平配置错误会导致通讯失败。通常引脚 RX 和 TX 都会配置为内部上拉。

出现问题时,软件上需要检查:

  1. kernel dts 中 UART RX 和 TX 对应引脚电平配置是否正确
  2. 根据芯片 datasheet,检查电源域 io domain 配置(1v8 或 3v0)是否正确
  3. 直接测量 UART RX 和 TX 引脚电平是否为高电平,电压值是否正确

案例​

硬件 PCB 设计错误​

软件配置正确后仍无法解决问题,请检查硬件 PCB 设计:

  • RX 和 TX 信号线接反、接到低电平或高电平
  • UART 打开流控,CTS 引脚被外部电平强制拉高
  • 外设实际电平不匹配、电压值错误、没有漏电保护等

硬件设计与实际使用的芯片型号不一致​

Rockchip 芯片通常按系列推出,同一系列存在不同型号细分。需要确认硬件参考设计使用的芯片手册是否是实际使用的型号。可以使用 Rockchip EVB 板进行相同测试对比。

外设 TX 无法拉低主控 RX​

出现外设 TX 无法拉低主控 RX 的问题时:

  1. 先检查外设是否能拉低其它 GPIO 引脚
  2. 检查外设和主控中间是否存在电平转换芯片
  3. 检查外设引脚驱动能力是否足够

中断相关​

UART 中断包括 TX 发送中断和 RX 接收中断。需要和 FIFO 配置结合分析。

主要关注寄存器组:

IER 寄存器(中断使能寄存器):

位名称说明
0x80PTIME可编程 THRE 中断模式使能。TX FIFO 数据触发水线时提前产生 THRI 中断
0x08MSIModem 状态中断(第四优先级)
0x04RLSI接收线路状态中断(最高优先级),包括 overrun/parity/framing errors 等
0x02THRI发送保持寄存器中断(第三优先级),TX FIFO 为空时触发
0x01RDI接收数据中断(第二优先级),RX FIFO 数据达到水线时触发;或超时触发
  • IIR 寄存器:中断识别寄存器,读取触发中断的子中断号
  • FCR 寄存器:FIFO 控制寄存器,控制 RX/TX FIFO 水线配置等

案例​

配置接收中断的触发水线​

UART RX FIFO 通常为 64 Bytes 或 32 Bytes,可以配置为四种触发水线:

值触发条件
2'b001 character in the FIFO
2'b01FIFO 1/4 full
2'b10FIFO 1/2 full
2'b11FIFO 2 less than full
  • 触发水线越低 → 接收处理越及时,但中断多、CPU 消耗大
  • 默认配置为 FIFO 1/2 full
  • 修改位置:drivers/tty/serial/8250/8250_port.c 的 serial8250_do_set_termios() 函数中的 UART_FCR_R_TRIG_10 参数
  • 如果出现接收数据错误,可以尝试将 RX FIFO 触发水线改为 2'b00(每收到一个字符就产生一次中断)

DMA 相关​

Rockchip 芯片平台的 DMA 控制器通常为 PL330,部分型号使用 DW 控制器。由于 UART 传输数据量不定长,UART 对 DMA 的配置策略比较特殊。

注意事项:

  1. 默认配置 DMA 传输为 burst 模式,但 burst length 为 1(1 个 byte 触发一次 DMA 请求)。这是由 PL330 控制器特性决定的
  2. 需要确认 FCR 寄存器中的 FIFO 水线已修改为 2'b00(1 个 byte 触发),与 DMA 配置匹配
  3. 可以构造 burst length 对齐的传输数据来提高效率,但最后一笔数据需要补齐
  4. 如果 burst length 和 FIFO 水线超过 1 个 byte,且最后一包数据没有补齐冗余数据,则最后一包数据会通过 Timeout 机制使用 CPU 中断接收,存在数据丢失风险
  5. 使用 DW DMA 控制器不会出现以上问题,但为统一策略仍使用同种方案

使用 DMA 时需要注意:

  • 确认每一笔 DMA 数据的开始和结束位置
  • 确认下一笔 DMA 传输开始前,DMA 控制器的开关状态

案例​

不同版本 kernel 的 DMA 配置可能不同​

升级系统版本、移植 DTS 中的 DMA 配置参数时,不能直接沿用之前的配置。

例如:

  • kernel 4.4 中 burst 模式配置参数为 peripherals-req-type-burst
  • kernel 4.19 中为 arm,pl330-periph-burst

高波特率下 DMA 模式接收数据出现概率性丢失​

高波特率下使用 DMA 接收数据时,RX buffer 采用循环覆盖方式。如果 RX buffer 较小,未收走的数据会被覆盖,出现概率性丢失。

解决方法:修改驱动代码 drivers/tty/serial/8250/8250_dma.c 中 serial8250_request_dma 函数的 rx_size 大小(一个 PAGE_SIZE 为 4K)。


硬件流控相关​

建议在高波特率(1.5M 及以上)、大数据量的场景下使用硬件自动流控模式(四线 UART)。硬件流控增加 RTS 和 CTS 引脚,相比 DMA 模式传输可靠性更高。

注意

在 Rockchip 平台上使用 UART,请不要使用软件流控。

案例​

打开硬件流控后无法发送数据​

此类问题通常是硬件问题,排查方向:

  1. 开发板硬件设计错误、硬件故障等导致 CTS 引脚被强制拉高
  2. 对方 RTS 引脚无法正常拉高或拉低,导致连接后一直将我方 CTS 引脚拉高

数据接收错误相关​

UART 数据接收错误分为两类:接收不到 和 接收乱码。

排查方向:

层面排查内容
硬件RX 引脚是否内部上拉、外围电路电压电流是否异常、芯片引脚电平是否正确、更换优质串口线
驱动使用 io 命令关闭 IER 中断运行接收测试、检查 RFL 寄存器、确认 FCR 寄存器 RX FIFO 触发水线
应用应用程序是否取走数据、是否有其它应用使用同一 UART、处理策略是否匹配水线、接收 buffer 是否足够
提示

如果 SDK 版本较为老旧,请联系 Rockchip FAE 更新 UART 相关源码。

案例​

对方数据发送错误​

一定要确认是我方接收错误还是对方发送错误。可以使用示波器、USB 转 UART 小板等工具检测。如果我方测试均正常,请检查对方数据发送是否符合要求。

一次接收大量数据会出现分段​

请使用 UART 中断传输模式,并确认 FCR 寄存器中 FIFO 触发水线大小。

UART 中断接收机制中,通过 IIR 寄存器区分两种中断:

  • 4'b0100:接收数据触发水线产生的中断
  • 4'b1100:接收数据 Timeout 产生的中断

请查看驱动代码中数据上报逻辑:

  • kernel 3.10:drivers/tty/serial/rk_serial.c 的 receive_chars 函数中的 max_count
  • kernel 4.4/4.19:drivers/tty/serial/8250/8250_port.c 的 serial8250_rx_chars 函数中的 max_count

应用层读取数据出现延迟​

Linux Kernel 并非实时系统,UART 驱动通过 Work Queue 方式上报数据给应用层。如果 Work Queue 被阻塞,可能导致应用层读取延迟。

  • kernel 3.10:UART 驱动框架提供中断上报方式,可以提高实时性。添加 UPF_LOW_LATENCY 标志:
/* update recv data quickly instead of workqueue */
up->port.flags |= UPF_LOW_LATENCY;
  • kernel 4.4 / 4.19:UART 驱动框架只支持 Work Queue 上报。对实时性有较高要求请使用 RTOS。

连接外设或其它控制器相关​

UART 连接蓝牙、WIFI、NFC 等外设出现异常时,排查方向:

  1. 确认是主控端还是设备端出错:使用协议分析仪或两个 USB 转 UART 小板分别抓取主控端 RX 和 TX 数据
  2. 确认错误数据是否存在规律:分析错误数据的位置、数量和数值,推理可能原因
  3. 多出数据:检查是否有其他进程向该 UART 发送数据(特别是 console 切换为普通 UART 时)
  4. 丢失数据:检查是否有其他模块读走了数据、UART 配置是否正确(尤其是 DMA 模式)

案例​

蓝牙传输异常​

高波特率(1.5M 及以上)、大数据量场景下,请选择带硬件流控的四线蓝牙。两线蓝牙即使使用 DMA 模式也可能出现播放卡顿,瓶颈主要在蓝牙模块端。

注意事项:

  • 先确认蓝牙模块供应商提供的固件是否正确(系统版本升级时注意同步更新)
  • 硬件流控模式:四线蓝牙的 RTS 引脚 pinctrl 在蓝牙节点配置,CTS 引脚 pinctrl 在 UART 节点配置
  • DMA 模式:两线蓝牙配置为 DMA 模式可能出现没有环形 buffer 的警告,需自行配置 DMA buffer

使用 RS485 出现异常​

Rockchip UART 模块原生不支持 RS485,仅支持 RS232。需要外接转换模块。出现异常时请检查硬件电路设计以及相关电平状态。


控制台打印相关​

Rockchip 控制台打印使用 fiq_debugger。由于 fiq 会关闭全局中断,Rockchip 使用线程化打印(先写入内存,再打印出来)。配置中对应打开宏 CONFIG_RK_CONSOLE_THREAD=y。

注意事项:

  • 每一级的打印波特率需要保持一致
  • 如果需要打印大量数据,在驱动代码中增大 FIFO_SIZE 可以提高线程打印的缓存能力

案例​

使用控制台发送大量测试命令时出现 shell 卡死​

通常出现在自动化压力测试场景,原因是安卓 shell 造成的。建议更换基于 busybox 的 shell 或者 bash。

回车和换行的问题​

控制台打印通常对回车 \r(0x0D)和换行 \n(0x0A)进行特殊处理:输出 \n 时,在此字符前增加输出 \r。检查 ASCII 码形式的 UART 传输数据时需要特别注意这两个值。


休眠唤醒和功耗相关​

Rockchip UART 休眠唤醒的实现在 trust 中,kernel 中需要添加 wakeup source。所有 UART 唤醒源相关的问题都需要结合 trust 和 kernel 来排查。

建议:

  • 更新 SDK 最新的 trust 固件
  • 如果无法解决,联系负责 Rockchip Trust 的工程师
  • 对于休眠功耗有严格要求的场景,作为唤醒源的 UART 需要使用晶振作为工作时钟输入,以节省 CRU 小数分频器的功耗

关闭所有打印或切换所有打印到其他 UART​

Rockchip UART 打印包括:DDR 阶段 → Miniloader → TF-A → OP-TEE → U-Boot → Kernel 等阶段。

两种做法:

  1. 每一级均使用特殊固件:早期平台的方式,联系 Rockchip 获取
  2. 使用传参机制:只需修改 DDR Loader 的 UART 打印参数,前级所有阶段生效。Kernel 阶段需单独修改

DDR Loader 修改方法​

修改 rkbin/tools/ddrbin_param.txt 中的参数:

uart id=       # UART 控制器 id,配置为 0xf 为关闭打印
uart iomux= # 复用的 IOMUX 引脚
uart baudrate= # 115200 or 1500000

修改完成后重新生成 ddr.bin:

./ddrbin_tool ddrbin_param.txt path/to/ddr.bin

U-Boot 修改方法​

关闭打印:在 menuconfig 中打开 CONFIG_DISABLE_CONSOLE:

console --->
disable console in&out

切换打印:由传参机制决定,不需要额外修改。相关代码在 arch/arm/mach-rockchip/board.c 的 board_init_f_init_serial() 函数中。

Kernel 修改方法​

Kernel 中关闭打印​

  1. 在 menuconfig 中关闭 CONFIG_SERIAL_8250_CONSOLE:
Device Drivers --->
Character devices --->
Serial drivers --->
[ ] Console on 8250/16550 and compatible serial port
  1. 在 dts 中去掉 UART 基地址和 console 相关配置:
chosen: chosen {
bootargs = "earlycon=uart8250,mmio";
};
  1. 修改 fiq-debugger 节点,serial-id 改为 0xffffffff,去掉引脚复用配置:
fiq-debugger {
compatible = "rockchip,fiq-debugger";
rockchip,serial-id = <0xffffffff>;
rockchip,wake-irq = <0>;
rockchip,irq-mode-enable = <1>;
rockchip,baudrate = <1500000>;
interrupts = <GIC_SPI 252 IRQ_TYPE_LEVEL_LOW>;
status = "okay";
};
注意

需要保持 fiq-debugger 节点使能,保持 fiq-debugger 流程系统才能正常启动。

Kernel 中切换打印​

例如从 UART2 切换到 UART3:

  1. 修改 chosen 节点中的 earlycon 地址:
chosen: chosen {
bootargs = "earlycon=uart8250,mmio32,0xfe670000 console=ttyFIQ0";
};
  1. 修改 fiq-debugger 节点的 serial-id 和引脚配置:
fiq-debugger {
compatible = "rockchip,fiq-debugger";
rockchip,serial-id = <3>;
rockchip,wake-irq = <0>;
rockchip,irq-mode-enable = <1>;
rockchip,baudrate = <1500000>;
interrupts = <GIC_SPI 252 IRQ_TYPE_LEVEL_LOW>;
pinctrl-names = "default";
pinctrl-0 = <&uart3m0_xfer>;
status = "okay";
};
  1. 同时将切换为打印串口的 UART3 普通串口节点禁用。

将原控制台 UART 作为普通 UART 使用​

进行完关闭或切换控制台 UART 后,需要重新将该 UART 作为普通 UART 使能。注意:一定是先关闭/切换控制台,释放后再作为普通 UART 使能。

Android 修改方法​

安卓需要去掉 recovery 中对 console 的使用,否则恢复出厂设置时会卡住。

修改文件 android/device/rockchip/common/recovery/etc/init.rc,注释掉 console:

service recovery /sbin/recovery
#console
seclabel u:r:recovery:s0

案例​

由打印串口切换成普通串口后出现的问题​

使用由打印串口切换成的普通串口出现问题时,先使用另一个 UART 进行相同测试。如果其它 UART 不会出现同样问题,请联系 Rockchip 获取直接在源码中关闭 UART 打印的特殊固件再进行排查。

特殊情况下(如 DDR 变频等前级操作)会导致由打印串口切换成的普通串口出现数据增加或丢失。


其它问题​

案例​

使用某些上位机时 Kernel 无法正常启动​

某些上位机串口调试软件(如 MobaXterm、Putty 等)默认开启 UART 软件流控(Xon/Xoff)。

系统启动时,在 DDR 变频等操作下会发送大量打印数据,当数据量超过上位机接收 buffer 时会触发软件流控,向 RX 发送 Xoff(Control+S 字符)。Rockchip 平台在系统启动过程中接收到 Control+S 字符会触发 U-Boot Debug 模式,导致无法正常开机。

解决方法:检查上位机串口工具配置,关闭软件流控。

更换 UART 设备节点号​

以 ttyS3 和 ttyS4 交换为例,在 dts 中修改:

aliases {
serial3 = &uart4;
serial4 = &uart3;
};

如果是控制台 UART 和普通 UART 交换节点号,请直接修改 drivers/tty/tty_io.c 中的 tty_line_name() 函数。


参考资料​

  • 原始文档:Rockchip_Developer_Guide_UART_FAQ_CN.pdf V1.1.0
  • 瑞芯微官网:www.rock-chips.com