参考教程:正点原子【第二期】手把手教你学Linux之ARM(MX6U)裸机篇_哔哩哔哩_bilibili
一、I.MX6ULL IO命名与配置寄存器
1、STM32引脚的命名与相关寄存器
(1)在STM32中,引脚的命名简单直观,比如GPIOA,对应有PA0~PA15,一组有16个引脚。
(2)在STM32中配置引脚电气属性(上下拉、速度、推挽/开漏等)的方法是写该引脚对应的GPIO外设寄存器。
(3)STM32F1引脚的复用重映射可通过配置AFIO寄存器实现,而F4/H7等系列则是使用GPIO_AFRH/GPIO_AFRL寄存器选择外设复用功能。
2、I.MX6ULL引脚的命名与相关寄存器
(1)在I.MX6ULL中,一般引脚名的格式为"PAD_xxx",比如"PAD_BOOT_MODE0"。
(2)在I.MX6ULL中,每一个I/O引脚都配套两个GPIO外设寄存器:
①MUX复用寄存器:选择这个PAD引脚复用哪一个功能。
②PAD电气属性寄存器:配置上下拉、速率、驱动能力、开漏、施密特等电气参数。
(3)I.MX6U的GPIO一共有5组------GPIO1、GPIO2、GPIO3、GPIO4和GPIO5,其中GPIO1有32个IO,GPIO2有22个IO,GPIO3有29个IO,GPIO4有29个IO,GPIO5有12个IO,一共有124个GPIO引脚。
二、原理分析与引脚配置说明
1、LED原理图拆解
(1)LED指示灯电路如下图所示,它采用低电平驱动的方式,需要给LED0施加低电平,才能点亮它。

(2)在原理图中索引"LED0",可以发现它接在"GPIO 3"上(应该是"GPIO1_IO3",原理图标注不严谨)。

2、引脚配置说明
(1)I.MX6ULL的时钟管理配置说明在章节Clock Controller Module(CCM)中,其中CCM_CCGRx寄存器控制所有外设时钟的使能与否。
①每个外设的时钟配置由2bit位组成,定义如下:

②查看数据手册可知,GPIO1的时钟使能控制由CCM_CCGR1寄存器的bit27-26决定,为简单起见,可直接将其配置为11,无论什么时候,都将该时钟使能。
③由于本文主旨为学习ARM裸机开发,不涉及低功耗策略,所以为了更简单起见,默认将CCM_CCGRx全部设置为0xFFFFFFFF,即默认使能所有外设时钟。
(2)在芯片手册找到GPIO1_IO03这个引脚的MUX复用寄存器(在章节IOMUX Controller (IOMUXC)的IOMUXC Memory Map/Register Definition小节),如下图所示,如果希望把该引脚作为普通的GPIO引脚使用,应该将MUX_MODE配置成ALT5。

(3)在芯片手册找到GPIO1_IO03这个引脚的PAD电气属性寄存器(在章节IOMUX Controller (IOMUXC)的IOMUXC Memory Map/Register Definition小节),如下图所示。


GPIO的功能图如下,寄存器的每个配置项都对应着功能图的一个输入

|---------------|---------------------------------------------------------------------------------------------------------------|
| 位段 | 定义 |
| HYS(bit16) | 此位为0时禁止迟滞比较器,为1时使能迟滞比较器(迟滞比较器用于对输入波形整形) |
| PUS(bit15:14) | 00表示配置100K下拉电阻,01表示配置47K上拉电阻,10表示配置100K上拉电阻,11表示配置22K上拉电阻 |
| PUE(bit13) | 当为0时使用状态保持器,当为1时使用上下拉模式(状态保持器在IO作为输入时才有用,如果使用状态保持器,当外部电路断电以后,此IO口可以保持住以前的状态) |
| PKE(bit12) | 为0时禁止上下拉电阻/状态保持器,为1时使能上下拉电阻和状态保持器 |
| ODE(bit11) | 此位为0时禁止开路输出,当此位为1时使能开路输出功能 |
| SPEED(bit7:6) | 当IO用作输出的时候,此位用来设置IO速度,00表示50M,01和10表示100M,11表示200M |
| DSE(bit5:3) | 设置IO的驱动能力,000表示驱动输出关闭,其它7个枚举表示不同的驱动能力,枚举值越大驱动能力越强 |
| SRE(bit0) | 此位为0时是低压摆率,为1时是高压摆率(这里的压摆率指的是IO电平跳变所需要的时间,比如从0到1需要的时间越短,波形就越陡,压摆率越高;压摆率不是越高越高,在高速通信中往往希望压摆率更高,但也要注意是否满足EMC需求) |
(4)I.MX6U一共有GPIO1~GPIO5共五组GPIO,每组GPIO都有8个寄存器------DR、GDIR、PSR、ICR1、ICR2、EDGE_SEL、IMR和ISR,把它们当作普通GPIO引脚使用时,需要设置这些寄存器,寄存器的每个位对应一个GPIO引脚。
①DR数据寄存器用于保存IO引脚对应的电平值,在输出模式下,如果希望某组GPIO的一个引脚输出对应的电平值,就在DR寄存器对应位写入该电平值即可;在输入模式下,可以通过访问DR寄存器的某个位获取对应IO引脚当前的电平值。如果希望GPIO1_IO03引脚输出低电平,应该将GPIO1_DR的bit3配置成0。(寄存器地址为基地址+0H offest)

