ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

RK3576嵌入式Linux SD卡启动与根文件系统挂载避坑指南

RK3576嵌入式Linux SD卡启动与根文件系统挂载避坑指南 1. 一块RK3576板子引发的连环翻车拿到RK3576这块板子的时候我心态是很放松的。毕竟RK3399、RK3568这些瑞芯微的经典平台我都摸过不少按经验来说换一代芯片无非就是SDK更新一下、设备树改改引脚、编译烧录跑起来能有多大事结果这一趟下来从SD卡识别到CD检测从初始化时序到根文件系统挂载我几乎把能踩的坑挨个踩了一遍。这篇文章不打算写成一份正经的移植文档那种东西官方手册里都有我想聊的是手册里不会写、但你在实际操作中一定会撞上的那些问题。RK3576是瑞芯微这两年在边缘计算和嵌入式AI场景里推得比较猛的一颗SoC八核架构加上还算能打的NPU做嵌入式Linux项目、嵌入式AI测试、工业网关这类活儿都挺合适。但它的SDK成熟度和当年的RK3399比还有差距尤其是SD卡相关的部分从硬件设计到软件初始化坑点相当密集。如果你正在用RK3576做项目或者准备从SD卡启动、从SD卡安装证书、从SD卡加载根文件系统那这篇内容应该能帮你省下不少通宵的时间。我这次的场景其实很朴素板子通过SD卡启动根文件系统放在SD卡上同时还要支持热插拔检测。听起来是不是特别基础但就是这么一个基础需求让我在CD检测、初始化时序、设备树配置这几个环节上反复折腾。下面我把整个过程拆开讲包括我是怎么定位问题的、每一步为什么这么改、以及那些看起来能跑其实埋着雷的配置。2. RK3576的SD卡控制器和你想的不太一样2.1 先搞清楚这块板子的SD卡走的是哪条控制器很多人拿到板子第一反应是直接抄参考设计的设备树我也不例外。但RK3576的SD卡控制器分配和上一代有明显区别它有几个不同的MMC控制器实例分别对应SDIO、SD卡、eMMC等不同功能。如果你不先确认自己的硬件原理图上SD卡座子接的是哪个控制器后面所有配置都是瞎猜。我的板子SD卡接的是sdmmc这个控制器但参考设计里默认把它配成了SDIO模式给WiFi模组用。这就是第一个坑设备树里sdmmc节点的bus-width、cd-gpios、no-sdio这些属性如果和实际硬件对不上内核启动时要么直接不识别卡要么识别了但读写报错。我当时的做法是先把原理图翻出来确认SD卡的CLK、CMD、DATA0-3这几根线具体连到SoC的哪一组引脚然后对照RK3576的引脚复用表逐个核对。这里有个经验瑞芯微的引脚复用表里同一个物理引脚可能对应三四种功能光看名字很容易看错。我建议你把原理图上的网络标号和SoC数据手册里的pinmux表格并排放着对确认每一个引脚的功能选择寄存器该配成什么值。这一步偷懒后面就会用各种莫名其妙的报错来惩罚你。2.2 CD检测引脚一个被大多数人忽略的细节CDCard Detect检测是SD卡里最容易被低估的部分。很多人觉得不就是个GPIO嘛配一下就行。但实际用起来你会发现CD引脚的极性、上拉电阻、去抖时间任何一个不对都会导致卡插进去没反应或者拔出来系统还以为卡在。RK3576的设备树里CD检测通过cd-gpios属性配置。我一开始照抄了参考设计里的写法结果发现卡插拔完全没反应。排查了半天才搞明白两件事第一我的板子CD引脚是低电平有效但参考设计里配的是高电平有效第二CD引脚外部没有上拉电阻需要在内核里启用内部上拉否则引脚悬空时电平飘忽不定。正确的配置大概是这样sdmmc { bus-width 4; cd-gpios gpio1 RK_PA0 GPIO_ACTIVE_LOW; pinctrl-names default; pinctrl-0 sdmmc_clk sdmmc_cmd sdmmc_bus4 sdmmc_cd; status okay; };注意GPIO_ACTIVE_LOW这个宏它决定了内核怎么解读引脚电平。如果你不确定自己的硬件是高有效还是低有效最简单的办法是拿万用表量一下卡插入和拔出时CD引脚的电平变化别靠猜。还有一个容易被忽略的点是去抖。机械式的卡座在插拔瞬间会有抖动如果内核没有做去抖处理可能会触发多次检测中断。RK3576的SDHCI驱动里其实有软件去抖机制但需要你在设备树里正确配置cd-debounce-delay-ms属性。我设的是200毫秒实测下来插拔响应很稳不会出现反复识别的情况。2.3 初始化时序为什么你的卡总是识别失败SD卡的初始化流程是有严格时序要求的从上电、时钟稳定、CMD0复位、CMD8电压协商、ACMD41初始化到最后的CMD2/CMD3获取CID和RCA每一步都有超时限制。RK3576的控制器在这些步骤上基本遵循标准协议但有一个地方特别容易出问题时钟频率的初始值。标准协议要求SD卡初始化阶段时钟不能超过400kHz初始化完成后再切换到高速模式。但有些参考设计的设备树里直接把max-frequency设成了50MHz甚至更高内核在初始化阶段如果没有正确降频卡就会因为时序不满足而识别失败。我遇到的现象是卡插进去内核日志里能看到mmc0: new high speed SDHC card但紧接着就是一堆mmc0: error -110 whilst initialising SD card然后卡就掉了。解决办法是在设备树里明确限制初始化频率或者确认你的SDK版本里驱动已经正确处理了分频。我最后的配置是sdmmc { max-frequency 150000000; no-sdio; no-mmc; supports-sd; ... };max-frequency设的是控制器支持的最高频率驱动会在初始化阶段自动降频但前提是你的SDK版本足够新。如果你用的是老版本SDK建议手动在驱动里确认一下sdhci_set_clock的调用逻辑。3. 从SD卡启动到根文件系统挂载的完整链路3.1 启动模式选择拨码开关和eFuse的优先级RK3576支持从多种介质启动包括eMMC、SD卡、SPI Flash等。启动顺序由拨码开关或者eFuse配置决定。我这次要从SD卡启动所以第一步是确认拨码开关拨到了正确的档位。但这里有个坑如果eFuse里已经烧录了启动配置拨码开关可能不生效。瑞芯微的启动流程是先读eFuse里的启动配置如果eFuse没有烧录再读拨码开关的GPIO状态。我手上这块板子是量产过的eFuse里已经烧了从eMMC启动的配置所以不管我怎么拨开关它都从eMMC启动。最后是短接了eFuse的烧录点重新烧了一遍启动配置才解决。如果你也遇到拨码开关不起作用的情况先别怀疑硬件坏了查一下eFuse的状态。用rkdeveloptool或者瑞芯微的烧录工具可以读取eFuse内容确认启动配置到底是从哪里来的。3.2 分区布局根文件系统放哪里很讲究从SD卡启动Linux分区布局是个需要提前想清楚的事。我的方案是第一个分区放内核和设备树第二个分区放根文件系统。这样做的原因是内核和设备树需要被BootROM和U-Boot直接读取放在FAT分区里兼容性最好根文件系统用ext4性能和稳定性都更优。具体分区如下分区大小文件系统用途p164MBFAT32内核镜像、设备树、启动脚本p2剩余空间ext4根文件系统这里有个细节RK3576的BootROM在读取SD卡时对分区的起始扇区有要求。如果第一个分区不是从默认的偏移量开始BootROM可能找不到引导文件。我建议直接用瑞芯微提供的分区工具生成分区表不要手动用fdisk去分否则很容易踩到对齐的坑。3.3 内核命令行参数root和rootwait不能少根文件系统挂载失败是嵌入式Linux里最常见的问题之一。我这次遇到的现象是内核启动到最后打印VFS: Cannot open root device mmcblk1p2 or unknown-block(0,0)然后直接panic。原因有两个第一root参数写的是mmcblk1p2但实际SD卡枚举出来的设备节点是mmcblk0p2因为eMMC占了mmcblk0第二没有加rootwait参数内核在SD卡还没枚举完成的时候就尝试挂载根文件系统自然找不到设备。正确的内核命令行参数应该是root/dev/mmcblk1p2 rootwait rw consolettyS2,1500000rootwait的作用是让内核等待所有MMC设备枚举完成后再挂载根文件系统。对于SD卡这种枚举时间不确定的设备这个参数几乎是必须的。另外consolettyS2,1500000是RK3576的调试串口配置波特率1500000别写成115200否则串口输出全是乱码。3.4 根文件系统里的坑库初始化和设备节点根文件系统挂载成功不代表就能正常跑起来。我遇到的下一个问题是系统启动到用户空间后一堆服务报库初始化失败尤其是跟图形和AI相关的库。排查后发现是根文件系统里的/dev目录没有正确填充/dev/mmcblk1p2这些设备节点不存在。解决办法是确保根文件系统里配置了udev或者mdev来动态创建设备节点。如果你用的是BusyBox的mdev需要在/etc/init.d/rcS里加上echo /sbin/mdev /proc/sys/kernel/hotplug mdev -s另外RK3576的NPU和GPU驱动需要在根文件系统里放对应的固件和库文件这些文件在SDK的buildroot或debian输出目录里都有但如果你是自己从头构建根文件系统很容易漏掉。我建议直接用SDK自带的根文件系统镜像跑通之后再逐步裁剪。4. 那些让我熬夜的报错和最终定位过程4.1mmc0: error -110超时背后的真凶error -110是MMC子系统里最常见的报错之一意思是命令超时。这个报错可能的原因非常多时钟不对、引脚复用冲突、电源不稳、卡本身有问题。我一开始以为是卡的问题换了好几张卡现象一样才排除掉卡的因素。然后我用示波器量了SD卡的CLK和CMD线发现CLK在初始化阶段频率明显偏高超过了400kHz。这说明驱动没有正确降频。进一步查SDK代码发现我用的这个版本里sdhci_set_clock函数在初始化阶段的分频逻辑有bug没有根据max-frequency正确计算分频值。临时解决办法是在设备树里把max-frequency设成一个较低的值比如25MHz强制驱动在初始化阶段使用更低的分频。虽然这样会牺牲一点性能但至少能稳定识别。后来我更新了SDK到最新版本这个问题就消失了。所以如果你遇到类似的超时问题先确认SDK版本很多坑其实官方已经修了只是你用的版本太老。4.2 CD检测中断不触发GPIO配置的隐藏陷阱CD检测不触发这个问题让我卡了整整一个下午。设备树里cd-gpios配了引脚复用也设了但插拔卡就是没反应。用cat /proc/interrupts看中断统计发现对应的GPIO中断号计数一直是0。排查过程是这样的先确认GPIO引脚本身能不能读——用gpioinfo和gpioget工具直接读引脚电平发现插拔卡时电平确实在变说明硬件没问题。然后查设备树里的pinctrl配置发现sdmmc_cd这个pinctrl节点里只配了引脚功能没有配偏置bias。RK3576的GPIO默认是浮空输入没有内部上拉所以引脚电平在卡没插的时候是不确定的中断控制器可能一直认为没有边沿变化。解决办法是在pinctrl里加上bias-pull-upsdmmc_cd: sdmmc-cd { rockchip,pins 1 RK_PA0 1 pcfg_pull_up; };加上拉之后CD检测立刻正常了。这个坑的教训是GPIO中断不触发先查偏置配置。浮空输入在数字电路里是大忌尤其是检测类引脚。4.3 根文件系统挂载后init跑不起来根文件系统挂载成功但init进程启动失败内核打印Kernel panic - not syncing: No working init found。这个问题通常是因为根文件系统里的/sbin/init不存在或者动态链接库缺失。我用file命令检查了/sbin/init发现它是动态链接的依赖libc.so.6和ld-linux-aarch64.so.1。但我的根文件系统里只放了busybox的静态链接版本没有放glibc的运行时库。解决办法是把交叉编译工具链里的sysroot目录下的lib和lib64完整拷贝到根文件系统的对应目录。这里有个小技巧用readelf -d /sbin/init可以查看一个可执行文件依赖哪些动态库然后逐个确认这些库在根文件系统里是否存在。比盲目拷贝整个sysroot要精准得多。5. 硬件设计层面那些值得提前确认的事5.1 SD卡座的选型和PCB布局建议如果你还在硬件设计阶段有几个点值得提前注意。首先是SD卡座的选型带CD引脚的卡座和不带CD引脚的卡座在软件配置上完全不同。不带CD引脚的卡座只能靠轮询或者定时检测体验差很多。我建议尽量选带CD引脚的卡座软件上省事很多。PCB布局方面SD卡的CLK线要尽量短并且远离其他高速信号线。CLK是SD卡里频率最高的信号容易对外辐射也容易被干扰。DATA线之间要保持等长尤其是走4位模式的时候不等长会导致采样时序偏差。另外电源去耦电容要靠近卡座的VDD引脚容值建议用0.1uF和10uF并联。5.2 电平匹配3.3V还是1.8VSD卡支持3.3V和1.8V两种电压模式高速模式通常需要1.8V。RK3576的SDMMC控制器支持电压切换但需要硬件上有对应的电平转换电路。如果你的板子只设计了3.3V供电那SD卡就只能跑在默认速度模式高速模式用不了。我这次用的板子是3.3V固定供电所以设备树里没有配vqmmc-supply。如果你需要高速模式硬件上必须加电平转换芯片软件上要在设备树里配置vmmc-supply和vqmmc-supply两个 regulator并且确保控制器支持sd-uhs-sdr104等高速模式。5.3 调试串口和SD卡引脚的复用冲突RK3576的引脚复用很灵活但灵活也意味着容易冲突。我遇到过一个情况调试串口的TX引脚和SD卡的某个DATA引脚复用了同一个物理引脚结果就是要么串口没输出要么SD卡识别不了。这种冲突在原理图评审阶段就要发现等到板子打回来再改就麻烦了。建议在硬件设计完成后把所有用到的外设引脚列一张表逐个核对复用关系。瑞芯微的引脚复用表里会标注每个引脚的默认功能和可选功能如果两个外设用了同一个引脚必须通过pinctrl在软件上做取舍或者改硬件设计。6. 跑通之后回头看哪些经验值得记下来6.1 先确认硬件再调软件我这次踩的坑里至少有一半是因为没有先确认硬件状态就开始改软件。CD引脚极性、上拉电阻、时钟频率这些其实用万用表和示波器量一下就能确定但我一开始偷懒直接抄参考设计结果在软件上绕了一大圈。现在的习惯是拿到新板子先量电源、量时钟、量关键GPIO电平确认硬件没问题再动软件。这个顺序看起来慢实际上是最快的。6.2 内核日志要逐行读不要只看最后一行内核启动日志里很多关键信息在中间就打印了。比如SD卡识别失败真正的错误可能在mmc0: error -110之前几行就已经提示了时钟或者电压问题。我后来养成了一个习惯把完整的内核日志重定向到文件然后用grep搜mmc、sdhci、regulator这些关键词逐条看不放过任何一条warning。6.3 设备树改动要小步走每次只改一个地方设备树里一个属性的改动可能影响好几个外设。我一开始图省事一次性改了好几个节点结果出了问题根本不知道是哪个改动导致的。后来改成每次只改一个属性改完编译、烧录、验证确认没问题再改下一个。虽然麻烦但定位问题的时候省心太多。6.4 善用/sys和/proc里的调试接口Linux的MMC子系统在/sys/kernel/debug/mmc0/下面提供了很多调试信息包括当前时钟频率、总线宽度、电压模式、错误统计等。遇到SD卡问题时先cat一下这些文件比盲目猜要高效得多。另外/proc/interrupts可以看中断触发情况/sys/kernel/debug/gpio可以看GPIO状态这些都是排查问题的利器。6.5 SDK版本很关键别死磕老版本我这次遇到的时钟分频问题在更新SDK之后就消失了。瑞芯微的SDK更新频率不算低很多早期版本的bug在新版本里已经修了。如果你在一个问题上卡了很久不妨先看看有没有新版本SDK升级一下可能比你自己改代码更快。最后说一个我个人的体会嵌入式开发里SD卡这种看起来简单的模块往往是最能暴露硬件和软件配合问题的。它涉及电源、时钟、引脚复用、协议时序、文件系统、内核启动流程几乎把嵌入式Linux的各个环节都串了一遍。把SD卡这一块彻底搞明白对理解整个系统的启动链路帮助非常大。RK3576这块板子虽然让我踩了不少坑但踩完之后对瑞芯微平台的理解确实上了一个台阶。如果你也在用这块板子希望这些经验能让你少走点弯路。
返回列表