刚拿到一块 RK3566/RK3568 的开发板,想把 OpenHarmony 跑起来,第一道坎往往不是编译,而是怎么把系统刷进去。很多教程把刷机写得玄乎,其实拆开看就三件事:让板子能从 SD 卡启动、把系统烧进 eMMC、处理好分区表让系统能认盘。这篇就围绕“SD-eMMC-单分区”这条完整链路,把 OpenHarmony 刷机的全流程走一遍。整个过程我会按实际操作的顺序来讲,包括为什么选单分区、SD 卡怎么处理、eMMC 烧录用什么工具、遇到 110 这类报错怎么定位,适合刚接触 OpenHarmony 设备开发、准备在 RK 平台板上做系统移植或固件验证的朋友参考。
1. 项目整体设计与思路拆解
1.1 明确刷机目标:SD 启动与 eMMC 烧录分别解决什么问题
先说一个容易混淆的点:很多人以为刷机就是把固件往 eMMC 里一写就完事。实际上在 OpenHarmony 开发中,SD 卡和 eMMC 承担着不同的任务。
SD 卡启动的核心价值是“快速迭代”。编译出来的系统镜像动辄几百 MB,甚至是多个分区的完整镜像。如果每次都往 eMMC 里烧,一次烧录要几分钟,而且烧坏了还要想办法恢复。用 SD 卡启动,插上去就能跑,拔下来就能改,极大缩短开发调试的反馈周期。在 OpenHarmony 这类大型系统开发里,SD 卡口几乎是开发者的“逃生通道”。
eMMC 烧录的核心价值是“产品化落地”。调试结束之后,系统最终要跑在板载存储上,不能依赖外部 SD 卡。设备的正常启动流程是 BootROM → 引导加载程序 → 内核 → 用户态服务,而这个链路在正式产品里必须全部从 eMMC 读取。所以,烧录 eMMC 是让板子从“开发态”变成“可交付态”的必经步骤。
那“单分区”又是什么意思?OpenHarmony 的通用镜像通常涉及多个分区,比如 boot、system、vendor、userdata 等。单分区方案在实践中通常指一种简化的镜像布局:把内核、ramdisk、根文件系统整合到一个分区中,通过 read-only 根文件系统 + 可写数据分区的方式工作。对于开发板刷机验证场景,单分区方案配置简单、维护容易、出问题的排查面小,是我在实操中比较推荐的方式。
1.2 为什么选 RK3568 平台:设备树与刷机关联很紧密
OpenHarmony 对 RK3568 系列芯片支持比较完善,社区和官方代码仓里有大量可参考的默认配置。但这也带来一个尴尬:源码里设备树文件很多,到底该选哪个?比如rk3568-evb1-ddr4-v10.dtb、rk3568-evb2-lpddr4-v10.dtb、rk3568-evb2-lpddr3-v10.dtb这类命名,初看很容易懵。
这个选择核心看三样东西:板子的 SoC 型号、内存颗粒类型、板厂对 GPIO 和外围接口的复用定义。比如 EVB1 和 EVB2 的硬件接口定义本身就有差异,DDR4 和 LPDDR4/LPDDR3 的驱动时序也不同。选错了设备树,最典型的现象有两个:要么内核启动阶段直接 panic,要么启动到一半发现某个外设 probe 失败——因为没有节点描述这个 GPIO 对应的功能。刷机前务必根据板卡型号确认设备树名称,不要想当然地拿默认 dtb 一把梭。
1.3 “SD-eMMC-单分区”方案的适用场景与局限
这套方案最典型的适用场景是:你拿到一块 RK 板卡,OpenHarmony 源码已经能编译通过,当前核心目标是先把固件跑到桌面/系统界面,验证基础外设和系统服务。此时 SD 启动 +eMMC 烧录 + 单分区布局,能够以最小的配置复杂度完成验证。
局限性也要说清楚:单分区方案在生产环境并不常见,原因在于系统升级牵扯整个分区重写,而不是只更新某个子模块;而多分区方案下的 A/B 分区、OTA 增量升级、userdata 独立格式化等能力,在量产的设备上几乎是必需品。所以如果你是做产品级方案评估,还是建议日后切换到标准多分区布局;本文这套流程更适合开发调试和教学验证。
2. 刷机前置准备与核心原理
2.1 硬件准备清单与连接方式
实际刷机的硬件准备并不复杂,但每一件都有可能让你卡壳,我列一个清单供对照:
- 一台 Ubuntu 主机(建议 18.04 或 20.04,Windows 下做 eMMC 烧录工具兼容性麻烦,后续尽量用 Ubuntu)
- RK3568 开发板(根据板卡型号选择对应设备树)
- 一张 Micro SD 卡,建议 16GB 以上,品牌不限但质量要靠谱,劣质卡在写 eMMC 时长时段读拷数据容易出错
- 一个读卡器,用于在主机上制作 SD 启动卡
- USB Type-C 数据线(用于烧写 eMMC 时让板子进入 MaskROM/Loader 模式)
连接顺序建议:先插 SD 卡、接电源、再连 USB 线到 Ubuntu 主机,最后按下板子的 recovery 键并重新上电进入烧录模式。这样能尽量避免 USB 枚举失败。
2.2 软件工具链:刷机工具、镜像工具和系统工具
OpenHarmony 刷机涉及的软件工具主要有这么几类:
烧录工具:RK 平台的工具链核心是RKDevTool(Windows 版本)和 Linux 下的upgrade_tool(有时也叫rkdeveloptool)。它们负责和设备端的烧录协议通信,将镜像逐 block 写入 eMMC。编译 OpenHarmony 的 RK 平台镜像产物中,通常会生成一个config.cfg或分区表描述文件,烧录工具按这个配置解析各个分区的偏移地址和大小。
SD 卡格式化工具:SD Memory Card Formatter 是 SD 协会官方的格式化工具,推荐使用。它比操作系统自带的格式化工具更“懂” SD 卡的内部结构和擦除块边界,能清除分区残留信息。注意,普通格式化不会抹掉 U-Boot 写进 SD 卡特定扇区的引导代码,所以如果你要做“干净卡”,建议用这个工具选择“Overwrite format”。
系统处理工具:主要涉及dd、fdisk、parted、gpt这些 Linux 工具。制作 SD 启动卡本质上就是往 SD 卡的特定偏移位置写入引导镜像和分区镜像,dd是这里的主力工具。
2.3 固件获取和校验
从我实际操作的经验来看,刷机失败的根源中,固件本身损坏或被截断的情况不在少数。如果你是从编译服务器拉取镜像,建议固定用一个 SHA-256 校验流程:
sha256sum <镜像文件>把生成的值和编译日志或官方发布说明中的值比对。另外,OpenHarmony 编译产物路径通常在out/rk3568/packages/phone/images/下面,这里会有boot_linux.img、system.img、vendor.img、uboot.img等。用哪个取决于你的烧录方式:如果你走完整 eMMC 烧录,则需要把所有分区镜像一次性写入;如果从 SD 卡启动,往往只需要uboot.img、boot_linux.img和system.img这几个关键部分。
提示:不要用来源不明的二手镜像。开发中的 OpenHarmony 版本推进很快,编译工具链和内核接口不匹配的话,镜像本身没问题也会在启动阶段抛出奇怪的错误。
3. 分步实操:从空白 SD 卡到 eMMC 烧录
3.1 制作 SD 启动卡:分区表设计与写卡细节
这一节是整个流程的基础。先说分区表设计。单分区方案下,我的推荐做法是:不维护复杂的 GPT 分区表,而是直接用dd把引导镜像按偏移写入,再用传统 MBR 分区表放一个可写的 userdata 分区。有的朋友会问:这样是不是有点“野”?其实在开发板上非常常见,因为 OpenHarmony 的引导加载程序在 SD 启动模式下不一定按照标准分区表去找内核,它有自己固定的扇区偏移。
具体怎么做呢?在 Ubuntu 主机上先确认 SD 卡设备名。插上读卡器后执行:
lsblk一般会看到类似/dev/sdb的设备,千万确认清楚,别把电脑硬盘给 dd 了。确认后开始做卡:
sudo dd if=uboot.img of=/dev/sdb bs=512 seek=64 sudo dd if=boot_linux.img of=/dev/sdb bs=512 seek=16384这里seek的偏移量是怎么来的?RK 平台在 SD 卡启动时,BootROM 会从 SD 卡的扇区 64(即偏移 32KB 处)开始检索 U-Boot 的 idblock。U-Boot 编译产物里包含了 idblock 信息,写到 64 扇区是通用约定。boot_linux.img写到 16384 扇区,也就是偏移 8MB 处,这是 U-Boot 环境变量里配置的默认加载地址对应的偏移。如果你改过 U-Boot 的CONFIG_SYS_MMCSD_RAW_MODE_KERNEL_SECTOR这个配置,需要按实际值调整。
写完这两个镜像后,再创建 userdata 分区:
sudo fdisk /dev/sdb交互式操作中建议先p打印当前分区表,然后d删除已存在的分区(如果之前格过),再n新建主分区,大小根据卡容量自行分配。创建完成后w写入并退出,最后格式化:
sudo mkfs.ext4 /dev/sdb1至此,一张 SD 启动卡就制作完成。插入板子并上电,如果看到串口输出 U-Boot 日志并进入内核启动流程,说明 SD 启动正常。
3.2 烧写 eMMC 的两种方式:从 SD 卡烧与从 USB 烧
eMMC 的烧录路径有两条,我分别讲一下,因为不同场景下选择不一样。
方式一:从 SD 卡系统内部烧写 eMMC
这种方式适合你已经从 SD 卡启动到了 OpenHarmony 系统,或者进到了 U-Boot 命令行。在 U-Boot 里可以这样写 eMMC:
mmc dev 1 fatload mmc 0:1 ${loadaddr} update.img mmc write ${loadaddr} 0 0x2000实际上 OpenHarmony 的update.img是一个打包好的完整升级镜像,可以用upgrade_tool烧写,也可以直接在 U-Boot 下逐块写。这种方式的优点是不需要额外的 USB 线,而且能看到每一块的写入进度;缺点是如果 eMMC 本身没烧过任何可用引导,板子无法启动到 U-Boot,那这个方式就无从谈起。
方式二:从 USB 烧写(MaskROM/loader 模式)
这是最主流、最可靠的完整烧录方式。操作要点如下:
- 完全断电,拔掉 SD 卡(避免干扰 eMMC 枚举);
- 按住板子上的 recovery 按键(或 MaskROM 按键,不同板卡位置不同,建议看板卡原理图或丝印);
- 通过 USB Type-C 线连接 Ubuntu 主机;
- 上电,保持按键按住 3~5 秒后松开。此时设备应被识别为处于 Loader 模式。
在 Ubuntu 下用rkdeveloptool工具可以查看设备连接状态:
sudo rkdeveloptool ld如果输出类似DevNo=1 Vid=0x2207 Pid=0x350b之类的信息,说明设备已正确枚举。整个 eMMC 烧录的过程,核心是先擦除,再逐个分区写入:
sudo rkdeveloptool db rk3568_loader.bin sudo rkdeveloptool ul update.imgdb命令是下载 bootloader 到 SRAM 中运行,ul命令是升级固件,它会解析update.img里的分区表并自动写入。如果单独刷某个分区,或者在调试中想只更新 boot 镜像,可以用:
sudo rkdeveloptool wl 0x4000 boot_linux.img0x4000是boot_linux.img在 eMMC 上的偏移(单位为扇区,0x4000 = 16384 扇区 = 8MB)。这组偏移必须与你的分区表配置保持一致。
3.3 单分区布局下的参数计算与偏移量讲解
这里补一下偏移计算的逻辑。eMMC 的地址通常按照 512 字节扇区为单位。OpenHarmony 默认的 RK3568 分区布局里uboot分区起始于偏移 0x2000 扇区(也就是 4MB 处),因为它前面需要留出 4MB 给 SPL/Trust 固件。boot分区起始于偏移 0x4000 扇区(8MB 处),一般大小 64MB。system分区起始于0x20000扇区(64MB 处),大小依据磁盘总容量动态调整。
在单分区方案中,我们要做减法:把原本分散的 boot、system 逻辑合并成一个启动分区。操作上,可以把内核镜像和根文件系统都打包进boot_linux.img,或者保持boot分区独立,但 rootfs 直接编译进内核镜像的 initramfs 中。前面我推荐保持原有分区表但用 SD 启动做验证,就是因为在开发阶段多保留一个分区并不会带来额外负担,而真正需要精简时再将 system 分区合并进去。
这里给出一张我整理的常用偏移速查表:
| 镜像 | 偏移地址(扇区) | 偏移大小 | 说明 |
|---|---|---|---|
| idblock/SPL | 64 | 32KB | BootROM 固定读取位置,不可随意更改 |
| uboot.img | 0x2000 | 4MB | U-Boot 主镜像,U-Boot 自己也会根据环境变量重新定位 |
| boot_linux.img | 0x4000 | 8MB | 内核 + dtb + ramdisk |
| system.img | 0x20000 | 64MB | 系统分区起始位置,单分区时 rootfs 挂载于此 |
| userdata | 动态 | 动态 | 用 fdisk 创建,存放可写数据 |
这张表在不同 SDK 版本间可能有差异,你在拿到具体代码包后,一定先看编译生成的parameter.txt或config.cfg,那才是这个版本真正的分区布局。
3.4 设备树选择的实操考量
前文提到了设备树选择问题,这里展开讲。在 OpenHarmony 的 RK3568 内核源码路径kernel/linux/arch/arm64/boot/dts/rockchip/下,你会看到大量 dts 文件。选择的关键点按优先级排序:
- SoC 型号必须一致(RK3568J、RK3568 等,注意 J 代表车规级,部分外设差异明显);
- 内存颗粒类型必须匹配(DDR4/LPDDR4/LPDDR3,决定 DDR 初始化时序);
- 板级外设接口尽量贴近(I2C、SPI、UART、GPIO 扩展、LCD 型号等)。
如果你的板子是第三方的核心板,而厂商只提供了 Android 的 dtb,OpenHarmony 通常也能沿用,因为内核版本大部分是 5.10 或相近分支,只要确认 dts 引用的宏和头文件在 OpenHarmony 内核源码树中存在即可。实操中最快的验证方法是先拿默认 EVB 的 dtb 试启动,U-Boot 阶段能看到日志说明引导链路通;如果卡在内核阶段并反复重启,大概率是内存初始化或串口控制台没有对齐,这时再针对板卡设备树做裁剪。
4. 常见问题与排查技巧实录
4.1 eMMC 偶发报错“-110”的定位与解决
这个报错在刷机和启动过程中都遇到过,表示命令超时(-ETIMEDOUT)。在 eMMC 读写场景下,常见诱因有三种:
- 电压不稳。eMMC 在高速模式(HS200/HS400)下对 VCCQ 电源质量要求较高,劣质电源适配器导致电压跌落时控制器命令超时。
- 时序参数不匹配。板级电路走线较长或者 eMMC 颗粒本身对时序要求严格,U-Boot 里的
mmc驱动需要手动降低速率模式。 - 镜像写入过程中主机断开。USB 供电不稳导致擦写流程中断,eMMC 进入异常状态。
排查步骤我建议按这个顺序来:先换电源适配器(高功率、稳压性能好的);再在 U-Boot 环境变量中强制降速,比如把mmc的speed-mode设为ddr52或者high-speed;最后检查 USB 线缆和接口,尽量直连主机后置 USB 口,不经过 Hub。
注意:不要把
-110当成偶发问题忽略。eMMC 有坏块管理机制,但如果同一个位置反复超时,可能是该存储区域已接近寿命极限,建议用mmc read/mmc write循环测试,确认坏块是否持续扩大。
4.2 U-Boot 无打印信息的排查
很多人刷完 SD 卡后上电,发现串口什么输出都没有,第一反应是“镜像坏了”。但从我的经验看,无打印的根因按出现频率排序是:
- 串口线接错或电平不匹配。RK 平台的调试串口一般是 TTL 电平,如果你用 USB 转串口模块,要确认交叉连接(TX-RX)、共地以及波特率设置(常见 1500000 波特率);
- SD 卡引导写失败了。卡里没有有效的 idblock,BootROM 找不到启动代码;
- 上电时序问题。部分核心板需要先给 IO 域供电再给核心供电,开发底板上电顺序不正确会导致 BootROM 没有完成初始化。
如果 U-Boot 一开始就没打印,先验证串口通路的畅通:用 USB 转串口模块短接 TX 和 RX,看是否能回显;再用示波器或万用表确认板卡是否有 3.3V 的 UART 信号。这个能排除至少一半问题。
4.3 SD 卡启动时挂载不了根文件系统怎么办
在内核启动阶段,日志停在这里:
VFS: Cannot open root device "mmcblk0p1" or unknown-block(179,1)这是典型的 root 设备参数没对上。单分区方案下,根文件系统在 SD 卡的哪个分区、什么文件系统类型,都需要在 U-Boot 环境变量中告知内核。检查这几个参数:
setenv bootargs 'root=/dev/mmcblk0p1 rw rootfstype=ext4 console=ttyFIQ0,1500000' saveenv如果是 MMC 设备的编号不对,需要确认mmcblk0是 SD 卡还是 eMMC。SD 卡在部分内核版本中可能枚举为mmcblk1,这取决于控制器注册顺序。在 U-Boot 里执行mmc list可以查询当前的 MMC 设备列表,用mmc dev 0或mmc dev 1分别切换后再mmc info确认是哪张卡。
4.4 常见问题速查表
为方便对照,这里整理一个我实际刷机中遇到的高频问题汇总:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| USB 无法识别设备 | 未进入 Loader/MaskROM 模式 | 按住 recovery 键上电,用rkdeveloptool ld检测 |
| 烧录到一半提示设备断开 | USB 线材质量差或电源不稳 | 换数据线,避免前置 USB 口,使用稳压电源 |
| 内核启动后反复重启 | 设备树选错,内存参数不匹配 | 换对应板卡的 dtb,检查 DDR 类型 |
| 系统启动后 SD 卡没有挂载 | 分区表未创建或未格式化 | 用 fdisk 创建分区,mkfs.ext4 格式化 |
| eMMC 写入速度极慢 | 降速模式过于保守 | 换 HS200/HS400 模式而非 DDR52 |
| 刷机后无法从 eMMC 启动 | 引导顺序未切换 | 确认 U-Boot 环境变量 boot_device 或启动拨码开关 |
4.5 几个不常被提及但非常实用的避坑技巧
第一,SD 卡格式化一定要用 SD Memory Card Formatter,而不要图省事用mkfs直接覆盖。因为部分 U-Boot 版本会在 SD 卡末尾区域保存环境变量(冗余 env 分区),如果你用操作系统工具重新分区,这块区域可能没被清除,下一次启动时旧环境变量会干扰启动参数。
第二,rkdeveloptool擦除 eMMC 前务必再三确认设备。因为擦除命令是按设备号操作的,如果你主机上同时插了多个 RK 设备,rkdeveloptool ld会列出多个,而默认操作目标是第一个。谨慎的做法是拔掉其他设备,只保留目标板。
第三,烧录 eMMC 之前强烈建议先备份原始固件。特别是核心板自带的 Android 镜像,如果后续要回退,没有原始备份会非常被动。在 Loader 模式下:
sudo rkdeveloptool rl 0 0x100000 backup.img可以把 eMMC 前 512MB 内容整个导出。这样即使分区表被破坏,也有原始数据可以恢复。
第四,OpenHarmony 镜像名的后缀与版本强相关。某些版本system.img实际上是稀疏镜像(sparse image),烧录时工具会做转换;而某些工具和驱动版本对稀疏镜像支持不佳,导致写入后分区大小异常。遇到这类问题时,可以先解包稀疏镜像再烧录:
simg2img system.img system_raw.img然后用system_raw.img执行烧写,往往会顺利很多。
5. 串口日志解读与启动链路验证
5.1 一份正常启动日志的关键节点
刷机完成后,怎么判断系统是否正常启动?看串口日志是最直接的方式。一份正常的 OpenHarmony 启动日志中,有几个关键节点值得关注:
- 第一阶段:BootROM 打印,通常带有
DDR初始化相关的微码信息,表明 eMMC/SD 控制器已经枚举成功; - 第二阶段:U-Boot 的版本横幅,包含日期和编译信息,说明 idblock 和 U-Boot 主镜像已正确加载;
- 第三阶段:内核解压以及设备树加载,能看到
Booting Linux on physical CPU 0x...和Kernel command line:,这里的 bootargs 要和你的分区方案匹配; - 第四阶段:系统服务启动,OpenHarmony 特有的服务管理进程开始工作,最终出现
OHOS相关字样或产品 logo。
如果在第三阶段后长时间没有输出,大概率是设备树与硬件不匹配,或者根文件系统没挂上。这时不要盲目重新编译,先用printenv查看 U-Boot 环境变量里的 bootargs,确认 root 指向是否正确。
5.2 用串口信息反向确认分区和镜像
串口不仅能看状态,还能辅助定位镜像烧写的位置。在 U-Boot 下执行:
mmc part如果 eMMC 是 GPT 分区表,这里会列出分区名和起止扇区。将输出和parameter.txt对比,可以确认你烧写时用的偏移是否正确。在实际项目中,我遇到过烧写时用了 A 版本分区表、而实际镜像包是 B 版本的情况,这种错位问题单靠启动日志很难一眼看出,但通过mmc part就能提前发现。
6. 刷机后的验证与后续扩展思路
6.1 从单分区过渡到标准分区布局
如果刷机后系统运行稳定,你可以开始考虑从单分区方案过渡到标准多分区布局。具体的做法是在parameter.txt中定义好分区表,然后重新生成对应的镜像并整体烧录。分区数量变多之后,单独更新某个模块(比如只更新vendor分区验证驱动)会变得非常方便。
提示:修改分区表之后,
bootargs中的 root 参数需要同步修改。多分区布局下system分区通常是只读的,而userdata分区是唯一可写区域,应用数据和配置会集中放到这里,这样 OTA 升级时可以只更新系统分区而保留用户数据。
6.2 与编译流程的衔接建议
刷机不是孤立的动作,它和编译流程强相关。我的建议是建立“编译-打包-刷机-验证”的闭环流程:编译完成后,先检查关键镜像的时间戳和大小;然后制作 SD 卡快速验证基础启动;确认无误后再烧录 eMMC。这样可以避免每次代码改动都经历一次完整的 eMMC 烧录,毕竟 eMMC 的擦写次数是有限资源,频繁整盘烧写不是好习惯。
另外,如果你经常需要刷机,可以考虑写一个简单的脚本,把上面的rkdeveloptool命令串起来,并且加一个-l日志输出选项方便留痕。脚本里强制执行擦除前确认,防止误操作。
7. 写在最后的实操心得
这套 SD-eMMC-单分区刷机流程,我前前后后在多种 RK 板卡上跑过,最大的体会是:刷机这件事,七分在准备,三分在操作。准备包括固件校验、分区表确认、硬件连接检查;操作其实就那么几条命令。很多刷机失败的朋友,回头复盘时往往发现是在某个“以为没问题”的环节翻了车,比如用错设备树、烧错偏移、卡没格式化干净。
个人建议是,第一次刷机务必全程开着串口日志,每一步都和预期行为对照。出现异常不要慌,把日志保存下来,回去对照分区表和设备树定义一条一条排查。熟悉了这套流程之后,日后不管是做内核调试、驱动移植,还是系统裁剪,你都会觉得整个 OpenHarmony 开发过程踏实很多。