一、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