建议收藏万字长文!嵌入式Linux系统移植原理与方法总结

Linux系统移植总结

摘要

本文是对整个Linux系统移植的讲解,适宜有一定基础的初学者进行复习,基本可以自己制作PCB之后自己根据这个方法烧写Linux系统,不涉及U-Boot与Linux的源码和编译流程的讲解(这东西后面再学没事的),只讲最实用的方法,如果你有跟着烧写过一遍Linux系统,那么本文会让你重新复习一遍整个流程,加深对Linux系统移植的理解与应用。

​ OK!移植 Linux之前我们需要先移植一个 bootloader 代码,这个 bootloader 代码用于启动 Linux 内核, bootloader有很多,常用的就是 U-Boot。移植好 U-Boot 以后再移植 Linux 内核,移植完 Linux 内核以后Linux 还不能正常启动,还需要再移植一个根文件系统(rootfs)。三个一起构成了一个完整的Linux系统,一个可以使用、功能完善的Linux系统。

一、U-Boot

1.简介

​ 一般直接编译一个移植好的U-Boot,然后烧写到SD卡里面启动。Linux系统要启动就必须要一个bootloader程序,这段bootloader程序会先初始化DDR等外设,然后将Linux内核从flash(NAND, NOR FLASH, SD, MMC等)拷贝到DDR中,最后启动Linux内核。

​ bootloader的实际工作很复杂,但它最主要的工作就是启动Linux内核,很庆幸我们有很多现成的bootloader可以使用比如U-Boot、vivi、RedBoot等等,为了方便书写我们后面都把U-Boot写为uboot。

​ uboot是一个遵循GPL协议的开源软件,是一个裸机代码,可以看作一个裸机综合例程,现在的uboot已经支持液晶屏、网络、USB等高级功能。uboot 官网为 http://www.denx.de/wiki/U-Boot/ 。

​ 一般官网里都是原汁原味的源码文件。但是我们一般不会使用uboot官方的源码,而是去自己的SOC芯片半导体厂商下载uboot,这个版本的uboot相当于是它们定制的,对自家的芯片支持会很全而且维护更好。NXP 就 维 护 的 2016.03 这 个 版 本 的 uboot , 下 载 地 址 为 :http://git.freescale.com/git/cgit.cgi/imx/uboot-imx.git/tag/?h=imx_v2016.03_4.1.15_2.0.0_ga&id=rel_imx_4.1.15_2.1.0_ga 。我们一般下载tar.bz2的压缩包。基本支持了NXP当前所有可以跑Linux的芯片,而且支持各种启动方式,比如EMMC、NAND、NOR FLASH等,这些都是uboot官方不支持的。

​ 但是我们自己做板子的话就需要修改NXP官方的uboot,使其支持我们自己做的板子。

2.编译uboot

​ 首先在 Ubuntu 中安装 ncurses 库, 否则编译会报错,安装命令如下:

sudo apt-get install libncurses5-dev

​ 在ubuntu中创建存放uboot的目录,然后存放自己的uboot源码。使用FileZilla软件将windows下源码放到自己的ubuntu中。然后对其进行解压缩:

tar -vxjf uboot-imx-2016.03-2.1.0-g8b546e4.tar.bz2

​ 1、如果使用512MB(DDR3)+8GB(EMMC)的核心板,则使用如下命令编译对应uboot:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_defconfig
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12

​ ARCH=arm设置目标为arm架构, CROSS_COMPILE 指定所使用的交叉编译器。第一条命令是清除工程。第二条指令配置uboot。第三条命令使用12核编译uboot。

​ 编译完成以后uboot源码就多了一些文件,其中u-boot.bin就是编译出来的uboot二进制文件。uboot是个裸机程序,因此需要在其前面加上头部才能在SOC上运行,u-boot.imx文件就是我们最终要烧写到开发板中的uboot镜像文件。

​ 每次编译都要输入上面一长串命令,可以建立个shell脚本,新建mx6ull_emmc.sh脚本文件然后在上面代码的第一行插入 “#!/bin/bash” 放入shell中就行。使用chmod给予脚本可执行权限,然后就可以使用脚本来重新编译uboot:./mx6ull_emmc.sh

​ 2、如果用的 256MB+512MB 的 NAND 核心板 ,只需要将上面第二行的配置文件emmc改为nand即可。

3.烧写与启动

​ uboot编译好以后就可以烧写到自己的板子上咯,将uboot烧写到SD卡然后通过SD卡来启动运行uboot。使用imxdownload软件烧写,命令如下:

chmod 777 imxdownload 
./imxdownload u-boot.bin /dev/sdd	//不能烧写到/dev/sda或sda设备里!
									//sdd具体参考自己的存储设备

​ 以前在学习单片机的时候编译完代码以后可以直接通过MDK下载到内部flash中,但SOC内部只有96K的ROM而且只对芯片厂家使用不向我们开放。为此一般的SOC支持外置的NOR Flash、NAND Flash、SD/EMMC等存储介质中启动。但NXP对烧写代码至SD卡中有严格规定,不能简单的通过电脑拖动文件进sd卡那样操作,否则是运行不起来的,一般在《芯片参考手册》中的System Boot中专门讲解启动方式。

