1.设置处理器模式
设置6ULL处于SVC模式下。设置下CPSR寄存器的bit4-0,也就是M[4:0]为10011=0x13.。读写状态寄存器需要用到MRS和MSR指令。MRS将CPSR寄存器数据读出到通用寄存器里面,MSR指令将通用寄存器的值写入到CPSR寄存器里面去。
2.设置SP指针
SP可以指向内部RAM,也可以指向DDR,我们将其指向DDR。SP设置到哪里?512MB的范围0x80000000---0x9FFFFFFF。栈大小,0x20000000=2MB。处理器栈增长方式,对于A7而言是向下增长的。
3.跳转到C语言
使用b指令,跳转到C语言函数,比如MAIN函数
1汇编部分实验程序编写
I.MX6U 的汇编部分代码和 STM32 的启动文件 startup_stm32f10x_hd.s 基本类似的,只是本
实验我们不考虑中断向量表,只考虑初始化 C 环境即可。在前面创建的 start.s 中输入如下代码:
2 C 语言部分实验程序编写
main.h
#ifndef __MAIN_H #define __MAIN_H/* * CCM 相关寄存器地址 */ #define CCM_CCGR0 *((volatile unsigned int *)0X020C4068) #define CCM_CCGR1 *((volatile unsigned int *)0X020C406C) #define CCM_CCGR2 *((volatile unsigned int *)0X020C4070) #define CCM_CCGR3 *((volatile unsigned int *)0X020C4074) #define CCM_CCGR4 *((volatile unsigned int *)0X020C4078) #define CCM_CCGR5 *((volatile unsigned int *)0X020C407C) #define CCM_CCGR6 *((volatile unsigned int *)0X020C4080)/* * IOMUX 相关寄存器地址 */ #define SW_MUX_GPIO1_IO03 *((volatile unsigned int *)0X020E0068) #define SW_PAD_GPIO1_IO03 *((volatile unsigned int *)0X020E02F4)/* * GPIO1 相关寄存器地址 */ #define GPIO1_DR *((volatile unsigned int *)0X0209C000) #define GPIO1_GDIR *((volatile unsigned int *)0X0209C004) #define GPIO1_PSR *((volatile unsigned int *)0X0209C008) #define GPIO1_ICR1 *((volatile unsigned int *)0X0209C00C) #define GPIO1_ICR2 *((volatile unsigned int *)0X0209C010) #define GPIO1_IMR *((volatile unsigned int *)0X0209C014) #define GPIO1_ISR *((volatile unsigned int *)0X0209C018) #define GPIO1_EDGE_SEL *((volatile unsigned int *)0X0209C01C)#endif
main.c1 #include "main.h" 2 3 /* 4 * @description : 使能 I.MX6U 所有外设时钟 5 * @param : 无 6 * @return : 无 7 */ 8 void clk_enable(void) 9 { 10 CCM_CCGR0 = 0xffffffff; 11 CCM_CCGR1 = 0xffffffff; 12 CCM_CCGR2 = 0xffffffff; 13 CCM_CCGR3 = 0xffffffff; 14 CCM_CCGR4 = 0xffffffff; 15 CCM_CCGR5 = 0xffffffff; 16 CCM_CCGR6 = 0xffffffff; 17 } 18 19 /* 20 * @description : 初始化 LED 对应的 GPIO 21 * @param : 无 22 * @return : 无 23 */ 24 void led_init(void) 25 { 26 /* 1、初始化 IO 复用, 复用为 GPIO1_IO03 */ 27 SW_MUX_GPIO1_IO03 = 0x5; 28 29 /* 2、配置 GPIO1_IO03 的 IO 属性 30 *bit 16:0 HYS 关闭 31 *bit [15:14]: 00 默认下拉 32 *bit [13]: 0 kepper 功能 33 *bit [12]: 1 pull/keeper 使能 34 *bit [11]: 0 关闭开路输出 35 *bit [7:6]: 10 速度 100Mhz 36 *bit [5:3]: 110 R0/6 驱动能力 37 *bit [0]: 0 低转换率 38 */ 39 SW_PAD_GPIO1_IO03 = 0X10B0; 40 41 /* 3、初始化 GPIO, GPIO1_IO03 设置为输出 */ 42 GPIO1_GDIR = 0X0000008; 43 44 /* 4、设置 GPIO1_IO03 输出低电平,打开 LED0 */ 45 GPIO1_DR = 0X0; 46 } 47 48 /* 49 * @description : 打开 LED 灯 50 * @param : 无 51 * @return : 无 52 */ 53 void led_on(void) 54 { 55 /* 56 * 将 GPIO1_DR 的 bit3 清零 57 */ 58 GPIO1_DR &= ~(1<<3); 59 } 60 61 /* 62 * @description : 关闭 LED 灯 63 * @param : 无 64 * @return : 无 65 */ 66 void led_off(void) 67 { 68 /* 69 * 将 GPIO1_DR 的 bit3 置 1 70 */ 71 GPIO1_DR |= (1<<3); 72 } 73 74 /* 75 * @description : 短时间延时函数 76 * @param - n : 要延时循环次数(空操作循环次数,模式延时) 77 * @return : 无 78 */ 79 void delay_short(volatile unsigned int n) 80 { 81 while(n--){} 82 } 83 84 /* 85 * @description : 延时函数,在 396Mhz 的主频下延时时间大约为 1ms 86 * @param - n : 要延时的 ms 数 87 * @return : 无 88 */ 89 void delay(volatile unsigned int n) 90 { 91 while(n--) 92 { 93 delay_short(0x7ff); 94 } 95 } 96 97 /* 98 * @description : main 函数 99 * @param : 无 100 * @return : 无 101 */ 102 int main(void) 103 { 104 clk_enable(); /* 使能所有的时钟 */ 105 led_init(); /* 初始化 led */ 106 107 while(1) /* 死循环 */ 108 { 109 led_off(); /* 关闭 LED */ 110 delay(500); /* 延时大约 500ms */ 111 112 led_on(); /* 打开 LED */ 113 delay(500); /* 延时大约 500ms */ 114 } 115 116 return 0; 117 }
clk_enable 函数是使能CCGR0~CCGR6 所控制的所有外设时钟。 led_init 函数是初始化 LED 灯所使用的 IO ,包括设置IO 的复用功能、 IO 的属性配置和 GPIO 功能,最终控制 GPIO 输出低电平来打开 LED 灯。led_on 和 led_off 这两个函数看名字就知道,用来控制 LED 灯的亮灭的。 delay_short() 和 delay()这两个函数是延时函数, delay_short() 函数是靠空循环来实现延时的, delay() 是对 delay_short()的 简 单 封 装 ,在 I.MX6U 工作 在 396MHz(Boot ROM 设 置的 396MHz) 的 主 频 的 时候delay_short(0x7ff)基本能够实现大约 1ms 的延时,所以 delay()函数我们可以用来完成 ms 延时。
main 函数就是我们的主函数了,在 main 函数中先调用函数 clk_enable() 和 led_init() 来完成时钟使能和 LED 初始化,最终在 while(1) 循环中实现 LED 循环亮灭,亮灭时间大约是 500ms 。编写 Makefile第 1 行定义了一个变量 objs , objs 包含着要生成 ledc.bin 所需的材料: start.o 和 main.o ,也就是当前工程下的 start.s 和 main.c 这两个文件编译后的 .o 文件。这里要注意 start.o 一定要放到最前面!因为在后面链接的时候 start.o 要在最前面,因为 start.o 是最先要执行的文件!第 3 行就是默认目标,目的是生成最终的可执行文件 ledc.bin , ledc.bin 依赖 start.o 和 main.o如果当前工程没有 start.o 和 main.o 的时候就会找到相应的规则去生成 start.o 和 main.o 。比如start.o 是 start.s 文件编译生成的,因此会执行第 8 行的规则。第 4 行是使用 arm-linux-gnueabihf-ld 进行链接,链接起始地址是 0X87800000 ,但是这一行用到了自动变量“ $^ ”,“ $^ ”的意思是所有依赖文件的集合,在这里就是 objs 这个变量的值:start.o 和 main.o 。链接的时候 start.o 要链接到最前面,因为第一行代码就是 start.o 里面的,因此这一行就相当于:第 5 行使用 arm-linux-gnueabihf-objcopy 来将 ledc.elf 文件转为 ledc.bin ,本行也用到了自动变量“ $@ ”,“ $@ ”的意思是目标集合,在这里就是“ledc.bin”,那么本行就相当于:第 6 行使用 arm-linux-gnueabihf-objdump 来反汇编,生成 ledc.dis 文件。第 8~15 行就是针对不同的文件类型将其编译成对应的 .o 文件,其实就是汇编 .s(.S) 和 .c 文件,比如 start.s 就会使用第 8 行的规则来生成对应的 start.o 文件。第 9 行就是具体的命令,这 行也用到了自动变量“$@ ”和“ $< ”,其中“ $< ”的意思是依赖目标集合的第一个文件。比如 start.s 要编译成 start.o 的话第 8 行和第 9 行就相当于:第 17 行就是工程清理规则,通过命令“ make clean ”就可以清理工程。Makefile 文件就讲到这里,我们可以将整个工程拿到 Ubuntu 下去编译,编译完成以后可以使用 软件 imxdownload 将其下载到 SD 卡中,命令如下:链接脚本在上面的 Makefile 中我们链接代码的时候使用如下语句:上面语句中我们是通过“ -Ttext ”来指定链接地址是 0X87800000 的,这样的话所有的文件都会链接到以 0X87800000 为起始地址的区域。但是有时候我们很多文件需要链接到指定的区 域,或者叫做段里面,比如在 Linux 里面初始化函数就会放到 init 段里面。因此我们需要能够 自定义一些段,这些段的起始地址我们可以自由指定,同样的我们也可以指定一个文件或者函 数应该存放到哪个段里面去。要完成这个功能我们就需要使用到链接脚本,看名字就知道链接 脚本主要用于链接的,用于描述文件应该如何被链接在一起形成最终的可执行文件。其主要目 的是描述输入文件中的段如何被映射到输出文件中,并且控制输出文件中的内存排布。比如我 们编译生成的文件一般都包含 text 段、 data 段等等。