U-boot移植
为什么需要移植?
需要移植 U-Boot,是因为原版 U-Boot 不能直接适配每一块具体的硬件板子。
U-Boot 本质上只是一个通用的启动框架。它已经提供了很多公共代码,比如启动流程、命令行、环境变量、镜像加载、内存初始化框架、设备模型框架等。但是,不同开发板的硬件条件并不一样。比如,CPU 型号可能不同,DDR 类型和时序不同,Flash 介质可能是 NAND、NOR、eMMC 或 SD 卡,串口编号和 GPIO 连接也可能不同。原版 U-Boot 不可能默认知道你这块板子的这些信息。所以,开发者必须把 U-Boot 改到“认识这块板子”,这个过程就是移植。
移植第一步:验证官方的NXP官方的u-boot
下载uboot:
首先是从NXP官方下载ubooy版本,uboot版本并不是越新月好,我自己的学习板式imx6ull韦东山的,学习资料中的uboot式2017的

找相近的板子的配置文件:
解压uboot,并进入configs文件夹,到和自己板子相接近的板级配置。
比如你要配置启动解值式emmc的6ull的板子,就是选中mx6ull_14x14_evk_emmc_deconfig这个配置文件。

设置编译工具链,准备对uboot进行编译:
注意这里的使用的默认配置就是前面选好的配置mx6ull_14x14_evk_emmc_deconfig。
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_evk_emmc_defconfig
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
是在 彻底清理 U-Boot 工程。ARCH=arm 表示目标架构是 ARM。CROSS_COMPILE=arm-linux-gnueabihf- 表示后面要调用的交叉编译工具链前缀是这个,比如会去找 arm-linux-gnueabihf-gcc 这类工具。最后面的 distclean 表示把以前编译留下来的中间文件、旧配置等都清掉。这样做的目的很简单,就是避免你上一次编译残留的东西影响这一次。书里也明确说了,这一步就是“每次编译之前都清理一下工程”。
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_evk_emmc_defconfig
这一句是在 给 U-Boot 载入默认配置,还没有真正开始大规模编译。mx6ull_14x14_evk_emmc_defconfig 是一份默认配置文件,放在 U-Boot 的 configs 目录里。它对应的是 NXP 官方的 I.MX6ULL 14x14 EVK 开发板的 EMMC 版本。书里说得很清楚,make xxx_defconfig 就是配置 U-Boot,xxx_defconfig 就是不同板子的默认配置文件。这里选择它,是因为本章讲的是 EMMC 版本移植,所以先用这个官方板配置做基线。
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12
这一句才是 正式开始编译 U-Boot。前一行已经把配置准备好了,这一行就根据那个配置去编整个工程。V=1 的意思是把编译过程输出得更详细,方便你看每一步到底调用了什么命令。-j12 表示开 12 个并行任务一起编译,也就是让 CPU 多核同时工作,提升编译速度。书里也说明了,-j 后面的数字最好和你的虚拟机或电脑分配的核心数比较匹配。
编译结果:
得到uboot.imx文件和uboot.bin文件

uboot运行测试:
uboot运行必须要的内容: DDR或则说DRAM,EMMC,串口,NAND ,板子正常,上电后芯片可以识别SD卡。 同时,这也决定了,半导体厂商提供的板子,这些内容都是跟NXP官方的一样的,以保证uboot可以启动。
把uboot拷贝到SD卡中,开发板设置为SD卡启动,然后SD卡插回开发板,然后复位开发板,之后就会进入 U-Boot 的启动过程。
uboot成功启动后结果如下,可以发现lcd,网卡等无法启动成功,i2c等可以

提问:
为什么要找相近的开发板配置,找的时候要注意什么?
找相近的配置,有点是大部分内容都可以直接使用,只需要修改小部分内容即可
找的时候注意,启动介质,ddr大小要注意一样,不然可能无法再自己的板子上成功启动
所以,不要把为一种硬件组合编出来的 U-Boot,直接烧到另一种硬件组合的板子上跑。这里至少有两层不匹配。第一层是 DDR 容量不一样,U-Boot 早期要先初始化 DDR。它如果按 512MB 板子的参数去初始化一块 256MB 板子,轻则内存识别异常,重则直接起不来。
第二层是 启动存储介质也不一样,一个版本是 EMMC 板,一个版本是 NAND 板,后面的启动设备、环境变量存放方式、读写驱动路径都可能不同,所以也不能混用。
(实操问题)SD卡拷贝新的uboot需要注意什么?
需要把原来的SD卡格式化,直接使用窗口的格式化命令是不行的,需要使用SDformat这种专业软件进行格式化,以消除原来的uboot的环境变量
移植第二步: 在uboot中添加自己的开发板
这一个部分主要是,注册新板子、建立新板级框架,并不是代表所有硬件已经适配完
1.添加板子的默认配置文件
通过复制添加