​ imxdownload就是专门将编译出来的.bin文件烧写到SD卡中的,只能在ubuntu下使用,需要放在工程目录下。然后需要确定要烧写的SD卡,ubuntu下所有的设备文件都在目录“/dev”里,ls /dev/sd*可以查看存储设备。

​ OK,介绍完了如何将uboot烧写到SD卡中后,就可以将SD卡插入开发板,BOOT设置从SD卡启动,使用USB线将USB_TTL和电脑连接,打开SecureCRT,在倒计时按回车进入uboot命令行模式,如果倒计时之后uboot就会使用默认参数来启动Linux内核了。

​ 进入命令行模式,就可以看到“=>”标志,下面全部是SOC的信息。

4.uboot裁剪与移植

​ 复习了一下uboot启动流程,对uboot有个初步的了解。这里就学习如何将芯片原厂(这里以NXP官方为例)官方的uboot移植到我们自己的板子上,学习如何在uboot中添加我们自己的板子~

​ 半导体厂商会将 uboot 移植到他们自己的原厂开发板上,测试好以后就会将这个 uboot 发布出去,这就是大家常说的原厂 BSP 包。我们一般做产品就会参考原厂的开发板做硬件,然后在原厂提供的BSP包上做修改,将uboot或者linux kernel移植到我们的硬件上。这就是uboot移植的一般流程:

​ ①、在uboot中找到参考的开发平台,一般是原厂的开发板
​ ②、参考原厂开发板移植uboot到我们自己的板子上

​ 在移植之前,我们先编译一下NXP官方开发板对应的uboot首先是配置uboot,configs目录下有很多跟你的SOC相关的配置,我们用的I.MX6ULL,所以使用mx6ull_14x14_evk_emmc_defconfig作为默认配置文件。

编译NXP官方开发板对应的uboot:

​ 找到NXP官方开发板对应的默认配置文件,以后就可以使用如下命令编译uboot:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_evk_emmc_defconfig
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16

​ 也可以在顶层Makefile中直接给ARCH和CORSS_COMPILE赋值,修改如下:

245 #set default to nothing for native builds
246 ifeq ($(HOSTARCH),$(ARCH))
247 CROSS_COMPILE ?=
248 endif
249
250 ARCH = arm
251	CROSS_COMPILE = arm-linux-gnueabihf-

​ 这样我们就可以使用简短的命令来编译uboot了:

make mx6ull_14x14_evk_emmc_defconfig
make V=1 -j16

​ 还可以创建sheill脚本,这里不详细介绍。

​ 编译好以后就会生成u-boot.bin、u-boot.imx等文件,这些文件都是NXP官方开发板能不能用到我们自己的开发板上呢?试一试!

烧写验证与驱动测试

​ 将imxdownload软件拷贝到uboot源码根目录下,然后将uboot.bin烧写到SD卡中。烧写完以后将SD卡插入我们自己的开发板中,设置开发板从SD卡启动,打开SecureCRT设置好开发板使用的串口并打开。可以发现uboot启动正常,虽然我们使用的是NXP官方的开发板但是在我们自己的开发板上是可以正常启动的。

​ 1.SD卡和EMMC驱动检查:使用命令mmc list列出当前的MMC设备,可以看到两个MMC设备,先检查MMC设备0,输入以下命令:

mmc dev 0
mmc info

​ 可以看出mmc设备0是SD卡,容量为14.8GB,说明SD卡驱动正常。再检查设备1,即把mmc dev 0改为1。

​ 2.LCD驱动检查

​ 如果uboot中的LCD驱动正确的话,启动uboot以后LCD上会显示NXP的logo,如果使用其他分辨率的LCD就需要修改LCD驱动,这里我们先不修改LCD驱动了,我们只需要记得uboot的LCD需要修改就行。

​ 3.网络驱动,ping不了ubuntu主机,网络驱动也需要修改。

环境变量bootcmd和bootargs

bootcmd 保存着 uboot 默认命令, uboot 倒计时结束以后就会执行 bootcmd 中的命令。这些命令一般都是用来启动 Linux 内核的,比如读取 EMMC 或者 NAND Flash 中的 Linux 内核镜像文件和设备树文件到 DRAM 中,然后启动 Linux 内核。

setenv bootcmd 'mmc dev 1; fatload mmc 1:1 80800000 zImage; fatload mmc 1:1 83000000 imx6ullalientek-emmc.dtb; bootz 80800000 - 83000000;'

bootargs 保存着 uboot 传递给 Linux 内核的参数.

mmcargs=setenv bootargs console= ttymxc0, 115200 root= /dev/mmcblk1p2 rootwait rw

5.uboot启动Linux测试

​ uboot 已经移植好了, bootcmd 和 bootargs 这两个重要的环境变量也讲解了,接下来就要测试一下 uboot 能不能完成它的工作:启动 Linux 内核。我们测试两种启动 Linux 内核的方法,一种是直接从 EMMC 启动,一种是从网络启动。

