为了尽可能的保护计算机资源(CPU、内存、IO等),不同的CPU架构实现了不同的权限等级,等级越高可访问的系统资源越多(包括执行一些特权指令),这是因为普通用户级别的程序代码质量不能得到完全的保障,为了更好的保护计算机资源会让用户级别的代码工作在尽可能少的访问系统资源的模式下,而像操作系统这样经过千锤百炼的程序质量有保障,CPU可以放心的把计算机资源交出来让操作系统可以随意访问。
在当前的实验中,由于我们是工作在祼机状态下的程序,可以简单的认为我们的代码与操作系统代码是一个级别的,就算代码有问题那从CPU的角度来讲我们的祼机代码也是高质量的,也就是说可以工作在高等级权限的模式下。
ARM Coretex-A
系列处理器有以下几种工作模式:其中,SVC
是超级管理员模式,可以访问所有的计算机资源。
Coretex-A
提供了一个专门的寄存器CPSR
程序状态寄存器来控制当前CPU的工作模式, CPSR
的每一位的逻辑如下:
其中的低5位M[4:0]用于指定当前CPU的工作模式,具体表格如下:
M[4:0] | CPU工作模式 |
---|---|
10000 | User模式 |
10001 | FIQ模式 |
10010 | IRQ模式 |
10011 | Supervisor(SVC)模式 |
10110 | Monitor(MON)模式 |
10111 | Abort(ABT)模式 |
11010 | Hyp(HYP)模式 |
11011 | Undef(UND)模式 |
11111 | System(SYS)模式 |
通过表格可以看出,如果要设置成SVC
模式,则需要将CPSR
寄存器的低5位M[4:0]设置成10011
读写CRSR
寄存的操作指令与读取通用寄存器使用的ldr
与str
不要样,需要使用mrs
读取CPSR
的数据,msr
向CPSR
写入数据。
/// 将cpsr寄存器中的数据读取到r0
/// 这是因为cpsr是不能使用运算符指令对其进行直接的操作的,需要使用通用寄存器
mrs r0, cpsr
/// 通过使用bic指令将,r0的低5位都清空,这里使用了0x1f=11111. bic指令后面的操作数掩码位的值是1表示将此位清空,否则保持不变
bic r0, r0, #0x1f
/// 通过orr(或)指令将r0寄存器中的低5位设置成10011
orr r0, r0, #0x13
/// 将r0的数据写入cprs寄存器,完成svc模式切换
msr cprs, r0
C语言要工作,需要指定一个栈空间,通过指定sp
指针就可以给C语言开辟一块栈空间。
Coretex-A
的栈是向下生长的,也就是从高地址向低地址生成(即栈顶在高地址)。I.MX6ULL
的DDR
的起始地址是0x80000000,这里设置一个2MB的空间大小,实际上也可以设置成1MB或者512KB也是完全够用的。所以需要设置sp
指针的初始位置是0x802000000.
/// 设置sp指针地址
ldr sp, =0x80200000
/// 跳转到c语言的main函数开始执行
b main
#define CCM_CCGR1 *((volatile unsigned int *)0x020C406C)
CCM_CCGR1 = 0xFFFFFFFF;
SW_MUX_CTL_PAD_GPIO1_IO03
为GPIO1_IO03#define MUX_CTL_PAD_GPIO1_IO03 *((volatile unsigned int *)0x020E0068)
MUX_CTL_PAD_GPIO1_IO03 = 0x5; /// 10011
SW_PAD_CTL_PAD_GPIO1_IO03
电气属性#define SW_PAD_CTL_PAD_GPIO1_IO03 *((volatile unsigned int *)0x020E02F4)
SW_PAD_CTL_PAD_GPIO1_IO03 = 0x10B0
#define GPIO01_GDIR *((volatile unsigned int *)0x0209C004)
GPIO01_GDIR = 0x08;
#define GPIO01_DR *((volatile unsigned int *)0x0209C000)
void delay_ms(volatile unsigned int n) {
while (n--) { }
}
void delay(volatile unsigned int n) {
while (n--) {
delay_ms(0x7FF);
}
}
whilte(1) {
delay(500);
GPIO01_DR = 0x0;
delay(500);
GPIO01_DR = 0x8;
}
/// 定义目标文件变量
objs := start.o main.o
/// 生成ledc.bin,依赖objs
ledc.bin:${objs}
arm-linux-gnueabihf-ld -Ttext 0x87800000 -o ledc.elf $^ /// 将目标文件链接生成ledc.elf $^表示所有依赖文件的集合,使用空格分开,去重
arm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@ /// 将ledc.elf生成ledc.bin, $@指目标集合,这里的目标就是上面定义的ledc.bin
arm-linux-gnueabihf-objdump -D -m arm ledc.elf > ledc.dis /// 将ledc.elf反汇编成ledc.dis,主是要方便查看连接后的代码段指令是否符合预期
/// 当目标文件没有时会使用如下的模式规则去生成对应的目标文件
/// $@指要生成的.o文件, $<指生成.o的源文件,比如start.o:start.s, $@(start.o) $<(start.s)
%.o:%.s
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
%.o:%.S
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
%.o:%.c
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
/// 定义清楚方法
clean:
rm -rf *.o ledc.bin ledc.elf ledc.dis
make clean && make
编译完成之后烧写到SD卡,选择SD卡启动观察LED灯闪烁
全部0条评论
快来发表一下你的评论吧 !