ARTICLE DETAIL

资讯详情

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

i.MX8M Plus嵌入式Linux开发:U-Boot编译与系统烧录全流程

i.MX8M Plus嵌入式Linux开发:U-Boot编译与系统烧录全流程 1. 项目概述与环境准备1.1 这块板子能做什么为什么选它i.MX8M Plus是NXP在嵌入式边缘计算领域的一块招牌芯片它最大的特点是把四核Cortex-A53、一个Cortex-M7实时核、2.3 TOPS算力的NPU、以及ISP图像处理单元全部塞进了一颗SoC里。这意味着什么呢如果你接触过传统ARM开发板过去需要外挂DSP、外挂GPU、外挂AI加速模块的活儿现在一块芯片就全包了。我最早拿到这块板子的时候第一个感受就是它的定位非常清晰跑Linux系统做业务逻辑用Cortex-M7核处理实时性要求高的任务再配合NPU做视觉推理一套完整的机器视觉终端方案就搭起来了。这次的项目内容是完整的“从零到一”流程目标是把一块空白的i.MX8M Plus开发板变成一台能跑Ubuntu系统、能启动到用户登录界面、并且后续可以继续开发应用的开发平台。整个过程中最核心的三个环节就是源码编译、烧录和镜像运行。很多人拿着一块开发板第一反应是去网上下现成的镜像直接刷这当然可以但如果你需要定制内核、修改设备树、裁剪文件系统、或者把驱动程序编进内核里手头有一套完整的编译环境就非常重要了。这套流程适合谁适合刚入手i.MX8M Plus开发板、对嵌入式Linux有一定基础但还没完整走过一遍编译烧录全流程的人。如果你已经玩过i.MX6ULL或者树莓派那上手会更顺滑如果你是完全的新手也没关系文中的每一步我都会把原理说清楚照着操作一样能跑通。1.2 开发环境搭建与工具链选型先说我自己的开发环境这决定了后面所有命令行操作的基础。我用的是一台x86_64架构的Ubuntu 20.04主机内存32GB硬盘预留了至少200GB空闲空间。编译i.MX8M Plus的完整镜像对内存和磁盘都有要求内存低于16GB的话编译过程中可能会因为并行任务太多直接把内存吃满硬盘空间就更关键了Yocto构建一次要下载大量软件包整个build目录轻松超过100GB所以磁盘空间千万不能省。这里有一个非常重要的选型问题到底用Yocto还是用NXP官方的MCUXpresso SDK我的答案是看需求。如果你只想快速跑起来一个系统Yocto的imx-image-core或imx-image-multimedia是官方推荐路径它会把U-Boot、内核、文件系统一次性构建出来生成可直接烧录的镜像文件。但Yocto的问题在于编译时间极长首次构建四五个小时是常态而且中间下载依赖包还会受网络影响。如果你只是改一改内核配置、换一个设备树我推荐走“分步编译”路线单独编U-Boot、单独编内核、单独做文件系统这样可以精确控制每一个环节调试定位也更方便。我的习惯是主机上先装好基础工具一条命令搞定sudo apt-get install -y git build-essential flex bison \ libssl-dev libncurses-dev u-boot-tools device-tree-compiler \ lz4 lzop zstd bc cpio rsync python3 python3-pip这些工具里flex和bison是编译U-Boot和内核时必须的语法解析器libssl-dev解决内核签名和模块编译的依赖device-tree-compilerdtc则用于编译设备树源文件dts为dtb文件。缺一个都不行我见过有人卡在编译中段报错最后发现是缺了某个看似不起眼的包。2. 源码获取与U-Boot/内核编译全流程2.1 源码结构梳理BSP包里到底有什么NXP官方为i.MX8M Plus提供了完整的BSPBoard Support Package它的源码管理方式是基于repo工具的这也是谷歌Android项目常用的方式。全套BSP包含三个最关键的部分U-Boot引导加载程序、Linux内核、以及Yocto构建系统。U-Boot负责初始化DDR、加载内核镜像到内存、并最终把控制权交给内核内核负责管理硬件资源并提供系统调用给用户空间Yocto则负责把这一切打包成可烧录的镜像文件。如果你下载的是官方BSP发布包解压后会看到一堆以imx-开头的目录。其中imx-mkimage目录很关键NXP提供的镜像打包脚本就放在这里它会根据你选择的板型和启动方式SD卡、eMMC、NOR Flash生成对应的烧录镜像。firmware-imx目录里存放的是DDR固件、HDMI固件、以及NPU固件这些是闭源二进制文件NXP以预编译的方式提供U-Boot在初始化DDR的时候需要装载对应的二进制固件。这里插一句我踩过的坑不要随意修改firmware-imx里的DDR固件版本。i.MX8M Plus对DDR初始化时序非常敏感不同容量的LPDDR4颗粒需要匹配特定的固件参数如果你的板子DDR容量和官方EVK板不一致一定要先确认固件支持否则轻则无法启动重则DDR稳定性出现问题系统跑着跑着就死机。2.2 U-Boot编译实操与参数选择先设置交叉编译环境NXP官方BSP默认使用gcc-arm-9.2-2019.12版本的工具链。我习惯把工具链放在/opt目录下然后导出环境变量export CROSS_COMPILE/opt/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu- export ARCHarm64进入U-Boot源码目录后官方EVK板子对应的配置文件是imx8mp_evk_defconfig如果你的板子是第三方核心板厂商一般会提供自己的defconfig直接使用即可。编译命令非常简单make distclean make imx8mp_evk_defconfig make -j$(nproc)编译完成后会得到u-boot.bin但这还不是最终可以直接烧录的镜像。i.MX8M Plus的启动流程要求U-Boot必须经过mkimage工具封装并在头部加上boot_loader的容器格式。NXP在imx-mkimage目录下提供了脚本执行make SOCiMX8MP flash_evk会自动生成flash.bin这就是最终要烧录到SD卡或eMMC的引导镜像。整个过程大约需要两三分钟如果编译报错90%以上是环境问题比如缺少某些依赖库或者工具链版本不匹配。2.3 内核编译与设备树修改要点内核编译是另一个重头戏。我使用NXP官方BSP内置的Linux内核源码版本基于5.15。内核编译前有个细节如果从Yocto环境中拷贝过内核源码记得先清理环境变量否则可能会连接到主机上不存在的交叉编译器。make imx_v8_defconfig make -j$(nproc) Image dtbs这里imx_v8_defconfig是i.MX8M系列通用配置覆盖了大部分默认外设驱动。如果你需要做个性化配置运行make menuconfig在弹出的图形界面里修改。设备树文件在arch/arm64/boot/dts/freescale/目录下EVK板对应的文件是imx8mp-evk.dts。如果使用第三方核心板需要参考厂商提供的设备树修改接口定义。我之前调试一块带MIPI-CSI摄像头的板子就在设备树里添加了摄像头节点信息修改完设备树后手动编译dtb文件即可无需重新编译整个内核这个技巧在调试外设时非常实用make dtbs生成的dtb文件会输出到arch/arm64/boot/dts/freescale/目录下之后搭配镜像打包时替换掉原来的dtb即可。3. 烧录流程与镜像运行详解3.1 系统镜像组成分析U-Boot、内核、文件系统各自的位置在进入烧录环节前我先把镜像组成拆开讲清楚因为很多人在烧录阶段出问题根源是没搞懂这几个部分的工作分工。一套完整的嵌入式Linux系统包含三部分引导程序U-Boot、内核镜像Image dtb、根文件系统rootfs。分区布局一般如下分区/位置内容作用偏移0x0SD卡/VTOCflash.bin含U-Boot与DDR固件上电后ROM从启动介质加载并执行FAT分区Image、dtb、uEnv.txt内核镜像与设备树存放处EXT4分区rootfsLinux根文件系统Ubuntu/Debian等用户空间这种布局的好处是引导程序、内核、文件系统相对独立更新任意一个部分不影响其他部分非常适合开发调试阶段。比如我只改了设备树只需要把新的dtb拷贝到FAT分区不需要重新烧整个根文件系统。3.2 SD卡烧录步骤从格式化到启动分区SD卡烧录是最常用的方式也是开发阶段的首选因为可以随时拔下来在电脑上修改内容。我用的工具是dd和gparted。先把SD卡插入读卡器确认设备号sudo fdisk -l假设识别到的是/dev/sdb注意这里一定要看清不要搞错设备号否则会把主机硬盘内容给抹掉。然后使用gparted或者命令行工具进行分区布局。我习惯用fdisk手动分区sudo fdisk /dev/sdb # 输入 o 创建新的分区表 # 输入 n 创建第一个分区起始扇区假设为32768大小为512MB类型为 cFAT32 # 输入 n 创建第二个分区占满剩余空间类型为 83Linux # 输入 w 保存分区表分区完成后格式化sudo mkfs.vfat -F 32 /dev/sdb1 sudo mkfs.ext4 /dev/sdb2接着烧录U-Boot引导镜像到SD卡起始位置。这里注意flash.bin是烧在SD卡的前若干个扇区不是某个分区里sudo dd ifflash.bin of/dev/sdb bs1k seek1 convfsyncseek1的意思是跳过第一个1KB区域这是因为i.MX8M Plus的ROM启动代码从SD卡的偏移1KB处开始寻找有效的启动头。下一步把内核镜像和设备树拷贝到FAT分区sudo mount /dev/sdb1 /mnt/sd sudo cp Image /mnt/sd/ sudo cp imx8mp-evk.dtb /mnt/sd/ sudo umount /mnt/sd最后把根文件系统解压到EXT4分区。如果你用的是NXP官方Yocto生成的imx-image-multimedia-imx8mpevk.rootfs.tar.bz2解压时一定要带-p参数保留文件权限sudo mount /dev/sdb2 /mnt/rootfs sudo tar -jxvf imx-image-multimedia-imx8mpevk.rootfs.tar.bz2 -C /mnt/rootfs sudo umount /mnt/rootfs3.3 eMMC烧录从SD卡启动后自烧录第一次接触这块板子的人可能有个误区认为eMMC烧录一定要用专用的烧录器。实际上i.MX8M Plus的常见做法是先用SD卡启动一个最小系统然后在目标板上运行命令把镜像烧录进eMMC。流程如下首先按上一节的方法准备一张SD卡作为启动盘确认系统能正常启动进入命令行后检查eMMC设备名ls /dev/mmcblk*正常情况下会看到/dev/mmcblk0eMMC和/dev/mmcblk1SD卡两个设备具体哪个对应哪个可以通过cat /proc/cmdline查看root参数来确认。确认无误后先烧U-Boot到eMMCsudo dd ifflash.bin of/dev/mmcblk0 bs1k seek1 convfsync然后重新分区eMMC步骤和SD卡分区类似只是设备号换成了/dev/mmcblk0。最后把内核、设备树、根文件系统全部拷贝进去。这个过程看起来简单实际上有一个很容易忽略的点eMMC的写寿命虽然比SD卡强很多但在开发阶段频繁烧写仍然有一定风险建议把开发中常用的系统和最终稳定的系统分开存放调试用SD卡出正式版本才烧eMMC。3.4 烧录完成后的首次启动与串口调试烧录完成后把SD卡插回开发板或者板子已经带eMMC系统连接电源、串口线和网线。串口调试是嵌入式开发最重要的观察窗口我使用的是USB转TTL模块波特率设置为115200。连接时注意三个引脚TXD接开发板的RXD、RXD接开发板的TXD、GND接GND。上电后串口终端如果输出类似以下内容说明U-Boot已经正常启动U-Boot SPL 2022.04-imx_v2022.04_5.15.71-2.2.0ga5a5b5f3a3SPL是U-Boot的二级引导阶段它负责初始化DDR和加载完整的U-Boot到内存。接着能看到U-Boot主版本号、DRAM容量检测信息然后进入倒计时。在倒计时结束前按任意键可以进入U-Boot命令行这对调试非常有用。如果不干预U-Boot会默认从FAT分区的uEnv.txt文件读取启动参数把内核镜像加载到内存指定地址然后启动内核。看到内核启动日志后耐心等待根文件系统挂载。如果一切正常最后会看到登录提示符。这里我强烈建议在首次启动时把完整的串口日志保存下来后续排查问题会方便很多。4. 镜像运行调试与问题排查心得4.1 U-Boot阶段常见启动失败原因启动失败是最常见的问题而且不同阶段的失败有不同的表现。如果上电后串口完全没有任何输出可以先量测开发板的电源指示灯和核心板电路的温升没有灯亮或者芯片完全不热基本可以判断是电源问题。如果SPL加载到一半卡住很大概率是DDR配置不匹配回头检查firmware-imx的DDR固件选择。如果U-Boot能启动但加载内核失败重点检查FAT分区里的Image和dtb文件是否存在、命名是否匹配uEnv.txt中的设定。我印象最深的一次调试经历是U-Boot启动后一直提示Card did not respond to voltage select排查了很久发现是SD卡座接触不良。换了SD卡后问题依旧最后把卡座焊下来一看有个引脚虚焊。所以遇到启动异常不妨先排除硬件层面的接触问题再回看软件配置。4.2 内核启动阶段常见问题与日志分析方法内核启动阶段的问题通常更明显因为串口会输出大量日志。如果内核启动在某个驱动初始化处卡住先看最后几行日志定位是哪个驱动出问题。常见的一种情况是挂载根文件系统失败报错信息类似VFS: Unable to mount root fs这种情况要检查两个地方一是内核是否编译了对应的文件系统驱动比如EXT4驱动是否编入了内核二是启动参数里的root设备路径是否正确比如root/dev/mmcblk1p2还是root/dev/mmcblk0p2SD卡和eMMC的设备名在不同配置下会有区别。另一个高频问题是MIPI-DSI屏幕或者HDMI屏幕没有画面。遇到这种情况优先看内核日志中Display相关的初始化信息确认imx8mp-drm驱动是否正常加载。我曾经调试过一块第三方MIPI屏幕发现驱动日志显示failed to find panel检查设备树后发现panel节点的compatible兼容字符串和驱动不匹配修改设备树后屏幕正常点亮。这类问题几乎都是设备树配置不对导致的。我把常见问题和排查方法整理成了表格方便对照异常现象可能原因排查方法串口无任何输出电源故障、串口接线错误、flash.bin未烧录检查电源指示灯、确认TXD/RXD接法、重新烧录SPL阶段卡死DDR固件不匹配、DDR配置错误核对firmware版本、检查DDR容量配置U-Boot提示找不到启动设备SD卡分区错误、FAT分区文件缺失重新分区、检查Image与dtb是否存在内核启动后无法挂载rootfs启动参数错误、内核缺文件系统驱动检查uEnv.txt的root参数、确认驱动编译屏幕无显示设备树panel节点错误、HDMI固件缺失查看drm日志、检查firmware-imx是否包含HDMI固件网络不通设备树MAC节点配置错误、PHY驱动缺失查看phy芯片型号、对照设备树mdio节点4.3 文件系统损坏与扩容操作开发过程中还有一种常见场景文件系统跑了一段时间后无法正常启动或者在启动后磁盘空间不足。第一种情况通常是异常断电导致文件系统不一致解决方法是重新格式化eMMC或SD卡的rootfs分区重新解压根文件系统。第二种情况更简单SD卡容量如果大于分区容量使用resize2fs扩大分区即可。sudo resize2fs /dev/mmcblk1p2如果是通过SD卡启动、但板载eMMC已经烧好的系统还可以利用dd命令把SD卡整个备份成镜像方便后续批量生产或者备份恢复sudo dd if/dev/mmcblk1 of~/backup.img bs4M statusprogress4.4 我的几点实操心得整套流程走下来我最想强调的几点心得体会如下。第一编译环境一次性搭建到位非常重要。交叉编译环境、依赖工具、网络代理如果有、磁盘空间这些前期准备工作如果没做好编译中途报错再回头补非常浪费时间。我现在会在新机器上先用一个脚本把U-Boot完整编译一遍确认环境无误后再进入正式开发。第二设备树修改是嵌入式Linux开发的灵魂。i.MX8M Plus的几乎所有外设功能都需要在设备树里描述学会看设备树、改设备树比纯调驱动更频繁也更实用。建议新入手的朋友先把EVK板的设备树完整读一遍对照芯片手册理解每个节点的含义。第三串口日志是排查问题的第一现场。无论遇到什么问题养成先看完整串口日志的习惯。我个人的做法是用SecureCRT记录完整日志同时开启时间戳方便回溯问题发生的精确时间点。第四保持模块化思维。U-Boot、内核、设备树、根文件系统四个部分相对独立修改任何一个部分后只需要单独编译和更新对应模块不要每次都重新做整盘烧录。只更新内核时把新的Image拷贝到FAT分区即可如果配合tftp网络加载还会更加方便。5. 后续功能扩展方向这套开发流程跑通之后i.MX8M Plus的能力才真正开始发挥。NPU单元支持TensorFlow Lite、ONNX Runtime等主流推理框架可以把图像分类、目标检测模型部署到板端运行。我目前正在做的一个项目就是把YOLOv5s模型转换成ONNX格式再通过NPU的推理引擎在i.MX8M Plus上实现实时检测整条流程已经跑通后续有时间再写一篇专门的NPU部署实战文章。如果你是做工业视觉方向的i.MX8M Plus自带的ISP和MIPI-CSI接口非常适合接工业相机或USB摄像头做图像采集和预处理如果做边缘计算网关双千兆以太网接口和丰富的IO扩展能力也很适合。这块板子的应用边界其实很宽从HMI人机交互界面到智能门禁、再到工业控制都有它发挥的余地。我个人在这几个月开发和调试过程中最大的体会是嵌入式开发的成败往往不是取决于某一个高深的技术点而是取决于你对工具链、启动流程、设备树这些基础环节的熟悉程度。把“从零到一”的整个链路走通一遍后面不管是业务开发还是性能调优都站在了一个更踏实的基础上。这篇文章里提到的每一步都是我实际操作过的如果你在照做的过程中遇到了别的坑也欢迎一起交流我在后续的文章里也会继续补充更深入的内容。
返回列表