1.从EMMC启动Linux系统

​ 从EMMC启动也就是将编译出来的Linux镜像文件zImage和.dtb文件保存在EMMC中,uboot从EMMC读取这两个文件并启动,这也是产品最终的启动方式。在zImage和.dtb文件烧写到了EMMC中后,检查以下EMMC的分区1中有没有zImage和.dtb文件,输入“ls mmc 1:1”即可。

如果有则设置bootargs和bootcmd两个环境变量:

	setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
	setenv bootcmd 'mmc dev 1; fatload mmc 1:1 80800000 zImage; fatload mmc 1:1 83000000 imx6ull-alientek-emmc.dtb; bootz 80800000 - 83000000;'
	saveenv

​ 然后可以直接输入boot启动linux内核。

2.从网络启动Linux系统

​ 从网络启动 linux 系统的唯一目的就是为了调试!这样就不用频繁的烧写EMMC,加快开发速度。我们可以通过nfs或者tftp从ubuntu下载zImage文件和设备树文件,根文件系统可以通过nfs挂载,后面再说。设置环境变量:

	setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
	setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull-alientek-emmc.dtb; bootz
80800000 - 83000000'
	saveenv

6,uboot图形化配置

uboot可以通过defconfig来配置,或者通过对应SOC文件mx6ull_…emmc.h来配置uboot。还有一种配置uboot的方法就是图形化配置。

​ uboot或Linux内核可以通过输入“make menuconfig”来打开图形化配置界面,menuconfig使一套图形化的配置工具,需要ncurses库支持,它提供了一系列的API函数供调用者生成基于文本的图形界面,需先在ubuntu下安装ncurses库:

sudo apt-get install build-essential
sudo apt-get install libncurses5-dev

​ 打开图形化配置界面之前,需要对uboot进行一次默认配置,只需要一次即可。如果使用了make clean就要重新配置。进入uboot根目录输入命令:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_alientek_emmc_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

如果已经在uboot顶层Makefile定义了ARCH和CROSS_COMPILE值那么可以简化为:

make mx6ull_alientek_emmc_defconfig		//你自己的defconfig文件
make menuconfig							//打开图形化配置界面

​ 当我们修改了uboot配置以后,使用如下命令编译uboot:

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

​ 编译完成后烧写到SD卡中。

以后就可以通过图形化来配置uboot,这样就不需要导出找命令的配置宏,然后在配置文件里面去定义,舒服!

OK!uboot移植到此结束,简单再总结以下uboot移植过程:

①、自己做开发板基本都是参考半导体原厂的demo板,而厂商会在自己的开发板上移植好uboot、linux kernel和rootfs,最终制作好BSP包提供给用户。我们可以在官方提供的BSP包的基础上添加我们的板子,也就是移植
②、我们做开发板一般不会原封不动抄半导体厂商的demo板,都会根据实际情况来修改,既然有修改就必然涉及到uboot下驱动的移植
③、一般uboot中需要解决串口、NAND、EMMC或SD卡、网络和LCD驱动,因为uboot主要目的就是启动Linux内核,所以不需要考虑太多的外设驱动。
④、在uboot中添加自己的板子信息,根据自己板子的实际情况来修改uboot中的驱动

二、Linux内核

1.内核获取+编译

​ Linux 官网为 https://www.kernel.org ,想获得最新的Linux版本都可以下载。而NXP会下载某个版本的Linux内核,然后将其移植到自己的CPU上,测试成功以后就会将其开放给NXP的CPU开发者,开发者下载NXP提供的Linux内核,然后将其移植到自己的产品上。

​ 我们将Linux源码放到ubuntu下然后解压,示例:

tar -vxjf linux-imx-4.1.15-2.1.0-g8a006db.tar.bz2

​ 编译内核之前需要现在ubuntu上安装lzop库,否则内核会编译失败!

sudo apt-get install lzop

​ 以EMMC核心板为例,看看如何编译出对应的Linux镜像文件。新建shell脚本“mx6ull_emmc.sh”:

#!/bin/sh
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v7_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16

​ 然后给脚本可执行权限:chmod 777 ./mx6ull_emmc.sh。最后运行shell脚本:./mx6ull_emmc.sh开始编译。编译的时候会弹出Linux图形配置界面,不需要做任何配置,直接退出即可,等待编译完成。

​ 编译完成以后就会在 arch/arm/boot 这个目录下生成一个叫做 zImage 的文件, zImage 就是我们要用的 Linux 镜像文件。另外也会在 arch/arm/boot/dts 下生成很多**.dtb** 文件,这些.dtb 就是设备树文件。

2.内核移植

​ 然后就是将Linux内核移植到我们自己的开发板上了。掌握如何将半导体厂商提供的BSP包移植到我们自己的平台上。

NXP官方开发板Linux内核编译:

1.修改顶层Makefile

​ 直接在顶层 Makefile 文件里面定义 ARCH 和 CROSS_COMPILE 这两个的变量值为 arm 和 arm-linux-gnueabihf-

