下为转载信息:
【这是一个文章系列的一部分,介绍基于MK802这类MiniPC的扩展开发,并展示他在计算机视觉、机器人控制方面的潜能】
【欢迎转载,但请保留原始作者信息(Shikai Chen, http://www.csksoft.net),以及指向本文原始出处的链接!】
【访问目录:基于MK802 MiniPC的扩展开发应用-系统自制(http://www.csksoft.net/blog/post/mk802_dev_sysbuild.html)】
基于MK802 MiniPC的扩展开发应用-系统自制
这是一个文章系列的一部分,介绍基于MK802这类MiniPC的扩展开发,并展示他在计算机视觉、机器人控制方面的潜能
欢迎转载,但请保留原始作者信息(Shikai Chen, http://www.csksoft.net),以及指向本文原始出处的链接!
访问目录:基于MK802 MiniPC的扩展开发应用-简介篇(http://www.csksoft.net/blog/post/mk802_dev_intro.html)
revision: 5
为嵌入式设备定制系统是一个需要很多背景知识的技能。这里无法假设各位的背景知识结构,因此在文章组织上,我将默认拥有相关经验的直接操作过程写在最前面。随后是为新手提供的简单的背景介绍。最后的部分是对一些问题的深入探讨。对于一些超出本文范围的内容,我也给出了对应的参考文献,方便感兴趣的人进一步的研究。
为何需要系统自制?
MK802出厂内置的是Android 4.0 (ICS) 系统,虽然这个系统在芯片厂家和设备厂家的优化下具有最好的硬件兼容性,但是对于我们接下来所需要进行的二次开发来说,采用其他传统的Linux发行版将比较方便。
当然也有人会坚持继续采用Android,这点其实没有任何问题,但需要注意这个平台的一些不同之处。对此在本文的后半部分我将专门讨论这一问题,大家可以根据自身情况来做取舍。
所谓系统自制,其实就是让MK802运行由我们为他所定制的基于Linux Kernel的系统,在这个过程中我们将加入后面开发所需要的驱动/组件,删除无关的组件,并作必要的性能优化。从而构建出适合于进行扩展开发所需要的系统环境。
而这个过程将包含如下几个阶段:
1. 配置并编译Linux Kernel/uboot等核心
2. Rootfs的选取和打包
3. 向MK802部署自制系统
MK802支持的系统部署模式
MK802,或者说采用Allwinner A10芯片的设备,支持从两种渠道启动系统:内部NAND Flash以及外部的SD卡。他的具体运作机制,可以参考[7]。这个方式很类似PC的多重启动:在启动过程中,如果发现SD卡中存在有可以用的系统时,则直接从SD卡引导系统。否则才会去加载内置的NAND flash中的系统。
这对我们来说是一个好消息,这样我们的自制系统可以直接保存在一张外部的SD卡上。一方面可以保证原生的Android系统不被破坏,另一方面也有利于对我们的自制系统进行备份。
当然,如果愿意,也是可以将自制系统写入内部的NAND flash。这部分的操作其实与保存在SD卡上类似,可以参考文献[10]。
本文将采用SD卡上系统的模式进行介绍。
图:可以将自制系统安装在SD卡上
会不会破坏设备?无法使用?变砖?
在前文已经介绍了,如果将系统保存在SD卡上,则完全不会破坏MK802自带的Android系统。除此之外,Allwinner A10芯片设计上也有一个很好用的模式:支持USB固件下载[7]。如果内部的NAND flash上的系统被破坏,只要芯片本身没有损坏,则可以通过usb连接PC,通过官方提供的LiveSuite工具进行原始固件恢复[12]。因此即使是将自制系统部署在内部对NAND Flash上,也完全不必担心变砖。
这里提供给大家三个方案供选择:
1- 完全定制自己的系统
这是相对繁琐,最本质的操作过程,但有无可比拟的优势。
2- 使用社区有的编译打包脚本
使用社区提供的编译脚本,简化第一种方案的流程。同时在必要的时候可以重新按照方案1的部分流程做更进一步的定制。不过这里还需要做一些额外的人手干预修改编译行为。
3-使用别人提供的系统镜像
相对简单,但存在一些限制。具体采用哪个方案,大家可以看每个方案前的介绍自己衡量。
2012/11/22 updated:
这里也直接发布出我制作的系统完整镜像,大家可以直接按照方案3的描述,直接将镜像写入自己的SD卡。该系统按照本文的描述进行配置,采用linaro 12.07 ubuntu armhf rootfs以及带有USB摄像头等必要驱动支持的linux kernel。
可以直接使用命令:
将镜像写入SD卡,请确保SD卡容量>=8Gb。具体细节,请阅读方案3
同时,为了方便大家,这里也给出我预先编译的Linux Kernel/Uboot包,他采用方案2描述的hardware pack形式系统,可以参考方案二了解细节。其中包含了对USB Camera、USB串口的支持。
下载地址:http://www.csksoft.net/data/mk802/mk802_hwpack_cskbuild.7z
1. 方案1 – 完全定制
这里首先介绍几乎是从头到尾完全定制的方案,即下载对应的linux kernel、uboot进行编译,寻找合适的rootfs tar包,并将它们合成并部署到SD卡上。
这样的过程虽然繁琐,但是好处也是直接使用其他人现成系统无法比拟的:
1. 完全的系统定制和控制
自行编译的kernel可以按照每个人的喜好加入需要的设备驱动或者移除不必要的驱动。比如如果需要支持UVC的免驱摄像头,则目前网上发布的所有MK802镜像都不支持。必须自己编译kernel。
同时自己编译的kernel也使得后续开发kernel驱动程序成为可能。
2. 了解其中的运作机制
虽然本文针对的是MK802系统,但是几乎所有arm设备乃至嵌入式设备的系统定制过程都是如此或者相近的。了解具体的过程,有利于举一反三。
不过目前社区已有将这里描述的过程编写成了辅助脚本,因此可以简化很多手工劳动。对于希望完全自己定制系统,但又不想花费太多时间在具体操作上的朋友,不妨在阅读完1.3.获取MK802编译工具集合 之后直接跳至:方案2 - 使用打包脚本简化流程 阅读,在必要时再重新在本节中需求细节。
1.1. MK802的可用文档/代码资源:
MK802所基于的是国内芯片厂商全智(Allwinner)的A10系列芯片。虽然厂家提供的BSP是不对外公开的,但有目前由社区维护的uboot和linux kernel的源代码可用[13]。
文档方面,在参考文献[13]也给出了诸如IO端口模式设置等信息。在本系列文章的第二部分我也将介绍一些可以用于做hack的文章。
目前社区可用的Linux kernel的代码来自:
以及:
可用的uboot代码来自:
可用的rootfs主要采用的是Linaro官方[15]发布的ubuntu/alip的armhf发行版。
对于具体的编译过程和配置,下面将具体介绍。本文采用的方法主要来自[13]以及[14]的介绍。
1.2. 前期准备
对Kernel和Uboot的编译工作需要在PC上交叉编译,因此首选需要有一台安装有Linux的PC。这里推荐使用Ubuntu 12.04 LTS发行版,因为大部分的工具可以直接使用apt-get安装。这里所有的操作都将在命令行模式下进行,因此这里假设大家拥有一定的Linux命令行使用基础。
整个编译和定制过程需要如下的软件包:
基本的开发工具链,gcc\make等。
Ubuntu发行版可通过如下命令安装:
arm-gcc交叉编译器
这里我们使用的是支持硬件浮点的arm-gcc 4.6版本(arm-linux-gnueabihf-gcc)。
使用ARM硬件浮点后可以提高系统地工作性能。关于它和软件模拟浮点的问题和性能讨论可以看本文后半段的进一步探讨部分。
如果是Ubuntu发行版,可以直接使用apt-get获取:
此外,也可以在linaro社区网站[15]下载到他们的arm-gcc。不过需要注意的是,较高版本的arm-gcc在编译kernel时可能存在问题,将导致产生的kernel出现异常的情况[16]。
Git
由于编译过程是直接从各社区的代码仓库中获取代码,因此需要准备好对应的客户端。
如果是Ubuntu发行版,可以直接使用apt-get获取:
7zip、uboot-mkimage、wget
将用于系统定制打包阶段的操作。
libusb-1.0、libncurses-dev
编译阶段或者工具需要依赖的库
1.3. 获取MK802编译工具集合
除了上述进行代码编译和打包所依赖的工具外,MK802还需要有专门的打包工具以及自制系统所需要的配置文件。得益于[17]的工作,这部分可以从社区的git repos获得。
这里假设我们将在~/mk802sys这个目录下进行后续的编译打包工作。
首先使用命令:
获取对应的工具集合。我们主要将会用到fex2bin和bin2fex这两个工具。对于他们的使用问题,将在本系列文章的第二部分介绍,或者参考文挡[13] [17]。
在完成了代码库的clone后,需要进入该目录进行编译。
make
如果一切顺利,应该可以看到如下的画面:
图:编译a10配套工具
此时在sunxi-tools目录下应该能看到fex2bin这类程序。为了能让后续的编译工作找到这些程序,需要设置$PATH环境变量指向这个目录:
export PATH=`pwd`:$PATH
1.4. MK802的配置文件
从这一步开始将进行正式的系统编译过程。如果觉得相对繁琐,可以直接阅读方案2: 使用打包脚本简化流程。
接下来需要下载MK802所用到的各类配置,如uboot的配置文件,用于rootfs的补丁配置、以及后续文章会提到的Allwinner A10芯片特有的fex配置脚本。
同样适用git进行clone:
随后进入a10-config/script.fex目录,可以找到名为mk802.fex文件。该文件用于uboot阶段对系统设备的配置。修改它可以改变系统的一些特性,这部分将在后续系列文章介绍。
这里我们使用前面编译得到的fex2bin工具,将上述文件转换成用于部署的二进制版本:
fex2bin mk802.fex script.bin
mv script.bin ~/mk802sys/output/kernel/
另外还需要制作uboot的启动脚本,他的源代码位于:a10-config/uboot。这里使用mkimage进行打包:
mkimage -A arm -O u-boot -T script -C none -n "boot" -d mk802.cmd boot.scr
mv boot.scr ~/mk802sys/output/kernel/
此时uboot阶段的几个配置文件已经在~/mk802sys/output/bootloader/就绪。在接下来的编译过程中还会用到该代码树中的其他文件。
1.5. Uboot的配置和代码编译
可用于MK802的uboot代码位于:https://github.com/linux-sunxi
git clone https://github.com/linux-sunxi/u-boot-sunxi.git
由于我们将使用armhf-gcc编译,在开始编译uboot前,需要修改下编译脚本。具体原因见后文的进一步讨论部分。
打开位于u-boot-sunxi/arch/arm/cpu/armv7的文件config.mk:
将其中的-msoft-float部分删除:
图:删除config.mk中的软浮点编译开关
对于1Gb内存的设备,还需要做如下改动:
原始的uboot代码是针对512Mb内存的MK802设备的,对于1Gb的配置,需要修改如下文件:u-boot-sunxi/board/allwinner/mele_a1000/dram.c
将其中的.density = 2048修改为:.density = 4096。
图:修改dram.c适应1Gb内存设备
该语句用于uboot初始化DDR内存控制器,上述的修改将原先单颗芯片的容量从原先的2048Mbit(128Mbyte)提高到4096Mbit(512Mbyte)
在完成了上述配置修改动作后便可以编译uboot,在u-boot-sunxi目录下执行:
这里采用了4线程编译(-j4),可以按照自身需求修改并行编译的线程数量。
在完成编译后,使用如下命令将uboot程序复制到用于后续打包的目录下:
cp u-boot-sunxi/spl/sunxi-spl.bin ~/mk802sys/output/uboot/
cp u-boot-sunxi/u-boot.bin ~/mk802sys/output/uboot/
1.6. Linux Kernel的配置和代码编译
目前可用于MK802的Linux Kernel有两个主要的repos可用:
以及
从amery repos的最新README来看,后续的开发均已经转移到了linux-sunxi上开发了,linux-sunxi repos上的linux kernel也相对较新(3.4)。由于我当时尝试的kernel还位于amery上,因此这里仍旧以amery repos作为例子给大家介绍。实际2个repos的编译过程完全一致。
首先回到这次的工作根目录下:
cd ~/mk802sys
clone amery repos的linux kernel
git clone https://github.com/amery/linux-allwinner.git
如果需要使用与我当时编译kernel一致的版本,可以checkout 如下的版本:
23e5456879db0175f571dec43095c49e181e0b10
在完成代码下载后,进入linux-allwinner目录:
随后进行配置,这里推荐使用Toby Corkindale[18]的kernel配置: http://dryft.net/mk802/config_3.0.36-t3.gz
该配置相对于MK802的默认配置做了如下的改动:
- 删除了CPU频率动态调节(CPUFreq)
- 修改IO调度策略
- 增加了一些USB外设的支持
具体的细节大家可以自行用diff命令对比,这样的修改在使用上会发觉Kernel相比使用默认配置的版本运行的快很多。但是由于关闭了CPU频率调节支持,导致MK802的CPU将永远满负荷运作,因此功耗将比极高。如果对功耗看的比较重要,不妨参考默认的配置重新增加CPUFreq支持。
不过他的配置也有不足之处,主要是没有加入UVC Camera等摄像头的驱动。这在后面我们做扩展开发时候会显得不方便,因此建议在这里一起将这些驱动程序加上。
具体过程为,首先下载上述的config.gz,使用gunzip命令解压缩
gunzip -c config_3.0.36-t3.gz > .config
此时Linux kernel的编译配置已经设置为Toby Corkindale的版本了。然后我们将按照自己的需求,对他做进一步配置:
此时将展示配置界面:
图:Menuconfig画面
对于Linux Kernel的配置也是一个复杂的话题,就不做具体展开了。这里仅介绍如何增加UVC以及gscpa摄像头驱动的编译支持:
进入[Device Drivers]->[Multinedia support]。首先将光标至于Video capture adapters项目上,按下空格后,在标题前方会出现[*]符号,表示该项目将参与kernel的编译:
图:加入Video capture adapters子项目的编译
随后按下回车,进入后续的菜单:[Video capture adapters]->[V4L USB devices]。此时用前述办法,分别按空格选中USB Video Class (UVC)以及GSPCA based webcams条目。(注意,此时前缀是M,表示采用外部模块方式,*-表示对应驱动将和linux kernel编译在一起)
图:加入UVC和gspca的驱动编译
目前市面上的绝大多数摄像头均为UVC协议[19]的,也就是常说的免驱动摄像头,这是由于Windows下自带了UVC驱动。在Linux下UVC的驱动程序不一定会带有,就像这里我们遇到的,需要自己编译内核加入。而比较老式的摄像头以及一些特殊芯片的摄像头(比如支持高清的),以及Kinect传感器,则需要使用GSPCA类的驱动程序。
由于GSPCA类的摄像头不像UVC类使用了USB协会的统一规范,所以不同摄像头(芯片)都会有不同的驱动程序存在,这里就需要我们按照需求加入。
将光标至于GSPCA based webcams,按回车进入下一级菜单:
这里可能会用的驱动有:
Kinect sensor device USB Camera Driver – Kinect传感器
OV534 OV772x USB Camera Driver – Sony PS eye摄像头
ZC3XX USB Camera Driver – 老式的中芯微的摄像头
按照自身的需求选择好对应的驱动程序后,可以按ESC返回上级菜单,一直到最后提示是否保存配置并退出:
图:保存config并退出
如果需要使用MK802的默认配置,可以使用命令:
此时默认的基于Allwinner A10的linux kernel配置将会替代之前的配置,为此需要做好必要的备份工作。
在完成了kernel配置后,就可以进行编译工作了,使用如下命令:
此时将使用4线程并行编译的模式开始编译linux kernel部分,结果将自动调用uboot-mkimage打包成uboot的uImage格式,保存在arch/arm/boot目录下。
图:完成对kernel的编译
使用如下命令将uImage kernel镜像存放在输出目录下:
不过光有Linux kernel还不够,还需要进一步编译对应的外部内核模块文件,这些文件将在部署阶段复制到目标rootfs的/lib/modules目录。
使用如下命令编译对应的内核模块。这里要注意的是内核模块是与kernel的版本、所用的配置一一对应的,不同的kernel是不能相互加载彼此的内核模块的(否则可能会出现各种奇怪的状况)
在完成编译后,使用如下命令将编译的内核模块暂时存放在~/mk802sys/output/modules目录下。
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=~/mk802sys/output/modules/ modules_install
1.7. Rootfs选取
目前除了原生的Android 4.0系统外,社区也提供Ubuntu、Debian等诸多发行版的rootfs供使用。关于rootfs的选用可以按照大家的喜好。我使用的是由Linaro基于Ubuntu 12.07定制的rootfs版本。
该rootfs使用了armhf模式产生,因此在性能上会比传统的基于armel的rootfs快一些。关于这方面的细节和选择,可以看本文后半段的进一步探讨章节。将有比较深入的探讨。
可以通过如下链接获取该rootfs:
另外,参考文献[17]也给出了基于12.06 armel的rootfs供选择。此外也可以通过其他人提供的镜像获取rootfs,这部分的操作在后续部分会介绍。
可以使用命令
将上述rootfs下载到本地,一般rootfs都比较大,上述rootfs压缩包为239.4Mb。我们将在后续步骤使用这里的rootfs包。
1.8. 将系统部署到SD卡
现在可以准备一张至少为4Gb容量的MicroSD(TF)卡进行最终系统的部署了。
将存储卡通过usb读卡器连接至Linux PC,或者通过虚拟机的设备映射机制连接。
图:对于虚拟机中的Linux系统,可以使用设备映射机制将SD读卡器连接至系统
为了证明SD卡已经连接入设备,且知道当前SD卡的设备名,使用如下命令:
图:利用fdisk查看连接至Linux系统的存储设备列表
上图为执行了上述命令的效果,可以在其中找到一条/dev/sdd的项目,可以看到它的存储空间为7964MB,正好与我使用的8Gb TF卡一致。此时需要记录下这个/dev/sdd设备名。接下来将使用它进行系统的部署。这里要非常小心,弄错设备名的话,可能会破坏其他磁盘的数据。
如果觉得使用fdisk看来过于复杂,也可以在系统插入SD读卡器后不久,使用dmesg命令查看来自kernel的日志:
图:dmesg|tail的输出
可以找到类似上图红框部分的文字,其中sdd就是前面插入sd卡对应的设备名。将他加上/dev/前缀(/dev/sdd)就是我们后续需要用到的完整名称了。后续命令中,请将下划线的部分替换成你实际sd卡的设备名。
SD卡格式化、分区:
接下来需要对SD卡进行格式化和重新分区操作,此时原本的SD上数据就会抹去,要做好必要的备份。
有一些发行版,例如ubuntu会自动对插入的sd卡进行挂载,所以为了确保万一,可以执行umount命令保证要操作的sd卡不再被设备使用:
随后删去原先的分区信息
上述语句将向/dev/sdd设备前1M字节写入0。这是一个比较危险的操作,请务必保证使用了正确的设备名。
接着可以使用parted命令进行分区操作(如果系统没有这个程序,可以用apt-get安装):
sudo parted /dev/sdd –script mkpart primary fat32 2048s 16MB
vfat_end=`sudo fdisk -lu /dev/sdd | grep /dev/sdd1 | awk '{ print $3 }' `
ext4_offset=`expr $vfat_end + 1`
sudo parted /dev/sdd --script mkpart primary ext4 ${ext4_offset}s -- -1
上述操作将SD卡分为2个分区,一个将采用vfat(即fat32)文件格式,用于存放kernel和uboot启动脚本配置,另一个将采用ext4文件系统,存放MK802的rootfs。
上述分区动作也可以使用图形化的工具或者cfdisk工具进行。
随后分别完成这2个分区文件系统的初始化:
sudo mkfs.ext4 /dev/sdd2
此时SD卡的分区就绪
部署Uboot:
由于在芯片通电后无法直接从fat32这类文件系统读取文件执行,因此uboot不能保存在上述的任何一个分区中。而是需要将他保存在SD卡原始存储数据块的开头位置。
使用命令:
sudo dd if=~/mk802sys/output/uboot/u-boot.bin of=/dev/sdd bs=1024 seek=32
部署kernel和uboot配置脚本:
按照当前uboot的设计,kernel和uboot用到的配置脚本都存在于sd的第一个fat分区上,这里只要简单的将对应的几个文件复制过去即可。
挂在sd卡的vfat分区
sudo mount /dev/sdd1 `pwd`/sdmount
复制文件
卸载sd卡分区
部署rootfs:
挂载sd卡的ext4分区:
将前面的rootfs包(linaro-precise-alip-20120724-274.tar.gz请替换成自己下载的rootfs包名)解压缩:
sudo tar xvf linaro-precise-alip-20120724-274.tar.gz -C rootfs --numeric-owner
解压缩将花费一定的时间。这里需要注意从linaro社区获得的rootfs包与传统的不同,tar解压缩后,实际的rootfs目录位于其中的rootfs.tmp/binary/boot/filesystem.dir子目录。
复制解包的rootfs到sd卡:
添加Linux kernel配套的内核模块:
将前面编译的内核模块复制到目标rootfs的/lib/modules目录下:
添加MK802额外的二进制库:
按照[17]的说法,虽然目前Allwinner A10厂家并没有为传统Linux的X-windows GUI提供Mali GPU的驱动,但是社区的用户态驱动也可以起到一定作用。这部分的库文件可以通过如下git repos获取:
https://github.com/cnxsoft/a10-bin
首先同样先将这个repos clone到本地:
随后将其中的二进制库复制到MK802的rootfs中即可:
sudo cp -r –f lib/ `pwd`/sdmount/
增加MK802的启动模块配置文件至rootfs中:
最后将sd卡挂载,一切就绪。
2. 方案2 - 使用打包脚本简化流程
该部分的方案实质与方案1一致,由于很多细节的操作已经编写成为了脚本,因此可以省去很多人力劳动。不过其实还需要一些额外的修改和扩展,才能满足本文的后续开发需要。这里假设读者已经按照方案1中1.3.获取MK802编译工具集合,在开发系统中安装了必要的工具和库。
2.1. 下载编译打包脚本
这里和方案1一样假设整个开发的根目录是~/mk802sys
在此目录下,clone由文献[17]提供的编译打包脚本:https:// github.com/cnxsoft/a10-tools
其中我们将使用如下2个脚本:
a10-hwpack-bld.sh
用于下载uboot、linux kernel的代码,并完成编译工作。最终产生名叫hardware pack的文件包。其中包括了uboot、linux kernel的程序镜像、相关的配置文件以及rootfs的补丁。
a1x-media-create.sh
用于将a10-hwpack-bld.sh生成的hardware pack包和rootfs包部署到目标SD卡上。
可以看出用这两个脚本,就可以把前文繁琐的过程极大地简化。一般只要准备好一个可用的rootfs包(请参考方案1中的 1.7. Rootfs选取 )。另外将uboot、linux kernel等文件打包成hardware pack的文件包也方便大家之间分享。
在下载完毕后,需要将该目录加入PATH环境变量,方便接下来使用
2.2. 标准的过程
这里先介绍脚本的正常使用过程,这个流程虽然简单,这是其中有诸多问题,需要后续处理。为此,下一小节将介绍如何对其中的过程做修改,产生符合需要的系统。
首先执行脚本
该脚本将自动完成方案一中uboot下载、编译,kernel下载编译的过程,如果网络通讯畅通,最快等待数分钟后在当前目录下得到类似如下的目录:
bld_a10_hwpack_2012.11.12
其中,2012.11.12是执行本脚本的日期。在该目录下存放了上面提到的hardware pack,比如mk802_hwpack_2012.11.12.7z
就此kernel和uboot已经编译好了,并且完成了打包。
接下来可以按照方案1中,1.7. Rootfs选取小节的介绍,下载一个符合自身需要的rootfs。这里同样采用linaro 发布的ubuntu 12.07 armhf rootfs:
接下来插入SD卡,确定SD卡的设备名(可以参考 1.8.将系统部署到SD卡 描述的方法),这里同样假设sd卡的设备名为/dev/sdd。
这里只要简单的执行如下命令,就可以完成对SD卡的分区、格式化、以及系统的部署
ROOTFS=linaro-precise-alip-20120724-274.tar.gz
sudo ./a1x-media-create.sh /dev/sdd $HWPACK $ROOTFS
(提示,将上述命令下划线部分替换为符合自身情况的文件/路径)
就此SD卡中就已经包含了自制的系统,并且一般情况下这个系统是可以工作的。但是上述过程存在几个问题:
在编译kernel的时候没有允许用户对kernel配置的环节,而是采用了MK802的默认配置。
这样的后果是一方面这样的kernel的工作性能并不是最优的,另外也缺少诸如USB摄像头驱动、USB串口驱动等我们后续开发所需要的驱动支持。
编译的uboot仅支持512Mb的内存。
对于1Gb内存的机型,则需要像方案1那样,额外的修改uboot代码。
a10-hwpack-bld.sh每天都会新产生一个hardware pack,并且重新下载最新的代码并且重新编译
相信大家都留意到了前面编译产生的bld_a10_hwpack_2012.11.12这类目录的日期后缀了。如果第二天重新执行上述脚本,则会重新生成一个hwpack目录,并且脚本会重新下载所有的代码并编译。这样无法维护一个稳定的版本。
2.3. 对脚本进行修改以及人工干预,产生符合需要的系统
正如上文所说,辅助编译脚本仍旧存在不足之处。这里就针对前文提出的问题,对脚本本身做出修改,并且对其中的一些步骤采用人工干预的手段。这样不但可以解决上述的问题,同时相比方案1来说仍旧省力很多。
对a10-hwpack-bld.sh的修改:
对该文件的修改主要目的是
取消每天都新编译产生一个hwpack的特性
修改编译所使用的linux kernel代码来源
这里提到的修改都比较简单,因此这里也不给出修改过的脚本了,大家按照下面的过程自己修改即可:
首先修改文件开头的blddate=`date +%Y.%m.%d`。这句话获取了当前系统日期,用于指导产生的hwpack目录。只要将它设置成一个常量,即可稳定产生的目录和编译的代码:
图:将编译产生目录的日期后缀固定
对于使用的linux kernel代码来源的问题,具体的讨论可以参考 1.6. Linux Kernel的配置和代码编译。 目前社区有2套kernel代码库可用,这里的脚本使用的相对比较新的代码树。不过如果希望使用Toby Corkindale[18]的优化kernel配置的话,还是需要切换到amery的repos上。
在脚本大约129行(不同版本可能会有差距)可以找到下载linux kernel代码的语句:
如果要修改成使用armery的版本,可以替换成如下语句
pushd linux-sunxi
git checkout 23e5456879db0175f571dec43095c49e181e0b10 #使用 1.6. Linux Kernel的配置和代码编译 提到的版本。
popd
图:修改下载的kernel代码源
完成了脚本的修改后,需要删除原先产生的hwpack文件夹,重新下载源代码并进行一次编译过程。而今后在此运行本脚本,只要原先的hwpack中的对应条目的目录存在,则不会重新下载/编译对应项目。我们可以利用这点特性,用方案1介绍的方式,手工修改uboot、linux kernel的代码和配置,完成我们的需求。
这里主要会涉及的修改和方案一中描述一致,包括:
- 修改uboot代码,支持1Gb的内存
- 修改linux kernel config,增加对USB摄像头、USB串口等设备的驱动支持
在前文的方案一中对这部分已经有了详细介绍,可以直接参考对应章节:
1.5. Uboot的配置和代码编译
1.6. Linux Kernel的配置和代码编译
其中,uboot的代码存放在bld_a10_hwpack_2012.11.12/u-boot-sunxi目录,linux kernel位于bld_a10_hwpack_2012.11.12/linux-sunxi。进入对应目录后,按照上述章节的介绍进行操作。
不过对于Kernel module的编译部署过程这里有所不同,在使用了make modules编译好内核模块后,需要将其安装到linux kernel代码目录的output目录下,a10-hwpack-bld.sh脚本将会从这里获取文件进行打包:
编译完毕后,不必像方案1里面介绍的复制编译结果,而是直接使用a10-hwpack-bld.sh脚本进行打包即可。
2.4. 仅更新SD卡上的Linux kernel、uboot和配置,不修改rootfs
如果希望修改之前部署系统的SD卡进行升级,比如替换编译的linux kernel,不必重新完全更新sd卡的所有内容。a1x-media-create.sh脚本支持仅仅更新hardware pack的模式。方式为在之前的调用命令中,将rootfs的文件名替换为norootfs即可:
ROOTFS=norootfs
sudo ./a1x-media-create.sh /dev/sdd $HWPACK $ROOTFS
(提示,将上述命令下划线部分替换为符合自身情况的文件/路径)
3. 方案3 – 使用别人的镜像文件
这里介绍操作最简单,但是灵活度有限制的方案。
目前网上有不少人直接将自制系统通过镜像文件(image file)的形式提供出来。一般只要准备一张足够容量的SD卡,使用dd等命令,将镜像文件写入系统即可。
不过目前已有的镜像都不能满足我们的后续开发,他们都没有包括usb摄像头驱动、usb串口的驱动。以及一些镜像使用的是armel(具体见进一步探讨部分)的rootfs,性能较差。
如果在后续开发中使用usb摄像头,则重新编译kernel再所难免。此时,就需要读者重新采用前面的2个方案进行系统自制了。
3.1. 可用的现成镜像
我制作的镜像
这里也直接发布出我制作的系统完整镜像,大家可以直接按照方案3的描述,直接将镜像写入自己的SD卡。该系统按照本文的描述进行配置,采用linaro 12.07 ubuntu armhf rootfs以及带有USB摄像头等必要驱动支持的linux kernel。
可以直接使用命令:
将镜像写入SD卡,请确保SD卡容量>=8Gb。具体细节,请继续阅读
Toby Corkindale[18]发布的基于ubuntu 12.06 armhf rootfs的镜像:
https://www.miniand.com/forums/forums/2/topics/82
镜像地址:http://dl.miniand.com/toby_corkindale/linaro-alip-armhf-t4.7z
这是比较推荐的现成镜像,因为它采用了armhf的rootfs,并且kernel的配置比较好。
Miniand发布的ubuntu系列镜像
https://www.miniand.com/forums/forums/2/topics/1
镜像地址(仅列举了部分):
1G 1080p Xubuntu:
http://dl.miniand.com/allwinnera10/ubuntu/lubuntu-desktop-12.04-4-1080p-1GB-miniand.com.img.7z
1G 720p:
http://dl.miniand.com/allwinnera10/ubuntu/lubuntu-desktop-12.04-4-720p-1GB-miniand.com.img.7z
这类镜像也不错,不过缺点是采用了armel的rootfs。
MK802官方论坛发布的Linaro Ubuntu 12.04镜像
http://www.rikomagic.co.uk/forum/viewtopic.php?f=2&t=90
镜像地址:http://store.cloudsto.com/downloads/lubuntu-1204-rikomagicuk.7z
同样采用了Toby Corkindale的kernel编译配置,不过似乎也是Armel的rootfs
这里列举的镜像都没有对usb摄像头提供驱动支持,如果不打算使用摄像头,则他们都是不错的镜像。这里以Toby Corkindale提供的镜像为例,介绍如何烧写到SD卡。其他的镜像均采用一样的操作方式。
首先将镜像文件下载到本地:
随后解压缩:
如果没有7z的命令行工具,可以参考1.2.前期准备 小节安装。
此时将解压缩得到名为linaro-alip-armhf-t4.img的文件。
利用dd命令直接烧录到sd卡。这里需要知道目标sd卡的设备名,可以参考 1.8. 将系统部署到SD卡 提到的方法,了解当前sd卡的设备名称,这里同样以/dev/sdd 这个设备名为例子介绍。
sudo dd if=linaro-alip-armhf-t4.img of=/dev/sdd bs=64k
这里务必确保下划线部分是你当前SD卡的路径(上文只是例子,需要你按照实际情况替换)。否则将出现文件损坏的情况。
执行该语句,一般几分钟后sd卡就包含了可以工作的系统了。
Windows用户的操作:
要给SD卡烧录镜像,也可以直接在windows系统下进行,无需Linux。方法是采用Win32-Imager-Writer这类工具[19]。关于他的使用,网上有不少资料也比较简单,这里就不介绍了。
4. 小结
到此3种自制系统的方式就介绍完了,如果一切顺利,在SD卡插入MK802,重启开机后,我们的自制系统就会被加载。如果连接HDMI到电视机或者显示器的话,就会看到自己选用发行版的桌面了,比如下图是基于Linaro Ubuntu的桌面画面(已做过修饰)。
图:自制系统的运行画面
不过也可能什么画面都没有,这时候如何判断自制系统是否开始工作了?或者可能出现什么问题?答案就是下一个系列文章要介绍的内容,如何扩展MK802的硬件,引出串口信息。(在我blog上已经有引出串口信号的文章,见[20])
最后,这个小结并不是本文的终结,对于想了解背景知识或者深入了解更多细节的朋友,我准备了背景知识和进一步讨论章节,供大家参考:-)
5. 背景知识
这部分为不熟悉这个领域的朋友编写。如果不感兴趣,可以掉过这个部分。
常见的基于ARM的Linux/Android设备的系统构成
(这里仅为简单介绍,具体细节请参阅有关文档)
在介绍系统自制的具体过程前,需要了解ARM-linux设备的常见核心组件以及启动顺序。
对于大家使用的x86 PC来说,启动过程是从系统通电->CPU执行位于主板Flash ROM中的BIOS代码完成底层硬件初始化->BIOS读取硬盘的主引导记录(MBR)加载OS引导程序->MBR引导程序加载目标系统的kernel (Windows是ntoskrnl.exe,linux是vmlinux)->OS kernel从文件系统读取其他系统组件。经历了上述过程后,就会出现大家熟悉的各种操作系统的界面了
执行顺序 | 执行的组件 | 描述 | 对应PC的阶段 |
1 | Uboot[3] | 完成对系统硬件的简单配置,比如初始化DDR内存控制器。 加载Linux Kernel并执行 | Bios 以及 MBR bootloader |
2 | Linux Kernel | Linux/Android系统的核心,接管所有硬件设备的控制,实现OS的基本职能 从Rootfs加载额外的组件 | 与PC相同 |
3 | Rootfs上的各类组件程序 | 负责从文件系统(rootfs)加载额外的组件/进程 | 与PC相同 |
上表只是一个粗略的对启动过程的划分,不过这样的划分正好对应了我们接下来所进行系统自制的三大部分:
1. Uboot
2. Linux Kernel
3. Rootfs
所谓Uboot[3]可以认为是类似于PC从通电执行的BIOS程序到os bootloader过程的程序。他其实也并非是ARM平台特有的。一般对于ARM设备而言,uboot程序会同系统一起存放在系统内部的NAND flash或者外部的SD卡上。正因为如此,我们就有机会去将原先的uboot程序替换成我们所定义的。
uboot的具体机制和功能显然超越了本文的范围,但是有一点需要明确:每种类型的arm设备的uboot都是专门特制的,且很少可以互用。这点其实和PC BIOS一样,因为uboot将负责初始化当前设备的底层硬件,不具备多少的通用性。对我们使用的MK802来说,市面能买到的1Gb内存和512Mb内存的两种机型的uboot程序也是不同的。
Linux Kernel相比uboot就具有一定的通用性,由于uboot已经完成了底层硬件的初始化,一般同一款芯片的kernel是可以共用的。但是由于linux kernel也包含了与设备硬件打交道的驱动程序,因此实际当中仍旧是每种机型使用不同kernel。
Rootfs即所谓的“根文件系统”,一种粗略但直观的理解是rootfs就是组成当前系统存储器上文件的合集,就好比PC磁盘中存储的所有文件一样。其中包含了一个系统所需的所有应用程序、库、工具和资源的集合。一般我们所说的Linux各类发行版,比如Ubuntu、Debian、Redhat甚至Android,他们之间的最大区别就在于rootfs的内容不同。换句话说,选用什么样的rootfs的内容,就决定了我们最终的系统的发行版类型:是Ubuntu、还是Debian还是Android(Android还需要Linux Kernel做出必要修改,此处为粗略说法)。
Rootfs中包含的基本是不与硬件直接交互的用户态应用程序,因此一般同一种构架的rootfs就可以在不同设备上相互使用。比如基于ARM的Ubuntu rootfs,几乎可以在所有的ARM设备上运行。当然这需要有一个前提,那就是uboot和Linux kernel已经支持了该设备。
不过在ARM设备上还存在软件浮点模拟(armel)/硬件浮点(armhf)的区别,这也给rootfs通用性造成了限制,具体可参考本文的进一步探讨部分。
Rootfs由于包含的的文件众多,一般在存储、传播过程中会将他们用tar[4]打包并压缩成一个文件(tar.gz/tar.bz2等),只有在部署目标系统的时候,rootfs的文件才会解包复制到目标的存储介质当中。同时,一般各大发行版的厂家或者一些爱好者也会将他们制作的标准系统的rootfs打包发布出来。在我们的定制过程当中,就会采用现成的rootfs包,作一些配置后直接使用。
6. 进一步探讨:
选用Android系统还是传统的Linux发行版?
虽然Android也是一个基于Linux Kernel的发型版本,但是相对于其他的Linux发行版而言具有一定的区别,主要表现在如下方面(具体的细节已经超越了本文的讨论范围,可以参考文献[1]):
1. 使用了Bionic Libc[2],导致开发工具不同
Bionic libc与其他linux程序依赖的glibc存在诸多不同,从提供的库函数类型和二进制接口规范(ABI)上都不一致。
这样导致的后果一方面是其他arm-linux的程序无法正常运行在Android平台上,另外就是针对Android平台编译的程序必须使用Binoiclibc-gcc来编译。而传统的arm-gcc则无法产生可以正常运行的程序。这在开发上存在一些不方便的因素
2. 仅仅包含少量的linux命令行工具
Android的目标应用场景是手机、平板、机顶盒等面向消费市场的终端产品,而非一个开发平台或者服务器系统。因此在rootfs中也仅包含了非常有限的命令行工具。一般如vim、ldd等工具都不会包含。当然这些工具可以自行加入,但这也给开发带了额外的负担
3. 一些基础的构架与其他系统明显不同
从引入了binoic libc造成的底层函数库不同外,Android的早期版本都不支持C++ STL。同时需要特殊的gcc也使得很多软件包没有现成的android版本。
其次在系统服务上,Android不采用X-server图形系统,其他部分构架也与标准Linux发行版不同。
上述列举了Android与传统Linux发行版的一些差异,虽然这些都不是不可逾越的障碍。但他们多少为开发造成了障碍。因此这里我更倾向于使用传统的Linux发行版。
当然,使用Android也有自身的好处,由于诸如OpenGL GPU 3D加速、视频编解码等驱动涉及了厂家的机密,因此厂家会将这部分的驱动以用户态程序方式提供,从而避免了内核驱动所必需开源的限制。这也意味着在当前ARM设备广泛使用Android而非其他传统Linux系统的环境下,芯片厂家往往仅对Android提供了最佳的软件支持,而很少提供针对传统Linux的驱动。又因为上述的系统差异,这类驱动也无法再传统Linux发行版中使用。就以MK802为例,芯片的3D GPU(Mali-400)和1080p视频的硬件编解码是无法在标准的Linux发行版中使用的。
如果需要开发使用了视频编解码或者需要3D硬件加速的程序,那么基于Android开发仍旧是比较明智的。而我们这里的二次开发主要将利用CPU的运算能力和其他外部设备,因此即使没有这些硬件加速支持,也显得无关紧要。
ARM软件浮点(Armel)与ARM硬件浮点(Armhf)的区别于对Rootfs的限制:
所谓软件浮点,本质就是通过软件模拟的方式实现浮点数的运算操作。相对于直接使用硬件浮点(fpu)而言性能上自然差很多。不过是使用软件模拟还是硬件执行,这是每个程序个体自己可以定义的事情。不过在ARM处理器构架上这个问题显得复杂一些:
对于ARM处理器,浮点数的硬件支持最早是从ARMv5构架才开始引入的。虽然现在主流的cortex-A8 cortex-A9构架的arm处理器都带有硬件浮点数的支持,但市面上仍有不少以往的arm芯片不支持硬件浮点。因此,为了保证兼容性,arm-gcc的默认编译仍旧会使用软件模拟的方式处理浮点数运算。不过开发者仍旧可以通过编译开关(-mfloat-abi=)来告诉编译器采用硬件来处理浮点数运算。为了最大限度的提高兼容性,arm-gcc提供了3种浮点数的支持模式:
1. soft
2. softfp
3. hardfloat
而这就是问题复杂化的开端。从名字上看,选项1和2似乎都是使用软件模拟的方式处理浮点操作。但实际上真正完全用软件模拟的方式是选项1。选项2和选项3其实都会让程序执行硬件浮点的指令完成浮点数计算。那么2和3的区别是什么呢?
这里就要涉及到二进制程序接口(ABI[8])规范的问题。(他的具体细节这里不做展开,可参考对应文献。)对于一个使用浮点数作为函数参数的函数调用过程,比如void foo(float value)。对这个函数的调用过程被编译器编译成arm指令后,就需要考虑如何将value这个浮点数进行传递的问题,这便是ABI接口规范需要定义的内容。对于上述的选项1(soft)来说,由于编译器认为程序使用的都是软件浮点数模拟,因此只能假设硬件设备是不存在fpu硬件的。因此value的数据将通过ARM的通用寄存器来传递。而选项2(softfp),编译器假设程序可能同时会在存在硬件浮点以及不存在硬件浮点支持的设备上执行,因此在函数调用时候,产生的arm指令仍旧使用和选项1一样的方式,即使用通用寄存器来传递value的数据。这样的好处就是可以保证模式1与模式2产生的程序之间相互通讯。这样做的现实意义是由于历史原因,还存在不少的旧程序使用软件浮点模拟(模式1)的方式编译,当这类程序运行在支持硬件浮点的机器上时,它们所调用的新式函数库就可以充分利用硬件浮点的特性,同时保证了旧式程序的执行。
而对于选项3(hardfloat)而言,此时编译器认为产生的程序只可能运行在有硬件浮点支持(fpu)的设备上,而对于这类设备,除了通用寄存器外,还存在一类称为浮点寄存器的暂存空间。因此,此时产生的函数调用指令将使用浮点寄存器保存这个value数据。这样做有一个非常大的好处,就是使用浮点寄存器传输参数,可以大幅的提高效率。在文献[9]中提到,同样适用硬件浮点支持,hardfloat模式可以比softfp模式快出5-40%。
不过hardfloat模式有个比较大的问题:采用hardfloat选项产生的程序完全不可能与soft模式以及softfp模式的程序相互通讯了(函数库之间的调用)。如果一个rootfs文件系统中同时混在有hardfloat模式和softfp/soft模式的程序话,就会变得非常难以维护。一旦这两类程序存在相互调用(这很难保证不会发生),就会出现各种奇怪又危险的状况。
为此,为了保证兼容性,首先业界把使用softfp/soft模式产生的程序归类为armel。而hardfloat模式的程序叫做armhf。这样一来,rootfs就对应产生出两大类:仅包含采用armel的类型以及仅包含armhf的类型。
从性能角度上看,自然是armhf的rootfs性能更优。但是由于历史问题,仍旧存在不少armel的程序,因此使用armhf的rootfs往往需要自行编译一些现有的程序。
参考文献:
[1] Android_(operating_system)
http://en.wikipedia.org/wiki/Android_(operating_system)
[2] The Six Million Dollar LibC
http://codingrelic.geekhold.com/2008/11/six-million-dollar-libc.html
[3] Das U-Boot
https://en.wikipedia.org/wiki/Das_U-Boot
[4] Tar_(computing)
http://en.wikipedia.org/wiki/Tar_(computing)
[5] Allwinner A10 - ARM Cortex A8 SoC
http://rhombus-tech.net/allwinner_a10/
[6] ArmHardFloatPort
http://wiki.debian.org/ArmHardFloatPort
[7] Allwinner boot process
http://minixandroid.wordpress.com/2012/09/24/allwinner-boot-proces/
[8] Application binary interface
http://en.wikipedia.org/wiki/Application_binary_interface
[9] Linaro ARM hardfloat support
https://wiki.linaro.org/Linaro-arm-hardfloat
[10] Allwinner/Unpacking and building LiveSuit images
https://www.miniand.com/wiki/Allwinner/Unpacking+and+building+LiveSuit+images
[11] Hack A10 devices
http://elinux.org/Hack_A10_devices
[12] MK802 Downloads
http://www.rikomagic.co.uk/support.html
[13] AllWinner A10/A1X Processor Resources, Development Board and SDK
http://www.cnx-software.com/2011/12/28/allwinner-a10a1x-processor-resources-development-board-and-sdk
[14] Miniand Tech论坛
https://www.miniand.com/forums/forums/development
[15] Linaro- Open source software for ARM SoCs
http://www.linaro.org/
[16] 全志A10平板上的ubuntu内核编译,支持LCD触摸屏
http://hi.baidu.com/lang_byebye/item/ea52c13e466708f9e7bb7ae4
[17] Hardware Packs for AllWinner A10 Devices and Easier Method to Create a Bootable Ubuntu 12.04 SD Card
http://www.cnx-software.com/2012/06/13/hardware-packs-for-allwinner-a10-devices-and-easier-method-to-create-a-bootable-ubuntu-12-04-sd-card/
[18] Toby Corkindale的网站
http://blog.dryft.net/
[19] Win32-Imager-Writer
https://launchpad.net/win32-image-writer
[20] 给MK802(USB大小的Android4.0小PC)引出串口信号,变成ARM开发版
http://www.csksoft.net/blog/post/288.html