②GDIR方向寄存器用于设置GPIO引脚的工作方向,可配置为输入模式或输出模式。如果希望把GPIO1_IO03引脚作为输出引脚使用,应该将GPIO1_GDIR的bit3配置成OUTPUT。(寄存器地址为基地址+4H offest)

③PSR状态寄存器是只读寄存器,只有在访问时该寄存器才会对输入信号进行采样,所以每次访问它时都需要两个等待状态以进行同步。(寄存器地址为基地址+8H offest)

④ICR1和ICR2都是中断控制寄存器,ICR1用于配置低16个GPIO,ICR2用于配置高16个GPIO,下图仅示出ICR1寄存器ICR15的定义,其它位段定义可以此类推,00表示低电平触发,01表示高电平触发,10表示上升沿触发,11表示下降沿触发。(ICR1寄存器地址为基地址+CH offest,ICR2寄存器地址为基地址+10H offest)

⑤IMR中断屏蔽寄存器用于控制GPIO的中断禁止或使能。(寄存器地址为基地址+14H offest)

⑥ISR中断状态寄存器用于保存GPIO中断标志位,当GPIO发生中断时,其对应的标志位会被置位,不过它不会自动清除,所以在中断处理完成后,需要在程序中手动清除中断标志位,方法是向需要清除的位写1清零。(寄存器地址为基地址+18H offest)

⑦EDFE_SEL边沿选择寄存器用于设置边沿中断,它会覆盖ICR1和ICR2的设置。(寄存器地址为基地址+1CH offest)

⑧寄存器的地址可参考数据手册的GPIO memory map,下图仅示出GPIO1相关寄存器的地址。(在数据手册中,任意一个外设寄存器的介绍页面,都会标有该寄存器的地址)

