- 一、C语言运行环境测试
- 一、设置处理器模式
- 二、设置sp指针
- 三、跳转到C语言
- 二、软件编写
- 三、链接脚本
.global _start _start: mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0x13 msr cpsr, r0 ldr sp, =0x80200000 b main一、设置处理器模式
设置6ULL处于SVC模式 下。设置CPSR寄存器的bit4-0,也就是M[4:0]为10011=0x13。读写状态寄存器需要用到MRS和MSR指令。MRS将CPSR寄存器数据读出到通用寄存器里面,MSR指令将通用寄存器的值写入到CPSR寄存器里面去。
二、设置sp指针 Sp可以指向内部RAM,也可以指向DDR,我们将其指向DDR。Sp设置到哪里?512MB的范围0x80000000~0x9FFFFFFF。栈大小,0x200000=2MB。处理器栈增长方式,对于A7而言是向下增长的。设置sp指向0x80200000。
三、跳转到C语言 使用b指令,跳转到C语言函数,比如main函数。
二、软件编写#ifndef __MAIN_H #define __MAIN_H #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) #define SW_MUX_GPIO1_IO03 *((volatile unsigned int *)0x020E0068) #define SW_PAD_GPIO1_IO03 *((volatile unsigned int *)0x020E02F4) #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
#include "main.h"
void clk_enable(void)
{
CCM_CCGR1 = 0xFFFFFFFF;
CCM_CCGR2 = 0xFFFFFFFF;
CCM_CCGR3 = 0xFFFFFFFF;
CCM_CCGR4 = 0xFFFFFFFF;
CCM_CCGR5 = 0xFFFFFFFF;
CCM_CCGR6 = 0xFFFFFFFF;
}
void led_init(void)
{
SW_PAD_GPIO1_IO03 = 0x5;
SW_PAD_GPIO1_IO03 = 0x10B0;
GPIO1_GDIR = 0x8;
GPIO1_DR = 0x0;
}
void delay_short(volatile unsigned int n)
{
while(n--){}
}
void delay(volatile unsigned int n)
{
while(n--)
{
delay_short(0x77f); //1毫秒(396MHZ)
}
}
void led_on(void)
{
GPIO1_DR &= ~(1<<3);
}
void led_off(void)
{
GPIO1_DR |= (1<<3);
}
int main(void)
{
clk_enable();
led_init();
while(1)
{
led_on();
delay(500);
led_off();
delay(500);
}
return 0;
}
三、链接脚本
链接脚本描述了要连接的文件,以及链接顺序,和链接首地址
链接脚本的语法很简单,就是编写一系列的命令,这些命令组成了链接脚本,每个命令是一个带有参数的关键字或者一个对符号的赋值,可以使用分号分隔命令。像文件名之类的字符串可以直接键入,也可以使用通配符“*”。最简单的链接脚本可以只包含一个命令“SECTIONS”,我们可以在这一个“SECTIONS”里面来描述输出文件的内存布局。我们一般编译出来的代码都包含在 text、data、bss 和 rodata 这四个段内,
我们本试验的链接脚本要求如下:
①、链接起始地址为 0x87800000
②、start.o 要被链接到最开始的地方,因为 start.o 里面包含这第一个要执行的命令
根据要求,在 Makefile 同目录下新建一个名为“imx6ul.lds”的文件,然后在此文件里面输入如下所示代码:
SECTIONS{
. = 0x87800000;
.text :
{
start.o
main.o
*(.text)
}
.rodata ALIGN(4) : {*(.rodata*)}
.data ALIGN(4) : { *(.data) }
__bss_start = .;
.bss ALIGN(4) : { *(.bss) *(COMMON) }
__bss_end = .;
}
第 1 行我们先写了一个关键字“SECTIONS”,后面跟了一个大括号,这个大括号和第14行的大括号是一对,这是必须的。看起来就跟 C 语言里面的函数一样。
第 2 行对一个特殊符号“.”进行赋值,“.”在链接脚本里面叫做定位计数器,默认的定位计数器为 0。我们要求代码链接到以 0x87800000为起始地址的地方,因此这一行给“.”赋值0x87800000,表示以0x87800000开始,后面的文件或者段都会以0x87800000为起始地址开始链接。
第 3 行的“.text”是段名,后面的冒号是语法要求,冒号后面的大括号里面可以填上要链接到“.text”这个段里面的所有文件,“(.text)”中的“”是通配符,表示所有输入文件的.text段都放到“.text”中。
第5行设置链接到开始位置的文件为start.o,因为 start.o 里面包含着第一个要执行的指令,所以一定要链接到最开始的地方。第 6 行是 main.o这个文件,其实可以不用写出来,因为 main.o 的位置就无所谓了,可以由编译器自行决定链接位置。
第10行定义了一个名为“.data”的段,然后所有文件的“.data”段都放到这里面。ALIGN(4)是用来对“.data”这个段的起
始地址做字节对齐的,ALIGN(4)表示 4 字节对齐。也就是说段“.data”的起始地址要能被 4 整除,一般常见的都是 ALIGN(4)或者 ALIGN(8),也就是 4 字节或者 8 字节对齐。
在第 11、13 行的__bss_start和__bss_end是符号,第 11、13 这两行其实就是对这两个符号进行赋值,其值为定位符“.”,这两个符号用来保存.bss 段的起始地址和结束地址。.bss 段是定义了但是没有被初始化的变量,我们需要手动对.bss 段的变量清零的,因此我们需要知道.bss 段的起始和结束地址,这样我们直接对这段内存赋 0 即可完成清零。通过第 11、13 行代码,.bss 段的起始地址和结束地址就保存在了__bss_start和__bss_end中,我们就可以直接在汇编或者C 文件里面使用这两个符号。
执行后LED闪动