2.配置并编译Linux内核

​ 每个板子都有对应的默认配置文件,配置文件保存在arch/arm/configs目录中。我们使用imx_v7_mfg_defconfig这个默认配置文件。进入ubuntu中的Linux源码根目录,执行命令配置Linux内核:

make clean					//先清理一下
make imx_v7_mfg_defconfig	//配置Linux内核

​ 然后就可以编译了,使用如下命令编译Linux内核:

make -j16

​ 编译完成后就会在 arch/arm/boot目录下生成zImage镜像文件,如果使用设备树就会在 arch/arm/boot/dts目录下生成对应的.dtb文件。

启动测试

​ 确保uboot中环境变量bootargs内容如下:

console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw

​ 然后将zImage 和 .dtb文件复制到ubuntu的tftp目录下,因为我们要在uboot中使用tftp命令将其下载到开发板中:

cp arch/arm/boot/zImage /home/book/linux/tftpboot/ -f
cp arch/arm/boot/dts/imx6ull-14x14-evk.dtb /home/book/linux/tftpboot/ -f

​ 进入uboot命令行模式,将俩文件下载到开发板中并启动:

tftp 80800000 zImage
tftp 83000000 imx6ull-14x14-evk.dtb
bootz 80800000 - 83000000

然后会报错根文件系统缺失错误,说明Linux内核移植完成。

3.Linux中添加自己的开发板

​ 将arch/arm/configs目录下的imx_v7_mfg_defconfig重新复制一份,命名为imx_mine_emmc_defconfig,就可以当作自己的开发板默认配置文件了:

cd arch/arm/configs
cp imx_v7_mfg_defconfig imx_mine_emmc_defconfig

以后就可以使用如下命令来配置自己的开发板对应的Linux内核:

make imx_mine_emmc_defconfig

添加开发板对应的设备树文件

​ 添加适合自己开发板的设备树文件,进入arch/arm/boot/dts中,复制一份你的开发板对应的soc型号的设备树文件,比如我用的就是imx6ull-14x14-evk.dts,将其复制并重命名为imx6ull-mine-emmc.dts即可。

​ 创建好.dts后还需要修改文件 arch/arm/boot/dts/Makefile,找到"dtb-$(CONFIG_SOC_IMX6ULL)"配置项,加入自己重命名对应的.dtb文件名 “imx6ull-mine-emmc.dtb” 。这样编译Linux的适合就可以从.dts编译出.dtb文件了。

编译测试

​ 现在Linux内核已经添加了自己的配置文件和.dts文件,创建一个shell脚本imx6ull_mine_emmc.sh:

#!/bin/sh
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihfimx_mine_emmc_defconfig#注意这个地方
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16

然后给予脚本可执行权限并运行编译Linux内核:

chmod 777 imx6ull_alientek_emmc.sh 
./imx6ull_mine_emmc.sh 

​ 编译完成以后就会在目录arch/arm/boot下生成zImage镜像文件。arch/arm/boot/dts目录下生成.dtb文件。将这两个文件拷贝到tftp目录下,然后重启开发板进入uboot命令模式中使用tftp命令下载文件并启动:

tftp 80800000 zImage
tftp 83000000 imx6ull-mine-emmc.dtb
bootz 80800000 - 83000000

4.CPU主频和网络驱动修改(附)

​ 这需要先确保emmc中的根文件系统可用,先要移植好根文件系统。重启开发板进入终端后输入命令查看cpu信息: cat /proc/cpuinfo 。我们可以通过BogoMIPS大概地判断CPU性能。

目录/sys/bus/cpu/devices/cpu0/cpufreq中,记录了CPU频率等信息。

查看当前CPU频率:cat cpuinfo_cur_freq

​ 如果开发板做一些高负载的工作,比如播放视频等操作那么CPU频率就会提升少去,查看stats目录下的time_in_state文件可以看CPU在各频率下的工作事件,命令:cat/sys/bus/cpu/devices/cpu0/cpufreq/stats/time_in_state

​ 如果想让CPU一直工作在高频怎么办?很简单,配置Linux内核,将调频策略修改为performance高性能模式。或者修改之前提到的defconfig默认配置文件,文件中有下面几行:

41 CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
42 CONFIG_CPU_FREQ_GOV_POWERSAVE=y
43 CONFIG_CPU_FREQ_GOV_USERSPACE=y
44 CONFIG_CPU_FREQ_GOV_INTERACTIVE=y

第一个是默认调频策略,后面三个是其他的调频策略。将示例代码中的第 41 行屏蔽掉,然后在 44 行后面添加:CONFIG_CPU_FREQ_GOV_ONDEMAND=y 。修改完成以后重新编译Linux内核,编译之前清理一下工程。编译完以后我们再次查看/sys/devices/system/cpu/cpu0/cpufreq/ cpuinfo_cur_freq 文件的值 ,当前CPU频率就为792MHz了

