上一节,我们提到了为什么使用汇编来写点亮led的驱动程序。因为没有c语言的运行环境,本节我们就来配置c语言的运行环境,毕竟c语言看起来还是舒服的。具体的寄存器地址和值请见上一节。
一.配置C语言的运行环境
C语言的运行环境包括:
1.处理器模式的选择
2.堆栈指针(sp)的设置
3.转到C语言的入口程序main
1.处理器模式的选择
我们用的是arm-cortexA7的芯片有9种处理器模式,简单起见,我们选择超级管理员模式。接下查看reference sheet。处理器模式在寄存器CPSR的M[4:0]设置。可以看到我们使用超级管理员权限,即SVC模式。所以设置为10011。即0x13。
2.堆栈指针(sp)的设置
DDR开始的地址是0x80000000,因为堆栈是从大到小分配内存的,所以SP指针设置为0x80200000,大小为2M。
3.编写汇编程序
start.s
.global _start
_start :
@设置cpu为超级管理员模式
/*读取cspr的内容,用到的函数是MRS*/
MRS r0,cpsr
/*将r0中的M[4:0]清空*/
BIC r0,r0,#0x1f
/*将r0与0x13按位或*/
ORR r0,r0,#0x13
/*将r0中的数据写道cspr中 */
MSR cpsr,r0
@设置sp指针
LDR sp ,=0x80200000
@跳转到main函数
B main
二.编写c语言led驱动程序
我们将寄存器地址写在main.h文件中。
1.main.h
#ifndef __MIAN_H
#define __MIAN_H
/*CCM时钟寄存器的地址*/
#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)
/*复用功能和电器属性*/
#define MUX_GPIO1_IO03 *((volatile unsigned int*)0x020E0068)
#define PAD_GPIO1_IO03 *((volatile unsigned int*)0x020E02F4)
/*设置GPIO的输入输出*/
#define GPIO1_DR *((volatile unsigned int*)0x0209C000)
#define GPIO1_GDIR *((volatile unsigned int*)0x0209C004)
#endif
2.main.c
#include "main.h"
//使能时钟
int enable_clock(){
CCM_CCGR0 = 0xffffffff;
CCM_CCGR1 = 0xffffffff;
CCM_CCGR2 = 0xffffffff;
CCM_CCGR3 = 0xffffffff;
CCM_CCGR4 = 0xffffffff;
CCM_CCGR5 = 0xffffffff;
CCM_CCGR6 = 0xffffffff;
return 0;
}
//设置复用功能和电器属性和输入输出
int led_init(){
MUX_GPIO1_IO03 = 0x5;
PAD_GPIO1_IO03 = 0x000010B0;
GPIO1_GDIR = 0x8;
GPIO1_DR = 0x0;
return 0;
}
void led_on(){
GPIO1_DR &= ~(1<<3);
}
void led_off(){
GPIO1_DR |= 1<<3;
}
//一个短的延时
void delay_short(volatile unsigned int n){
while (n--) {}
}
//大约n ms的延时
void delay(int n){
while (n--){
delay_short(0x7ff);
}
}
int main(void){
//使能时钟
enable_clock();
//设置复用功能和电器属性和输入输出
led_init();
while (1)
{
led_on();
delay(500);
led_off();
delay(500);
}
return 0;
}
3.Makefile
obj = start.o main.o
led.bin : $(obj)
arm-linux-gnueabihf-ld -Ttext 0x87800000 $^ -o led.elf
arm-linux-gnueabihf-objcopy -O binary -S led.elf $@
arm-linux-gnueabihf-objdump -D -m arm led.elf > led.dis
%.o : %.c
arm-linux-gnueabihf-gcc -Wall -nostdlib -c $< -o $@
%.o : %.s
arm-linux-gnueabihf-gcc -Wall -nostdlib -c $< -o $@
.PHONY:clean
clean:
-rm -r main.o start.o led.bin led.dis led.elf load.imx
.PHONY:load
load:
./imxdownload led.bin /dev/sdb
4.链接文件的编写
因为我们指定start.o文件(汇编配置c语言环境的文件)必须放在0x87800000地址上,即必须放在开头的地方。而在上面的链接方式中,是不固定那个文件在前面的,所以我们需要自己写链接文件,将指定的文件放在指定的段里面。
imx6ull.lds文件
SECTIONS{
. = 0x87800000;
.text :
{
start.o
main.o
*(.text)
}
.rodata ALIGN(4) : {*(.rodata)}
.data ALIGN(4) : {*(.data)}
_bss_start = .;
.bss ALIGN(4) : {*(.bss) *(.COMMON)}
_bss_end = .;
}
#修改Makefile
arm-linux-gnueabihf-ld -Timx6ull.lds $^ -o led.elf
总结
本节内容只是将上一节的汇编语言实现变成了c语言实现,还是往相同的寄存器里写值。汇编语言只是为c语言配置好环境,跳转到c语言的main函数。至此,敬礼。