一、Makefile
1、语法
目标…… : 依赖文件集合……
 命令 1
 命令 2
 ……
 例子:
 生成main可执行文件需要main.o input.o calcu.o,命令是gcc -o main main.o input.o calcu.o
 main: main.o input.o calcu.o
 gcc -o main main.o input.o calcu.o
 
 main.o: main.c
 gcc -c main.c
 input.o: input.c
 gcc -c input.c
 calcu.o: calcu.c
 gcc -c calcu.c
 
 clean:
 rm *.o
 rm main
 
2、变量
Makefile中的变量只能是字符串
 例 :将object赋值 main.o input.o calcu.o,变量引用的时候要加$()
 objects = main.o input.o calcu.o
 main: $(objects)
 gcc -o main $(objects)
 
3、几种赋值运算符
“=” :不一定要用定义好的值也可以用后面的值,与c语言赋值很像
 “:=” :只使用定义的值后面的赋值不起作用
 “?=”:如果没有赋值则赋值
 “+=”:在后面附加
###4、当“%”出现在目标中的时候,目标中“%”所代表的值决定了依赖中的“%”值
(泛化了),使用方法如下:
%.o : %.c
命令
 
5、自动化变量(常用的三种:$ @、$ <和$ ^)

 例
 objects = main.o input.o calcu.o
 main: $(objects)
 gcc -o main $(objects)
 
 %.o : %.c
	 gcc -c $<
 clean:
 rm *.o
 rm main
 
二、系统移植
1、何为uboot?
1、uboot是一个裸机程序,比较复杂。
2、uboot就是一个bootloader,作用就是用于启动Linux或其他系统。Uboot最主要的工作就是初始化DDR。因为Linux是运行在DDR里面的。一般Linux镜像zImage(uImage)+设备树(.dtb)存放在SD、EMMC、NAND、SPI FLASH等等外置存储区域。
这里就牵扯到一个问题,需要将Linux镜像从外置flash拷贝到DDR中,再去启动。
Uboot的主要目的就是为系统的启动做准备。
Uboot不仅仅能启动Linux,也可以启动其他系统,比如vxworks。
Linux不仅仅能通过uboot启动。
Uboot是个通用的bootloader,他支持多种架构。
 
2、Uboot获取
1、首先就是uboot官网。缺点就是支持少,比如某一款具体芯片驱动等不完善。
2、SOC厂商会从uboot官网下载某一个版本的uboot,然后在这个版本的uboot上加入相应的SOC以及驱动。这就是SOC厂商定制版的uboot。NXP官方的I.MX6ULL EVK板子
3、做开发板的厂商,开发板会参考SOC厂商的板子。开发板必然会和官方的板子不一样。因此开发板厂商又会去修改SOC厂商做好的uboot,以适应自己的板子。
 
3、uboot编译
首先在 Ubuntu 中安装 ncurses 库,否则编译会报错,安装命令如下:
sudo apt-get install libncurses5-dev
 
新建一个 shell 脚本文件,每次只需要执行 shell 脚本即可完成编译工作(这个脚本会清除之前的配置)。如下:
#!/bin/bash
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
 
第 1 行是 shell 脚本要求的,必须是“#!/bin/bash”或者“#!/bin/sh”。
 第 2 行使用了 make 命令,用于清理工程,也就是每次在编译 uboot 之前都清理一下工程。
 这里的 make 命令带有三个参数,第一个是 ARCH,也就是指定架构,这里肯定是 arm;第二个
 参数 CROSS_COMPILE 用于指定编译器,只需要指明编译器前缀就行了,比如 arm-linux-gnueabihf-gcc 编译器的前缀就是“arm-linux-gnueabihf-”;最后一个参数 distclean 就是清除工程。
 第 3 行也使用了 make 命令,用于配置 uboot。同样有三个参数,不同的是,最后一个参数
 是 mx6ull_14x14_ddr512_emmc_defconfig。前面说了 uboot 是 bootloader 的一种,可以用来引导
 Linux,但是 uboot 除了引导 Linux 以外还可以引导其它的系统,而且 uboot 还支持其它的架构
 和外设,比如 USB、网络、SD 卡等。这些都是可以配置的,需要什么功能就使能什么功能。所
 以在编译 uboot 之前,一定要根据自己的需求配置 uboot。 mx6ull_14x14_ddr512_emmc_defconfig就是正点原子针对 I.MX6U-ALPHA 的 EMMC 核心板编写的配置文件,这个配置文件在 uboot源码的 configs 目录中。在 uboot 中,通“makexxx_defconfig”来配置 uboot,xxx_defconfig就是不同板子的配置文件,这些配置文件都在 uboot/configs 目录中。
 第 4 行有 4 个参数,用于编译 uboot,通过第 3 行配置好 uboot 以后就可以直接“make”编
 译 uboot 了。其中 V=1 用于设置编译过程的信息输出级别;-j 用于设置主机使用多少线程编译
 uboot,最好设置成我们虚拟机所设置的核心数,如果在 VMware 里面给虚拟就分配了 4 个核,
 那么使用-j4 是最合适的,这样 4 个核都会一起编译。
 使用 chmod 命令给予 mx6ull_alientek_emmc.sh 文件可执行权限,然后就可以使用这个 shell
 脚本文件来重新编译 uboot,命令如下:
