C语言驱动实现

描述

ARM Coretex-A的不同模式

为了尽可能的保护计算机资源(CPU、内存、IO等),不同的CPU架构实现了不同的权限等级,等级越高可访问的系统资源越多(包括执行一些特权指令),这是因为普通用户级别的程序代码质量不能得到完全的保障,为了更好的保护计算机资源会让用户级别的代码工作在尽可能少的访问系统资源的模式下,而像操作系统这样经过千锤百炼的程序质量有保障,CPU可以放心的把计算机资源交出来让操作系统可以随意访问。

在当前的实验中,由于我们是工作在祼机状态下的程序,可以简单的认为我们的代码与操作系统代码是一个级别的,就算代码有问题那从CPU的角度来讲我们的祼机代码也是高质量的,也就是说可以工作在高等级权限的模式下。

ARM Coretex-A系列处理器有以下几种工作模式:cpu其中,SVC是超级管理员模式,可以访问所有的计算机资源。

设置CPU工作模式为SVC

Coretex-A提供了一个专门的寄存器CPSR程序状态寄存器来控制当前CPU的工作模式, CPSR的每一位的逻辑如下:cpu

其中的低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寄存的操作指令与读取通用寄存器使用的ldrstr不要样,需要使用mrs读取CPSR的数据,msrCPSR写入数据。

/// 将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语言运行环境

C语言要工作,需要指定一个栈空间,通过指定sp指针就可以给C语言开辟一块栈空间。

Coretex-A的栈是向下生长的,也就是从高地址向低地址生成(即栈顶在高地址)。I.MX6ULLDDR的起始地址是0x80000000,这里设置一个2MB的空间大小,实际上也可以设置成1MB或者512KB也是完全够用的。所以需要设置sp指针的初始位置是0x802000000.

/// 设置sp指针地址
ldr sp, =0x80200000
/// 跳转到c语言的main函数开始执行
b main

编写C语言LED驱动程序

  • 1.使能CCM_CCGR1
#define CCM_CCGR1 *((volatile unsigned int *)0x020C406C)
CCM_CCGR1 = 0xFFFFFFFF;
  • 2.复用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
  • 3.配置SW_PAD_CTL_PAD_GPIO1_IO03电气属性
#define SW_PAD_CTL_PAD_GPIO1_IO03 *((volatile unsigned int *)0x020E02F4)
SW_PAD_CTL_PAD_GPIO1_IO03 = 0x10B0
  • 4.设置GPIO1_GDIR的信号输出方向
#define GPIO01_GDIR *((volatile unsigned int *)0x0209C004)
GPIO01_GDIR = 0x08;
  • 5.LED闪烁
#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;
}

编译代码

  • 编写Makefile
/// 定义目标文件变量
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灯闪烁

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分