Fork me on GitHub

嵌入式底层驱动笔记2-ARM编程

一、裸机编程的思路

思路:
(1)分析原理图
(2)理解硬件的控制原理
(3)找到对应的寄存器
(4)理解寄存器的控制流程
(5)根据地址访问寄存器
(6)实现硬件的控制

二、分析原理图

1.实物图

实物图

实物图

2.通过丝印层D7~D10,在原理图gec6818_base_V1.1-底板.pdf找到如下。

原理图

原理图
如果控制D7亮,GPIOE13输出低电平;控制D7 灭,GPIOE13 输出高电平。

三、寄存器配置

找对应的寄存器必须要找到S5P6818芯片数据手册SEC_S5P6818X_Users_Manual_preliminary_Ver_0.00.pdf。

数据手册

1、找到GPIO输出模式的配置方法

数据手册

1)配置某个端口某个引脚为输出模式
GPIOE多功能寄存器,设置对应的为b’00(这里是二进制格式)

2)使能当前的引脚输出使能
GPIOE 输出使能寄存器对应的位设置为1

3)输出高低电平
GPIOE输出寄存器对应的位设置为1/0.

2、对应的寄存器

数据手册

3、多功能选择寄存器

1)多功能寄存器0

数据手册
这里是GPIO多功能选择寄存器0,主要配置端口的0~15引脚。

2)多功能寄存器1
数据手册
这里是GPIO多功能选择寄存器1,主要配置端口的16~31引脚。

3)多功能寄存器的ALT Function0~ALT Funciton2功能描述

ALT Function0~ALT Funciton2代表功能要查阅前面的章节2.3.1。

数据手册

4、输出使能寄存器

数据手册

5、输出电平寄存器

数据手册

四、 源码

该源码用于控制D7灯的亮与灭。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//1.定义寄存器

#define GPIOEOUT (*(volatile unsigned int *)0xC001E000)
#define GPIOEOUTENB (*(volatile unsigned int *)0xC001E004)
#define GPIOEOUTALTFN0 (*(volatile unsigned int *)0xC001E020)
#define GPIOEOUTALTFN1 (*(volatile unsigned int *)0xC001E024)

static void delay(void);

//2.c程序的入口,同时不使用标准的c库,因此入口函数名字为_start

void _start(void)
{

//配置GPIOE13为输出模式
GPIOEOUTALTFN0&=~(3<<26); //GPIOE13的多功能配置[27:26]清零


//允许GPIOE13输出电平
GPIOEOUTENB|=1<<13;

while(1)
{

//点亮
GPIOEOUT&=~(1<<13);

//延时一会
delay();

//熄灭
GPIOEOUT|=1<<13;

//延时一会
delay();
}
}

void delay(void)
{
//思考为什么要加volatile
volatile unsigned int i=0x2000000;

while(i--);
}

五、使用交叉编译器进行编译

1.检查ubuntu是否有交叉编译器。

which arm-linux-gcc```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
`/usr/local/arm/5.4.0/usr/bin/arm-linux-gcc`

### 2.进入共享目录去编译led_demo1裸机程序。

1)将led.c编译为目标文件led.o,且不使用标准c的库
`arm-linux-gcc -o led.o -c led.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`


**思考1**
思考源码的delay函数为什么加上volatile关键字?
答案:如果使用更加高等级的编译优化,不使用volatile关键字进行修饰,这个时候delay函数会直接返回。
1.编译代码步骤:

`arm-linux-gcc -o led.o -c led.c -nostdlib -O2` //添加了二级优化
`arm-linux-ld -Ttext 0x40000000 -o led.elf led.o`
`arm-linux-objcopy -O binary led.elf led.bin`
`arm-linux-objdump -D led.elf > led.dis` //生成反汇编代码文件

2.delay函数添加了volatile关键字
```c
void delay(void)
{
//思考为什么要加volatile
volatile unsigned int i=0x2000000;
while(i--);
}

使用了volatile关键字之后,告诉编译器谨慎去对待i变量,里面有对i变量操作的代码,都帮我去生成对应的机器代码。
以下反汇编可以看到delay函数有对i变量进行减法运算操作:

1
2
3
4
5
6
7
8
9
10
11
40000000 <delay>:
40000000: e24dd008 sub sp, sp, #8
40000004: e3a03402 mov r3, #33554432 ; 0x2000000
40000008: e58d3004 str r3, [sp, #4]
4000000c: e59d3004 ldr r3, [sp, #4]
40000010: e3530000 cmp r3, #0
40000014: e2433001 sub r3, r3, #1
40000018: e58d3004 str r3, [sp, #4]
4000001c: 1afffffa bne 4000000c <delay+0xc>
40000020: e28dd008 add sp, sp, #8
40000024: e12fff1e bx lr //函数返回

3.delay函数无添加volatile关键字

void delay(void)
{

unsigned int i=0x2000000;
while(i--);

}

以下反汇编代码可以看到delay函数变为空函数,优化掉对i变量的操作,直接函数返回,原因在于编译器觉得i变量没有起到作用(编译器是这么想的,只是i变量自己做减法操作,但是没有给其他变量与其他函数使用,无用功般的占用CPU资源),对代码影响不大,直接对它进行删除。

40000000 :
40000024: e12fff1e bx lr //函数返回

思考2
思考对寄存器某些位进行清零,为什么使用位与、取反的操作。

//对GPIOEOUT13寄存器的第13位进行清零
//假如GPIOEOUT寄存器的初值为0xFFFFFFFF。
GPIOEOUT&=~(1<<13);

1.1<<13的值为如下:
示意图

2.~(1<<13)的值为如下:
示意图

通过上图可以知道,只要GPIOEOUT寄存器跟~(1<<13)进行相与(跟1与,结果不变;跟0与,结果为0),从而知道只有第13bit位变为0,其他bit位保持不变。

总结:
1)将变量a的第30位置1,其他位保持不变。
a|=1<<30;

2)将变量a的第24、30位置1,其他位保持不变。
a|=(1<<30)|(1<<24);

3)将变量a的第30位取反,其他位保持不变。
a^=(1<<30);

4)将变量a的第30位清零,其他位保持不变。
a&=~(1<<30);

点击查看
0%