目录
前言
一、原理
1、源码编译
2、I.MX6ULL镜像文件
二、实现过程
1、源码编写
2、源码编译
3、镜像文件制作、烧写、运行
参考资料
I.MAX6ULL是一款NXP出品的,528~900MHz的Cortex-A7内核的MPU
一个工程中肯定有很多的.c源文件和.s汇编文件,要想让这些文件在I.MAX6ULL芯片上能运行起来是需要经历一系列过程的,如下图,中间还经历了预处理、编译、汇编、链接几个过程。
其中链接是将众多的.o目标文件(芯片可认识的二进制文件)文件链接到一个指定的链接位置(运行地址)上去,然后生成一个可运行的可执行文件。我的理解就是汇编只是将源文件转换成了芯片可识别的二进制文件,这些二进制数据要想在芯片上运行,肯定还得在芯片运行的地址上才行啊,这样芯片才能挨个去执行。就好比一个小孩在一个大桌子上吃饭,桌子上面很多菜他都知道是可以吃的,但是他够不着啊,只能够着自己碗里的,没办法想吃就只能让大人帮他把菜夹到他的碗里。链接就是将将菜(二进制文件)夹到小孩碗里(跟运行地址关联起来)的过程。
这里要说明下两个概念:
“存储地址”:就是可执行文件存储的地方。在STM32单片机中,就是单片机的内部FLASH,地址从0x08000000开始的。但是I.MAX6ULL中,这个存储地址可以随意选择的,虽然I.MAX6ULL内部有96K的ROM,但是这 96K的ROM是 NXP自己用的,不向用户开放。所以相当于说I.MAX6ULL没有内部FLASH,但是I.MAX6ULL支持将代码存储到NAND、SD卡、EMMC等这些外部存储器中。
“运行地址(链接地址)”:代码运行的时候所处的位置,在STM32中,运行地址就是代码的存储起始地址,都是丛0x08000000开始的。在I.MX6ULL中,可以在内部 128KB RAM中(0x900000~0x91FFFF),也可以在外部的 DDR(0x80000000开始)中。
经过上述过程得到一个.bin的可执行文件,直接将其烧写到指定的存储位置后就可以运行起来了吗?不是的!!!根据NXP的规定,还必须在bin文件的头部添加一些信息。因为在选择了内部BOOT模式启动的时候,芯片会执行内部的 boot ROM代码,这段 boot ROM代码会初始化CPU、时钟等,初始化内存(一般是DDR),初始化启动设备比如 SD、EMMC、NAND,从启动设备上把程序读入内存,运行内存的程序。添加的头部信息就是boot ROM运行所需的配置信息,这样boot ROM才能正确的将代码拷贝到指定的RAM中。综上所述:还需要在bin文件中添加一些头部信息组成一个镜像文件,才能再烧到启动设备中去。
这个头部信息包含哪些内容呢?图镜像文件的组成如下图所示:
注意:IVT中的地址信息都是Dest Memory的地址,而不是启动设备中地址
(1) IVT(Image vector table):IVT里面包含了一系列的地址信息,这些地址信息在ROM中按照固定的地址存放着。boot ROM 程序会根据这些地址来确定镜像文件中其他部分在哪里。格式如下:
IVT中个字段含义如下:
字段 |
说明 |
header |
包含了tag 、 l ength 、 version 。 length 表示IVT的大小,它是32字节。要注意是的,它是大字节序的 |
entry |
应用程序的链接地址,就是应用程序被复制到内容的哪里 |
dcd |
镜像文件被复制到内存以后,DCD数据的地址 |
boot data |
镜像文件被复制到内存以后,Boot data数据的地址 |
self |
镜像文件被复制到内存以后,IVT自己所处的地址 |
(2) Boot data(启动数据):包含了镜像文件要拷贝到哪个地址,拷贝的大小是多少
Boot data中个字段含义如下:
字段 |
说明 |
start |
映像文件在内存中的地址,它不等IVT在内存中的地址(IVT中self字段)。比如IVT被保存在启动设备SD卡1024偏移地址处,IVT被复制到内存地址 0x87000000那么 start= 0x87000000-1024 。boot ROM会把启动设备开头的数据也复制到内存中去,而不仅仅是从IVT开始复制 |
length |
保存在启动设备上的整个镜像文件长度+镜像文件相对启动设备开始地址的偏离长度 |
plugin |
插件,boot ROM支持有限的启动设备。用于设置支持更多的启动设备 |
(3) DCD(设备配置数据):设备配置信息,其实就是 I.MX6U寄存器地址和对应的配置信息集合,Boot ROM会使用这些寄存器地址和配置集合来初始化相应的寄存器,比如开启某些外设的时钟、初始化 DDR等等。命令格式如下:
(4) Application:用户的应用程序,也就是上面生成的bin文件
以上4 部分内容合并成为一个映像文件,烧写在 EMMC 、SD 卡或TF卡等启动设备的某个固定地址,bootROM 程序去这个固定地址读出映像文件。启动设备不同,固定地址不同,如下图:
假如有main.c、start.s、两个源文件,在main.c中中调用了初始化LED灯控制LED闪烁,start.s初始化C语言运行环境。那么上述过程是怎么实现的呢?这里以正点原子的I.MA6ULL开发板点灯例程说明
第一步:使用汇编初始化C语言运行环境,也就是初始化SP指针,因为C语言中的函数调用涉及到出栈入栈,出栈入栈就要对堆栈进行操作,这里就是开辟一段RAM用作栈并将SP指针指向这块内存。因为I.MX6ULL的内部RAM是不开放给用户的,所以SP指向的是片外的DDR,而DDR已经在boot ROM中初始化了,所以不需要初始化DDR了,否则还需要初始化DDR。
start.s启动文件:
.global _start /* 全局标号 */
/*
* 描述: _start函数,程序从此函数开始执行,此函数主要功能是设置C
* 运行环境。
*/
_start:
/* 进入SVC模式 */
mrs r0, cpsr /*将特殊寄存器CPSR里面的数据复制到 R0中。*/
bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
orr r0, r0, #0x13 /* r0或上0x13,表示使用SVC模式 */
msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */
ldr sp, =0X80200000 /* 设置栈指针 */
b main /* 跳转到main函数 */
第二步:I.MX6ULL IO 初始化、控制
1、arm-linux-gnueabihf-gcc 将源文件编译成.o的目标文件
2、arm-linux-gnueabihf-ld 将众多的.o文件链接到一个指定的链接位置
3、arm-linux-gnueabihf-objcopy 格式转换,需要将led.elf文件转换成led.bin文件
4、 arm-linux-gnueabihf-objdump 反汇编,将elf链接后的文件反汇编成汇编代码以便调试代码
//-g选项是产生调试信息 GDB能够使用这些调试信息进行代码调试。
//-c选项是编译源文件,但是不链接。
//-o选项是指定编译产生的文件名字
arm-linux-gnueabihf-gcc -g -c led.s -o led.o
//-Ttext选项就是指定链接地址,0x87800000是外部DDR中的地址
//-o选项指定链接生成的elf文件名
arm-linux-gnueabihf-ld -Ttext 0x87800000 led.o -o led.elf
//-O选项指定以什么格式输出,后面的 binary表示以二进制格式输出
//-S表示不要复制源文件中的重定位信息和符号信息
//-g表示不复制源文件中的调试信息
arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
//-D选项表示反汇编所有的段,反汇编完成以后就会在当前目录下出现一个名为led.dis文件
arm-linux-gnueabihf-objdump -D led.elf > led.dis
使用正点原子的 “imxdownload” 软件可以将编译出来的bin文件制作成一个镜像文件然后烧写到SD卡中。过程为:
第一步:将SD卡通过读卡器插到电脑的USB上,然后连接到Ubuntu下
第二步:给予 imxdownload可执行权限
第三步:在电脑的Ubuntu下将执行imxdownload软件完成烧写
格式:./imxdownload <.bin file>
执行命令:./imxdownload ledc.bin /dev/sdb
第四步:将SD卡从电脑拔出插入到开发板的TF卡槽中,复位开发板,就会看见小灯欢快的闪起来了
《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.3》
《嵌入式Linux应用开发完全手册第2版_韦东山全系列视频文档全集》