目录
设置处理器模式
设置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指针由高地址向低地址方向增长)。所以我们将SP指针设置到0X80000000+0X200000→0X80200000
跳转C语言
使用b指令直接跳转到main函数所在位置。
利用汇编代码完成上述配置
.global _start
_start:
/*设置处理器进入SVC模式----修改0-4位但是不能改变其他位 */
MRS R0, CPSR /*将CPSR中的数据先进行读取 */
BIC R0, R0, #0x1f /*清除CPSR的bit4-0 (本质为与运算)*/
ORR R0, R0, #0x13 /*设置为SVC模式(利用或运算) */
MSR CPSR, R0 /*将R0写入CPSR中 */
/*设置sp指针 */
LDR sp, =0x80200000 /*恩智浦的处理器在bin文件的头部已经初始化DDR了,所以只需要设置sp指针 */
b main /*跳转到C语言main函数 */
软件编写
首先编写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)
/*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_H
然后进行main函数的写入,本次实现的效果为led灯闪烁
#include "main.h"
/*使能外设时钟*/
void clk_enable(void)
{
//将时钟全部使能
CCM_CCGR0 = 0xFFFFFFFF;
CCM_CCGR1 = 0xFFFFFFFF;
CCM_CCGR2 = 0xFFFFFFFF;
CCM_CCGR3 = 0xFFFFFFFF;
CCM_CCGR4 = 0xFFFFFFFF;
CCM_CCGR5 = 0xFFFFFFFF;
CCM_CCGR6 = 0xFFFFFFFF;
}
/*初始化led*/
void led_init(void)
{
//复用为GPIO1---IO03
SW_MUX_GPIO1_IO03 = 0x5;
/*设置GPIO1_IO03的电气属性
* bit0: 0 低速率(压摆率)
* bit5~3: 110 R0/6的驱动能力
* bit7~6: 10 100MHz的速度
* bit11: 0 关闭开路输出
* bit12: 1 使能pull/keeper
* bit13: 0 keeper
* bit14~15: 00 100k下拉
* bit16: 0 关闭hys
*/
SW_PAD_GPIO1_IO03 = 0x10B0;
//GPIO初始化
//设置为输出模式
GPIO1_GDIR = 0x8;
//led默认打开
GPIO1_DR = 0x0;
}
/*短延时*/
void delay_short(volatile unsigned int n)
{
while(n--){}
}
/*延时,一次循环大概是一毫秒 在主频396MHz
* n:延时ms数
*/
void delay(volatile unsigned int n)
{
while(n--)
{
delay_short(0x7ff);
}
}
/*打开led灯*/
void led_on(void)
{
GPIO1_DR &= ~(1<<3); //bit3清零
}
/*关闭led灯*/
void led_off(void)
{
GPIO1_DR |= (1<<3); //bit3设置为1
}
int main(void)
{
//初始化led
clk_enable();
led_init();
//设置led闪烁
while(1)
{
led_on();
delay(500);
led_off();
delay(500);
}
return 0;
}
链接脚本
为了解决在编译过程中,文件链接顺序出现问题我们需要写一个链接脚本,如果不写这个脚本,就要注意连接时文件的先后顺序。
链接脚本描述了要链接的文件,以及顺序,和链接首地址(文件后缀为.lds)
SECTIONS
{
. = 0x87800000;
.text :
{
start.o
*(.text)
}
.rodata ALIGN(4) : {*(.rodata)}
.data ALIGN(4) : {*(.data)}
__bss_start = .;
.bss ALIGN(4) : {*(.bss) *(COMMON)}
__bss_end = .;
}
利用makefile进行编译
编译和烧录过程可见https://blog.csdn.net/wusuowei12138/article/details/132059178
makefile如下:
objs = start.o main.o
ledc.bin:$(objs)
arm-linux-gnueabihf-ld -Timx6ull.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 -c -O2 -o $@ $<
%.o : %.s
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<
clearn:
rm -rf *.o ledc.elf ledc.dis ledc.bin