现在可以用于构建根文件系统的软件有很多,比如buildroot,yocto,而且这些软件构建的根文件系统软件更全,为什么要使用busybox呢,因为比较简单,对于我这种初学者来说,从简单开始学习才是最好的,可以了解原理,原理清楚了,再使用其他软件来构建就简单多了。
tar -vxjf busybox-1.29.0.tar.bz2
CROSS_COMPILE ?= /home/book/share/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
将路径修改为自己的交叉编译工具的绝对路径,将190行修改为:
ARCH ?= arm
make defconfig
然后打开图形配置界面,执行:
make menuconfig
分别配置下面的路径:
Location:
-> Settings
-> Build static binary (no shared libs) //取消选中,动态编译,不然编译出来的根文件系统很大
Location:
-> Settings
-> vi-style line editing commands //选中
Location:
-> Linux Module Utilities
-> Simplified modutils //取消选中
Location:
-> Linux System Utilities
-> mdev (16 kb) //确保下面的全部选中,默认都是选中的
make install CONFIG_PREFIX=/home/book/nfs_rootfs/blogrootfs
CONFIG_PREFIX指定输出路径,指向nfs服务器的路径即可。编译完成后可以查看rootfs路径下的文件:
cp *so* *.a ~/nfs_rootfs/blogrootfs/lib/ -d
后面的“-d”表示拷贝符号链接。在rootfs中lib执行ls ld-linux-armhf.so.3 -l,可以看到ld-linux-armhf.so.3是一个软链接,所以需要重新复制一下。先删除rootfs/lib下的ld-linux-armhf.so.3,然后进入arm-linuxgnueabihf/libc/lib执行:
cp ld-linux-armhf.so.3 ~/nfs_rootfs/blogrootfs/lib/
再次查看rootfs/lib下的ld-linux-armhf.so.3,已经不是软链接了。
cp *so* *.a ~/nfs_rootfs/blogrootfs/lib/ -d
cp *so* *.a ~/nfs_rootfs/blogrootfs/usr/lib/ -d
可能会有警告,不用管。
setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs rootwait rw nfsroot=192.168.101.5:/home/book/nfs_rootfs/rootfs ip=192.168.101.6:192.168.101.5:192.168.101.1:255.255.255.0::eth0:off'
/* root=/dev/nfs nfsroot=[:][,] ip=::::::::*/
设置好后保存环境变量,然后使用tftp加载内核和设备树并启动:
tftp 80800000 zImage
tftp 83000000 imx6ull-100ask-emmc.dtb
bootz 80800000 - 83000000
启动完成后如下图:
可以执行基本的命令了,但是提示没有/etc/init.d/rcS文件,所以需要完善一下文件系统。
linux启动后需要运行一些服务,而 /etc/init.d/rcS文件就是规定启动哪些服务,创建该文件并添加如下内容:
#!/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 #通过这两行,linux内核就可以在/dev目录下自动创建设备节点
mdev -s
将rcS添加可执行权限chmod 777 rcS。
fstab 在 Linux 开机以后自动配置哪些需要自动挂载的分区,创建该文件并添加如下内容:
#
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
sysfs /sys sysfs defaults 0 0
init 程序会读取/etc/inittab这个文件, inittab 由若干条指令组成,创建该文件并添加如下内容:
::sysinit:/etc/init.d/rcS
console::askfirst:-/bin/sh
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/umount -a -r
::shutdown:/sbin/swapoff -a
/etc/resolv.conf文件用于配置域名服务器,没有域名服务器就不能将网址转换成ip地址,创建该文件并添加如下内容:
nameserver 114.114.114.114
nameserver 192.168.101.1
创建完以上三个文件后,重启linux,发现没有错误提示了。ping一下百度也可以ping通,网络也可以使用了,此时可以通过交叉编译器来编译可以在100ASK开发板上运行的linux程序了。
其实并没有修改内核在启动的时候显示的小企鹅,只是在linux启动完后,运行一个app,这个app可以显示bmp图片,设置该app开机自动运行即可(在rcS中添加两行代码就可以了)。
bmp图片的格式网上有详解,编写代码displaybmp.c:
#include
#include
#include
#include
#include
#include
#define SCREEN_HEIGHT 600
#define SCREEN_WIDTH 1024
#pragma pack(1) // 让编译器做1字节对齐
struct bmp_file //BMP文件头结构
{
char type[2]; //位图文件的类型,必须为BM,我这里类型不对,所以显示有误。
unsigned int size; //位图文件的大小,以字节为单位
short rd1; // 位图文件保留字,必须为0
short rd2; // 位图文件保留字,必须为0
unsigned int offset; // 位图数据的起始位置,以相对于位图
};
struct bmp_info //图像信息区
{
unsigned int bsize; //本结构体所占用字节数,即40个字节
int width; // 位图的宽度,以像素为单位,像素数量是4字节对齐的
int height; // 位图的高度,以像素为单位,如果该位为正,说明图像是倒向的,为负则说明是正向,大多数都是正向,但是QQ截图保存的BMP好像都是正向的
unsigned short planes; // 目标设备的级别,必须为1
unsigned short count; // 每个像素所需的位数,必须是1(双色)// 4(16色),8(256色)或24(真彩色)之一
unsigned int compression; // 位图压缩类型,必须是 0(不压缩),// 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
unsigned int sizeimage; // 位图的大小,以字节为单位
unsigned int xmeter; // 位图水平分辨率,每米像素数
unsigned int ymeter; // 位图垂直分辨率,每米像素数
unsigned int cused; // 位图实际使用的颜色表中的颜色数
unsigned int cimportant; // 位图显示过程中重要的颜色数
};
struct bmp_head {
struct bmp_file file;
struct bmp_info info;
};
#pragma pack() // 取消1字节对齐,恢复为默认对齐
struct bmp_head bmphead;
unsigned char *bmpBuff = NULL;
unsigned int *fbBuff = NULL;
int bmp_width = 0;
int bmp_height = 0;
int actual_width = 0, actual_height = 0; /* 实际显在屏幕上的大小,如果BMP图片比屏幕大,那么就只显示屏幕分辨率的大小*/
int flag = 0; /* 指示图像是否是倒向的,倒向为0,正向为1 */
int pix_count = 0; /* 指示每个像素占用的大小,字节为单位 */
void draw_screenbuf_point(int x, int y, unsigned int color) //在LCD buf上x, y处画一个点
{
if(flag)
fbBuff[y * SCREEN_WIDTH + x] = color;
else
fbBuff[(SCREEN_HEIGHT - 1 - y) * SCREEN_WIDTH + x] = color;
}
unsigned int read_bmp_point(int x, int y) //从bmp文件中取出x,y坐标处的点
{
unsigned int color = 0;
unsigned char R,G,B;
if(pix_count == 24) { /* bmp图片count不一样,数据区的格式就不一样 */
B = bmpBuff[(x + y*bmp_width)*3];
G = bmpBuff[(x + y*bmp_width)*3+1];
R = bmpBuff[(x + y*bmp_width)*3+2]; /* (x + y*bmp_width)*4+3存放alpha值,不使用 */
color = B | G<<8 | R<<16 | 0x80<<24; /* 0x80为透明度 */
return color;
}
else if(pix_count == 32) {
B = bmpBuff[(x + y*bmp_width)*4];
G = bmpBuff[(x + y*bmp_width)*4+1];
R = bmpBuff[(x + y*bmp_width)*4+2]; /* (x + y*bmp_width)*4+3存放alpha值,不使用 */
color = B | G<<8 | R<<16 | 0x80<<24; /* 0x80为透明度 */
return color;
}
else
return 0x800000ff;
}
int main(int argc,char *argv[])
{
if(2 != argc) {
printf("Usage:%s xxx.bmp\n",argv[0]);
return -1;
}
int bmpfd = open(argv[1],O_RDWR);
if(-1 == bmpfd) {
perror("open faild\n");
return -1;
}
int fbdev = open("/dev/fb0",O_RDWR);
if(-1 == fbdev) {
perror("open failed\n");
return -1;
}
if (-1 == read(bmpfd, &bmphead, sizeof(bmphead))) {
perror("read failed\n");
return -1;
}
pix_count = bmphead.info.count;
if((pix_count != 24) && (pix_count != 32) ) {
printf("This format is not supported! pix_count:pix_count:%d\n", pix_count);
return 0;
}
bmp_width = bmphead.info.width;
if(bmphead.info.height < 0) {
bmp_height = 0-bmphead.info.height; //换成正数
flag = 1;
}
else {
bmp_height = bmphead.info.height;
flag = 0;
}
actual_width = bmp_width;
actual_height = bmp_height;
if(bmp_width > SCREEN_WIDTH) {
actual_width = SCREEN_WIDTH;
}
if(bmp_height > SCREEN_HEIGHT) {
actual_height = SCREEN_HEIGHT;
}
// printf("%s:%dx%d, flag:%d\n", argv[1], bmp_width, bmp_height, flag);
// printf("screen size:%dx%d\n", SCREEN_WIDTH, SCREEN_HEIGHT);
// printf("display size:%dx%d\n", actual_width, actual_height);
bmpBuff = (unsigned char *)malloc(bmphead.file.size - sizeof(struct bmp_head));
if(NULL == bmpBuff) {
perror("malloc failed\n");
return -1;
}
fbBuff = (unsigned int *)malloc(SCREEN_WIDTH*SCREEN_HEIGHT*sizeof(int));
if(NULL == fbBuff) {
perror("malloc failed\n");
return -1;
}
if (-1 == read(bmpfd, bmpBuff, bmphead.file.size - sizeof(struct bmp_head))) {
perror("read failed\n");
return -1;
}
int x = 0,y = 0;
unsigned int color;
for(y=0; y < actual_height; y++) {
for(x=0; x < actual_width; x++) {
color = read_bmp_point(x, y);
draw_screenbuf_point(x, y, color);
}
}
write(fbdev, fbBuff, SCREEN_WIDTH*SCREEN_HEIGHT*4); /* 写入frambuffer */
free(fbBuff);
free(bmpBuff);
close(fbdev);
close(bmpfd);
return 0;
}
该程序的原理就是根据bmp的格式读取RGB数据,然后写入linux的framebuffer就可以显示了,只支持位图数据为32位或者24位的bmp图像显示。
编译测试:
arm-linux-gnueabihf-gcc disbmp.c -o displaybmp
sudo cp displaybmp ~/nfs_rootfs/blogrootfs/
使用QQ或者起他工具截图并保存为bmp格式,然后将bmp图片放入roots目录下,执行:
displaybmp xxx.bmp
一切正常的话LCD上应该就显示截图了。然后在/etc/init.d/rcS末尾添加:
./displaybmp xxx.bmp &
重启后就会自动显示了。