1. 为什么调试串口波特率值得单独拿出来聊
拿到一块 RK3588 的板子,第一件事是什么?插电、接串口、打开终端,看它能不能正常打印启动日志。这个动作看起来简单到不值一提,但我见过太多人卡在第一步——屏幕上全是乱码,或者干脆一个字都不出。十有八九,问题就出在调试串口的波特率上。
RK3588 这颗芯片的调试串口(通常叫 debug UART 或者 console UART)默认跑在1500000 bps,也就是 1.5M 波特率。这个数字对很多刚接触 Rockchip 平台的人来说是陌生的,因为大家习惯了 STM32 上的 115200,习惯了 ESP32 上的 921600,突然看到一个 1.5M,第一反应往往是"这什么鬼"。更麻烦的是,这个 1.5M 不是随便定的,它贯穿了 RK3588 的整个启动链路——从 BootROM 到 U-Boot SPL,到 U-Boot proper,再到 Kernel 和根文件系统里的 getty,每一级都得对齐,任何一级对不上,你看到的就只有乱码。
所以这篇东西,我想把 RK3588 调试串口波特率这件事从头到尾讲清楚。不只是告诉你"改成 115200 就行",而是把为什么默认是 1.5M、改的时候要动哪些地方、每一级怎么验证、改完出问题怎么排查这些环节都拆开揉碎。适合正在 bringup RK3588 板子的嵌入式工程师,也适合做 RK3588 移植 Ubuntu 或者 Android 的开发者,哪怕你只是想让串口输出别乱码,这里面的思路也能直接用。
我自己的经历是,第一次拿到 RK3588 开发板的时候,用 115200 打开串口,满屏的方块和问号,折腾了快一个小时才反应过来是波特率不对。后来做产品定制,又需要把调试串口统一改成 115200 以适配客户现有的工装和日志采集设备,前前后后踩了不少坑。这些经验我都会写进来,尽量让你少走弯路。
2. RK3588 启动链路与串口波特率的绑定关系
2.1 从 BootROM 到 Kernel:波特率在哪几级被设定
RK3588 的启动流程比很多 MCU 复杂得多,它是一个典型的多级引导架构。简单梳理一下:
- BootROM:芯片内部固化的代码,上电后最先运行。它会根据启动引脚或者 eFuse 配置,从 SPI Flash、eMMC、SD 卡等介质加载下一级引导程序。BootROM 阶段本身也会通过调试串口输出一些信息,但它的波特率是芯片内部固定的,RK3588 上这个固定值就是 1500000。
- U-Boot SPL(TPL/SPL):BootROM 加载的第一段可执行代码,负责初始化 DDR、时钟等关键硬件,然后加载完整的 U-Boot。SPL 阶段的串口配置由
CONFIG_SPL_BAUD_RATE或者板级头文件里的宏决定。 - U-Boot proper:完整的引导程序,负责加载 Kernel、设备树、根文件系统。它的串口波特率由
CONFIG_BAUDRATE或者环境变量baudrate控制。 - Kernel:Linux 内核启动后,调试串口由设备树里的
stdout-path或者console=内核命令行参数指定,波特率写在chosen节点的stdout-path属性里。 - 用户空间:Kernel 启动完成后,getty 或者 systemd 的 serial-getty 服务会接管串口,提供登录终端。它的波特率由服务配置文件或者 inittab 决定。
这五级里,BootROM 的波特率是不可改的,这是硬编码在芯片里的。所以如果你想让整个启动过程都用 115200,BootROM 那一段的输出你必然收不到——除非你用 1.5M 去抓那一段,然后再切到 115200 看后面的。这是很多人困惑的地方:为什么改了配置,前面一段还是乱码?因为那一段根本不归你管。
2.2 为什么 Rockchip 默认选 1.5M 而不是 115200
这个问题我被问过很多次。答案其实很直接:速度。
RK3588 是一颗八核处理器,启动过程中要打印的信息量非常大。DDR 训练日志、时钟树初始化、各个外设的 probe 信息、设备树解析结果……如果用 115200 波特率,这些日志全部打印出来可能要几十秒甚至更久。1.5M 是 115200 的 13 倍左右,同样的日志量,时间缩短到十分之一以下。对于产线批量测试和开发调试来说,这个时间差异是巨大的。
另一个原因是Rockchip 的工装和测试设备默认就支持 1.5M。他们的参考设计、SDK 默认配置、甚至工厂里的测试夹具,都是围绕 1.5M 来做的。你如果拿到的是官方 EVB 或者核心板厂商的参考板,默认就是 1.5M,这已经形成了一套生态惯性。
但问题在于,很多终端工具和 USB 转串口芯片对 1.5M 的支持并不好。比如常见的 CH340,官方标称最高支持 2M,但实际在 1.5M 下经常出现丢数据或者根本连不上。CP2102 稍微好一点,但也不是所有批次都稳。FT232 系列相对可靠,但价格贵。所以当你用了一个便宜的 USB 转串口模块,发现 1.5M 下乱码,不一定是板子的问题,可能是转换芯片扛不住。
2.3 改波特率之前必须想清楚的三个问题
在动手改之前,我建议你先回答三个问题:
- 你为什么要改?是因为你的串口工具不支持 1.5M,还是因为你要对接一套已有的 115200 日志系统?不同的动机,改的范围不一样。如果只是工具不支持,换个 FT232 的模块可能比改板子更省事。
- 你要改哪几级?只改 U-Boot 和 Kernel,还是连 SPL 一起改?如果 SPL 不改,那 SPL 阶段的输出你还是看不到。通常的做法是 SPL 保持 1.5M,U-Boot proper 和 Kernel 改成 115200,这样你至少能看到大部分启动日志。
- 改完之后怎么验证?你需要一个能同时支持 1.5M 和 115200 的工具,或者两个不同的工具。否则改完发现看不到输出,你连是哪一级出的问题都不知道。
这三个问题想清楚了,后面的操作才有方向。
3. 逐级修改波特率的完整实操
3.1 U-Boot SPL 阶段的波特率配置
SPL 阶段的波特率在 RK3588 的 U-Boot 源码里通常由板级配置头文件控制。以 Rockchip 官方 SDK 为例,路径一般在:
u-boot/configs/rk3588_defconfig或者板级头文件:
u-boot/include/configs/rk3588_common.h你需要找的是CONFIG_SPL_BAUD_RATE这个宏。如果没有显式定义,它可能会 fallback 到CONFIG_BAUDRATE。在 RK3588 的默认配置里,通常是这样的:
CONFIG_BAUDRATE=1500000 CONFIG_SPL_BAUD_RATE=1500000如果你想改 SPL 的波特率,就把CONFIG_SPL_BAUD_RATE改成 115200。但这里有个坑:SPL 阶段串口驱动可能还没有完全初始化,有些板子改了这个宏之后 SPL 反而不输出了。原因是 SPL 的串口初始化依赖时钟配置,如果时钟分频算出来的实际波特率和设定值偏差太大,输出就会乱码甚至没有。
我的建议是:SPL 阶段尽量别动。保持 1.5M,用支持 1.5M 的工具抓这一段。等 U-Boot proper 起来之后再切到 115200。这样风险最小。
如果你确实需要改 SPL,改完之后一定要用示波器或者逻辑分析仪量一下 TX 引脚的实际波特率,确认时钟分频是对的。RK3588 的 UART 时钟源通常是 24M 或者 48M,分频系数要算清楚。
3.2 U-Boot proper 阶段的波特率修改
U-Boot proper 的波特率修改相对简单,有两种方式:
方式一:改 defconfig
在rk3588_defconfig里找到:
CONFIG_BAUDRATE=1500000改成:
CONFIG_BAUDRATE=115200然后重新编译 U-Boot。这是最彻底的方式,编译出来的 U-Boot 默认就用 115200。
方式二:改环境变量
如果 U-Boot 已经烧录到板子上,你可以在 U-Boot 命令行里直接改:
setenv baudrate 115200 saveenv但注意,saveenv之后 U-Boot 会立即切换波特率,你的终端如果还停在 1.5M,就会看到乱码。这时候需要把终端也切到 115200,然后重新上电或者复位。
这两种方式我推荐第一种,因为环境变量方式在恢复出厂设置或者擦除 env 分区后会丢失,而 defconfig 是编译进去的,更可靠。
还有一个细节:U-Boot 的baudrate环境变量和CONFIG_BAUDRATE的关系。如果 env 里没有baudrate,U-Boot 会用CONFIG_BAUDRATE的值。如果 env 里有,则优先用 env 的值。所以如果你改了 defconfig 但没擦除 env,可能还是旧值。这时候需要env default -a或者擦除 env 分区。
3.3 Kernel 命令行与设备树的波特率设置
Kernel 阶段的波特率由设备树的chosen节点控制。在 RK3588 的设备树文件里,通常是:
arch/arm64/boot/dts/rockchip/rk3588s.dtsi或者板级 dts:
arch/arm64/boot/dts/rockchip/rk3588-evb.dts找到:
chosen { stdout-path = "serial2:1500000n8"; };把1500000改成115200:
chosen { stdout-path = "serial2:115200n8"; };这里的serial2是调试串口的别名,具体是哪个 UART 取决于你的板子设计。RK3588 通常用 UART2 作为调试串口,但有些板子会用 UART0 或者 UART9。你需要确认原理图。
另外,Kernel 的console=参数也可能在 U-Boot 的bootargs里指定。如果bootargs里有console=ttyS2,1500000,那它会覆盖设备树的设置。所以改完设备树之后,还要检查 U-Boot 的bootargs:
setenv bootargs console=ttyS2,115200 ...两处都改一致,才能保证 Kernel 用 115200 输出。
3.4 用户空间 getty 服务的波特率调整
Kernel 启动完成后,用户空间的 getty 会接管串口。在 Ubuntu 或者 Debian 系统上,通常是 systemd 的serial-getty@ttyS2.service。它的波特率由服务文件里的ExecStart决定:
ExecStart=-/sbin/agetty -o -p -- \u --keep-baud 115200,38400,9600 %I $TERM这里的--keep-baud表示保持当前波特率,后面的115200,38400,9600是备选列表。如果 Kernel 已经用 115200 启动了,getty 通常会自动匹配。但如果你发现登录终端乱码,可以手动指定:
ExecStart=-/sbin/agetty -o -p -- \u 115200 %I $TERM改完之后:
sudo systemctl daemon-reload sudo systemctl restart serial-getty@ttyS2.service在 Android 系统上,getty 的配置在init.rc或者ueventd.rc里,通常是console服务。Android 的调试串口主要用于 kernel log 输出,不一定提供登录 shell,所以波特率主要影响 log 的可读性。
3.5 一张表看清各级波特率的修改位置
| 启动阶段 | 配置文件/位置 | 关键宏或属性 | 默认值 | 建议值 |
|---|---|---|---|---|
| BootROM | 芯片内部固化 | 不可改 | 1500000 | 1500000 |
| U-Boot SPL | include/configs/rk3588_common.h | CONFIG_SPL_BAUD_RATE | 1500000 | 1500000(不建议改) |
| U-Boot proper | configs/rk3588_defconfig | CONFIG_BAUDRATE | 1500000 | 115200 |
| Kernel | 设备树chosen节点 | stdout-path | 1500000 | 115200 |
| Kernel bootargs | U-Boot 环境变量 | console= | 1500000 | 115200 |
| 用户空间 getty | systemd service 文件 | ExecStart | 自动 | 115200 |
这张表建议保存下来,改的时候逐项对照,避免漏掉某一级。
4. 实操验证与常见问题排查
4.1 怎么确认每一级的波特率真的生效了
改完配置重新编译烧录之后,怎么验证?我的做法是分阶段抓日志:
第一步:用 1.5M 抓 BootROM 和 SPL 阶段
把终端设成 1.5M,上电。你应该能看到 BootROM 的打印,类似:
BootRom Version: ...然后是 SPL 的 DDR 训练日志。如果这一段正常,说明 SPL 之前的链路没问题。
第二步:在 U-Boot 启动瞬间切到 115200
这一步比较考验手速。你可以在 U-Boot 打印第一行的时候,迅速把终端波特率切到 115200。如果 U-Boot proper 已经改成 115200,切换后应该能看到正常的 U-Boot 命令行或者启动日志。
如果切换后还是乱码,说明 U-Boot proper 的波特率没改成功。检查CONFIG_BAUDRATE是否真的编译进去了,可以用strings u-boot.bin | grep 115200看看。
第三步:看 Kernel 日志确认 console 波特率
Kernel 启动后,第一行通常是:
[ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x412fd050]如果这行正常显示,说明 Kernel 的 console 波特率是对的。如果乱码,检查设备树和 bootargs。
第四步:登录终端验证 getty
如果系统提供了登录提示符,比如:
Ubuntu 22.04 LTS rk3588 ttyS2 rk3588 login:说明 getty 也正常了。如果看不到提示符,检查 serial-getty 服务是否启动。
4.2 乱码问题的排查思路
乱码是波特率问题最常见的表现。排查的时候,我一般按这个顺序来:
- 确认终端波特率:先确认你的串口工具设的是不是目标波特率。有时候是工具设错了,不是板子的问题。
- 确认数据位、停止位、校验位:RK3588 调试串口通常是 8N1,即 8 数据位、无校验、1 停止位。如果设成 7E1 或者 8E1,也会乱码。
- 确认 USB 转串口芯片支持:CH340 在 1.5M 下经常不稳,换 FT232 试试。如果换模块就好了,说明是芯片问题。
- 确认时钟分频:如果改了波特率之后乱码,可能是时钟分频算错了。RK3588 的 UART 时钟源和分频系数需要对照 TRM 计算。
- 确认各级配置一致:如果 U-Boot 是 115200 但 Kernel 是 1.5M,那 Kernel 启动瞬间就会乱码。逐级检查。
我整理了一个速查表:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 全程乱码 | 终端波特率不对 | 检查串口工具设置 |
| 前段正常后段乱码 | 某一级波特率不一致 | 逐级检查配置 |
| 1.5M 下乱码,115200 正常 | USB 转串口芯片不支持 1.5M | 换 FT232 模块 |
| 改完配置后完全无输出 | SPL 串口初始化失败 | 恢复 SPL 波特率,检查时钟 |
| 登录终端乱码 | getty 波特率不匹配 | 检查 systemd service 文件 |
| 偶发丢字符 | 波特率偏差过大或线材质量差 | 量 TX 实际波特率,换短线 |
4.3 几个我踩过的坑
坑一:改了 defconfig 但没生效
有一次我改了rk3588_defconfig里的CONFIG_BAUDRATE,编译烧录后发现还是 1.5M。查了半天才发现,板子的 env 分区里存了旧的baudrate变量,U-Boot 优先用了 env 的值。解决办法是擦除 env 分区,或者在 U-Boot 里env default -a然后saveenv。
坑二:设备树改了但 bootargs 没改
设备树的stdout-path改成 115200 了,但 U-Boot 的bootargs里还有console=ttyS2,1500000。结果 Kernel 启动时先用设备树的 115200 初始化,然后被 bootargs 覆盖成 1.5M,日志前半段正常后半段乱码。这种"半截乱码"最容易让人误判。
坑三:USB 转串口模块的锅
用了一个十几块钱的 CH340 模块,1.5M 下死活连不上。换了一个 FT232 的模块,立马正常。后来查资料才知道,CH340 在高于 1M 的波特率下误码率会显著上升,不是不能用,是不稳定。所以如果你要长期在 1.5M 下工作,建议直接上 FT232 或者 CP2105。
坑四:getty 服务没重启
改完 Kernel 波特率后,Kernel 日志正常了,但登录终端还是乱码。原因是 serial-getty 服务还在用旧的波特率。systemctl restart serial-getty@ttyS2.service之后就好了。这个坑很小,但很容易忽略。
4.4 波特率计算公式与时钟分频验证
如果你需要精确验证波特率,或者改了时钟源之后发现波特率不对,可以自己算一下。UART 的波特率计算公式是:
BaudRate = ClockSource / (Divisor * 16)其中Divisor是分频系数,通常是 16 的倍数加上小数部分。RK3588 的 UART 时钟源一般是 24M 或者 48M。以 24M 为例,要得到 115200:
Divisor = 24000000 / (115200 * 16) = 13.0208实际分频系数取 13,则实际波特率:
24000000 / (13 * 16) = 115384.6误差约 0.16%,在 UART 允许的 2% 误差范围内,没问题。
要得到 1.5M:
Divisor = 24000000 / (1500000 * 16) = 1.0正好是 1,所以 1.5M 在 24M 时钟源下是精确的。这也是为什么 Rockchip 选 1.5M 的一个隐藏原因——分频系数是整数,没有误差。
如果你改了时钟源,比如改成 48M,那 115200 的分频系数是:
Divisor = 48000000 / (115200 * 16) = 26.0417取 26,实际波特率:
48000000 / (26 * 16) = 115384.6误差一样。但如果取 27,就是 111111,误差 3.5%,超出范围了。所以分频系数的选择很关键。
5. 不同系统下的适配差异与经验总结
5.1 Ubuntu 与 Android 的波特率配置差异
RK3588 可以跑 Ubuntu,也可以跑 Android。两个系统在调试串口波特率上的处理方式不太一样。
Ubuntu用的是标准 Linux 启动流程,设备树 + bootargs + systemd getty,改起来比较直观。Rockchip 社区维护的 Ubuntu 镜像通常默认就是 1.5M,你需要自己改设备树和 U-Boot。
Android的启动流程更复杂,它有自己的 init 系统和 log 系统。Android 的调试串口主要用于 kernel log 和 early log,波特率在设备树里指定。但 Android 的init.rc里可能还有额外的 console 配置。如果你在 Android 上改波特率,除了设备树,还要检查init.rc里的console服务定义。
另外,Android 的 logcat 和 kernel log 是分开的,串口上通常只输出 kernel log。如果你想让 Android 的 logcat 也走串口,需要额外配置,这不是改波特率能解决的。
5.2 移植 Ubuntu 26 时的注意事项
最近 RK3588 移植 Ubuntu 26 是个热门话题。Ubuntu 26 用的是更新的 Kernel 和 systemd,串口配置方式基本没变,但有几个细节要注意:
- systemd 版本更新:新版本 systemd 的 serial-getty 服务文件路径可能略有不同,用
systemctl status serial-getty@ttyS2确认。 - Kernel 版本更新:新 Kernel 的设备树绑定可能有变化,
stdout-path的格式要对照新的 binding 文档。 - U-Boot 版本更新:新 U-Boot 的
CONFIG_BAUDRATE可能被CONFIG_BAUDRATE和CONFIG_SPL_BAUD_RATE分开控制,要确认清楚。
我的建议是,移植新系统时,先用默认的 1.5M 把系统跑起来,确认所有功能正常,再改波特率。这样出问题的时候,你能确定是波特率改出来的,还是移植本身的问题。
5.3 产线工装与日志采集的适配建议
如果你做的是产品,调试串口最终要对接产线工装或者日志采集设备,那波特率的选择就不只是"能不能看到"的问题了,还涉及稳定性和兼容性。
产线上的工装通常用 PLC 或者工控机,它们的串口卡可能只支持标准波特率(9600、19200、38400、57600、115200)。1.5M 这种非标准波特率,很多工控设备根本不支持。所以产品化的时候,把调试串口改成 115200 是更稳妥的选择。
日志采集方面,如果你用minicom或者picocom做自动化采集,115200 的兼容性更好。picocom对高波特率的支持不如minicom,但minicom的配置又比较麻烦。我一般用picocom加-b 115200,简单直接。
还有一个经验:产线工装建议用 FT232 芯片的串口模块,虽然贵一点,但稳定性好,不会因为波特率问题导致误判。CH340 在产线上批量使用,出问题的概率明显更高。
5.4 我个人的几点实操体会
折腾 RK3588 调试串口波特率这件事,前后加起来我改过不下二十块板子。有几个体会比较深:
第一,不要一上来就改 SPL。SPL 的串口初始化很脆弱,改了容易出问题,而且 SPL 阶段的日志量不大,1.5M 和 115200 的体验差异不明显。保持 SPL 1.5M,改 U-Boot proper 和 Kernel,是性价比最高的方案。
第二,改之前先备份。U-Boot 的 defconfig、设备树文件、bootargs,改之前都备份一份。出问题的时候能快速回退,不用重新拉代码。
第三,验证要逐级来。不要改完所有配置再一起烧录,那样出问题你不知道是哪一级的锅。改一级,烧一级,验证一级,虽然麻烦一点,但定位问题快得多。
第四,工具很重要。一个靠谱的 USB 转串口模块能省掉一半的排查时间。FT232 模块我用了好几年,1.5M 下从来没出过问题。CH340 便宜,但关键时刻掉链子。
第五,文档要记。每次改了什么、改成什么值、验证结果如何,都记下来。RK3588 的 SDK 版本更新频繁,不同版本的配置项可能不一样,有记录才能快速对照。
最后分享一个小技巧:如果你不确定板子的调试串口是哪个 UART,可以在 U-Boot 里用dm tree命令查看串口设备,或者看原理图上标注的DEBUG_UART。RK3588 的 UART2 是最常见的调试串口,但不是唯一的,有些板子会用 UART0 或者 UART9。确认清楚再改,免得改错地方白忙活。