裸机编程的思路
一、通过寄存器实现LED灯工作
如果控制D7亮,GPIOE13输出低电平;如果控制D7灭,GPIOE13要输出高电平。
二、寄存器的配置
找到对应的寄存器必须阅读芯片手册,本篇用到的是S5P6818芯片的数据手册
1. 找到GPIO输出模式的配置方法
1)配置某个端口某个引脚为输出模式
GPIOE多功能寄存器,设置对应的bits为b“00”
2)使能当前引脚的输出
GPIOE输出使能寄存器对应的位设置为1
3)输出高低电平
GPIOE输出寄存器的对应的位设置为1或0.
2. 对应的寄存器
1)GPIOxOUT寄存器
2)GPIOxOUTENB寄存器
3)GPIOxALTFN0寄存器,有些引脚需要阅读GPIOxALTFN1寄存器,这点要注意。
//0~15Pin(要配置的是GPIOxALTFN0寄存器)
//16~31Pin(要配置的是GPIOxALTFN1寄存器)
//由于官方文档这里没有详细描述ALT Function0 ~ ATLFunction3,因此需要各位还得阅读第二章节。
最后知道GPIOE13要配置为ALT Function0.
三、编写代码
1. 源码如下
/***定义寄存器***/
/*定义GPIOE的输出寄存器地址*/
#define GPIOEOUT (*(volatile unsigned int *)0xC001E000)
/*定义GPIOE输出使能寄存器地址*/
#define GPIOEOUTENB (*(volatile unsigned int *)0xC001E004)
/*定义GPIOE功能配置寄存器地址*/
#define GPIOEALTFN0 (*(volatile unsigned int *)0xC001E020)
static void delay(void); //声明一个延时函数
/**
* C程序的入口,同时不需要使用标准C的库,因此入口函数为_start.
**/
void _start(void)
{
/*GPIOE13配置为输出模式*/
GPIOEALTFN0&=~(3<<26); //将GPIOEALTFN0寄存器[27:26]清零
/*GPIOE13输出使能*/
GPIOEOUTENB|=1<<13;
while(1)
{
//点亮D7
GPIOEOUT&=~(1<<13); //将GPIOEOUT寄存器中[13]清零
//延时一会
delay();
//熄灭D7
GPIOEOUT|= (1<<13); //将GPIOEOUT寄存器中[13]置1
//延时一会
delay();
}
}
/**
* 延时函数
**/
void delay(void)
{
//思考为什么要加volatile,如果不加,会出现什么结果?
volatile unsigned int i=0x20000000;
while(i--);
}
四、使用交叉编译器来进行编译
1. 检查Ubuntu是否存在交叉编译器
root@ubuntu:/home/gec# which arm-linux-gcc
/usr/local/arm/5.4.0/usr/bin/arm-linux-gcc
2. [可选操作]进入共享目录去编译
1)将demo.c编译为目标文件led.o,且不使用标准C的库(因为使用标准C的库需要使用汇编配置堆和栈)
arm-linux-gcc -o led.o -c demo.c -nostdlib
2)将led.o文件链接到0x40000000这个地址,输出新的执行程序为led.elf
arm-linux-ld -Ttext 0x40000000 -o led.elf led.o
3)由于uboot不是linux操作系统,它不具有运行应用程序的能力,需要转换为bin文件。
arm-linux-objcopy -O binary led.elf led.bin
五、下载led.bin文件到开发板执行
1. 使用uboot的tftp进行下载
tftp 0x40000000 led.bin
2. 执行led.bin
go 0x40000000
3. 演示效果
1)点亮
2)熄灭
注:
1、delay函数的源码为什么要加volatile关键字,如果不加,会出现什么结果?
1) 加上volatile关键字,可以防止被编译器优化删除掉,这个时候,led灯的闪烁是正常的。
40000088
: //函数或标签
运行地址 机器码 指令
40000088: e52db004 push {fp} ; (str fp, [sp, #-4]!)
4000008c: e28db000 add fp, sp, #0
40000090: e24dd00c sub sp, sp, #12
40000094: e3a03202 mov r3, #536870912 ; 0x20000000
40000098: e50b3008 str r3, [fp, #-8]
4000009c: e1a00000 nop ; (mov r0, r0)
400000a0: e51b3008 ldr r3, [fp, #-8]
400000a4: e2432001 sub r2, r3, #1
400000a8: e50b2008 str r2, [fp, #-8]
400000ac: e3530000 cmp r3, #0
400000b0: 1afffffa bne 400000a0
400000b4: e1a00000 nop ; (mov r0, r0)
400000b8: e24bd000 sub sp, fp, #0
400000bc: e49db004 pop {fp} ; (ldr fp, [sp], #4)
400000c0: e12fff1e bx lr //函数返回
以上就是delay函数的反汇编代码,起到延时的作用。
对led.elf输出反汇编代码
arm-linux-objdump -D led.elf > led.dis1.
2) 不加上volatile关键字,这个时候,led灯的闪烁是有的,但是人眼是观察不到,因为delay函数被编译器优化删除掉,导致闪烁速度太快!