​ 除此之外还可以用linux内核的图形化配置界面:输入make menuconfig。进入路径CPU Power Management-> CPU Frequency scaling-> Default CPUFreq governor 打开默认调频策略选择界面就能选择不同调频策略了。

使能8线EMMC驱动

​ 直接修改设备树即可,打开自己的.dts文件找到usdhc2设备,修改成如下代码:

734 &usdhc2 {
735 	pinctrl-names = "default", "state_100mhz", "state_200mhz";
736 	pinctrl-0 = <&pinctrl_usdhc2_8bit>;
737 	pinctrl-1 = <&pinctrl_usdhc2_8bit_100mhz>;
738 	pinctrl-2 = <&pinctrl_usdhc2_8bit_200mhz>;
739 	bus-width = <8>;
740 	non-removable;
741 	status = "okay";
742 };

​ 使用make dtbs重新编译设备树。

修改网络驱动

​ 略了吧.

三、根文件系统

1.BusyBox构建根文件系统

​ 这是一个将一堆可执行文件和其他文件收集并打包,然后供我们开发者直接使用的软件。这是一个继承了大量Linux命令和工具的软件,一般下载BusyBox的源码,然后配置BusyBox选择自己想要的功能最后编译即可。官网地址为: https://busybox.net/ 。左侧的“Get BusyBox”栏有一行“Download Source” 。准备好以后就可以构建根文件系统了。

编译BusyBox构建rootfs

​ 一般我们在linux驱动开发的时候都是通过nfs挂载根文件系统的,当产品最终成型才会将rootfs烧写到emmc或nand中,所以在之前nfs服务器目录中创建一个名为rootfs的子目录,比如我的路径就是"/home/book/linux/nfs/rootfs",将下载的busybox的tar.bz2压缩包发送到ubuntu中,压缩包位置随便放,然后使用如下命令将其解压:

tar -vxjf busybox-1.29.0.tar.bz2

​ 解压完成后进入到目录中:

1.修改Makefilie,添加编译器

164 CROSS_COMPILE ?= /usr/local/arm/gcc-linaro-4.9.4-2017.01-
x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
......
190 ARCH ?= arm

2.busybox中文字符支持

​ 如果默认编译busybox在使用SecureCRT的时候中文字符是显示不正常的。所以需要修改busybox源码,取消对busybox对中文显示的限制,打开busybox1.29.0/libbb/printable_string.c,找到函数printable_string,缩减后的内容如下:

12 const char* FAST_FUNC printable_string(uni_stat_t *stats, const char
*str)
13 {
14 	char *dst;
15 	const char *s;
16
17 	s = str;
18 	while (1) {
19 		unsigned char c = *s;
20 		if (c == '\0') {
......
28 		}
29 		if (c < ' ')
30 			break;
31 		/*if (c >= 0x7f)
32 			break;*/
33 		s++;
34 	}
35
36 	#if ENABLE_UNICODE_SUPPORT
37 	dst = unicode_conv_to_printable(stats, str);
38 	#else
39 	{
40 		char *d = dst = xstrdup(str);
41 		while (1) {
42 			unsigned char c = *d;
43 			if (c == '\0')
44 				break;
45 			/*if (c < ' ' || c >= 0x7f)*/
			if( c < ' ')
46 				*d = '?';
47 			d++;
48 		}
......
55 #endif
56 return auto_string(dst);
57 }

接着打开文件 busybox-1.29.0/libbb/unicode.c,修改成如下内容:

1003 static char* FAST_FUNC unicode_conv_to_printable2(uni_stat_t
*stats, const char *src, unsigned width, int flags)
1004 {
1005 	char *dst;
1006 	unsigned dst_len;
1007 	unsigned uni_count;
1008 	unsigned uni_width;
1009
1010 	if (unicode_status != UNICODE_ON) {
1011 		char *d;
1012 		if (flags & UNI_FLAG_PAD) {
1013 			d = dst = xmalloc(width + 1);
......
1022 			/* 修改下面一行代码 */
1023 			/* *d++ = (c >= ' ' && c < 0x7f) ? c : '?'; */
1024 			*d++ = (c >= ' ') ? c : '?';
1025 			src++;
1026 		}
1027 		*d = '\0';
1028 	} else {
1029 		d = dst = xstrndup(src, width);
1030 		while (*d) {
1031 			unsigned char c = *d;
1032 			/* 修改下面一行代码 */
1033 			/* if (c < ' ' || c >= 0x7f) */
1034 			if(c < ' ')
1035 				*d = '?';
1036 			d++;
1037 		}
1038 	}
......
1044 	return dst;
1045 }
......
1047
1048 return dst;
1049 }

3.配置busybox

​ 有以下几种配置选项:

	defconfig,缺省配置,也就是默认配置选项
	allyesconfig,全选配置,选中busybox所有功能
	allnoconfig,最小配置

​ 我们一般使用默认配置即可,busybox也支持图形化配置,使用make menuconfig。

进入路径Location:-> Settings-> Build static binary (no shared libs) ,不要选中Build static binary。

Location:-> Settings-> vi-style line editing commands ,选中vi-style line editing commands 。