三、驱动程序编写与编译
1、使用汇编语言配置引脚
(1)在第一章中搭建开发环境时,在用户目录下建立了一个linux文件夹,现在在这个文件夹中新建一个名为"IMX6ULL"的文件夹,该文件夹用于存放I.MX6ULL相关的工程文件,然后在该文件夹下继续新建一个名为"Board_Drivers"的文件夹,用于存放项目工程。
(2)在Board_Drivers文件夹中新建文件夹"1_leds",然后在该文件夹中新建led.s文件,本小节的LED驱动汇编代码将写在该文件中。
(3)在led.s文件中先将程序基本框架搭起来,如下所示:
cpp
.global _start @ 全局标号
_start:
<驱动代码(待编写)>
loop:
b loop @ 死循环,相当于C语言的while(1)
(4)添加汇编代码,使能所有外设时钟,如下所示:
cpp
@ 使能所有外设时钟
ldr r1, =0xffffffff @ 往r1中写入0xffffffff
ldr r0, =0x020c4068 @ 将CCGR0的地址写入r0
str r1, [r0] @ 将r1中的数据写入r0指向的寄存器
ldr r0, =0x020c406c @ 将CCGR1的地址写入r0
str r1, [r0] @ 将r1中的数据写入r0指向的寄存器
ldr r0, =0x020c4070 @ 将CCGR2的地址写入r0
str r1, [r0] @ 将r1中的数据写入r0指向的寄存器
ldr r0, =0x020c4074 @ 将CCGR3的地址写入r0
str r1, [r0] @ 将r1中的数据写入r0指向的寄存器
ldr r0, =0x020c4078 @ 将CCGR4的地址写入r0
str r1, [r0] @ 将r1中的数据写入r0指向的寄存器
ldr r0, =0x020c407c @ 将CCGR5的地址写入r0
str r1, [r0] @ 将r1中的数据写入r0指向的寄存器
ldr r0, =0x020c4080 @ 将CCGR6的地址写入r0
str r1, [r0] @ 将r1中的数据写入r0指向的寄存器
(5)添加汇编代码,配置GPIO1_IO03的复用寄存器,如下所示:
cpp
@ 配置IO复用,将IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03的MUX_MODE配置为ALT5
@ 即配置GPIO1_IO03复用为普通GPIO
ldr r1, =0x5 @ 往r1中写入0x5
ldr r0, =0x020e0068 @ 将IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03的地址写入r0
str r1, [r0] @ 将r1中的数据写入r0指向的寄存器
(6)添加汇编代码,配置GPIO1_IO03的电气属性寄存器,如下所示:
cpp
@ 配置IO电气属性,操作IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03
@ bit0 0 ------ 压摆率配置为低速率
@ bit5:3 110 ------ R0/6驱动能力
@ bit7:6 10 ------ 100MHz的速率
@ bit11 0 ------ 关闭开路输出
@ bit12 1 ------ 使能上下拉电阻和状态保持器
@ bit13 0 ------ 使用状态保持器
@ bit15:14 00 ------ 100K下拉电阻
@ bit16 0 ------ 关闭迟滞比较器
ldr r1, =0x10b0 @ 往r1中写入0x10b0
ldr r0, =0x020e02f4 @ 将IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03的地址写入r0
str r1, [r0] @ 将r1中的数据写入r0指向的寄存器
(7)添加汇编代码,配置GPIO1_IO03的GPIO功能寄存器,如下所示:
cpp
@ 配置GPIO功能
ldr r1, =0x8 @ 往r1中写入0x8(设置GPIO1_GPIO03为输出模式)
ldr r0, =0x0209c004 @ 将GPIO1_GDIR的地址写入r0
str r1, [r0] @ 将r1中的数据写入r0指向的寄存器
ldr r1, =0x0 @ 往r1中写入0x0(设置GPIO1_GPIO03输出低电平0)
ldr r0, =0x0209c000 @ 将GPIO1_DR的地址写入r0
str r1, [r0] @ 将r1中的数据写入r0指向的寄存器【LED灯被点亮】
(8)最终led.s的文件内容如下所示:
cpp
.global _start @ 全局标号
_start:
@ 使能所有外设时钟
ldr r1, =0xffffffff @ 往r1中写入0xffffffff
ldr r0, =0x020c4068 @ 将CCGR0的地址写入r0
str r1, [r0] @ 将r1中的数据写入r0指向的寄存器
ldr r0, =0x020c406c @ 将CCGR1的地址写入r0
str r1, [r0] @ 将r1中的数据写入r0指向的寄存器
ldr r0, =0x020c4070 @ 将CCGR2的地址写入r0
str r1, [r0] @ 将r1中的数据写入r0指向的寄存器
ldr r0, =0x020c4074 @ 将CCGR3的地址写入r0
str r1, [r0] @ 将r1中的数据写入r0指向的寄存器
ldr r0, =0x020c4078 @ 将CCGR4的地址写入r0
str r1, [r0] @ 将r1中的数据写入r0指向的寄存器
ldr r0, =0x020c407c @ 将CCGR5的地址写入r0
str r1, [r0] @ 将r1中的数据写入r0指向的寄存器
ldr r0, =0x020c4080 @ 将CCGR6的地址写入r0
str r1, [r0] @ 将r1中的数据写入r0指向的寄存器
@ 配置IO复用,将IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03的MUX_MODE配置为ALT5
@ 即配置GPIO1_IO03复用为普通GPIO
ldr r1, =0x5 @ 往r1中写入0x5
ldr r0, =0x020e0068 @ 将IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03的地址写入r0
str r1, [r0] @ 将r1中的数据写入r0指向的寄存器
@ 配置IO电气属性,操作IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03
@ bit0 0 ------ 压摆率配置为低速率
@ bit5:3 110 ------ R0/6驱动能力
@ bit7:6 10 ------ 100MHz的速率
@ bit11 0 ------ 关闭开路输出
@ bit12 1 ------ 使能上下拉电阻和状态保持器
@ bit13 0 ------ 使用状态保持器
@ bit15:14 00 ------ 100K下拉电阻
@ bit16 0 ------ 关闭迟滞比较器
ldr r1, =0x10b0 @ 往r1中写入0x10b0
ldr r0, =0x020e02f4 @ 将IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03的地址写入r0
str r1, [r0] @ 将r1中的数据写入r0指向的寄存器
@ 配置GPIO功能
ldr r1, =0x8 @ 往r1中写入0x8(设置GPIO1_GPIO03为输出模式)
ldr r0, =0x0209c004 @ 将GPIO1_GDIR的地址写入r0
str r1, [r0] @ 将r1中的数据写入r0指向的寄存器
ldr r1, =0x0 @ 往r1中写入0x0(设置GPIO1_GPIO03输出低电平0)
ldr r0, =0x0209c000 @ 将GPIO1_DR的地址写入r0
str r1, [r0] @ 将r1中的数据写入r0指向的寄存器【LED灯被点亮】
loop:
b loop @ 死循环,相当于C语言的while(1)
2、程序编译与链接
(1)该程序编写完成后,需要编译出能够在ARM开发板上运行的可执行文件,因此需要使用交叉编译器arm-linux-gnueabihf-gcc,由于本节只有一个源文件,整个编译流程比较简单。
(2)首先将led.s编译为对应的.o文件,命令为"arm-linux-gnueabihf-gcc -g -c led.s -o led.o"(文件名视具体情况替换),其中"-g"选项用于产生调试信息供GDB使用,"-c"选项指定编译源文件但是不链接,"-o"选项指定编译产生文件的名字,执行该命令后,当前目录下将产生一个led.o文件。