然后可以写一个新的脚本,进行环境配置,注意给予可执行权限
对自己的配置文件进行修改

第一行修改内容:
这一行里最关键的是 IMX_CONFIG=.../imximage.cfg。书里原文已经点出来了,新建的 mx6ull_alientek_emmc_defconfig 和原来的官方配置基本一样,只改了第 1 行和第 4 行。第 1 行把 IMX_CONFIG 指向了你自己新建的板级目录 board/freescale/mx6ull_alientek_emmc/ 下面的 imximage.cfg。这一步的意思是:以后生成启动镜像时,U-Boot 不再使用官方 EVK 板的镜像配置文件,而是使用你自己板子的镜像配置文件。也就是说,这一改是为了把“镜像构建入口”从官方板切到你自己的板子。
imximage.cfg是什么?
imximage中包含了DDR初始化相关的代码,并且我们编译后的uboot.imx其实就是uboot.bin + imximage.cfg 组成的。
第4行修改内容:
这一行的作用更直接。它是在告诉 U-Boot:当前被选中的目标板已经不是官方的 mx6ull_14x14_evk_emmc,而是我新加的 mx6ull_alientek_emmc。这说明第 4 行其实是“选中你的新目标板”的开关。只有把这个目标宏打开,后面 Kconfig、头文件、板级目录这些内容才会沿着你新板子的名字继续往下匹配。
2.添加板子对应的头文件
为什么都不都在deconfig里面配置,而是在.h头文件中配置?
因为宏相关的定义需要在.h中配置
复制对应的头文件,作为我们自己的头文件。

先修改以下内容:

3.添加开发板对应的板级文件夹
把官方板子的板级目录整套复制一份,改成你自己板子的名字,然后把这个目录里的关键文件都改到指向自己的板子。 这一节不是只改一个文件,而是先改“目录身份”,再改“目录里的构建入口和配置入口”。
首先,直接复制对应的文件夹作为我们的文件夹就行

第二步是把目录里的主板级源文件改名。书里要求把 mx6ullevk.c 改成 mx6ull_alientek_emmc.c。这不是表面改名,而是在做“板级主文件切换”。因为这个 .c 文件通常就是这块板子的核心板级初始化文件,后面 Makefile 也会围绕这个新名字继续调整


第三步是改这个目录下的 Makefile。书里给出的关键修改是把第 6 行改成:
obj-y := mx6ull_alientek_emmc.o书里还专门说明,重点就是这行 obj-y,改成这个名字以后,编译系统才会去编译 mx6ull_alientek_emmc.c 这个文件。也就是说,前面你只是把 .c 文件改了名字,如果 Makefile 不跟着改,编译系统还是不会正确编译你自己的板级源文件。

第四步是改 imximage.cfg。书里把里面原来的:
PLUGIN board/freescale/mx6ullevk/plugin.bin 0x00907000
改成:
PLUGIN board/freescale/mx6ull_alientek_emmc /plugin.bin 0x00907000
如下图所展示


这一改的核心意思是:启动镜像生成时,如果要用到 plugin.bin,就不要再去官方 mx6ullevk 目录下找,而是去你自己新建的 mx6ull_alientek_emmc 目录下找。也就是说,这一步是在把镜像构建链路从“官方板目录”切到“自己板目录”。
第五步是改这个目录下的 Kconfig。书里修改后的内容是:在 if TARGET_MX6ULL_ALIENTEK_EMMC 条件下,把 SYS_BOARD 设成 mx6ull_alientek_emmc,把 SYS_VENDOR 设成 freescale,把 SYS_SOC 设成 mx6,再把 SYS_CONFIG_NAME 也设成 mx6ull_alientek_emmc。这里最关键的是 SYS_BOARD 和 SYS_CONFIG_NAME 这两个值。前者决定板目录名,后者决定配置头文件名。这样改完以后,U-Boot 的配置系统才能从名字上把“当前目标板”真正和你的新目录、新头文件对应起来。

第六步是改 MAINTAINERS 文件。书里改成了:
MX6ULL_ALIENTEK_EMMC BOARDF: board/freescale/mx6ull_alientek_emmc/F: include/configs/mx6ull_alientek_emmc.h
这一步你可以理解成“登记注册”。它不是决定能不能启动的核心逻辑,但它把这块新板子的板级目录和头文件路径正式登记到了工程条目里。这样从工程组织上看,你这块板子已经是一块独立板子了。
4.对Kconfig进行修改
这一步很重要,如果不进行Kconfig修改的话,所有的前面的修改,都不会其效果