Location:-> Linux Module Utilities-> Simplified modutils ,不要选中Simplified modutils

Location:-> Linux System Utilities-> mdev (16 kb) 确保这个下面全部选中,默认就是全选。

Location:-> Settings-> Support Unicode(选中)-> Check $LC_ALL, $LC_CTYPE and $LANG environment variables(选中)

4.编译busybox

我们可以指定编译结果的存放目录,我们肯定要将编译结果存放到前面创建的rootfs目录中,输入命令:

make
make install CONFIG_PREFIX=/home/book/linux/nfs/rootfs

​ 然后可以进入rootfs目录,发现有bin、sbin和usr这三个目录,以及linuxrc这个文件夹。busybox的工作就完成了,但是此时的根文件系统还不能使用,还需要继续完善。

2.向rootfs添加lib库

1.向rootfs的“/lib”目录添加库文件

​ Linux 中的应用程序一般都是需要动态库的, 当然你也可以编译成静态的,但是静态的可执行文件会很大。如果编译为动态的话就需要动态库,所以我们需要向根文件系统中添加动态库。在 rootfs 中创建一个名为“lib”的文件夹

​ lib库文件从交叉编译器中获取,前面我们搭建交叉编译器的时候将其存放到了“/usr/local/arm/”目录中。交叉编译器里有很多的库文件,我们后续会学习这些库文件具体是什么以及对其裁剪,这里我们直接全部存放到我们的根文件系统中:

​ 进入如下路径对应的目录:

/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/armlinuxgnueabihf/libc/lib

此目录下有很多的 *so* 和 .a 文件,这些就是库文件,将此目录所有的这些文件拷贝到rootfs/lib中!

cp *so* *.a /home/book/linux/nfs/rootfs/lib/ -d

​ -d表示拷贝符号链接,这里有个特殊的库文件:ld-linux-armhf.so.3。此文件也是个符号链接,输入"ls ld-linux-armhf.so.3 -l"查看此文件详细信息,后面有个“->”表示这是个软连接文件,即“快捷方式”。我们需要重新复制ld-linux-armhf.so.3:先将rootfs/lib中的这个文件删掉,然后重新单独拷贝这个文件:“zp ld-linux-armhf.so.3 /home/book/linux/nfs/rootfs/lib/”。

​ 然后继续进入目录:/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/lib :此目录下也有很多的 *so* 和 .a 库文件,将这些拷贝到rootfs/lib目录中:“cp so *.a /home/book/linux/nfs/rootfs/lib/ -d”

2.向rootfs的“usr/lib”目录添加库文件

​ 在rootfs的usr目录下创建lib目录,进入目录/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/lib。同上:cp so *.a /home/book/linux/nfs/rootfs/usr/lib/ -d 。

​ 至此,根文件系统的库文件就全部添加好了,可以使用“du”命令查看rootfs/lib和rootfs/usr/lib这两个目录的大小:du ./lib ./usr/lib/ -sh 。可以看到两个文件夹大小分别为57MB和67MB,不裁剪还是挺大的

3.创建其他文件夹

​ 在根文件系统中创建其他文件夹,如 dev、 proc、 mnt、 sys、 tmp 和 root 等 。

3.根文件系统测试

​ 测试方法就是使用NFS挂载,uboot里面的bootargs环境变量会设置“root”值,所以我们将root的值改为NFS挂载即可。Linux内核源码里面有相应文档讲解如何设置:Documentation/filesystems/nfs/nfsroot.txt。

bootcmd:

setenv bootcmd 'tftp 80800000 zImage;tftp 83000000 imx6ull-alientek-emmc.dtb;bootz 80800000 - 83000000;'

bootargs要设置为:

setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.5.11:/home/book/linux/nfs/rootfs,proto=tcp rw ip=192.168.5.89:192.168.5.11:192.168.5.1:255.255.255.0::eth0:off'

saveenv

​ 设置好以后可以使用boot命令成功启动linux内核,但现在还没完善。

创建/etc/init.d/rcS

​ rcS是个shell脚本,Linux内核启动以后需要启动一些服务,而这就是规定启动哪些文件的脚本。在rootfs中创建此文件然后输入如下内容:

#!/bin/sh

PATH=/sbin:/bin:/usr/sbin:/usr/bin:$PATH
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib:/usr/lib
export PATH LD_LIBRARY_PATH

mount -a
mkdir /dev/pts
mount -t devpts devpts /dev/pts

echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s

​ PATH环境变量保存着bin文件可能存在的目录;LD_LIBRARY_PATH保存库文件所在目录;export来导出上面的环境变量,相当于声明为“全局变量”;mount命令挂载所有的文件系统,由/etc/fstab来指定这些文件系统;然后是创建目录/dev/pts,将devpts挂载到/dev/pts目录中;最后使用mdev来管理热插拔设备,通过这两行Linux内核可以在/dev目录下自动创建设备节点。

​ 然后给予/etc/init.d/rcS可执行权限:chmod 777 rcS

创建/etc/fstab文件

