嵌入式Linux系统的安装配置,是众多开发者从入门迈向进阶时,必须要跨越的一道难关。这与在PC上轻点几下鼠标就能完成安装的Ubuntu不同,对于一块ARM开发板而言,要从头构建能运行的系统,常常就意味着,得弄好从芯片上电开始,一直到应用启动的整个链路,这里面涉及到Bootloader、内核裁剪、设备树以及根文件系统等多个技术环节。
目前在嵌入式领域,U - Boot是被视作事实标准的引导加载程序,这一程序承担着初始化DDR内存、时钟以及存储控制器等关键硬件的职责。就拿恩智浦i.MX6ULL处理器来说,在官方SDK里,提供了与之对应的默认配置模板,该模板处于configs目录之下,其文件名是mx6ull_14x14_evk_defconfig。借助make ARCH等于arm,CROSS_COMPILE等于arm-linux-gnueabihf,利用mx6ull_14x14_evk_defconfig命令,便可载入基础配置。
下达执行make menuconfig这一操作,能够进一步去对细节选项予以调整,像设置网络协议栈支持,或者是对存储于SPI Flash里的环境变量的存储偏移地址加以调整。针对于开发调试阶段而言,理应启用netconsole功能,如此一来,U-Boot的调试信息便能借助网线径直输出至宿主机的终端,从而避免频繁地插拔串口线。编译结束之后,会于当前目录生成u-boot.bin二进制文件,要依据芯片手册把该文件写入到SD卡的特定偏移扇区。
sudo apt-get update
sudo apt-get install build-essential git vim
sudo apt-get install libncurses5-dev libssl-dev
sudo apt-get install bison flex bc
sudo apt-get install u-boot-tools device-tree-compiler
sudo apt-get install nfs-kernel-server tftp-hpa tftpd-hpa
系统所用内存大小以及能支持的硬件外设种类,由内核配置直接把关。针对资源紧张的嵌入式设备,一般借助make menuconfig逐一精简,拿掉不需要的驱动模块。如设备仅用USB Host功能,无需OTG模式,那就可把CONFIG_USB_OTG选项关掉,如此能让内核镜像体积减少约200KB。
# 下载Linaro工具链
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
# 解压
tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
# 配置环境变量
echo "export PATH=$PATH:/path/to/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin" >> ~/.bashrc
source ~/.bashrc
# 验证安装
arm-linux-gnueabihf-gcc --version
内核与硬件之间的桥梁是设备树文件,它以文本格式记述CPU核心数量,所述内存起始地址,各外设的引脚复用关系。于项目实践里,时常要依据电路原理图去修改dts文件,像把UART3的引脚自普通GPIO模式转变为串口功能,还配置对应的时钟频率。编译之际借助make dtbs命令生成dtb二进制文件,跟内核镜像zImage一道部署至存储介质上。
git clone https://github.com/u-boot/u-boot.git
cd u-boot
git checkout v2021.01 # 选择一个稳定版本
正常情况下,开发主机架构多为x86,目标设备架构多是ARM或是RISC - V,所以得安装交叉编译工具链才行。ARM官方给出的GCC工具链是一个被认可的稳定选择,下载了arm - gnu - toolchain - 12.2版本后,要把它的bin目录添加至PATH环境变量里。验证的办法是在终端输入arm - none - linux - gnueabihf - gcc --version,正确显示版本号就意味着配置成功了。
# 查看支持的配置
ls configs/ | grep stm32mp1
# 使用默认配置
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- stm32mp15_basic_defconfig
# 进行详细配置(可选)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
在实际进行的项目当中,工具链的版本是需要和内核源码相互匹配的,比如说在使用Linux 5.4内核之际,搭配GCC 9.3版本时兼容性是最为良好的,对于那些需要进行硬浮点运算的Cortex - A系列处理器而言,一定得选择带有hf后缀的工具链,不然的话生成的程序在运行的时候会因为浮点指令出现异常从而导致崩溃,与此同时还需要安装make、bc、bison、flex等基础工具,这些软件包借助apt - get install就能够完成。
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8
sudo dd if=u-boot.bin of=/dev/sdb bs=1k seek=8 conv=fsync
制作最小根文件系统的核心工具是BusyBox,它把ls、cp、init等常用命令合并成一个体积约1MB的单一可执行文件。从busybox.net下载源码后,利用交叉编译工具链执行make defconfig,再经由make menuconfig选择静态编译模式,如此生成的文件不依赖外部库,适宜极简环境。
git clone https://github.com/STMicroelectronics/linux.git
cd linux
git checkout v5.10-stm32mp # 选择对应版本
构建完成之后,得要手动去创建那些必要的目录结构,像是/proc、/sys、/dev、/etc等等,并且要在/etc/inittab里配置启动脚本。对于那些需要图形界面或者Python运行时的场景而言,建议去使用Buildroot这类自动化构建工具。Buildroot经由menuconfig界面选择软件包之后,会自动去解决依赖关系,进而生成完整的文件系统镜像,它支持ext4、jffs2或者squashfs等多种格式。
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- multi_v7_defconfig
要把编译好了的U-Boot、内核镜像以及设备树文件,部署到SD卡或者eMMC之上,一般会采用dd命令写入指定的偏移量,像是U-Boot需要写入到SD卡的0x400偏移位置,命令是dd if=u-boot.imx of=/dev/sdb bs=512 seek=2,开发板通电之后通过串口终端查看输出,U-Boot启动倒计时的这段期间按任意键进去命令行,执行printenv去确认环境变量配置。
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
常见设置的启动参数是将其设为setenv bootargs ‘console=ttymxc0,115200 root=/dev/mmcblk0p2 rootwait rw’,这里面console所指定的是串口设备,而root是指向根文件系统分区而言的。通过使用tftp服务器于网络加载内核能够明显提升调试效率,在U - Boot当中执行tftp 0x80800000 zImage,再配合bootz命令就能够直接从内存启动。
针对嵌入式设备存在的资源限制情况,要去禁用内核里那些不必要的调试功能,像CONFIG_DEBUG_INFO会极大地增加内核体积,所以应该在正式版本当中把它关闭,对于有着高实时性要求的工业控制场景而言,可以开启内核的抢占式调度选项,把CONFIG_PREEMPT_RT设定为开启的状态,这样能够将中断响应延迟控制在微秒级别。
// stm32mp157c-myboard.dts
/dts-v1/;
#include "stm32mp157.dtsi"
#include "stm32mp15xc.dtsi"
#include "stm32mp15-pinctrl.dtsi"
/ {
model = "My Custom Board";
compatible = "st,stm32mp157c-myboard", "st,stm32mp157";
chosen {
stdout-path = "serial0:115200n8";
};
memory@c0000000 {
device_type = "memory";
reg = <0xc0000000 0x20000000>; // 512MB DDR
};
};
&uart4 {
pinctrl-names = "default";
pinctrl-0 = <&uart4_pins_a>;
status = "okay";
};
ðernet0 {
status = "okay";
pinctrl-0 = <ðernet0_rgmii_pins_a>;
pinctrl-names = "default";
phy-mode = "rgmii";
};
存储介质的挑选同样是关键所在,要是选用NAND Flash当作存储,那就得在内部核心里开启UBIFS文件系统的支持,并且配置坏块管理的机制。在车载项目当中,每每会开启看门狗定时器,于/etc/inittab里设定watchdog守护进程,一旦系统出现死锁状况,硬件就会自动复位设备,以此保证长时间运行而不出现宕机现象。
# 编译内核镜像
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage -j8
# 编译设备树
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
# 编译内核模块
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules -j8
从引导加载程序到根文件系统,每一回的配置都会直接对嵌入式Linux系统的最终呈现效果产生影响你在实际的项目当中碰到过哪些由于配置不合适而引发的启动失败或者性能方面的问题欢迎在评论区域分享你调试的经验。
git clone https://github.com/buildroot/buildroot.git
cd buildroot
git checkout 2021.02 # 选择稳定版本

相关标签: # 嵌入式Linux # 系统安装 # 配置过程 # Bootloader # 内核编译