(3)arm-linux-gnueabihf-ld用于将众多的.o文件链接到一个指定的链接位置,这个指定位置就是上电后第一条运行代码的地址(不一定是第一条代码存储的地址)。
①对于6ULL来说,链接起始地址应该指向RAM地址(几乎所有Cortex-A芯片内部都没有Flash)。
②本文所有裸机例程如无特殊说明,可执行文件都是烧写到SD卡中,上电以后,I.MX6U的内部boot rom程序(芯片出厂自带一段固化在芯片内部的小程序)会将可执行文件拷贝到链接地址处,这个链接地址可以在I.MX6U的内部128KB RAM中(0X900000~0X91FFFF),也可以在外部的DDR中,本文所有裸机例程的链接地址都在DDR中,链接起始地址为0X87800000。
③I.MX上电的时候,DDR没有初始化,由于DDR是外部内存,那么外部内存中的.bin文件不能直接运行,需要添加一个头部,这个头部叫IVT(镜像向量表)+DCD(设备配置数据),DCD里写了DDR控制器寄存器配置(DDR初始化参数),Boot ROM自动从SD卡/eMMC读出这个头部,按照DCD里面的参数把DDR初始化,再把.bin代码拷贝到DDR中,最后跳过去执行所拷贝的代码。基于这一点,.bin的代码实际被拷贝到DDR里的地址必须与链接起始地址一致(位置无关代码除外)。
④确定链接地址为0X87800000后,可以使用命令"arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf"链接(文件名视具体情况替换),生成.elf格式的可执行文件,"-o"选项指定编译产生文件的名字,执行该命令后,当前目录下将产生一个led.elf文件。

(4)最终烧写到SD卡中的可执行文件需要是.bin格式的,对此可使用arm-linux-gnueabihf-objcopy工具,命令为"arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin"(文件名视具体情况替换),"-O"选项指定以何种格式输出文件("binary"表示二进制格式),"-S"选项表示不要复制源文件中的重定位信息和符号信息,"-g"选项表示不复制源文件中的调试信息,执行该命令后,当前目录下将产生一个led.bin文件。