5.验证是否成功
要剪裁默认配置头文件,以及板级配置文件是否都成功了。
首先检查,编译后有没有,XXX自己的板子XXX.o文件
再检查头文件是否被引用


提问:
这个时候就修改好了吗?
并没有,这个修改过后,其效果和原来的uboot是一摸一样的,不过,我们已经再uboot中添加了自己的板子,后面只需要进行对应的修改就可以了
移植第三步:ubootb内置驱动修改
U-Boot 里改驱动,通常重点就在板级的 .h 和 .c 文件里。
LCD驱动修改:
1.修改
改 LCD 时主要看三类内容:LCD 用到的 GPIO、背光 GPIO、LCD 时序参数。
然后又因为正点原子的 I.MX6U-ALPHA 板在 LCD IO 和背光 IO 上和 NXP 官方板是一致的,所以 IO 部分不用改,真正要改的是 LCD 参数。具体修改不做演示
2.修改环境变量

3. 环境变量修改失败
在 U-Boot 命令行里先用 print 看 panel 的值。如果不是我们设置的值,就执行setenv panel TFT7016 再执行saveenv 这两个命令,就OK了。
提问:
LCD不亮一定是引脚问题吗?
不一定,可能是参数问题,就算uboot过来用之后,引脚一样,但是参数不一样也是用不了的。
也就是说,这一节先帮你排除了一个常见误区。很多人一看到 LCD 不亮,就以为一定要去改引脚复用、GPIO 输出或者背光控制。这里不是这样。因为硬件连法和官方板一致,所以 U-Boot 里原来的 LCD IO 配置和背光配置还能继续用。问题出在默认屏幕型号不对。官方默认参数是给 TFT43AB 这种 480×272 的屏幕准备的,而你现在要适配的是正点原子的 7 寸 1024×600 屏。参数一旦不匹配,LCD 就可能黑屏、花屏或者根本不显示。
环境变量为什么会修改失败?
明明已经把 .h 里的 panel 改成 TFT7016 了,为什么上电还是黑屏?原因在于,U-Boot 启动后会优先从 EMMC 里读取之前保存的环境变量。只有 EMMC 里没有环境变量时,才会退回去使用 mx6ull_alientek_emmc.h 里的默认环境变量。
网卡驱动修改:
网络方案:
大部分的板子的网络方案是 内部MAC + 外部PHY
MAC 理解成“网络数据的组织者和管理者”。它负责收发以太网帧,和内存打交道,和协议栈的下层对接。但是它不能直接接网线。真正和网线、电平、链路速度、双工模式这些物理层事情打交道的,是外面的那颗 PHY 芯片。
PHY驱动:
因为所有PHY芯片的前32个寄存器都是一样的,所以理论上uboot的中已经有了PHY的通用驱动

1.网络 PHY 地址修改
在对应的宏下面修改地址,地址从原理图中找出来,就算只有一个ENET也要进行修改


引脚添加完成后,还需要进行复位,需要在对应的函数中进行修改,修改后结果如下。
imx_iomux_v3_setup_multiple_pads(),函数的任务是“把这些脚复用成 MDC、MDIO、TX、RX 这些网络信号脚”。
gpio_direction_output(ENET1_RESET, 1) 和 gpio_direction_output(ENET2_RESET, 1) 的作用。它先把复位脚设置成输出模式
imx_iomux_v3_setup_multiple_pads() 解决的是“线接通了没有”,也就是把 CPU 侧的这些管脚切换到正确功能上。gpio_direction_output + gpio_set_value + mdelay 解决的是“芯片醒了没有”,也就是把 PHY 真正复位并拉起来。前者只是把通路准备好,后者才是让外部 PHY 进入可识别、可工作的状态。两者不是一回事,所以都要做。

4.修改 drivers/net/phy/phy.c 文件中的函数 genphy_update_link
前面你已经改了 PHY 地址,也改了复位 GPIO,还在 setup_iomux_fec() 里对 LAN8720A 做了一次硬件复位。接下来需要修改genphy_update_link() 函数
genphy_update_link() 不是某一块板子的私有函数,而是 PHY 层里一个比较通用的函数。也就是说,U-Boot 在判断“网线有没有插上、链路是不是 up、当前速度是多少”这些事情时,会走到这个函数。修改如下,主要思路就是,因为我们换了PHY芯片,所以PHY驱动有一点点不一样,我们直接加一个IF判断,如果用的是我们使用的新的PHY芯片,就使用我们自己的驱动,不用原来的驱动。这样简单添加一下就可以了
流程总结:

