为pcDuino构建启动镜像(基于pcDuino3 nano)

本教程是讲述如何为pcduino3 nano构建启动所需要的各种镜像:uboot,kernel,devicetree和rootfs。(使用TF卡启动)
这里写图片描述


一 环境搭建

虚拟机搭建

本教程使用 vmware player 14 + 64位 ubuntu16.04 搭建虚拟机,安装好虚拟机后安装vim。
然后安装以下依赖库
sudo apt-get install lib32ncurses5 lib32z1 swig python-dev libncurses5-dev libncursesw5-dev
再安装一个功能强大的分区软件
sudo apt-get install gparted

安装交叉编译器

对于ARM平台来说,交叉编译器可以从Linaro网站上去下载。pcduino3 nano使用的是32位arm处理器,所以这里选择32位的交叉编译器,如下图中红色标记。
这里写图片描述
下载下来后把交叉编译器拷贝到虚拟机里(需要设置虚拟机和主机的共享文件夹,也可以在虚拟机里直接下载,ubuntu自带火狐浏览器)
在/home/wh下建立pcduino目录,然后在pcduino文件夹下建立以下目录
这里写图片描述
把下载好的交叉编译器拷贝到cross_compiler目录下并解压,
这里写图片描述
解压后的这个目录名字很长,不方便在终端里操作,这里修改为gcc-linaro
这里写图片描述
切到gcc-linaro下的bin目录,看到如下内容
这里写图片描述
这些是交叉编译器提供的elf文件(类似windows下的exe文件),我们把交叉编译器的路径添加到系统的环境变量里,输入以下命令
sudo vim /etc/bash.bashrc
在最后添加以下内容
这里写图片描述
保存并退出,然后输入以下命令(或重启终端也行)让刚才的添加生效
source /etc/bash.bashrc
输入以下命令查看编译器版本,遇到错误,
这里写图片描述
提示找不到文件(如果是32位的ubuntu就没有这个问题,64位的要安装一些兼容库),需要安装2个库,
这里写图片描述
安装好之后,再次查看编译器版本就可以了。至此,交叉编译器就设置好了,下面就可以开始编译源码了。


二 构建镜像

现在开始去构建启动镜像:uboot,kernel,devicetree和rootfs。一步一步来。

uboot

uboot是一段引导程序。板子上电后会先运行板子上的ROMCode(这是出厂固化好的),ROMCode会去加载uboot到内存里运行,后面就由uboot去引导内核启动。
这里选择下载最新的稳定版本的uboot,即u-boot-2018.03.tar.bz2,
这里写图片描述
下载好之后放到之前ubuntu中建好的uboot目录下,并解压,
这里写图片描述
首先cd到u-boot-2018.03/configs目录下,在这个目录下有很多配置文件,其中有pcduino3 nano的配置文件,
这里写图片描述
下面开始编译uboot:

  1. cd到uboot源码的根目录
  2. 输入以下命令把uboot跟板子进行适配
    make ARCH=arm Linksprite_pcDuino3_Nano_defconfig
  3. 输入以下命令进行源码编译
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
  4. 编译后在uboot根目录下生成了uboot镜像,u-boot-sunxi-with-spl.bin,把它拷贝到之前建好的images目录下

至此,uboot的编译结束了。

kernel和devicetree

首先下载linux kernel源码,这里选择4.9.75版本的kernel,下载好后把源码包放到kernel目录下并解压,
这里写图片描述
kernel下32位arm的配置文件是在arch/arm/configs下
这里写图片描述
在这个目录下,跟pcduino3 nano相关的配置文件是sunxi_defconfig,因为pcduino3 nano属于全志的sunxi系列。
这里写图片描述
经过sunxi_defconfig的适配之后,内核镜像是可以运行在任何sunxi系列的板子上的,而具体的板子则是用devicetree来区分的。cd到arch/arm/boot/dts目录下
这里写图片描述
这个是pcduino3 nano的devicetree文件,用来描述板子上具体硬件信息的。
有了以上了解,就可以编译kernel和devicetree了。

  1. cd到内核源码目录
  2. 适配
    make ARCH=arm sunxi_defconfig
    3.编译源码
    make zImage uImage dtbs LOADADDR=0x40008000 LOCALVERSION= -j4
    第一次编译在最后阶段生成镜像时会出错,如下
    这里写图片描述
    提示的意思是找不到mkimage,其实mkimage是在编译uboot时生成的,存放在uboot根目录下的tools目录下,只需要把tools目录添加到环境变量里去就行了,和添加交叉编译器路径的操作方式一样。
    设置好后,再次编译内核,就通过了,同时,设备树也被编译好了。