(5)大多数情况下都是用C语言写代码,有时候需要查看其汇编代码来调试代码,这时需要进行反汇编,一般可以将.elf文件反汇编,命令为"arm-linux-gnueabihf-objdump -D led.elf > led.dis"(文件名视具体情况替换),"-D"选项表示反汇编所有段,反汇编完成以后,在当前目录下出现一个名为led.dis文件。
3、Makefile文件编写
(1)每次编译程序都要运行上面的指令,当项目工程越来越大时,纯敲指令的方式会特别麻烦,为了简化编译步骤,可以使用上一部分介绍的Makefile文件。
(2)在工程根目录下创建一个名为"Makefile"的文件,然后在文件中输入以下内容,后续只需要在工程根目录执行一次"make",就可以完成程序的编译。
bash
led.bin:led.s
arm-linux-gnueabihf-gcc -g -c led.s -o led.o
arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf
arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
arm-linux-gnueabihf-objdump -D led.elf > led.dis
clean:
rm -rf *.o led.bin led.elf led.dis # 执行"make clean"可清除编译结果文件
四、程序烧写
1、imxdownload
(1)本文例程使用正点原子开发的imxdownload制作.bin文件的头部以及实现SD卡烧写,该软件(或者说脚本)仅适用于NXP的I.MX6U系列芯片。
(2)imxdownload的源码如下,可在Ubuntu中直接用gcc编译器编译出可执行文件(类型为文件,无后缀)。
①imxdownload.h文件:
cpp
#ifndef _IMXDOWNLOAD_H
#define _IMXDOWNLOAD_H
/* IMX6U IVT DCD表信息 暂时定义为1K Bytes,此表是读取的u-boot.imx前1K Bytes
* imx6_ivedcd_table[9]是指明代码长度的,本应该根据实际的代码长度来修改
* 这里为了方便,就直接定义为2M Bytes,即
*/
const int imx6_512mb_ivtdcd_table[256] = {
0X402000D1,0X87800000,0X00000000,0X877FF42C,0X877FF420,0X877FF400,0X00000000,0X00000000,
0X877FF000,0X00200000,0X00000000,0X40E801D2,0X04E401CC,0X68400C02,0XFFFFFFFF,0X6C400C02,
0XFFFFFFFF,0X70400C02,0XFFFFFFFF,0X74400C02,0XFFFFFFFF,0X78400C02,0XFFFFFFFF,0X7C400C02,
0XFFFFFFFF,0X80400C02,0XFFFFFFFF,0XB4040E02,0X00000C00,0XAC040E02,0X00000000,0X7C020E02,
0X30000000,0X50020E02,0X30000000,0X4C020E02,0X30000000,0X90040E02,0X30000000,0X88020E02,
0X30000C00,0X70020E02,0X00000000,0X60020E02,0X30000000,0X64020E02,0X30000000,0XA0040E02,
0X30000000,0X94040E02,0X00000200,0X80020E02,0X30000000,0X84020E02,0X30000000,0XB0040E02,
0X00000200,0X98040E02,0X30000000,0XA4040E02,0X30000000,0X44020E02,0X30000000,0X48020E02,
0X30000000,0X1C001B02,0X00800000,0X00081B02,0X030039A1,0X0C081B02,0X0B000300,0X3C081B02,
0X44014801,0X48081B02,0X302C4040,0X50081B02,0X343E4040,0X1C081B02,0X33333333,0X20081B02,
0X33333333,0X2C081B02,0X333333F3,0X30081B02,0X333333F3,0XC0081B02,0X09409400,0XB8081B02,
0X00080000,0X04001B02,0X2D000200,0X08001B02,0X3030331B,0X0C001B02,0XF3526B67,0X10001B02,
0X630B6DB6,0X14001B02,0XDB00FF01,0X18001B02,0X40172000,0X1C001B02,0X00800000,0X2C001B02,
0XD2260000,0X30001B02,0X23106B00,0X40001B02,0X4F000000,0X00001B02,0X00001884,0X90081B02,
0X00004000,0X1C001B02,0X32800002,0X1C001B02,0X33800000,0X1C001B02,0X31800400,0X1C001B02,
0X30802015,0X1C001B02,0X40800004,0X20001B02,0X00080000,0X18081B02,0X27020000,0X04001B02,
0X2D550200,0X04041B02,0X06100100,0X1C001B02,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000
};
const int imx6_256mb_ivtdcd_table[256] = {
0X402000D1,0X87800000,0X00000000,0X877FF42C,0X877FF420,0X877FF400,0X00000000,0X00000000,
0X877FF000,0X00076000,0X00000000,0X40E801D2,0X04E401CC,0X68400C02,0XFFFFFFFF,0X6C400C02,
0XFFFFFFFF,0X70400C02,0XFFFFFFFF,0X74400C02,0XFFFFFFFF,0X78400C02,0XFFFFFFFF,0X7C400C02,
0XFFFFFFFF,0X80400C02,0XFFFFFFFF,0XB4040E02,0X00000C00,0XAC040E02,0X00000000,0X7C020E02,
0X30000000,0X50020E02,0X30000000,0X4C020E02,0X30000000,0X90040E02,0X30000000,0X88020E02,
0X30000C00,0X70020E02,0X00000000,0X60020E02,0X30000000,0X64020E02,0X30000000,0XA0040E02,
0X30000000,0X94040E02,0X00000200,0X80020E02,0X30000000,0X84020E02,0X30000000,0XB0040E02,
0X00000200,0X98040E02,0X30000000,0XA4040E02,0X30000000,0X44020E02,0X30000000,0X48020E02,
0X30000000,0X1C001B02,0X00800000,0X00081B02,0X030039A1,0X0C081B02,0X04000000,0X3C081B02,
0X3C013C01,0X48081B02,0X38324040,0X50081B02,0X28304040,0X1C081B02,0X33333333,0X20081B02,
0X33333333,0X2C081B02,0X333333F3,0X30081B02,0X333333F3,0XC0081B02,0X09409400,0XB8081B02,
0X00080000,0X04001B02,0X2D000200,0X08001B02,0X3030331B,0X0C001B02,0XF352433F,0X10001B02,
0X630B6DB6,0X14001B02,0XDB00FF01,0X18001B02,0X40172000,0X1C001B02,0X00800000,0X2C001B02,
0XD2260000,0X30001B02,0X23104300,0X40001B02,0X47000000,0X00001B02,0X00001883,0X90081B02,
0X00004000,0X1C001B02,0X32800002,0X1C001B02,0X33800000,0X1C001B02,0X31800400,0X1C001B02,
0X30802015,0X1C001B02,0X40800004,0X20001B02,0X00080000,0X18081B02,0X27020000,0X04001B02,
0X2D550200,0X04041B02,0X06100100,0X1C001B02,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,
};
#endif
②imxdownload.c文件:
cpp
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "imxdownload.h"
#define SHELLCMD_LEN (200)
#define BIN_OFFSET (3072)
/* 此宏指明是否打印u-boot.imx的IVT DCD表信息,不同的开发板其IVT和DCD
* 表的数据是不同的,因此需要获取所使用的开发板的IVT和DCD表信息,最
* 简单的方法就是读取开发板配套资料里面的u-boot.imx的前1KB数据,理论上
* 应该读取3KB的数据,但是表信息远远没有3K这么多,因此读1KB即可
*/
#define PRINT_TAB 0
/*
* 介绍: 此软件是针对NXP的IMX6U系列芯片的,软件用来烧写bin文件到SD卡里面,
* 本软件会自动添加IVT、DCD等信息到原始的bin文件里面,主要用于裸机和uboot的烧写。
* 使用方法: 1、编译好原始的二进制bin文件,如,u-boot.bin等,并将编译好的.bin文件和本
* 软件放置到同一个目录下!!!!
* 2、执行命令sudo ./imxdownload <soucre_bin> <sd_device>
* 如烧写u-boot.bin到/dev/sdd中即可使用如下所示命令:
* sudo ./imxdownload u-boot.bin /dev/sdd
*/
/*
* 输出一些信息
*/
void message_print(void)
{
printf("I.MX6ULL bin download software\r\n");
printf("Edit by:zuozhongkai\r\n");
printf("Date:2019/6/10\r\n");
printf("Version:V1.1\r\n");
printf("log:V1.0 initial version,just support 512MB DDR3\r\n");
printf(" V1.1 and support 256MB DDR3\r\n");
}
int main(int argc, char *argv[])
{
FILE *fp;
unsigned char *buf;
unsigned char *cmdbuf;
int nbytes, filelen;
int i = 0, j = 0;
int ddrsize = 0; /* 0为512MB,1为256MB,2为128MB...... */
message_print();
if((argc != 3) && (argc != 4)){
printf("Error Usage! Reference Below:\r\n");
printf("sudo ./%s <-512m or -256m> <source_bin> <sd_device>\r\n", argv[0]);
return -1;
}
/* 查找参数,获取DDR容量 */
for(i = 0; i < argc; i++)
{
char *param = argv[i];
if(param[0] != '-')
continue;
if(strcmp(param, "-256m") == 0) /* 256MB */
ddrsize = 1;
else if(strcmp(param, "-512m") == 0) /* 512MB */
ddrsize = 0;
}
if(argc == 3) /* 三个参数,也就是不输入DDR容量的话默认为512MB */
ddrsize = 0;
/* 打开bin文件 */
fp = fopen(argv[1], "rb"); /* 以二进制只读方式打开bin文件 */
if(fp == NULL){
printf("Can't Open file %s\r\n", argv[1]);
return -1;
}
/* 获取bin文件长度 */
fseek(fp, 0L, SEEK_END);
filelen = ftell(fp);
fseek(fp, 0L, SEEK_SET);
printf("file %s size = %dBytes\r\n", argv[1], filelen);
/* 读取bin文件到缓冲区buf中 */
buf = malloc(filelen + BIN_OFFSET);
if(buf == NULL){
printf("Mem Malloc Failed!\r\n");
fclose(fp);
return -1;
}
memset(buf, 0, filelen + BIN_OFFSET); /* 清零 */
/* 读取bin源码文件 */
fread(buf + BIN_OFFSET, 1, filelen, fp);
/* 关闭文件 */
fclose(fp);
#if PRINT_TAB
printf("IVT DCD Table:\r\n");
for(i = 0; i < 1024/32; i++){
for(j = 0; j < 8; j++)
{
printf("0X%08X,",*(int *)(buf + BIN_OFFSET + (((i * 8) + j) * 4)));
}
printf("\r\n");
}
free(buf);
return 0;
#endif
/* 添加IVT DCD等表信息到bin文件里面 */
if(ddrsize == 0) { /* 512MB */
printf("Board DDR SIZE: 512MB\r\n");
memcpy(buf, imx6_512mb_ivtdcd_table, sizeof(imx6_512mb_ivtdcd_table));
}
else if (ddrsize == 1) { /* 256MB */
printf("Board DDR SIZE: 256MB\r\n");
memcpy(buf, imx6_256mb_ivtdcd_table, sizeof(imx6_256mb_ivtdcd_table));
}
/* 现在已经在buf中构建好了可以用于下载的bin文件,将buf中的数据保存到
* 到一个文件中,文件命名为load.imx
*/
printf("Delete Old load.imx\r\n");
system("rm -rf load.imx"); /* 先删除旧的load.imx文件 */
printf("Create New load.imx\r\n");
system("touch load.imx"); /* 创建新的load.imx文件 */
fp = fopen("load.imx", "wb"); /* 打开laod.imx */
if(fp == NULL){
printf("Cant't Open load.imx!!!\r\n");
free(buf);
return -1;
}
nbytes = fwrite(buf, 1, filelen + BIN_OFFSET, fp);
if(nbytes != (filelen + BIN_OFFSET)){
printf("File Write Error!\r\n");
free(buf);
fclose(fp);
return -1;
}
free(buf);
fclose(fp);
/* 构建烧写的shell命令 */
cmdbuf = malloc(SHELLCMD_LEN);
sprintf(cmdbuf, "sudo dd iflag=dsync oflag=dsync if=load.imx of=%s bs=512 seek=2",argv[2]);
printf("Download load.imx to %s ......\r\n", argv[2]);
/* 执行上面的shell命令 */
system(cmdbuf);
free(cmdbuf);
return 0;
}
2、IVT+DCD的制作与软件烧写步骤
(1)将imxdownload拷贝到工程的根目录下,也就是和led.bin处于同一个文件夹下,并赋予其可执行权限。

(2)准备一张全新的SD卡(烧写过程中会格式化SD卡,必须保证SD卡中没有重要文件),通过读卡器将它接入虚拟机Ubuntu,可通过在dev文件夹中运行"ls /dev/sd*"确认SD卡是否成功接入虚拟机。

(3)需要注意的是,将固件烧写到SD卡中,并不是直接把.bin文件拷贝进去,而是将.bin文件烧写到SD卡的绝对地址上,而且对于I.MX而言,所烧写的.bin文件还需要在烧写前添加头部,不过这一系列操作,都可以由imxdownload一步完成。
(4)使用imxdownload向SD卡烧写.bin文件,命令格式如下,烧写完成以后会在当前工程目录下生成一个load.imx的文件,这个文件就是所谓的头部+二进制代码文件,也是最终烧写到SD卡中的文件。
./imxdownload <.bin file> <SD Card>
./imxdownload led.bin /dev/sdb 将led.bin文件烧写到/dev/sdb设备中

(5)将SD卡插到开发板的SD卡槽中,然后设置拨码开关为SD卡启动模式,拨码开关设置如下图所示,设置好后按下开发板的复位键,如果代码运行正常,LED0会被点亮。