移植第四步:bootcmd和bootargs的修改
U-Boot 移植完以后,我们希望uboot自己把linux系统拉起来,需要修改bootcmd,和bootargs
bootcmd 决定“怎么启动 Linux”,bootargs 决定“Linux 启动以后按什么参数运行”。并且这个就是uboot的两个重要的环境变量。
什么是bootcmd?
bootcmd 就是 自动启动命令。U-Boot 启动后会倒计时,如果在倒计时期间你没有按键打断,那么倒计时结束后就会自动执行 bootcmd。如果你按下任意键,就会停在 U-Boot 命令行,不再自动启动 Linux。
bootcmd一般包含什么?
一般要完成以下这些事情,选择启动设备,从启动设备把 Linux 内核读到内存,把设备树文件读到内存,调用 bootz 启动 Linux。
emmc启动命令
mmc dev 1; //选择设备1
fatload mmc 1:1 80800000 zImage; //从EMMC的FAT 分区把zImage读到内存地址 0x80800000
fatload mmc 1:1 83000000 imx6ull-alientek-emmc.dtb; //把设备树读到 0x83000000
bootz 80800000 - 83000000; //用bootz启动zImage,-表示没又initrd,最后一个地址是dtb地址。
bootcmd总结
bootcmd 负责的是 U-Boot 阶段的动作。也就是“从哪读文件、把文件放到哪、最后用什么命令启动内核”。它管的是 Linux 启动之前 的事。
bootargs 是什么?
bootargs 是 传递给 Linux kernel 的启动参数。也可以理解成 Linux 的命令行参数。U-Boot 把内核启动起来时,会顺便把这串参数交给 Linux,Linux 再根据这些参数决定控制台、根文件系统位置、挂载方式等。
常见的参数形式是如下所示:
console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw
1.console=ttymxc0,115200 指定 Linux 控制台串口为 ttymxc0,波特率 115200。
也就是你看到内核打印信息的那个串口。
2.root=/dev/mmcblk1p2 指定根文件系统在 /dev/mmcblk1p2,也就是 EMMC 的第 2 分区。
3.rootwait 等待根文件系统所在设备准备完成,再去挂载。否则 MMC 设备初始化稍慢时,内核可能找不到根文件系统。
4.rw 以可读写方式挂载根文件系统。
bootcmd 和 bootargs 的配合关系?
bootcmd 做的事:负责把以下文件搬到内存: zImage ; .dtb ; (有时还有 initrd) 然后执行 bootz。
bootargs 做的事:告诉 Linux:控制台在哪;根文件系统在哪;挂载方式是什么
;如果是网络启动,NFS 参数、IP 参数是什么。
所以可以把它们分工记成:bootcmd:负责“搬运并启动”,bootargs:负责“内核运行配置”。
提问:
为什么移植uboot后,linux可以启动,但是内核打印到一半即卡住了?
因为bootargs配置错误。不能单纯的以为,只要 bootcmd 成功把内核读进内存并执行 bootz,Linux 就一定能起来。其实不一定。因为内核虽然启动了,但它还要继续找根文件系统。如果 bootargs 里的 root= 写错了,或者串口写错了,现象就可能是:内核其实已经启动,但你看不到输出内核打印到一半卡住报找不到根文件系统最后 Kernel panic。所以:bootcmd 配对了,Linux 才能“开始启动”;bootargs 配对了,Linux 才能“继续跑下去”。
移植第五步:linux启动测试
U-Boot 的工作不是“自己跑完就结束”,而是把 Linux 镜像和设备树装进内存,然后调用 bootz 把 Linux 拉起来。
从 EMMC 启动 Linux 系统
把 zImage 和设备树文件放到 EMMC 里,U-Boot 再从 EMMC 读取它们并启动 Linux,这是一种常用的启动方式。
1.先检查 EMMC 里有没有内核和设备树
检查1号设备的1号分区,这里1号设备就是EMMC
ls mmc 1:1
2.设置 bootargs
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
注意:在 EMMC 版本核心板上,/dev/mmcblk1 表示 EMMC,/dev/mmcblk1p2 就是 EMMC 的第 2 分区。
3.设置 bootcmd
setenv bootcmd 'mmc dev 1; fatload mmc 1:1 80800000 zImage; fatload mmc 1:1 83000000 imx6ull-alientek-emmc.dtb; bootz 80800000 - 83000000;'saveenv
1.mmc dev 1 切换到 EMMC 设备 1
2.fatload mmc 1:1 80800000 zImage 从 EMMC 的 1 号分区把 zImage 读到 DDR 地址 0x80800000
3.fatload mmc 1:1 83000000 imx6ull-alientek-emmc.dtb 把设备树读到 DDR 地址 0x83000000
4. bootz 80800000 - 83000000 用 bootz 启动 zImage,- 表示没有 initrd,后面的 83000000 是设备树地址。(在这一步zImage被启动)
4.设置完成以后,直接执行:
boot 或者
run bootcmd
如果 Linux 启动成功,串口就会打印出内核启动信息
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)