./mx6ull_alientek_emmc.sh
 
在Makefile254行加入这两条可以不用运行脚本 make就可以编译
 
4、uboot烧写与启动
1、我们要将 imxdownload (开发板光盘->5、开发工具->2、
 Ubuntu 下裸机烧写软件->imxdownload)拷贝到工程根目录下。
 2、ls /dev/sd*`看看烧写到那个sd卡
 3、chmod 777 imxdownload //给予 imxdownload 可执行权限,一次即可
 4、./imxdownload u-boot.bin /dev/sdd //烧写到 SD 卡,不能烧写到/dev/sda 或 sda1 设备里面!
5、uboot命令的使用
1、基础命令
help:查看所有命令
? (要查找命令):查看某一个命令信息
 
printenv:查看当前板子的环境变量
 
setenv:设置环境变量,也可以自定义环境变量,也可以删除环境变量(设置为空值就可以删除)
setenv author hsd 创建一个author环境变量赋值为hsd
 
saveenv:保存环境变量
 
md[.b, .w, .l] address [# of objects] :内存读写指令,要注意uboot中命令的数字都是16进制
比如你想查看以 0X80000000 开始的 20 个字节的内存值,显示格式为.b 的话,应该使用
如下所示命令:
md.b 80000000 14
而不是:
md.b 80000000 20
.b 格式显示,长度为 0x10,也就是 16 个字节;.w 格式显示,长度为 0x10,也就是 16*2=32
个字节;.l 格式显示,长度也是 0x10,也就是 16*4=64 个字节。
 
nm [.b, .w, .l] address  用于修改指定地址的内存值,输入q结束
 
mw [.b, .w, .l] address value [count]   用于使用一个指定的数据填充一段内存
例
mw.l 80000000 0A0A0A0A 10
将以 0X80000000 为起始地址的 0x10 个内存块(0x10 * 4=64 字节)填充为 0X0A0A0A0A
 

cp [.b, .w, .l] source target count   数据拷贝命令
cp.l 80000000 80000100 10	将 0x80000000 处的地址拷贝到 0X80000100 处,长度为 0x10 个
内存块(0x10 * 4=64 个字节)
 
cmp [.b, .w, .l] addr1 addr2 count
cmp.l 80000000 80000100 10	来比较 0x80000000 和 0X80000100 这
两个地址数据是否相等,比较长度为 0x10 个内存块(16 * 4=64 个字节)
 
2、网络相关的命令
ping主机之前一定要改下面这个配置
 

setenv ipaddr 192.168.1.50
setenv ethaddr b8:ae:1d:01:00:00
setenv gatewayip 192.168.1.1
setenv netmask 255.255.255.0
setenv serverip 192.168.1.253
saveenv
 
1、nfs命令
安装nfs
sudo apt-get install nfs-kernel-server rpcbind
 
等待安装完成,安装完成以后在用户根目录下创建一个名为“linux”的文件夹,以后所有
 的东西都放到这个“linux”文件夹里面,在“linux”文件夹里面新建一个名为“nfs”的文件夹。
 
 配置 nfs
sudo vi /etc/exports
 
打开/etc/exports 以后在后面添加如下所示内容:
/home/zuozhongkai/linux/nfs *(rw,sync,no_root_squash)
 

 重启 NFS 服务,使用命令如下:
sudo /etc/init.d/nfs-kernel-server restart
 
	nfs(Network File System)网络文件系统,通过 nfs 可以在计算机之间通过网络来分享资源,比如我们将 linux 镜像和设备树文件放到 Ubuntu 中,然后在 uboot 中使用 nfs 命令将 Ubuntu 中的 linux 镜像和设备树下载到开发板的 DRAM 中。
 
nfs [loadAddress] [[hostIPaddr:]bootfilename]
例:
nfs 80800000 192.168.1.253:/home/zuozhongkai/linux/nfs/zImage
命令中的“ 80800000 ” 表 示 zImage 保 存 地 址 ,
“192.168.1.253:/home/zuozhongkai/linux/nfs/zImage”表示 zImage 在 192.168.1.253 这个主机中,路径为/home/zuozhongkai/linux/nfs/zImage。
 
2、tftp 命令
执行下面两条安装TFTP
sudo apt-get install tftp-hpa tftpd-hpa
sudo apt-get install xinetd
 
创建一个tftp根目录(要给权限),此目录文件可以下载到开发板(与nfs一样)
mkdir /home/zuozhongkai/linux/tftpboot
chmod 777 /home/zuozhongkai/linux/tftpboot
 
最后配置 tftp,安装完成以后新建文件/etc/xinetd.d/tftp,输入以下内容
 server tftp
 {
	 socket_type = dgram
	 protocol = udp
	 wait = yes
	 user = root
	 server = /usr/sbin/in.tftpd
	 server_args = -s /home/hsd/linux/tftpboot/
	 disable = no
	 per_source = 11
	 cps = 100 2
	 flags = IPv4
 }
 
启动tftp
sudo service tftpd-hpa start
 
打开/etc/default/tftpd-hpa 文件,将其修改为如下所示内容:
 # /etc/default/tftpd-hpa
 TFTP_USERNAME="tftp"
 TFTP_DIRECTORY="/home/hsd/linux/tftpboot"
 TFTP_ADDRESS=":69"
 TFTP_OPTIONS="-l -c -s"
 
重启tftp服务器
sudo service tftpd-hpa restart
 
指令格式
tftpboot [loadAddress] [[hostIPaddr:]bootfilename]
 
loadAddress 是文件在 DRAM 中 的存 放 地 址 ,[[hostIPaddr:]bootfilename]是要从 Ubuntu 中下载的文件。但是和 nfs 命令的区别在于,只需要输入文件名即可。
 例:
tftp 80800000 zImage
 
看到“TFTP error: ‘Permission denied’ (0)”这样的错误提示,提示没有权限,出现这个错误一般有两个原因:
 ①、在 Ubuntu 中创建 tftpboot 目录的时候没有给予 tftboot 相应的权限。
 ②、tftpboot 目录中要下载的文件没有给予相应的权限。
 针对上述两个问题,使用命令“chmod 777 xxx”来给予权限,其中“xxx”就是要给予权限的文件或文件夹。
3、EMMC和SD卡
1、mmc read 命令
格式
mmc read addr blk# cnt
 
addr 是数据读取到 DRAM 中的地址,blk 是要读取的块起始地址(十六进制),一个块是 512字节,这里的块和扇区是一个意思,在 MMC 设备中我们通常说扇区,cnt 是要读取的块数量(十六进制)。比如从 EMMC 的第 1536(0x600)个块开始,读取 16(0x10)个块的数据到 DRAM 的0X80800000 地址处,命令如下:
mmc dev 1 0 //切换到 MMC 分区 0
mmc read 80800000 600 10 //读取数据
 
2、mmc list 命令
mmc list 命令用于来查看当前开发板一共有几个 MMC 设备
mmc dev 0 //切换到 SD 卡,0 为 SD 卡,1 为
 
有时候 SD 卡或者 EMMC 会有多个分区,可以使用命令“mmc part”来查看其分区
mmc dev 1 //切换到 EMMC
mmc part //查看 EMMC 分区
 

 如果要将 EMMC 的分区 2 设置为当前 MMC 设备,可以使用如下命令:
mmc dev 1 2
 
3、mmc write 命令
要将数据写到 MMC 设备里面,可以使用命令“mmc write”,格式如下:
mmc write addr blk# cnt
 
addr 是要写入 MMC 中的数据在 DRAM 中的起始地址,blk 是要写入 MMC 的块起始地址
 (十六进制),cnt 是要写入的块大小,一个块为 512 字节。
例:
 如果要在 uboot 中更新 EMMC 对应的 uboot,可以使用如下所示命令:
mmc dev 1 0 //切换到 EMMC 分区 0
tftp 80800000 u-boot.imx //下载 u-boot.imx 到 DRAM
mmc write 80800000 2 32E //烧写 u-boot.imx 到 EMMC 中
mmc partconf 1 1 0 0 //分区配置,EMMC 需要这一步!
 
千万不要写 SD 卡或者 EMMC 的前两个块(扇区),里面保存着分区表!
4、FAT 格式文件系统操作命令
对于I.MX6U来说,SD/EMMC分为三个分区:
第一个:存放uboot
第二个:存放Linux zImage,.dtb。FAT
第三个:系统的根文件系统,EXT4
 
1、fatinfo 命令
用于查询指定 MMC 设备分区的文件系统信息,格式如下:
fatinfo <interface> [<dev[:part]>]
 
interface 表示接口,比如 mmc,dev 是查询的设备号,part 是要查询的分区。比如我们要查
 询 EMMC 分区 1 的文件系统信息,命令如下:
fatinfo mmc 1:1
 

2、fatls 命令
用于查询 FAT 格式设备的目录和文件信息,命令格式如下:
fatls <interface> [<dev[:part]>] [directory]
 
interface 是要查询的接口,比如 mmc,dev 是要查询的设备号,part 是要查询的分区,directory
 是要查询的目录。比如查询 EMMC 分区 1 中的所有的目录和文件,输入命令:
fatls mmc 1:1
 

3、fstype 命令
用于查看 MMC 设备某个分区的文件系统格式,命令格式如下:
fstype <interface> <dev>:<part>
 
例
fstype mmc 1:0
fstype mmc 1:1
fstype mmc 1:2
 

 从上图可以看出,分区 0 格式未知,因为分区 0 存放的 uboot,并且分区 0 没有格式化,所
 以文件系统格式未知。分区 1 的格式为 fat,分区 1 用于存放 linux 镜像和设备树。分区 2 的格
 式为 ext4,用于存放 Linux 的根文件系统(rootfs)。
4、fatload 命令(重要)
用于将指定的文件读取到 DRAM 中,命令格式如下:
fatload <interface> [<dev[:part]> [<addr> [<filename> [bytes [pos]]]]]
 
interface 为接口,比如 mmc,dev 是设备号,part 是分区,addr 是保存在 DRAM 中的起始地址,filename 是要读取的文件名字。bytes 表示读取多少字节的数据,如果 bytes 为 0 或者省略的话表示读取整个文件。pos 是要读的文件相对于文件首地址的偏移,如果为 0 或者省略的话表示从文件首地址开始读取。我们将 EMMC 分区 1 中的 zImage 文件读取到 DRAM 中的0X80800000 地址处,命令如下:
fatload mmc 1:1 80800000 zImage
 

5、BOOT操作指令
1、bootz 命令(启动zImage命令)
要启动 Linux,可从 EMMC 或者 NAND 也可通过 nfs 或者 tftp 将 Linux 镜像文件和设备树文件下载到 DRAM 中,然后使用 bootz 命令来启动,bootz 命令格式如下:
bootz [addr [initrd[:size]] [fdt]]
 
addr 是 Linux 镜像文件在 DRAM 中的位置。
 initrd 是 initrd 文件在DRAM 中的地址,如果不使用 initrd 的话使用‘-’代替即可。
 fdt 就是设备树文件在 DRAM 中的地址。
例(tftp启动):
tftp 80800000 zImage
tftp 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb
bootz 80800000 - 83000000
 

 例(emmc启动):
fatload mmc 1:1 80800000 zImage
fatload mmc 1:1 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb
bootz 80800000 - 83000000
 

2、bootm 命令(启动uImage命令,其余与bootz一致)
3、boot 命令
boot 会读取环境变量 bootcmd 来启动 Linux 系统,这个环境变量保存着引导命令,其实就是启动的命令集合,具体的引导命令内容是可以修改的。命令如下:
setenv bootcmd 'tftp 80800000 zImage;tftp 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb;bootz 80800000 - 83000000'
saveenv
boot
 

 倒计时结束以后就会启动 Linux 系统,其实就是执行的 bootcmd 中的启动命令。
6、其他指令
1、reset 命令
复位
2、go 命令
跳到指定的地址处执行应用
tftp 87800000 printf.bin
go 87800000
 

3、run 命令
用于运行环境变量中定义的命令,比如可以通过“run bootcmd”来运行 bootcmd 中的启动命令,但是 run 命令最大的作用在于运行我们自定义的环境变量。
 例:
 创建环境变量 mybootemmc、mybootnet 和 mybootnand,命令如下:
setenv mybootemmc 'fatload mmc 1:1 80800000 zImage; fatload mmc 1:1 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb;bootz 80800000 - 83000000'
setenv mybootnand 'nand read 80800000 4000000 800000;nand read 83000000 6000000 100000;bootz 80800000 - 83000000'
setenv mybootnet 'tftp 80800000 zImage; tftp 83000000imx6ull-14x14-emmc-7-1024x600-c.dtb;bootz 80800000 - 83000000'
saveenv
 
通过下面方式运行
run mybootemmc
run mytoobnand
run mybootnet
 



















