RK3562 Android调试串口转普通串口完整配置指南
1. 项目概述与核心需求在嵌入式Android开发中调试串口通常指fiq-debugger是一个至关重要的工具它允许开发者在系统启动的早期阶段查看内核日志printk输出甚至在系统完全崩溃时进行交互式调试。然而当产品进入量产或特定应用阶段这个硬件串口资源往往有更“接地气”的用途——比如连接一个GPS模块、与下位机PLC通信或者驱动一个串口屏。这时我们就面临一个矛盾如何把这块被系统“征用”的调试串口释放出来作为普通的、可供应用程序读写的/dev/ttySx设备最近在基于触觉智能的RK3562开发板EVB3562进行一个工业网关项目时我就遇到了这个典型需求。板载的UART0默认被配置为fiq-debugger用于输出内核启动信息。但我的项目需要用它来连接一个Modbus RTU协议的温湿度传感器。这意味着我必须完成从“系统调试咽喉”到“用户可用外设”的转变。这个过程涉及到内核设备树DTS的修改、Android系统权限的配置以及最终的编译与验证。虽然瑞芯微官方Wiki和一些社区文章有零散提及但完整、可复现的步骤和其中的“坑点”却很少被系统性地梳理。本文将基于RK3562平台详细拆解这一配置过程其原理和方法同样适用于瑞芯微其他芯片平台如RK3568, RK3588等希望能为有类似需求的开发者提供一个清晰的“操作手册”和“避坑指南”。2. 核心原理与方案设计解析2.1 调试串口与普通串口的本质区别要完成这个转换首先得理解两者在系统层面的不同。这不仅仅是改个名字那么简单而是涉及内核驱动架构和系统初始化流程的差异。调试串口 (fiq-debugger)这是一个非常特殊的驱动。它注册的早期在内核console_init之前并使用了ARM的FIQ快速中断机制以确保即使在系统中断被禁用或出现严重锁死时也能接收字符并执行简单的调试命令如ps,sysrq。它通常映射到/dev/ttyFIQ0或类似的节点但其核心目的不是提供一个通用的read/write接口给用户空间而是作为内核的调试控制台。在Android设备树中它通常通过serialfdd50000以RK3562 UART0为例这个节点下的fiq-debugger属性来启用。一旦启用标准的串口驱动就不会再去管理这个硬件资源。普通串口 (8250或serial驱动)这是我们熟悉的/dev/ttyS0,ttyUSB0等设备。它们由标准的串行端口驱动在Linux中通常是8250系列或其衍生驱动管理提供了完整的字符设备接口支持open,read,write,ioctl设置波特率、数据位等等系统调用。应用程序可以通过这个接口与外部设备进行双向数据通信。因此“转换”的本质是在设备树中将某个串口硬件控制器从fiq-debugger的管辖中释放出来并交由标准的serial驱动来接管和初始化。2.2 RK3562平台串口资源与规划RK3562的串口UART控制器资源是有限的。以我使用的EVB3562 V1B板型为例其原理图上通常会将UART0硬件引脚通过一个USB转串口芯片连接到Type-C口方便调试。这也是为什么它默认被用作fiq-debugger——即插即用无需额外飞线。在进行转换前必须明确你的硬件连接目标串口你要释放的是哪个UART通常是UART0。引脚复用确认该UART的TX、RX引脚没有被其他功能如GPIO、SPI复用。在RK平台这由pinctrl配置决定。替代调试方案关闭这个最方便的调试串口后你如何获取系统日志常见备选方案有ADB over USB这是最推荐的方式。确保你的固件和电脑驱动支持ADB通过adb logcat和adb shell dmesg来查看日志。另一个UART如果你的板子有另一个空闲的UART并引出了引脚可以将其配置为新的fiq-debugger。网络调试通过netconsole将内核日志输出到网络但这需要网络驱动先启动。我的方案选择对于EVB3562我选择释放UART0给应用使用同时完全依赖ADB进行后续调试。因为该开发板的Type-C口同时支持ADB和供电在Android系统启动后这是一种更强大、更灵活的调试手段。2.3 整体修改流程设计整个操作可以概括为“内核层修改”和“系统层修改”两步最后进行编译和烧录验证。下图清晰地展示了从原始状态到目标状态的完整转换路径和关键操作点flowchart TD A[原始状态brUART0被fiq-debugger占用] -- B{开始转换流程} B -- C[第一步内核设备树修改] C -- C1[修改通用dtsi文件br关闭fiq-debugger] C -- C2[修改板级dtsi文件br启用标准serial驱动] C1 C2 -- D[第二步编译生成新boot.img] D -- E[第三步Android源码修改] E -- E1[修改init.rc文件br为/dev/ttyS0添加权限] E1 -- F[第四步编译生成新super.img] F -- G[第五步烧录与验证] G -- G1[单独烧录boot.img与super.img] G1 -- G2[通过ADB进入系统] G2 -- G3[检查/dev/ttyS0设备节点] G3 -- H{验证成功} H -- 是 -- I[目标状态达成brUART0可作为普通串口使用] H -- 否 -- J[排查问题br检查步骤、编译、烧录] J -- B这个流程是一个经典的嵌入式Linux/Android系统定制过程每一步都有其明确的目的任何一步出错都可能导致最终功能异常。接下来我们将深入每个步骤的细节。3. 内核设备树修改详解设备树Device Tree是描述硬件配置的数据库内核根据它来初始化驱动。我们的修改主要在两个文件中进行一个通用的芯片级文件和一个具体的板级文件。3.1 定位与修改关键文件在你的RK3562 Android SDK内核目录下通常是kernel-5.10找到需要修改的文件。路径可能会因SDK版本或板型略有差异但模式是固定的。1. 关闭 fiq-debugger (rk3562-android.dtsi)这个文件定义了RK3562芯片在Android系统下的通用配置。我们需要找到fiq-debugger节点并将其status设置为disabled。# 进入内核源码目录 cd kernel-5.10 # 使用你喜欢的编辑器打开文件例如vim vim arch/arm64/boot/dts/rockchip/rk3562-android.dtsi在文件中搜索fiq-debugger。你会找到类似这样的段落// 修改前 fiq-debugger { compatible rockchip,fiq-debugger; rockchip,serial-id 2; // 注意这个ID它可能指向UART2 rockchip,wake-irq 0; rockchip,irq-mode-enable 1; rockchip,baudrate 1500000; // 调试串口波特率 interrupts GIC_SPI 252 IRQ_TYPE_LEVEL_HIGH; status okay; // 关键所在当前是启用状态 };注意这里有一个非常重要的细节rockchip,serial-id 2;这个属性并不直接代表硬件UART控制器编号如UART0。在瑞芯微的平台中这个ID是一个内部映射。你需要根据芯片TRM和板级dtsi文件来确认它实际对应哪个物理UART。在EVB3562的配置中serial-id 2通常就对应着物理的UART0。这一点很多文档没有说明如果改错了会导致其他串口被禁用。我们的修改是将status改为disabled// 修改后 fiq-debugger { compatible rockchip,fiq-debugger; rockchip,serial-id 2; rockchip,wake-irq 0; rockchip,irq-mode-enable 1; rockchip,baudrate 1500000; interrupts GIC_SPI 252 IRQ_TYPE_LEVEL_HIGH; status disabled; // 关键修改关闭fiq-debugger功能 };2. 启用标准UART驱动 (ido-evb3562-v1b.dtsi)这个板级文件描述了EVB3562这块具体板子的硬件连接比如哪个UART连接到了外部引脚。我们需要找到对应的UART节点这里是uart0并启用它。// 文件路径arch/arm64/boot/dts/rockchip/ido-evb3562-v1b.dtsi // 在文件合适的位置例如靠近其他UART节点的地方添加或修改 // 添加以下内容启用uart0 uart0 { status okay; // 启用该节点 pinctrl-names default; pinctrl-0 uart0m0_xfer; // 指定引脚复用组必须与原理图一致 }; // 文件中原有的uart3配置保持不变作为参考 uart3 { status okay; pinctrl-names default; pinctrl-0 uart3m1_xfer uart3m1_ctsn; };关键点解析uart0这是一个引用它指向在芯片级DTSI如rk3562.dtsi中已经定义好的uart0节点。我们通过这个引用来覆盖或添加板级特定的属性。pinctrl-0 uart0m0_xfer;这是最容易出错的地方。uart0m0_xfer表示UART0使用“m0”功能组的引脚。不同的芯片同一UART可能有多种引脚复用选择m0, m1, m2...。你必须根据开发板的原理图来确定正确的pinctrl配置。如果这里配置错误硬件引脚将无法输出UART信号。3.2 单独编译内核修改完成后不需要编译整个Android系统只需单独编译内核生成新的boot.img。这能节省大量时间。# 确保在kernel-5.10目录下 # 设置环境变量指定架构和输出路径 # BOOT_IMG参数指向你SDK中boot.img的默认位置请根据实际路径调整 export ARCHarm64 # 以下编译命令中的路径‘../rockdev/Image-rk3562_t/’需要替换为你的SDK中实际存在的路径 make ARCHarm64 BOOT_IMG../rockdev/Image-rk3562_t/boot.img ido-evb3562-v1b-dsi-mipi.img -j20命令解释与避坑ARCHarm64: 指定目标架构。BOOT_IMG...:这个参数至关重要。它告诉编译系统将生成的boot.img输出到指定路径。这个路径通常是你的Android SDK中rockdev目录下对应板型的预置镜像位置。如果路径错误编译可能成功但生成的boot.img你不知道在哪或者会被后续的全系统编译覆盖。ido-evb3562-v1b-dsi-mipi.img: 这是RK SDK中定义的一个编译目标make target它包含了内核、设备树和必要的ramdisk配置。目标名称因板型和屏幕接口而异请根据你的实际板型选择。-j20: 使用20个线程并行编译数字可根据你的CPU核心数调整。编译成功后的输出编译完成后你会在当前目录kernel-5.10下找到生成的boot.img文件。同时在BOOT_IMG指定的路径下也会有一份拷贝。请记录好这个文件的位置我们稍后需要用它进行烧录。实操心得在第一次尝试时我遇到了编译错误提示找不到某个dts文件。原因是我的编译目标选错了。务必确认你的板型对应的make target名称。可以查看SDK中的build.sh或device/rockchip/rk3562/目录下的mk文件来确认正确的目标名。4. Android系统层修改与编译内核修改让硬件UART0可以被标准驱动识别并生成/dev/ttyS0设备节点。但在Android系统中默认的权限设置可能不允许普通应用访问这个节点。我们需要在系统初始化脚本中为其设置正确的权限。4.1 修改 init.rc 文件Android的init.rc文件在系统启动早期由init进程解析并执行用于创建设备节点、设置权限、启动服务等。找到你板型对应的init.rc文件。对于RK3562路径通常是device/rockchip/rk3562/init.rk3562.rc使用编辑器打开该文件我们需要在某个合适的阶段通常是在on post-fs或on post-fs-data阶段之后服务启动之前添加权限设置命令。# 在文件中找到类似以下内容的部分通常在设置其他设备权限的附近 on post-fs-data # ... 其他已有的命令 ... chown system system /sys/class/devfreq/dmc/system_status # 这是我们添加新命令的地方 chmod 0666 /dev/ttyS0 chown system system /dev/ttyS0添加内容解释chmod 0666 /dev/ttyS0: 将/dev/ttyS0的权限设置为所有用户root,system,others都可读可写。0666是八进制表示对应rw-rw-rw-。这确保了非root的应用也能打开这个串口设备。chown system system /dev/ttyS0: 将设备的所有者和组都设置为system。在Android中system用户和组拥有较高的系统权限许多系统服务也运行在此上下文中。重要注意事项直接设置0666权限虽然方便但在对安全性有要求的量产产品中可能过于宽松。更安全的做法是创建一个特定的用户组如serial将需要访问串口的应用加入该组然后设置设备权限为0660所有者与同组用户可读写。这需要更深入的init.rc和sepolicy修改超出了本文基础范围。在开发调试阶段使用0666是完全可以接受的。4.2 编译Android系统镜像修改完init.rc后需要重新编译Android系统镜像来包含这个改动。我们主要关心的是包含init.rc的super.img在Android动态分区系统中它包含了system,vendor等分区。# 返回到Android源码的根目录 cd /path/to/your/android/source # 设置编译环境通常需要执行source build/envsetup.sh和lunch source build/envsetup.sh lunch rk3562_t-userdebug # 选择你的目标产品名称可能不同 # 开始编译。如果之前编译过可以使用增量编译以节省时间 make -j4 # 或者只编译system镜像相关部分如果清楚的话但make是最稳妥的。编译输出编译完成后系统镜像文件将生成在out/target/product/rk3562_t/目录下。我们需要的super.img就在这个目录中。同时boot.img也会在这里被重新生成但请注意这个boot.img是使用未修改的内核源码编译的除非你修改了内核后也在这里执行了全编译。因此我们应该使用之前在kernel-5.10目录下单独编译生成的那个boot.img。5. 烧录、验证与问题排查这是最后一步也是最考验耐心的一步。我们将把修改后的两个关键镜像烧录到设备上并进行功能验证。5.1 镜像烧录你需要使用瑞芯微提供的烧录工具如RKDevTool或upgrade_tool。这里以图形化的RKDevTool为例。进入Loader模式开发板断电按住主板上的Recovery或Maskrom键不同板子按键名称不同EVB3562通常是Recovery键不放然后连接USB到电脑。RKDevTool应显示“发现一个LOADER设备”。加载配置文件在工具中加载你SDK中对应的配置文件.cfg文件它会列出所有需要烧录的分区。选择性烧录找到boot分区取消其默认的勾选然后点击右侧路径选择我们单独编译生成的boot.img来自kernel-5.10目录。找到super分区或者旧分区表下的system,vendor等取消默认勾选选择我们重新编译生成的super.img来自out/target/product/rk3562_t/目录。其他分区如misc,resource,kernel等保持不动不要烧录这是增量更新的关键避免擦除用户数据和其他配置。执行烧录点击“执行”按钮。烧录过程很快完成后设备会自动重启。烧录安全提示务必确认你选择的是正确的镜像文件。错误的boot.img可能导致设备无法启动。在烧录前最好先对能正常启动的原始固件进行备份。5.2 功能验证设备重启后由于调试串口UART0已被禁用你将看不到任何内核启动信息输出到串口调试工具如Putty,Minicom。这是第一个成功信号。接下来我们需要验证UART0是否已作为普通串口正常工作。通过ADB连接使用USB数据线连接开发板和电脑确保adb devices能列出设备。进入Shell并检查设备节点adb shell rk3562_t:/ $ su # 可能需要获取root权限 rk3562_t:/ # ls -l /dev/ttyS0期望的输出结果crw-rw-rw- 1 system system 4, 64 2024-05-27 10:30 /dev/ttyS0c表示这是一个字符设备。rw-rw-rw-权限为0666所有用户可读可写与我们init.rc中的设置一致。system system所有者和组均为system。4, 64主设备号4tty设备次设备号64对应ttyS0。这个信息是内核驱动创建的。基本读写测试可选但推荐为了进一步确认串口功能完好可以进行一个简单的回环测试。这需要将UART0的TX和RX引脚用杜邦线短接。安装一个串口测试工具例如通过adb push将编译好的busybox推送到设备或者使用已有的toybox。在一个adb shell中执行读操作cat /dev/ttyS0在另一个adb shell中执行写操作echo Hello UART0 /dev/ttyS0如果串口驱动和硬件连接正常在第一个shell中应该能看到输出的“Hello UART0”。这完全验证了串口的数据收发通路是畅通的。5.3 常见问题与排查技巧实录在实际操作中你可能会遇到以下问题。这里记录了我的排查过程和解决方法。问题1编译内核时BOOT_IMG路径错误导致找不到原始boot.img来重新打包。现象编译命令报错提示无法找到boot.img或resource.img等。排查仔细检查BOOT_IMG环境变量指向的路径。这个路径应该是你SDK中原始boot.img所在的位置编译系统会用它作为基础进行重新打包。可以尝试在SDK根目录下搜索boot.img找到在rockdev或image目录下的那个。解决使用绝对路径。例如BOOT_IMG/home/user/rk3562_sdk/rockdev/Image-rk3562_t/boot.img。问题2烧录新镜像后设备无法启动卡在开机Logo或黑屏。现象设备无法进入系统ADB也无法连接。排查思路检查内核设备树修改这是最常见的原因。确认fiq-debugger的status是disabled而不是拼写错误。确认uart0的status是okay。尤其检查pinctrl配置错误的引脚复用会导致系统关键功能异常。检查编译目标确认编译内核时使用的make target如ido-evb3562-v1b-dsi-mipi.img与你的硬件板型完全匹配。恢复出厂固件使用RKDevTool不勾选“Loader”模式下的旧选项直接加载完整的原始固件.img文件执行“修复设备”或完整擦除烧录以恢复到一个已知的正常状态。然后重新开始我们的修改步骤。解决回退修改使用git diff对比你修改的文件确保没有引入语法错误如缺少分号。最稳妥的方法是一次只修改一个地方编译烧录测试一次。问题3/dev/ttyS0设备节点不存在或权限不对。现象ls /dev/ttyS0提示“No such file or directory”或者权限不是0666。排查节点不存在说明内核驱动没有成功创建该节点。首先通过adb shell dmesg | grep ttyS或adb shell dmesg | grep uart0查看内核日志确认UART0驱动是否成功探测probe。如果看不到相关日志说明设备树修改未生效驱动未加载。重新检查并烧录boot.img。权限不对说明init.rc的修改未生效。检查init.rk3562.rc文件修改是否正确确认编译后super.img是否成功烧录。可以adb pull /init.rc ./拉取设备上的实际文件检查修改是否在其中。问题4应用程序可以打开/dev/ttyS0但无法收发数据。现象open()和write()调用成功但对方设备收不到数据或读不到数据。排查波特率等参数设置这是最常见原因。应用程序必须通过ioctl或termios库正确设置波特率、数据位、停止位、校验位。这些参数必须与对端设备严格匹配。默认的波特率可能是115200但需要确认。硬件流控检查是否意外启用了硬件流控RTS/CTS。在设备树中UART节点如果包含了cts或rts相关的pinctrl如示例中的uart3m1_ctsn驱动可能会尝试启用流控。如果对端设备不支持会导致通信卡死。尝试在应用程序中明确关闭流控或者在设备树中移除流控引脚配置如果硬件未连接。引脚连接确认TX、RX是否接反。用万用表或逻辑分析仪检查引脚是否有数据波形。问题5如何确认fiq-debugger确实被关闭了验证方法最直接的证据就是连接原来的调试串口到电脑打开串口终端设备上电后没有任何输出。此外可以查看内核cmdlineadb shell cat /proc/cmdline如果其中包含consolettyFIQ0或类似的参数说明fiq-debugger可能还在以某种方式启用。但在我们禁用其节点后内核通常不会添加这个参数。通过以上步骤和问题排查指南你应该能够成功地将RK3562开发板上的Android调试串口转换为一个功能完整的普通串口。这个过程的核心——即修改设备树切换驱动归属、在init.rc中设置权限——是通用的可以举一反三应用到其他瑞芯微平台甚至其他芯片平台的类似需求上。记住嵌入式开发总是伴随着反复的修改、编译和测试耐心和细致的日志分析是你最好的工具。