第四章 Bootloader与内核启动:U-Boot移植与配置、内核设备树(DTS)修改、启动参数设置

好,咱们进入第四章。这一章讲的是POS机从通电到跑起系统的关键环节——Bootloader和内核启动。说白了,就是让硬件“活过来”的那几步。

我刚开始做POS机那会儿,总觉得U-Boot就是个黑盒子,能跑就行。后来踩了坑才发现,这里面的门道深着呢。你想想看,POS机要刷IC卡、要打印小票、要连4G网络,这些外设能不能正常工作,很大程度取决于Bootloader和内核启动阶段配置得对不对。

4.1 U-Boot移植与配置

U-Boot,全称是Universal Bootloader。它就像PC上的BIOS,负责初始化硬件、加载内核。但POS机用的嵌入式芯片五花八门,U-Boot必须针对你的板子做定制。

4.1.1 移植前的准备

拿到一块新的POS机主控板,我一般先做三件事:

  • 确认芯片型号:比如全志V3s、瑞芯微RK3288、或者NXP的i.MX系列。不同芯片的启动流程差异很大。
  • 找到参考板:U-Boot源码里有很多现成的板级配置。比如你的芯片是RK3288,就找rk3288_evb这个配置作为起点。
  • 准备交叉编译工具链:ARM架构的POS机,一般用arm-linux-gnueabihf-这套工具链。
我的习惯:我会在U-Boot源码根目录下建一个board/mycompany/mypos文件夹,把参考板的配置复制过来改。这样以后维护起来清晰。

4.1.2 关键配置项

U-Boot的配置主要通过make menuconfig来完成。但有几个核心参数,我建议你直接改头文件,更可控。

以我做过的一款基于全志V3s的POS机为例,关键配置在include/configs/sun8i.h里:

/* 内存配置 */
#define CONFIG_NR_DRAM_BANKS    1
#define SDRAM_BASE              0x40000000
#define SDRAM_SIZE              0x08000000  /* 128MB */

/* 存储设备 */
#define CONFIG_MMC
#define CONFIG_SDMMC_BASE       0x01C0F000

/* 网络支持 - POS机需要联网 */
#define CONFIG_ETH_DESIGNWARE
#define CONFIG_GMAC_BASE        0x01C30000

/* 启动参数存储位置 */
#define CONFIG_ENV_IS_IN_MMC
#define CONFIG_ENV_OFFSET       0x80000
#define CONFIG_ENV_SIZE         0x20000

这里有个坑,我提醒你一下:内存大小一定要和硬件实际焊接的颗粒一致。我曾经遇到过一款POS机,硬件设计是128MB,但采购时换成了64MB的颗粒,U-Boot没改配置,结果内核启动到一半就崩了。查了两天才发现是内存超了。

4.1.3 编译与烧录

配置好之后,编译就简单了:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mypos_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4

编译出来的u-boot-sunxi-with-spl.bin就是最终要烧录到SPI Flash或SD卡里的镜像。

注意:烧录前一定要确认启动介质。POS机常用SPI Flash启动,烧录地址通常是0x0。如果用SD卡启动,需要把镜像写到SD卡的第一个扇区(偏移0x8000处)。搞反了的话,板子直接变砖。

4.2 内核设备树(DTS)修改

设备树,英文叫Device Tree,是Linux内核用来描述硬件信息的“说明书”。POS机的外设多,DTS改起来也最费劲。

4.2.1 DTS的基本结构

一个典型的DTS文件长这样:

/dts-v1/;
#include "sun8i-v3s.dtsi"

/ {
    model = "MyPOS Terminal V1.0";
    compatible = "mycompany,mypos", "allwinner,sun8i-v3s";

    /* 内存节点 */
    memory@40000000 {
        device_type = "memory";
        reg = <0x40000000 0x08000000>;
    };

    /* 串口 - POS机打印小票用 */
    &uart0 {
        pinctrl-0 = &uart0_pins_a;
        status = "okay";
    };

    /* SPI - 连接打印机头 */
    &spi0 {
        status = "okay";
        spidev@0 {
            compatible = "mycompany,pos-printer";
            reg = <0>;
            spi-max-frequency = <20000000>;
        };
    };

    /* I2C - 连接触摸屏 */
    &i2c0 {
        status = "okay";
        touch@38 {
            compatible = "goodix,gt911";
            reg = <0x38>;
            interrupt-parent = &pio;
            interrupts = <1 5 IRQ_TYPE_EDGE_FALLING>;
        };
    };
};

