准备工作:基于crosstool-ng这个交叉编译器制作
[zusi@centos6_master crosstool]$ vim build.sh 编写制作交叉编译器的脚本,该脚本主要是从FTP服务器上下载文件并修改 只制作arm920t的交叉编译器
#!/bin/bash
# This shell script used to download crosstool-ng install binary and compile it for arm920t
# Author: zusi<1446105267@qq.com>
CROSSTOOL=crosstool-ng-centos-LingYun-v1.0.0.binif [ ! -f ${CROSSTOOL} ] ; then
wget ftp://master.iot-yun.com/linux_tools/${CROSSTOOL}
sed -i -e "s|^sup_arch=.*|sup_arch=(\"\", \"arm920t\")|g" ${CROSSTOOL}
fi
chmod a+x ${CROSSTOOL}
./${CROSSTOOL}[zusi@centos6_master crosstool]$ sh build.sh 开始制作交叉编译器,这个过程较长
[zusi@centos6_master crosstool]$ /opt/xtools/arm920t/bin/arm-linux-gcc -v 验证交叉编译器制作是否正确
Using built-in specs.
Target: arm-arm920t-linux-gnueabi
Configured with: /home/zusi/crosstool-ng-1.16.0/.build/src/gcc-4.4.6/configure --build=x86_64-build_unknown-linux-gnu --host=x86_64-build_unknown-linux-gnu --target=arm-arm920t-linux-gnueabi --prefix=/opt/xtools/arm920t --with-sysroot=/opt/xtools/arm920t/arm-arm920t-linuxgnueabi/sysroot --enable-languages=c,c++ --with-arch=armv4t --with-cpu=arm9tdmi --with-tune=arm920t --with-float=soft --withpkgversion='crosstool-NG 1.16.0' --enable-__cxa_atexit --disable-libmudflap --disable-libgomp --disable-libssp --withgmp=/home/zusi/crosstool-ng-1.16.0/.build/arm-arm920t-linux-gnueabi/buildtools --withmpfr=/home/zusi/crosstoolng1.16.0/.build/arm-arm920t-linux-gnueabi/buildtools --with-ppl=/home/zusi/crosstool-ng-1.16.0/.build/arm-arm920t-linux-gnueabi/buildtools--with-cloog=/home/zusi/crosstool-ng-1.16.0/.build/arm-arm920t-linux-gnueabi/buildtools --with-host-libstdcxx='-static-libgcc -Wl,-Bstatic,-lstdc++,-Bdynamic -lm' --enable-threads=posix --enable-target-optspace --disable-multilib --with-local-prefix=/opt/xtools/arm920t/arm-arm920tlinux-gnueabi/sysroot --enable-c99 --enable-long-long
Thread model: posix
gcc version 4.4.6 (crosstool-NG 1.16.0)
一、 开发板简介
FL2440开发板是飞凌公司使用三星的ARM9 S3C2440 CPU做的一个ARM Linux学习开发板,该CPU是使用armv4t指令集的ARM920T核,工作主频最高在400MHz。每个CPU厂商在研发并生产出CPU时并不是直接将该CPU推给客户让客户自己研究该CPU特性后再做硬件设计(因为客户不懂这些具体的CPU使用细节,而研发该CPU的公司会更懂它),而会先使用该CPU设计出母板(demo板,如SMDK2440),在母板上尽可能将该CPU的硬件使用信息展示给客户,客户再参考母板根据实际的功能、市场需求来高度定制自己的硬件,例如产品不需要显示则可以把母板中的LCD硬件部分去掉、CS8900网卡供货存在问题那就换成DM9000等等。
相应的CPU厂商在开发出相应的硬件demo板后,也不是直接丢给客户来开发相应的软件,而是会安排软件研发人员针对母板硬件完成u-boot和Linux内核的开发,以支持相应硬件。因为我们的硬件是参考母板来定制开发的,所以我们的软件移植(u-boot和linux内核)也可以在相关源码里的母板代码上做针对硬件的修改来支持我们的产品。
在FL2440开发板的移植过程中,我们将SMDK2440为模版在它的基础上做些修改来支持我们相应的硬件。在开始移植之前,我们先创建FL2440整个项目的目录框架:
[zusi@centos6_master ~]$ mkdir fl2440
[zusi@centos6_master ~]$ cd fl2440
[zusi@centos6_master fl2440]$
mkdir -p {crosstool,bootloader,linux/{kernel,rootfs},driver,3rdparty,program,images}
[zusi@centos6_master fl2440]$ tree|-- 3rdparty 今后移植到ARM开发板上的第三方应用程序软件包
|-- bootloader u-boot 移植代码
|-- crosstool 交叉编译器
|-- driver 今后写的驱动文件
|-- images 编译出的image文件,如Linux内核,根文件系统等;
|-- linux
| |-- kernel Linux 内核源码路径
| `-- rootfs 根文件系统目录树路径
`-- program 我们今后自己写的应用程序
二、源码修改
Linux 是一个源码开放的操作系统,无论是普通用户还是企业用户都可以编写自己的内核代码,再加上对标准内核的裁剪从而制作出适合自己的操作系统。Linux操作系统有很多发行版本,如Redhat, CentOS, Ubuntu等等,但所有的这些Linux操作系统都是选择一个Linux内核稳定版本,外加不同的C基础库和应用程序构建的一个操作系统。
嵌入式Linux系统开发其本质就是我们拿到源码并进行修改DIY(Do It Yourself)一个属于我们自己的操作系统,这个过程包括Booloader和Linux内核(裁剪)移植、驱动模块编写、根文件系统制作、第三方应用程序移植等,这个工作叫做BSP(Board Support Packet,板级支持包)开发,这里面需要相应的硬件协议、操作系统、以及C语言和数据结构等知识,难度较高。
Linux内核下载地址: https://www.kernel.org/pub/linux/kernel/v3.x/
[zusi@centos6_master ~]$ cd gitee/fl2440/linux/
[zusi@centos6_master linux]$ wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.0.tar.bz2
[zusi@centos6_master linux]$ tar -xjf linux-3.0.tar.bz2
[zusi@centos6_master linux-3.0]$ ls
arch CREDITS drivers include Kbuild lib mm REPORTING-BUGS security usr
block crypto firmware init Kconfig MAINTAINERS net samples sound virt
COPYING Documentation fs ipc kernel Makefile README scripts tools
下图是Linux源码目录树的目录结构:
修改1:
SMDK2440上使用的是16MHz的晶振,而FL2440上使用的是12MHz的晶振,所以开发板相应代码要做修改:
[zusi@centos6_master linux-3.0]$ vim arch/arm/mach-s3c2440/mach-smdk2440.c
static void __init smdk2440_map_io(void)
{
s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc));
- s3c24xx_init_clocks(16934400);
+ s3c24xx_init_clocks(12000000);
s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs));
}
修改2:
我们的u-boot给Linux内核传的machine ID值为1999,而Linux内核里smdk2440开发板对应的machine ID是362,所以我们要修改内核代码让
smdk2440的machine ID与u-boot里的保持一致,这里我们在源码中将两个machine ID值互换:
[zusi@centos6_master linux-3.0]$ vim arch/arm/tools/mach-types
-s3c2440 ARCH_S3C2440 S3C2440 362
+s3c2440 ARCH_S3C2440 S3C2440 1999
... ...
-mini2440 MACH_MINI2440MINI24401999
+mini2440 MACH_MINI2440 MINI2440 362
修改3:
samsung的串口驱动设备名字默认叫ttySAC,而我们一般使用ttyS,所以将源码中的设备名改掉:
[zusi@centos6_master linux-3.0]$ vim drivers/tty/serial/samsung.c
-#define S3C24XX_SERIAL_NAME "ttySAC"
+#define S3C24XX_SERIAL_NAME"ttyS"
修改4:
修改顶层Makefile的ARCH为arm, CROSS_COMPILE为我们自己相应的交叉编译器:
[zusi@centos6_master linux-3.0]$ vim Makefile
-ARCH ?= $(SUBARCH)
-CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%)
+ARCH ?= arm
+CROSS_COMPILE ?= /opt/xtools/arm920t/bin/arm-linux- //这里改成自己的交叉编译器,注意arm-linux-后面应该紧跟回车,不能有其他任何字符
修改5:
添加DM9000网卡设备支持,下图是FL2440底板上的DM9000网卡原理图,注意这里网卡INT使用EINT7管脚,CMD连ADDR2,CS#片选管脚连了NGCS4,以及数据位数为LDATA0~LDATA15(16位模式)。下面是修改Linux内核源码添加DM9000网卡设备支持的过程。
[zusi@centos6_master linux-3.0]$ vim arch/arm/mach-s3c2440/mach-smdk2440.c
static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
+ &s3c_device_dm9000, /* 添加DM9000网卡的设备,告诉Linux内核现在有这个硬件连上来了 */
};
[zusi@centos6_master linux-3.0]$ vim arch/arm/plat-s3c24xx/devs.c //在该C文件中定义结构体变量 s3c_device_dm9000
... ... ...
EXPORT_SYMBOL(s3c_device_iis);
+#ifdef CONFIG_DM9000 /* DM9000 network device support add by guowenxue 2011.08.30*/
+#include <linux/dm9000.h>
+static struct resource s3c_dm9000_resource[] = {
+ [0] = {
+ .start = S3C2410_CS4 + 0x300, // DM9000网卡的CS片选管脚连接的是CS4
+ .end = S3C2410_CS4 + 0x300 + 0x3,
+ .flags = IORESOURCE_MEM
+ },
+ [1]={
+ .start = S3C2410_CS4 + 0x300 + 0x4, //CMD pin is A2
+ .end = S3C2410_CS4 + 0x300 + 0x4 + 0x7c,
+ .flags = IORESOURCE_MEM
+ },
+ [2] = {
+ .start = IRQ_EINT7, // DM9000网卡的中断管脚连的是 EINT7这个Pin
+ .end = IRQ_EINT7,
+ .flags = IORESOURCE_IRQ
+ },
+};
+
+static struct dm9000_plat_data s3c_device_dm9000_platdata = {
+ .flags= DM9000_PLATF_16BITONLY, // DM9000网卡的使用LDATA0~15 16位模式
+};
++
struct platform_device s3c_device_dm9000 = {
+ .name= "dm9000",
+ .id= 0,
+ .num_resources= ARRAY_SIZE(s3c_dm9000_resource),
+ .resource= s3c_dm9000_resource,
+ .dev= {
+ .platform_data = &s3c_device_dm9000_platdata,
+ }
+};
+#endif
+/
* RTC */
... ... ...
[zusi@centos6_master linux-3.0]$ vim arch/arm/plat-samsung/include/plat/devs.h //在该头文件中导出s3c_device_dm9000的声明
extern struct platform_device s3c_device_hsmmc1;
extern struct platform_device s3c_device_hsmmc2;
extern struct platform_device s3c_device_hsmmc3;
extern struct platform_device s3c_device_cfcon;
+extern struct platform_device s3c_device_dm9000; /* 声明devs.c中定义的结构体变量s3c_device_dm9000 */
extern struct platform_device s3c_device_spi0;
[zusi@centos6_master linux-3.0]$ vim drivers/net/dm9000.c //修改dm9000.c的驱动代码,添加中断的初始化
... ... ...
static int
dm9000_open(struct net_device *dev)
{
board_info_t *db = netdev_priv(dev);
- unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK;
+ unsigned long irqflags = (db->irq_res->flags|IRQ_TYPE_EDGE_RISING) & IRQF_TRIGGER_MASK;
++
irq_set_irq_type(dev->irq, IRQ_TYPE_EDGE_RISING);
if (netif_msg_ifup(db))
dev_dbg(db->dev, "enabling %s\n", dev->name);
... ... ...
三、内核配置和编译
[zusi@centos6_master linux-3.0]$ find -name *.c | wc -l
16198
[zusi@centos6_master linux-3.0]$ find -name *.S | wc -l
1223
Linux-3.0内核有16198个C文件和1223个汇编文件,就这一份源码支持了不同体系结构CPU, 并且包含了绝大部分硬件的驱动源码。那么根据不同的硬件设计和功能需求,定制化编译生成一个具有特定用途的嵌入式Linux系统image将是一场灾难,好在Linux的源码编译系统有一个非常巧妙的铁三角关系,导致这个过程显得没那么复杂。这个铁三角分别是Kconfig、.config和Makefile文件,他们三者之间的关系简单来说就是去饭店点菜:Kconfig是菜单,.config就是你点的菜,Makefile是做法。
Kconfig:一个文本形式的文件,存在内核源码中的每一个文件夹下,内核配置命令make menuconfig读取相应的Konfig文件生成菜单界面;
.config:隐藏文件存放在内核源码顶层目录中,make menuconfig命令配置的结果,里面的每个选项用来指导Makefile哪些C文件需要编译,哪些不需要编译;
Makefile:一个文本形式的文件,存在内核源码中的每一个文件夹下,用来控制编译该目录下的源码编译;
[zusi@centos6_master linux-3.0]$ make s3c2410_defconfig
[zusi@centos6_master linux-3.0]$ export TERM=vt100
[zusi@centos6_master linux-3.0]$ make menuconfig
//因为我们编译内核经常需要做下面几个命令,所以我们可以写个shell脚本完成这些相应命令。
[zusi@centos6_master linux-3.0]$ vim build.sh
#!/bin/bash
make
mkimage -A arm -O linux -T kernel -C none -a 30008000 -e 30008040 -n "Linux Kernel" -d arch/arm/boot/zImage linuxroms3c2440.bin
chmod a+x linuxrom-s3c2440.bin
[zusi@centos6_master linux-3.0]$ chmod a+x build.sh
[zusi@centos6_master linux-3.0]$ ./build.sh
//编译完成后将会生成linuxrom-s3c2440.bin,该文件即为u-boot里bootm命令能识别的uImage文件。
四、u-boot启动Linux内核及bug修复
在Linux内核还是调试阶段时,我们没必要每次将Linux内核下载到Nandflash上,而只需要在u-boot下使用tftp命令把Linux内核下载到0x30008000地址上后,直接使用bootm命令启动即可。等内核调试稳定后,我们在将它写入到Nandflash上上电直接启动。
[fl2440@zusi]# tftp 30008000 linuxrom-s3c2440.bin; bootm 30008000
系统启动时会抛出这个错误,这是因为串口驱动冲突。解决方法:
重新配置Linux内核,然后再编译:
[zusi@centos6_master linux-3.0]$ make menuconfig
[zusi@centos6_master linux-3.0]$ ./build.sh
重新启动Linux内核
[fl2440@zusi]# tftp 30008000 linuxrom-s3c2440.bin; bootm 30008000