编译好的内核镜像是在arch/arm/boot下,uImage就是需要的内核镜像,
这里写图片描述
编译好的设备树是在arch/arm/boot/dts下,名字是sun7i-a20-pcduino3-nano.dtb,
这里写图片描述
把这2个镜像文件拷贝到之前建好的images目录下。至此,内核和设备树的编译就完成了。

rootfs

rootfs,即根文件系统,是内核启动后加载的第一个文件系统,这就是”root”的意思。制作根文件系统一般使用busybox,该软件提供了很多有用的工具。

下载最新的稳定版本的busybox — 1.28.3 stable版本,放到之前建好的rootfs目录并解压,
这里写图片描述
在该目录下再新建一个目录rfs,用于存放根文件系统里的文件,
这里写图片描述
编译步骤:

  1. cd到rfs目录,然后执行以下命令建立常用目录
    sudo mkdir root home etc dev usr lib tmp mnt sys proc
    sudo mkdir usr/lib
  2. cd到busybox-1.28.3目录下,修改该目录下的Makefile
    找到ARCH ?= $(SUBARCH),修改为ARCH = arm
    找到CROSS_COMPILE ?=,后面添加arm-linux-gnueabihf-
  3. 配置busybox组件
    cd到busybox-1.28.3目录下,输入以下命令
    make ARCH=arm menuconfig
    弹出如下界面
    这里写图片描述
    进入Settings->Build Options,按空格选中[*]Build static binary (no shared libs)
    这里写图片描述
    同样在这一级目录,找到Installation Options,选中(./_install)Destination path for ‘make install’ (NEW),这个是设置编译busybox后生成的文件所安装的目录。
    找到后按回车,
    这里写图片描述
    弹出如下界面,
    这里写图片描述
    按backspace键删除里面的内容,然后输入之前建好的rfs目录的路径,如下所示
    这里写图片描述
    按ok返回。
    选择Exit回到上一层,选择进入Linux Module Utilities,按空格取消Simplified modutils
    这里写图片描述
    选择Exit回到上一级,再选择Exit,会提示是否要保存配置,选择Yes就可以了
    这样,busybox的组件就配置好了,可以开始编译了。
  4. 编译安装
    make
    make install
  5. 把 busybox 源码目录下的 etc 的内容拷贝到 rfs 目录下的 etc 文件夹里
    这里写图片描述
  6. cd到rfs/etc目录下,建立passwd文件,然后vim打开添加以下内容
    这里写图片描述
  7. cd到rfs/etc目录下,修改初始化文件 inittab 和 fstab
    inittab修改如下
sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
tty2::askfirst:-/bin/sh
::ctrlaltdel:/bin/umount -a -r

::restart:/sbin/init
::ctrlaltdel:/bin/umount -a -r
::shutdown:/bin/umount -a -r
::shutdown:/sbin/swapoff -a

fstab修改如下

  proc   /proc  proc   defaults  0  0
  none  /tmp   ramfs  defaults  0  0
  mdev  /dev   ramfs  defaults  0  0
  sysfs   /sys   sysfs   defaults  0  0
  1. cd到rfs/etc目录下,修改profile如下
    这里写图片描述
    这个主要是修改根文件系统启动后的终端提示符,后面就可以看到效果。

  2. cd到rfs/etc/init.d目录下,修改rcS。文件系统加载后会去执行rcS里的内容。
    用以下内容覆盖rcS里的原始内容

    #!/bin/sh -x
    PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin:
    runlevel=S
    prevlevel=N
    umask 022
    export PATH runlevel prevlevel
    trap ":" INT QUIT TSTP
    /bin/hostname ramaxel
    /bin/mount -n -t proc none /proc
    /bin/mount -n -t sysfs none /sys
    /bin/mount -t ramfs none /dev
    /sbin/mdev -s
    echo /sbin/mdev > /proc/sys/kernel/hotplug
    mkdir -p /dev/pts
    mkdir -p /dev/shm
    /bin/mount -n -t devpts none /dev/pts -o mode=0622
    /bin/mount -n -t tmpfs tmpfs /dev/shm
    /bin/mount -n -t ramfs none /tmp
    /bin/mount -t debugfs debugfs /sys/kernel/debug
    mkdir /var/run
    /sbin/ifup -a
    
  3. cd到rfs/dev目录下,创建console文件
    sudo mknod console c 5 1

  4. cd到rfs目录下,把linuxrc重命名为init,这个是initramfs启动的特殊要求(linuxrc是ramdisk启动时需要的)
    mv linuxrc init

  5. cd到rfs目录下,把rfs目录下所有文件修改为root权限
    sudo chown -R root:root *