你看,每个外设都是一个节点。节点里描述了它的地址、中断、时钟等信息。内核启动时,会解析这个DTS,然后加载对应的驱动。

4.2.2 我踩过的DTS坑

做POS机最头疼的是外设冲突。我记得有一次,打印机和读卡器共用了同一个SPI总线,但DTS里没配好片选信号。结果一打印小票,读卡器就死机。

排查方法很简单:用cat /proc/device-tree查看内核实际解析的设备树,对比硬件原理图。如果发现某个外设的reg地址不对,或者interrupts号错了,改DTS重新编译就行。

核心原则:DTS里的每个status属性,要么是"okay",要么是"disabled"。没用的外设一定要关掉,否则内核会浪费资源去初始化它。POS机的电池续航很宝贵,别让没用的外设偷电。

4.2.3 编译DTS

修改完DTS后,需要编译成DTB文件:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs

生成的sun8i-v3s-mypos.dtb就是内核要用的设备树二进制文件。把它和内核镜像、文件系统一起打包,烧录到POS机里。

4.3 启动参数设置

启动参数,就是U-Boot传给内核的命令行参数。它告诉内核:根文件系统在哪、控制台用哪个串口、内存多大等等。

4.3.1 常用启动参数

以我的POS机项目为例,U-Boot的bootargs环境变量通常这样设置:

setenv bootargs 'console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait rw
    init=/sbin/init
    mem=128M
    lpj=499712
    panic=5
    quiet'

解释一下每个参数的作用:

参数 含义 我的建议
console=ttyS0,115200 调试串口,波特率115200 POS机量产时建议去掉,防止被串口攻击
root=/dev/mmcblk0p2 根文件系统在SD卡第二个分区 如果用eMMC,改成/dev/mmcblk1p2
rootwait 等待根文件系统就绪 必须加,否则SD卡没准备好内核就崩了
mem=128M 限制内核使用128MB内存 和硬件实际内存一致,别写大了
panic=5 内核崩溃后5秒自动重启 POS机无人值守,这个参数很实用

4.3.2 启动流程实战

U-Boot启动内核的完整命令序列是这样的:

# 加载内核和设备树到内存
mmc dev 0
fatload mmc 0:1 0x41000000 uImage
fatload mmc 0:1 0x41800000 sun8i-v3s-mypos.dtb

# 设置启动参数
setenv bootargs 'console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait rw mem=128M'

# 启动内核
bootm 0x41000000 - 0x41800000

这里bootm命令的三个参数分别是:内核地址、ramdisk地址(没有就填-)、设备树地址。

避坑指南:我曾经把设备树地址写成了0x41000000,和内核地址冲突了。结果内核启动时,设备树被覆盖,系统直接挂掉。记住:内核和设备树要放在不同的内存区域,中间留够空间。

4.4 常见问题与调试技巧

做POS机Bootloader移植,有几个问题你大概率会遇到:

  • U-Boot启动卡在“Starting kernel...”:八成是设备树有问题。检查DTS里的compatible字符串是否和内核驱动匹配。
  • 内核启动后找不到根文件系统:检查root=参数是否正确,以及SD卡/eMMC的分区表是否和参数一致。
  • 外设驱动加载失败:用devicetree工具反编译DTB,确认节点是否被正确解析。

嗯,这一章的内容就这些。说白了,Bootloader和内核启动就是“搭桥”的过程——U-Boot把硬件初始化好,设备树把外设描述清楚,启动参数告诉内核怎么跑。这三样配合好了,你的POS机才能稳稳当当地跑起来。

下一章,咱们会深入文件系统的构建,看看怎么把应用程序、库文件、配置文件塞进POS机里。到时候见。