一、处理器模式的设置
Cortex-A 处理器有6种运行模式,当前实验需要将模式设置为SVC超级管理员模式,从寄存器设置中可以看到CPSR的M【4:0】控制着处理器的运行模式。因此可以将CPSR【4:0】设置为10011即为SVC模式。
二、SP指针的设置
Sp可以指向内部RAM,也可以指向DDR,在此实验中我们将指向DDR。I.MX6U-ALPHA 开发板上的 DDR3 地 址 范 围 是0X80000000~0XA0000000(512MB),也有可能是256MB的,但是其 DDR3 起始地址是 0X80000000。由于 Cortex-A7 的堆栈是向下增长的,所以将 SP 指针设置为 0X80200000。因此 SVC 模式的栈大小 0X80200000-0X80000000=0X200000=2MB,2MB 的栈空间已经很大了。最后就是跳转到main函数。
三、start.S文件代码如下
.global _start
_start:
/*设置处理器进入SVC模式 */
mrs r0, cpsr /*读取cpsr到r0 */
bic r0, r0, #0x1f /*清除cpsr的bit4:0*/
orr r0, r0, #0x13 /*使用SVC模式 */
msr cpsr, r0 /*将r0写入到cpsr */
/*设置SP指针 */
ldr sp, =0x80200000
b main /*跳转到C语言main函数 */
四、C语言代码的编写
C代码主要由main.c和main.h两部分组成,main.h主要定义寄存器地址,代码如下:
#ifndef __MAIN_H
#define __MAIN_H
/*定义要使用的寄存器*/
#define CCCM_CCGR0 *((volatile unsigned int*)0x020c4068)
#define CCCM_CCGR1 *((volatile unsigned int*)0x020c406c)
#define CCCM_CCGR2 *((volatile unsigned int*)0x020c4070)
#define CCCM_CCGR3 *((volatile unsigned int*)0x020c4074)
#define CCCM_CCGR4 *((volatile unsigned int*)0x020c4078)
#define CCCM_CCGR5 *((volatile unsigned int*)0x020c407c)
#define CCCM_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_GDIR *((volatile unsigned int*)0x0209c004)
#define GPIO1_DR *((volatile unsigned int*)0x0209c000)
#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_H
接下来就是main.c文件的编写,该文件是正式实现LED灯的闪烁,其代码如下:
#include "main.h"
/*使能外设时钟*/
void clk_enable(void)
{
CCCM_CCGR0 = 0xffffffff;
CCCM_CCGR1 = 0xffffffff;
CCCM_CCGR2 = 0xffffffff;
CCCM_CCGR3 = 0xffffffff;
CCCM_CCGR4 = 0xffffffff;
CCCM_CCGR5 = 0xffffffff;
CCCM_CCGR6 = 0xffffffff;
}
/*初始化LED*/
void led_init(void)
{
SW_MUX_GPIO1_IO03 = 0x5;
SW_PAD_GPIO1_IO03 = 0x10b0;
/*GPIO初始化*/
GPIO1_GDIR = 0x8; /*设置为输出*/
GPIO1_DR = 0x0; /*打开led灯*/
}
void delayshort(volatile unsigned int n)
{
while(n--){}
}
/*延时,一次循环大概是1ms 在主频396Mhz
*n:延时ms数
*/
void delay(volatile unsigned int n)
{
while(n--){
delayshort(0x7ff);
}
}
/*打开led灯*/
void led_on(void)
{
GPIO1_DR &= ~(1<<3); /*bit3清0*/
}
/*关闭led灯*/
void led_off(void)
{
GPIO1_DR |= (1<<3); /*bit3置1*/
}
int main(void)
{
clk_enable();
led_init();
/*初始化LED*/
/*设置LED闪烁*/
while(1){
led_on();
delay(1000);
led_off();
delay(1000);
}
return 0;
}
五、编写Makefile
objs = main.o start.o
ledc.bin : $(objs)
arm-linux-gnueabihf-ld -Timx6u.lds $^ -o ledc.elf
arm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@
arm-linux-gnueabihf-objdump -D -m arm ledc.elf > ledc.dis
%.o : %.c
arm-linux-gnueabihf-gcc -Wall -nostdlib -O2 -c -o $@ $<
%.o : %.S
arm-linux-gnueabihf-gcc -Wall -nostdlib -O2 -c -o $@ $<
clean:
rm -rf *.o ledc.bin ledc,elf ledc.dis
链接脚本的编写,主要是将我们编译出来的代码块链接到指定的地址,一般编译的代码块包含 text、data、bss 和 rodata 这四个段。其代码如下:
SECTIONS{
. = 0x87800000;
.text :
{
start.o
*(.text)
}
.rodate ALIGN(4) : {*(.rodate*)}
.date ALIGN(4) : {*(.date)}
__bss_start=.;
.bss ALIGN(4) : {*(.bss) *(COMMON)}
__bss_end=.;
}
完成以上工作后即可按照之前的步骤进行烧写验证。