至此,根文件系统就设置好了。根文件系统的启动方式有很多种,如ramdisk,initramfs,也可以在存储卡上单独分一个区格式化为ext2分区,然后把rfs里的内容都拷贝过去,最后还有网络启动。本教程使用initramfs方式启动,因为这种方式设置起来比较简单。

设置initramfs启动:

  1. cd到内核目录下
    这里写图片描述
  2. 输入make ARCH=arm menuconfig,弹出内核组件设置界面
    这里写图片描述
    操作方式和busybox设置是一样的。进入General setup,找到Initramfs source file(s),然后回车,
    这里写图片描述
    在弹出的输入框中写入rfs路径,
    这里写图片描述
    然后保存退出,再编译一次内核生成镜像文件。
    make zImage uImage dtbs LOADADDR=0x40008000 LOCALVERSION= -j4
    新编译出的uImage会比之前未设置initramfs编译出来的uImage大,这是因为我们把根文件系统也编译到内核里了。这样启动时就可以自动从镜像中得到根文件系统,然后加载。这种方式的缺点也显而易见,因为根文件系统融入了内核,以后每次修改根文件系统里的东西都需要重新编译内核,但是胜在简单方便。

后面启动就用新编译出的uImage。

三 烧录

至此,我们就集齐了uboot,内核,设备树和根文件系统的镜像,把它们都放在之前建好的images目录下。
这里写图片描述
接下来,第一步要做的是往TF卡里烧写u-boot-sunxi-with-spl.bin

烧录uboot镜像

因为pcduno3 nano板子上的romcode功能比较弱,只能直接读TF卡,不能解析文件系统,所以需要把uboot镜像烧到TF卡上没有文件系统的地方,这就需要对TF卡进行特殊格式化,把卡起始的一段存储空间(大约2MB)留给uboot镜像,剩下的空间格式化为FAT32。
这里使用linux下一款分区工具,GParted,免费且功能很强大(前面已经安装过了)。

操作步骤:

  1. 把TF卡插到电脑上(这里使用SD卡套,也可以使用读卡器),并设置与虚拟机连接。(TF卡与虚拟接机的连接可以百度之,vmware player和virtualbox操作方式不太一样)
  2. 启动GParted
    sudo gparted
    弹出如下界面,可以看到TF卡的信息,如果没看到可以在右上角(/dev/mmcblk0那个位置)点一下,选择我们的TF卡(TF卡是mmc设备,所以名字是mmcblk0),如果使用的是读卡器,那么对应的设备名就会变成sdx,注意区别,
    这里写图片描述
  3. 设置分区。点击菜单栏里的Partiton,选择Delete,删除分区
    这里写图片描述
    变成如下所示,TF卡上所有空间变成未分配状态,
    这里写图片描述
    再点击Partition,选择New,
    这里写图片描述
    在弹出的界面里进行设置,设置如下,
    这里写图片描述
    Free space preceding就是指TF卡起始位置预留的空间,这里留2MB就可以了(因为uboot镜像只有500多KB)。
    File system选项选择fat32,也就是把卡的剩余空间格式化为fat32。
    设置好后点击Add,返回,弹出如下界面,
    这里写图片描述
    单机红圈里的绿色对号,在弹出的提示中点击Apply,
    这里写图片描述
    然后就开始格式化了(时间稍微会有点长)。等格式化好之后,关闭gparted,此时可以烧录uboot镜像了。
  4. 烧录uboot。
    使用dd命令进行烧录,我这边TF卡对应的设备是/dev/mmcblk0,其它电脑上可能稍有不同,具体可通过sudo fdisk –l进行查看
    这里写图片描述
    /dev/mmcblk0是指整个TF卡,/dev/mmcblk0p1是指格式化为fat32的那部分空间。烧录是使用/dev/mmcblk0。
    cd到images目录下,然后使用下面的dd命令进行烧录(烧到TF卡上8KB位置处)
    sudo dd if=u-boot-sunxi-with-spl.bin of=/dev/mmcblk0 bs=1024 seek=8
    这里写图片描述

