C语言环境搭建
1.设置处理器模式
设置6ULL处于SVC模式 下。设置CPSR寄存器的bit4-0,也就是M[4:0]为10011=0X13。读写状态寄存器需要用到MRS和MSR指令。MRS将CPSR寄存器数据读出到通用寄存器里面,MSR指令将通用寄存器的值写入到CPSR寄存器里面去。
读写指令为什么不用LDR和STR是因为LDR和STR用在通用寄存器,而MRS和MSR指令用在特殊功能寄存器。
设置成SVC模式是因为我们可以完全操作,如果不在这个模式下,可能有一些操作会受到限制。
2.设置SP指针
SP可以指向内部RAM,也可以指向DDR,我们将其指向DDR。SP设置到哪里?512MB的范围0x80000000~0x9FFFFFFF。栈大小,0x200000=2MB。处理器栈增长方式,对于A7而言是向下增长的。设置SP指向0x80200000。
3.跳转到C语言函数
使用b指令,跳转到C语言函数,比如main函数。
代码解析
start.s
这个文件是设置处理器模式和指针地址
.global _start
_start:
//进入SVC模式
mrs r0,cpsr
bic r0,r0,#0x1F //将r0的低5位清零,也就是cpsr的M0~M4
orr r0,r0,#0x13 //将r0或上0x13,表示使用SVC模式
msr cpsr,r0 //将r0的数据写入到cpsr_c中
ldr sp,=0x80200000 //设置栈指针
b main //跳转到main函数
指令意思如下图:
设置SVC模式配置如下:
main.h
#ifndef __MAIN_H
#define __MAIN_H
/*
2 * CCM 相关寄存器地址
3 */
#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.c
#include "main.h"
void clk_enable(){ //使能I.MX6U所有外设时钟
CCM_CCGR0=0xFFFFFFFF;
CCM_CCGR1=0xFFFFFFFF;
CCM_CCGR2=0xFFFFFFFF;
CCM_CCGR3=0xFFFFFFFF;
CCM_CCGR4=0xFFFFFFFF;
CCM_CCGR5=0xFFFFFFFF;
CCM_CCGR6=0xFFFFFFFF;
}
void led_init(){ //初始化LED对应的GPIO
SW_MUX_GPIO1_IO03=0x5; //初始化IO复用,复用为GPIO1_IO03
SW_PAD_GPIO1_IO03=0x10B0; //配置GPIO1_IO03的IO属性,这里的配置上一篇文章也有详解
GPIO1_GDIR=0x8; //初始化GPIO,GPIO1_IO03设置为输出
GPIO1_DR=0x0; //设置GPIO1_IO03输出低电平,打开LED0
}
void led_on(){ //打开LED灯
GPIO1_DR &= ~(1<<3); //将GPIO1_DR的bit3清零
}
void led_off(){ //关闭LED灯
GPIO1_DR |= (1<<3); //将GPIO1_DR的bit3置1
}
//短时间延时函数,要延时循环次数(空操作循环次数,模式延时)
void delay_short(volatile unsigned int n){
while (n--){}
}
void delay(volatile unsigned int n){ //延时函数,在396Mhz的主频下延时时间大约为1ms
while (n--)
{
delay_short(0x7FF);
}
}
int main(){
clk_enable(); //使能所有的时钟
led_init(); //初始化 led
while(1){ //死循环
led_off(); //关闭LED
delay(500); //延时500ms
led_on(); //打开LED
delay(500); //延时500ms
}
return 0;
}
Makefile
objs=start.o main.o
ledc.bin:$(objs)
arm-linux-gnueabihf-ld -Ttext 87800000 $^ -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 -o $@ $<
%.o:%.s
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
clean:
rm -rf *.o ledc.bin ledc.elf ledc.dis
我们现在来详细解释一下Makefile
objs=start.o main.o
这一行定义了一个名为 objs
的变量,包含了 start.o
和 main.o
这两个目标文件。这些目标文件是从源代码文件编译而来的。
ledc.bin:$(objs)
这一行定义了一个目标 ledc.bin
,它依赖于 objs
变量中列出的所有目标文件(即 start.o
和 main.o
)。
arm-linux-gnueabihf-ld -Ttext 87800000 $^ -o ledc.elf
这一行使用 arm-linux-gnueabihf-ld
链接器将 objs
中的目标文件链接成一个 ELF 可执行文件 ledc.elf
。-Ttext 87800000
指定了代码段的加载地址。$^
是一个自动变量,代表所有的依赖文件(即 start.o
和 main.o
)。
arm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@
这一行使用 arm-linux-gnueabihf-objcopy
工具将 ledc.elf
文件转换成一个二进制文件 ledc.bin
。-O binary
指定输出格式为二进制文件,-S
表示去掉所有符号表和重定位信息。$@
是一个自动变量,代表当前目标(即 ledc.bin
)。
arm-linux-gnueabihf-objdump -D -m arm ledc.elf > ledc.dis
这一行使用 arm-linux-gnueabihf-objdump
工具将 ledc.elf
文件反汇编并输出到 ledc.dis
文件中。-D
表示反汇编所有部分,-m arm
指定目标架构为 ARM。
%.o:%.c
这一行定义了一个模式规则,指定如何将 .c
源文件编译成 .o
目标文件。
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
这一行定义了具体的编译命令。使用 arm-linux-gnueabihf-gcc
编译器编译 .c
文件。-Wall
打开所有警告,-nostdlib
表示不链接标准库,-c
表示只编译不链接,-o $@
指定输出文件名,$<
是一个自动变量,代表当前的依赖文件(即 .c
文件)。
%.o:%.s
这一行定义了一个模式规则,指定如何将 .s
汇编文件编译成 .o
目标文件。
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
这一行定义了具体的编译命令。使用 arm-linux-gnueabihf-gcc
编译器编译 .s
文件。-Wall
打开所有警告,-nostdlib
表示不链接标准库,-c
表示只编译不链接,-o $@
指定输出文件名,$<
是一个自动变量,代表当前的依赖文件(即 .s
文件)。
clean: rm -rf *.o ledc.bin ledc.elf ledc.dis
这一行定义了一个名为 clean
的目标,用于清理生成的文件。rm -rf
命令删除所有 .o
文件以及 ledc.bin
、ledc.elf
和 ledc.dis
文件。