​ 在rootfs中创建/etc/fstab文件,开机以后自动配置哪些需要自动挂载的分区。文件内容:

#<file system> <mount point> <type> <options> <dump> <pass>
proc 			/proc 			proc 	defaults 0 		0
tmpfs 			/tmp 			tmpfs 	defaults 0 		0
sysfs 			/sys 			sysfs 	defaults 0 		0

创建/etc/inittab文件

​ init程序会读取/etc/inittab这个文件,inittab由若干条指令组成格式为<id>:<runlevels>:<action>:<process>

创建一个/etc/inittab,内容:

#etc/inittab
::sysinit:/etc/init.d/rcS
console::askfirst:-/bin/sh
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
::shutdown:/sbin/swapoff -a

​ 系统启动以后会运行/etc/init.d/rcS这个脚本文件;第3行将console作为控制台;第4行重启的话运行/sbin/init;第5行按下ctrl+alt+del组合键就运行/sbin/reboot,重启系统;第6行,关机执行/bin/umount卸载各个文件系统;第7行关机执行/sbin/swqpoff关闭交换分区。

​ 至此,根文件系统就已全部完成。接下来就要对根文件系统进行其他测试,比如我们自己编写的软件运行是否正常、是否支持APP开启自启动、中文支持是否正常以及能不能链接等。

软件运行测试

​ 我们使用Linux的目的就是运行我们自己的软件,我们编译的应用软件一般都使用动态库,使用动态库的话APP的体积就很小,但是得提供库文件,库文件我们已经添加到了根文件系统中。我们编写一个小小的测试软件来测试以下库文件是否工作正常,在rootfs下创建一个drivers的文件夹,在ubuntu下使用vim编辑器新建一个hello.c文件:

#include <stdio.h>
int main(void)
{
	while(1) {
		printf("hello world!\r\n");
		sleep(2);
	}
	return 0;
}

因为我们是要在ARM芯片上运行的,所以要用交叉编译器去编译,命令:

arm-linux-gnueabihf-gcc hello.c -o hello

将编译出来的hello文件拷贝到rootfs/drivers目录下,然后在开发板中输入命令来执行:

cd /drivers
./hello

​ APP运行正常就说明我们的根文件系统中的共享库是没问题的。让APP在后台运行 在运行软件时加上&即可,ps查看后台进程ID,“kill -9 PID”可以用来删除后台应用程序。

中文字符测试

​ 1.设置SecureCRT使用UTF-8编码

​ 因为Linux使用的编码格式为UTF-8,因此要设置SecureCRT的编码格式。打开Options->Session Options,选择左侧的的Appearance,然后在右侧的Character encoding:选择UTF-8编码。

​ 2.创建中文文件

​ 在ubuntu中向rootfs目录新建一个名为“中文”的文档,然后在SecureCRT下查看中文名能不能显示正确。

开启自启动

​ 我们一般做好产品以后都是需要开机自启动相应的软件,进入根文件系统的时候会运行/etc/init.d/rcS这个shell脚本,因此我们可以在这个脚本里面添加自启动相关内容,添加完成后rcS文件内容如下:

1 #!/bin/sh
2 PATH=/sbin:/bin:/usr/sbin:/usr/bin
3 LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib:/usr/lib
4 runlevel=S
5 umask 022
6 export PATH LD_LIBRARY_PATH runlevel
7 
8 mount -a
9 mkdir /dev/pts
10 mount -t devpts devpts /dev/pts
11
12 echo /sbin/mdev > /proc/sys/kernel/hotplug
13 mdev -s
14
15 #开机自启动
16 cd /drivers
17 ./hello &
18 cd /

​ 第16行进入drivers目录,因为我们把hello软件存放在drivers下。第17行和我们在命令行运行软件一样。第18行返回根目录。

网络测试

​ 在rootfs中新建文件/etc/resolv.conf,然后输入以下内容:

nameserver 114.114.114.114
nameserver 192.168.1.1

这就是设置域名解析地址,可以设置为所处网络的网关地址(第二个),也可以设置为运营商的域名解析服务器地址(第一个)。

​ 然后ping 一下baidu试试。

​ 至此!我们的根文件系统就彻底制作完成,这个根文件系统最好打包保存一下,防止以后不小心破坏了根文件系统功亏一篑,又得从头制作根文件系统。

四、系统烧写

​ 移植好了uboot、Linux kernel和.dtb,制作好了rootfs。接下来就将这四个文件烧写到板子上的EMMC、NAND或QSPI Flash等其他存储设备上,这样我们的产品就可以正常运行了。我们通过NXP官方提供的MfgTool工具通过USB OTG来烧写系统。

​ 此软件在Windwos下使用,“without-rootfs”和“with-rootfs”,一个是不带rootfs和一个是带rootfs的,我们肯定选择with的,对其解压可以得到一个文件夹,文件夹就包含我们需要的烧写工具。进入目录mfgtools-with-rootfs\mfgtools 中,在此目录下有几个文件夹和很多的.vbs文件。