内核和设备树镜像

剩下的还有内核(内含根文件系统),设备树镜像,只需要把它们直接拷贝到TF卡上的fat32分区里就可以了。

四 启动

下面就可以开始启动了。观察信息使用常用的串口。

连接串口

pcduino3 nano上提供了串口,可以通过串口转usb与pc机相连。我们在windows上查看串口打印,使用secureCRT(需要自行破解,嘿嘿),也可以使用linux下的kermit。
串口配置如下(COM口数字需要到设备管理器里查看,每台电脑可能不一样)
这里写图片描述

启动 — 动起来

pcduino3 nano是使用usb口供电的,再加上串口转usb,所以总共需要占用2个usb口。
上电后,连接好secureCRT,串口首先会打印uboot的版本信息,说明uboot运行起来了。
这里写图片描述
和我们之前下载的uboot版本一致。
输出信息提示:从FAT分区里加载环境变量失败,转去使用默认环境变量。
接下来,发现一直重复打印如下信息,最后停在一个终端提示符“=>”那里。
这里写图片描述
**这是怎么回事呢?**因为uboot是用来引导内核的,但是内核在哪里uboot并不知道,需要通过简单的设置来告诉uboot。
下面就来设置下uboot吧。首先是设置uboot环境变量,之前启动时提示说从FAT里加载环境变量失败就去使用默认的环境变量,那么默认的环境变量在哪里呢,接着上面的终端提示符,输入printenv并回车,发现打印了很多信息,
这里写图片描述
打印的这些信息就是uboot默认的环境变量,这些变量信息没有存放在TF卡的fat32分区里,此时在终端提示符下输入saveenv并回车,就可以把默认环境变量保存到fat32分区里的uboot.env文件里,
这里写图片描述
此时重启板子,就不会再出现找不到环境变量的问题了,不过为了操作的连续性,暂时不要重启,等都设置好后再去重启。
接下来,我们让uboot去找到内核和设备树镜像,输入以下命令,
这里写图片描述
第一条命令的意思是从mmc设备的第0个分区(也就是fat32分区)找到uImage,并把它加载到内存地址0x42000000处;第二条命令的意思是从mmc设备的第0个分区(也就是fat32分区)找到sun7i-a20-pcduino3-nano.dtb,并把它加载到内存地址0x43000000处。

这样内核和设备树都已经加载到指定位置了,然后让uboot去引导它们,输入以下命令启动,
这里写图片描述
bootm命令用于引导内存中内核,根文件系统和设备树镜像,它的参数是这三个文件在内存中的地址,而且顺序是固定的,必须第一个地址对应内核,第二个地址对应根文件系统,第三个地址对应设备树,如果某个不需要,就用横杠”-“代替。由于使用initramfs启动,根文件系统融入到内核里了,所以这里就不需要提供根文件系统的内存地址了,就用横杠代替。

回车后,系统就启动啦,经过一串串打印后进入根文件系统,如下图(还记得fff是怎么来的吗,提示:profile)。
这里写图片描述
如果此时重启板子,会发现又需要重新设置上述命令来让uboot引导内核,比较蛋疼,还好uboot功能比较强大,可以通过添加环境变量来设置启动时执行的命令。
我们在uboot启动时进入的那个终端提示符开始(如果找不到内核镜像最后就会进入这个终端提示符,或者启动倒计时的时候按下任意键),输入下面的命令,
这里写图片描述
也就是把之前的加载和启动命令写成环境变量保存起来。其中bootcmd这个环境变量比较特殊,uboot运行时如果发现有bootcmd这个环境变量就会去执行bootcmd来引导内核镜像,这是uboot源码里写好的,感兴趣的童鞋可以研究下。

此时输入boot然后回车,就可以运行起来了。
这里写图片描述
下次重启就可以自动运行这些命令了。

五 总结

本教程主要记录了如何为pcduino3 nano构建启动镜像并运行这些镜像,整体过了一遍嵌入式开放的准备阶段所需要做的事,还是比较粗陋的,有很多可以改进的地方。以上这些方式还是比较传统的操作方式,而且随着时代的发展,一些更优秀的工具正在逐步取代这种传统开发方式,如buildroot,yocto等。以后会出一篇讲述如何使用buildroot的教程,敬请期待。
如果有写的不对的地方,希望能留言指正,谢谢阅读。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值