最近在折腾RK3588的开发板,碰到一个看起来很基础、但确实让不少人栽过跟头的问题:把调试串口默认的1500000波特率改成115200。RK3588这颗SoC的调试串口(debug uart)出厂固件默认是1500000,也就是常见的1.5Mbps。这个速率在RK官方文档、规格书里都写得很清楚,官方SDK用的也是这个值。但实际做项目时,很多工程师和玩家并不需要这么高的速度,反而会因为1.5M这个非标速率遇到一堆麻烦:USB转串口模块认不出来、CH340/PL2303驱动不给力、工控现场那台老旧的串口服务器只支持115200、SecureCRT/minicom配置起来麻烦,再往小了说,逻辑分析仪抓串口数据时也经常卡在采样率上。所以,把debug串口波特率改成115200,是一个相当高频、真实、覆盖面很广的刚需。
这篇文章就记录一下我从源码层面把RK3588调试串口完整改成115200的全过程。注意,我说的是“完整”二字,因为这里的水很深:U-Boot、Kernel、Android板级配置三处都要动,否则就会遇到启动到一半突然乱码、或者内核起来后串口直接没有输出的怪问题。我会把这几个位置的修改逻辑、具体命令、验证方法、常见坑全部拆开讲清楚。不管你是正在用RK3588做核心板、工控底板,还是刚买了开发板想统一终端工具参数,这篇内容都能直接抄作业。
1. 为什么RK3588调试串口默认是1500000,又为什么要改
1.1 RK平台使用1.5M波特率的历史原因
很多人第一次接触RK3588时,看到官方烧录工具里“波特率1500000”这个选项都会愣一下。实际上这不只是RK3588的习惯,Rockchip整个系列的芯片,从RK3288、RK3399到RK3568、RK3588,调试串口和Maskrom模式下的烧录通信,默认波特率基本都是1500000。官方之所以选这个非标数值,主要是从调试效率考虑的。RK3588启动阶段日志非常密集,从BootROM、SPL到U-Boot再到Kernel,一次完整开机打印出来的字符数量非常可观,在1500000波特率下传输明显比115200快得多,能减少开发阶段等待日志滚动的时间。
另外还有一个工程上的原因:RK的Loader(烧录模式下的下载固件通道)也要走这个串口,波特率太低会明显拉长整机烧录时间。对产线来说,每台设备烧录快几秒,累积起来就是可观的效率提升。所以从方案设计角度看,1.5M波特率是有它存在道理的,并不是随手填的一个数。
1.2 标准波特率115200在真实项目里的优势
那为什么还有这么多人想改回115200?我总结了一下,基本逃不开下面几类场景:
- USB转串口模块兼容性:市面上一大把CH340模块,官方驱动宣称支持到2Mbps,但实际在Windows老版本驱动、Linux某些内核版本下,跑1500000这个非标速率时不稳定,经常出现乱码、丢字符、甚至设备掉线。反而是115200这种标准速率,几乎所有芯片、所有驱动版本都支持得非常好。
- 工控设备对接:很多PLC、HMI、串口服务器、旧款工业主机,串口只支持标准波特率,它们只会列出9600、19200、38400、115200这类档位,根本不会有1500000这个选项。如果你要用RK3588去跟这类设备通过debug口联调,不改波特率连不上。
- 逻辑分析仪抓包:用逻辑分析仪直接抓RK3588串口波形来排查问题的时候,1500000对采样率要求很高,比较便宜的24MHz/48MHz采样率的分析仪抓1.5M波特率的信号会非常吃力,改动115200后抓包就轻松多了。
- 统一下发工具链:公司内部已有的产测工具、日志采集脚本如果都是按115200开发的,新增RK3588产品线时改波特率能减少一套工具链的维护成本。
我之前还遇到过一个更极端的例子——甲方要求debug口必须接到他们机房已有的串口终端服务器上,那台设备菜单里根本没有1500000这个选项,最高只到115200,最后只能从固件层面把波特率整个改掉。所以这次把RK3588改成115200的过程,本质上是把RK默认的“开发调试高速方案”切换成“全场景通用标准方案”。
1.3 修改前的总体认知:串口参数到底经过哪几层
在动手改之前,你脑子里得先有一张串口参数传递的链路图。RK3588的调试串口不是简单在某个配置文件里改一个数字就完事的,它至少经历四层:
| 层次 | 作用阶段 | 参数位置 | 影响 |
|---|---|---|---|
| U-Boot自身波特率 | BootROM到U-Boot启动早期 | u-boot源码config头文件里的CONFIG_BAUDRATE | 决定U-Boot阶段串口实际速率 |
| U-Boot设备树bootargs | U-Boot启动内核前 | u-boot/arch/arm/dts下dtsi的chosen节点 | 作为cmdline传给内核 |
| Kernel设备树bootargs | 内核启动阶段 | kernel/arch/arm64/boot/dts/rockchip下dtsi的chosen节点 | 决定内核console使用的串口与速率 |
| Android板级BOOTARGS | 系统启动阶段 | device/rockchip下的BoardConfig.mk | 可能覆盖或追加内核命令行参数 |
这四层只要有一层没改,就会出现“某个阶段正常、某个阶段乱码”的诡异现象。比如你只改了Kernel设备树,那U-Boot阶段大概率还是1500000,你会看到板子上电后先正常显示一段U-Boot日志,然后到了内核接管串口时刷的一下变成乱码——因为内核已经把波特率切成115200了,而你的终端还停在1500000。这种半改状态是最容易让人怀疑人生的。
2. 修改前的准备工作:硬件、软件与源码环境
2.1 硬件串口模块的选择与接线注意
RK3588调试串口一般是板子上的一个4针或3针排针,对应UART2的TX、RX、GND,有些板子还会引出VCC。接线原则很简单:开发板的TX接USB转串口模块的RX,开发板的RX接模块的TX,GND一定要共地。别小看共地这个问题,我接过不少只插了TX/RX、没接地线的板子,结果数据完全是花的,加了一根地线立刻恢复正常。
USB转串口模块方面,我的建议是首选CP2102或FT232,这两类的驱动和稳定性最好,跑1500000也完全没问题。CH340不是不能用,但如果你需要在1.5M下调串口,最好先去官网把最新驱动装上,Linux下用内核自带的ch341驱动一般也能识别。对了,修改波特率之前,先确认一下PC上能正确识别到串口设备。Linux下用lsusb看设备,然后用dmesg | tail -20确认有没有生成/dev/ttyUSB0;Windows下打开设备管理器看“端口(COM和LPT)”里有没有新增的COM口。这一步虽然基础,但在现场最容易出问题。
2.2 终端软件的波特率配置方法
串口终端软件这块,Linux下我个人最喜欢用picocom,轻量、参数清晰;minicom也是一个经典选择。接线和驱动确认正常后,先用当前固件的1500000波特率连一下,确保能进系统,确认硬件链路没问题,再做源码修改。命令参考:
# 使用picocom连接,波特率根据当前固件情况修改 picocom -b 1500000 /dev/ttyUSB0 # 使用minicom,先运行minicom -s进入配置界面 # 选择“Serial port setup”,将波特率改为1500000 # 保存配置后连接 minicom /dev/ttyUSB0Windows下建议用MobaXterm的Serial会话、SecureCRT或者Xshell,新建连接时选择SERIAL,速率填1500000,数据位8、停止位1、无校验、无流控。这里有一个非常重要的细节:串口参数是8N1(8数据位、无校验、1停止位),流控必须关掉。有些终端工具默认会打开硬件流控RTS/CTS,如果不关,就可能出现“能收不能发”或者干脆完全没反应的情况。
2.3 RK SDK源码目录结构与编译环境确认
这篇文章假设你手里已经有一套完整的RK3588 SDK源码,不管是官方Release包还是Rockchip GitHub拉下来的,常用的目录结构基本一致:
u-boot/:U-Boot源码,里面arch/arm/dts/下存放U-Boot侧设备树kernel-5.10/(或者kernel-6.1等版本):内核源码,设备树在arch/arm64/boot/dts/rockchip/device/rockchip/:Android板级配置目录build.sh:顶层编译脚本
开工之前先确认编译环境能正常出包,至少你要能编译U-Boot或Kernel其中之一。如果你只是拿别人编译好的镜像反编译来改,那我建议还是先搭建好源码编译环境,因为后续修改牵扯到的dts和mk文件,必须重新编译打包烧录才能生效,只改镜像里的文本是不现实的。
3. 完整修改流程:U-Boot、Kernel与Android三层同步改
3.1 第一步:确认调试串口对应的设备树节点
RK3588的debug串口默认接到UART2,对应设备树节点&uart2。不过不同核心板厂商可能有改动,所以最稳妥的办法是先在源码里搜一下,确认当前调试串口到底用的哪个节点。重点搜索两个东西:stdout-path和ttyFIQ0。注意U-Boot和Kernel的设备树是两份独立的文件,要分别搜索。
# 在U-Boot源码中搜索 grep -rn "stdout-path\|ttyFIQ0\|chosen" u-boot/arch/arm/dts/ | grep -i rk3588 # 在内核源码中搜索 grep -rn "stdout-path\|ttyFIQ0\|chosen" kernel-5.10/arch/arm64/boot/dts/rockchip/ | grep -i rk3588找到对应文件后打开chosen节点,通常会看到类似下面的内容:
chosen { stdout-path = &uart2; bootargs = "console=ttyFIQ0,1500000"; };console=ttyFIQ0是Rockchip平台特有的FIQ调试串口配置,ttyFIQ0背后绑定的是serial节点,由fiq_debugger驱动接管。看到1500000这个数字,就说明你找对地方了。另外顺手确认一下&uart2节点下有没有status = "okay",如果uart2都没打开,那串口肯定是不工作的。
3.2 第二步:修改U-Boot设备树和默认波特率
U-Boot阶段的修改是这次操作里最关键的一步,因为它决定了从板子上电到内核接手之前这一大段日志能否正常输出。我的建议是分两个地方同步改:
第一处,U-Boot设备树的chosen节点,把bootargs里的1500000改成115200:
// u-boot/arch/arm/dts/rk3588-evb.dtsi(或你手上的板级dts) chosen { stdout-path = &uart2; bootargs = "console=ttyFIQ0,115200"; };第二处,U-Boot配置头文件里的CONFIG_BAUDRATE。这是很多人容易漏掉的地方。U-Boot在启动早期、设备树还没完全解析的时候,串口波特率是用这个宏来确定的。如果只改bootargs而不改这个宏,SRAM里SPL阶段会继续用1500000输出日志,直到U-Boot某个阶段切换console参数后才变成115200,结果就是你看到一段“前面正常、某一行突然开始乱码”的输出。具体路径因SDK版本而异,一般在u-boot/include/configs/rk3588_common.h或rockchip-common.h里,搜一下就能找到:
grep -rn "CONFIG_BAUDRATE" u-boot/include/configs/找到后把1500000改成115200:
#define CONFIG_BAUDRATE 115200这里解释一下为什么必须在两个位置同时改。CONFIG_BAUDRATE是U-Boot编译期的默认波特率,偏底层;而chosen节点里的bootargs是U-Boot在启动内核时拼进cmdline用的,偏上层。两者职责不同,但都直接影响串口。双双改成115200之后,U-Boot从头到尾都是115200,不会出现阶段性的跳变。
3.3 第三步:修改Kernel设备树和console参数
U-Boot改完之后,接着改内核侧。打开Kernel源码下对应的RK3588设备树文件,同样找到chosen节点。这里要注意:有些SDK里,内核的dtsi不会直接写bootargs,而是留着让U-Boot通过tag方式传给内核,这种情况下你只需要保证U-Boot的bootargs正确就行。但如果内核dtsi里也有硬编码的bootargs,那必须同步修改,否则U-Boot传过去的参数有可能被覆盖。
// kernel-5.10/arch/arm64/boot/dts/rockchip/rk3588-evb.dtsi chosen { bootargs = "console=ttyFIQ0,115200"; };内核对调试串口的命名可能有几种,比如ttyFIQ0、ttyS2,具体要看内核的fiq_debugger配置。在RK3588的安卓SDK里,ttyFIQ0是常见写法,如果搜不到这个字符串,就搜bootargs.*115200或者chosen节点,看看现有配置里console=参数给的是哪个设备。另外,部分内核设备树里还有一个额外的fiq_debugger节点,里面可能会带rockchip,baudrate属性,这个属性同样需要检查,存在的话一并改成115200。例如:
fiq_debugger: fiq-debugger { compatible = "rockchip,fiq-debugger"; rockchip,serial-id = <2>; rockchip,wake-irq = <0>; rockchip,baudrate = <115200>; // 这里原来是1500000 interrupts = <GIC_SPI 252 IRQ_TYPE_LEVEL_LOW>; status = "okay"; };这个节点的baudrate属性直接决定了FIQ调试串口驱动初始化的速率。有些平台的fiq_debugger逻辑是优先读节点里的baudrate,而不是bootargs里的console参数,所以这一处漏改的话,内核启动后波特率可能还是1.5M,导致系统起来后串口乱码。
3.4 第四步:修改Android板级BOOTARGS配置
如果你用的是RK官方Android SDK,那么仅仅改完U-Boot和Kernel设备树还不算完。RK的Android构建系统会在打包阶段从device/rockchip/目录下的BoardConfig.mk里读取BOOTARGS变量,再把它拼到最终的内核启动参数里。这个变量的优先级很高,经常会把设备树里的bootargs整个覆盖掉。我见过太多人改了dts后重新编译Android整包,烧进去发现还是1.5M,最后一查,问题就出在这个mk文件上。
具体操作,先全局搜索一下:
grep -rn "BOOTARGS" device/rockchip/正常情况下会在device/rockchip/rk3588/BoardConfig.mk或device/rockchip/common/BoardConfig.mk里找到类似这样的配置:
BOOTARGS := ... console=ttyFIQ0,1500000 ...把其中的波特率改成115200,然后重新编译内核和boot镜像。举个例子,如果原本是:
BOOTARGS := androidboot.hardware=rk3588 console=ttyFIQ0,1500000 androidboot.console=ttyFIQ0改成:
BOOTARGS := androidboot.hardware=rk3588 console=ttyFIQ0,115200 androidboot.console=ttyFIQ0这里androidboot.console=ttyFIQ0的作用是把Android userspace的console也绑到FIQ调试串口上,波特率同样要跟着改。这一步不做的话,你会看到内核日志是115200正常,但Android系统起来后logcat或者shell输出又变乱码了。
3.5 第五步:重新编译与烧录验证
所有源码层面的修改完成后,就到了编译和烧录环节。以RK3588 SDK为例,如果你是Android整包开发,可以直接:
./build.sh -A Kernel -u这个命令会重新编译内核和U-Boot,并打包生成新的镜像。如果你的SDK结构不一样,也可以分开操作:
# 单独编译U-Boot cd u-boot make rk3588_defconfig make -j$(nproc) # 编译内核设备树和内核 cd ../kernel-5.10 make ARCH=arm64 rockchip_defconfig make ARCH=arm64 dtbs make ARCH=arm64 boot.img -j$(nproc)编译完成之后,把新生成的U-Boot镜像、boot.img(或resource.img)以及Android系统镜像通过RKDevTool烧录到板子上。烧录时建议进入Maskrom模式或者Loader模式,用升级固件的方式整体写入,避免只烧单个分区导致版本不一致。
烧录完成后,把串口终端软件波特率切到115200,重新上电观察日志。正常情况应该是从第一行U-Boot打印到最后系统完全启动,全程一字不差、无乱码、无断流。到这里,整个修改流程就闭环了。
4. 验证、排查与避坑:实战中遇到的典型问题
4.1 启动日志里的波特率切换现象分析
我在调试过程中特意记录了几种典型现象,方便你对照自己的情况判断是哪里没改到位:
- 现象A:U-Boot正常,内核开始后立刻乱码。这说明U-Boot侧已经是115200,但内核侧还是1500000,两边不一致。优先查Kernel设备树的chosen节点,再看fiq_debugger节点里的rockchip,baudrate。
- 现象B:U-Boot乱码,内核阶段正常。这种比较少见,通常是U-Boot的CONFIG_BAUDRATE没改,或者U-Boot设备树里的bootargs是115200,但编译头文件还是1500000。回查
CONFIG_BAUDRATE。 - 现象C:U-Boot和内核都正常,Android起来后没有shell或输出变乱码。说明问题出在Android userspace层,重点检查BoardConfig.mk里的BOOTARGS,特别是
androidboot.console=ttyFIQ0后面的波特率。 - 现象D:全程完全没有输出。先别怀疑源码,优先检查接线、终端软件波特率、串口模块驱动,大概率是硬件链路问题。尤其是USB转串口模块没插好或者GND没接。
为了方便排查,我整理了一份速查表:
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 上电无输出 | 接线错误、串口模块坏、终端波特率错 | 检查TX/RX/GND,换模块,换波特率 |
| U-Boot正常,内核乱码 | Kernel dts未改或fiq_debugger节点未改 | 改kernel dts的chosen和fiq_debugger |
| 内核正常,Android后乱码 | BoardConfig.mk的BOOTARGS未改 | 改device/rockchip下的mk文件 |
| 某一阶段突然无打印 | 流控被打开 | 关闭RTS/CTS |
| 烧录后进不了系统 | boot.img/resource.img未重新打包 | 重新编译并确认烧录了新镜像 |
| 全程偶发乱码 | USB转串口模块驱动问题 | 更新驱动或换FTDI/CP2102 |
4.2 容易被忽略的三个细节
第一个细节,U-Boot和Kernel的设备树是两套独立文件,千万别以为改了一个就万事大吉。RK SDK里U-Boot的dts在u-boot/arch/arm/dts/,内核的dts在kernel-5.10/arch/arm64/boot/dts/rockchip/,两者虽然大部分内容长得像,但编译时各用各的,必须同步修改。
第二个细节,部分核心板厂商会在SPL阶段读取eMMC或SD卡里预留的config分区,里面可能覆盖了bootargs中的波特率参数。如果你改了源码并重新编译,烧录后发现还是老样子,建议把config分区一并擦除或者重新写入。这个情况在RK3568、RK3588的核心板上都出现过,属于厂商自定义逻辑,不是标准SDK行为,但遇到了会让人非常困惑。
第三个细节,修改波特率后,如果后续还要用RKDevTool进行Maskrom烧录,注意烧录工具里的通信波特率可能也会变化。RK的Loader下载协议默认跟uboot的配置走,你已经改成115200了,那烧录工具里如果还写着1500000就会下载失败。遇到烧录失败时,先看看工具的波特率设置是不是也要跟着改成115200。
4.3 修改完之后的验证脚本与日常使用建议
改完之后,强烈建议把串口使用的波特率信息固化到项目文档里,尤其是团队协作时。我自己习惯在仓库里建一个docs/debug_uart.md,内容很简单:开发板型号、debug串口对应排针位置、USB转串口模块型号、系统改动前波特率、改动后波特率、终端软件推荐配置、常见问题链接。这样新同事或者客户拿到板子后,不需要猜,直接照着文档就能连上。
另外,如果你经常在Linux主机上切换不同开发板的串口,建议给RK3588单独写一个/etc/udev/rules.d/规则,把USB转串口固定成一个稳定的设备名,比如/dev/rk3588_uart,这样每次插拔后不会因为ttyUSB编号变化而找不到设备。规则示例:
# /etc/udev/rules.d/99-rk3588-uart.rules SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", SYMLINK+="rk3588_uart"这里的idVendor和idProduct需要根据你手头CH340的实际值修改,用lsusb就能查到。连接时直接picocom -b 115200 /dev/rk3588_uart,方便很多。
4.4 关于回退:改完之后想恢复1500000怎么办
如果你改完之后发现某些场景还是需要1.5M(比如想用RK官方更高速的日志抓取工具),回退也很简单,把U-Boot的CONFIG_BAUDRATE、U-Boot dts的bootargs、Kernel dts的bootargs、fiq_debugger节点的baudrate、以及BoardConfig.mk里的BOOTARGS全部改回1500000,重新编译烧录即可。改来改去的过程其实很机械化,最关键的还是别忘了“四层同步改”这个原则。
我个人在实际操作中的体会是,调试串口波特率这种修改,看起来只是“一个数字的事”,但牵一发动全身,U-Boot、内核、Android userspace只要有一层掉链子,表现出来都是非常折磨人的乱码或者静默。按照“先U-Boot、再Kernel、再Android板级配置”这个顺序一层层捋下来,半小时内就能搞定。如果你在改的过程中遇到其他奇怪的现象,欢迎在评论区留言,把你看到的启动日志贴出来,我们一起来定位。