​ 我们只关注Profiles这个文件夹,因为后面要烧写文件就放到这个文件夹中。MfgTool2.exe就是烧写软件,但是不会直接打开这个软件烧写,烧写之前要进行配置,指定烧写的是什么芯片,烧写到哪里去。下面的这些众多的.vbs 文件就是配置脚本,烧写的时候通过双击这些.vbs 文件来打开烧写工具。根据处理器的不同与存储芯片的不同选择.vbs文件。我们假如要向I.MX6U烧写系统,就有这些相关的.vbs文件:

脚本文件描述
mfgtool2-yocto-mx-evk-emmc.vbsEMMC 烧写脚本。
mfgtool2-yocto-mx-evk-nand.vbsNAND 烧写脚本
mfgtool2-yocto-mx-evk-qspi-nor-n25q256a.vbsQSPI Flash 烧写脚本,型号为 n25q256a
mfgtool2-yocto-mx-evk-sdcard-sd1.vbs如果 SD1 和 SD2 接的 SD 卡,这两个文件分 别向 SD1 和 SD2 上的 SD 卡烧写系统。
mfgtool2-yocto-mx-evk-sdcard-sd2.vbs同4

​ 其他的.vbs烧写脚本都用不到可以删除。我们选择EMMC烧写脚本。

1.MfgTool工作原理

​ 1.连接USB到USB OTG接口

​ 2.选择到USB下载模式。(注意弹出TF卡否则电脑识别不出USB)准备就绪以后按下开发板的复位键,此时就会进入USB模式,一旦第一次设置好设备以后,后面每次连接就不会有任何提示了至此,开发板与电脑连接完毕,可以开始烧写系统

​ 连接好以后双击“mfgtool2-yocto-mx-evk-emmc.vbs”,如果出现“符合 HID 标准的供应商定义设备”就说明连接正常,可以进行烧写。

​ 烧写之前我们要把那四个文件放在MfgTool能找到的地方,进入如下目录:

L4.1.15_2.0.0-ga_mfg-tools/mfgtools-with-rootfs/mfgtools/Profiles/Linux/OS Firmware

​ 文件夹“OS Firmware”看名字就知道是存放系统固件的,我们重点关注 files、 firmware 这两个文件夹,以及ucl2.xml这个文件。MfgTool的烧写原理:先通过 USB OTG 先将 uboot、 kernel 和.dtb(设备树)三个文件下载到开发板的 DDR 中,注意不需要下载 rootfs。就相当于直接在开发板的 DDR上启动 Linux 系统,等 Linux 系统启动以后再向 EMMC 中烧写完整的系统,包括 uboot、 linuxkernel、 .dtb(设备树)和 rootfs。

1.firmware文件夹

​ 打开 firmware 文件夹,里面有很多的.imx 结尾的 uboot 文件、一个 zImage 镜像文件、很多.dtb结尾的设备树文件。这些文件都是NXP官方开发板使用的,不同的板子使用不同的文件,我们只需要关心只有三个文件:

脚本文件描述
zImageNXP 官方 I.MX6ULL EVK 开发板的 Linux 镜像文件。
u-boot-imx6ull14x14evk_emmc.imxNXP 官方 I.MX6ULL EVK 开发板的 uboot 文件。
zImage-imx6ull-14x14-evk-emmc.dtbNXP 官方 I.MX6ULL EVK 开发板的设备树

我们将我们的这三个文件替换掉这三个文件,而且名字必须改为一致。

2.files文件夹

​ 一样的只关心四个文件:

脚本文件描述
zImageNXP 官方 I.MX6ULL EVK 开发板的 Linux 镜像文件。
u-boot-imx6ull14x14evk_emmc.imxNXP 官方 I.MX6ULL EVK 开发板的 uboot 文件。
zImage-imx6ull-14x14-evk-emmc.dtbNXP 官方 I.MX6ULL EVK 开发板的设备树
rootfs_nogpu.tar.bz2根文件系统,注意和另外一个 rootfs.tar.bz2 根文件系 统区分开。 nogpu 表示此根文件系统不包含 GPU 的内 容, I.MX6ULL 没有 GPU,因此要使用此根文件系统

​ 如果要烧写我们自己编译出来的系统,就需要用我们编译出来的 zImage、 u-boot.imx 和imx6ull-alientek-emmc.dtb 和 rootfs 这四个文件替换掉表 39.2.2.2 中这四个文件。名字一样要改为上面一样的。

3.ucl2.xml文件

​ files 和 firmware 目录下有众多的 uboot 和设备树,那么烧写的时候究竟选择哪一个呢?这个工作就是由ucl2.xml文件来完成的。

最后打开烧写软件“mfgtool2-yocto-mx-evk-emmc.vbs”,点击“Start”按钮开始烧写,由于我们自己制作的 rootfs 比较小,因此烧写相对来说会快一点。烧写完成以后设置开发板从EMMC 启动,启动我们刚刚烧写进去的系统,测试有没有问题。

至此!系统移植到最后的烧写全部讲解完毕,希望对大家有所帮助,对整个操作流程和原理有更深刻的了解。

  • 12
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值