第1个ARM裸板程序及引申
硬件知识_LED原理图(辅线)
怎样点亮一个LED?
- 看原理图,确定控制LED的引脚,
- 看主芯片手册,确定如何设置/控制引脚
- 写程序
原理图
led的电阻很小,接通电源以后,其本身的电流很大,会被烧毁
因此在他之前加了一个电阻:
这里的开关使我们手动的,在芯片里边都是引脚,引脚可以输出一个电压值
还可以
有些芯片的引脚驱动不足,电压很低,不能驱动LED,这个时候需要三极管
硬件知识
打开s3c2440的芯片手册,搜索LED
这个LED接了一个3.3V的电源,然后接到芯片的nLED_1,当nLED_1输出高电平的时候,LED熄灭,当nLED_1输出低电平的时候,n指的是低电平有效。
我们在s3c2440手册里边找到nLED_1连接的脚本是EINT4/GPT4
我们打开s3c2440找到gpt4的使用,芯片里边有A-J组引脚,gpt4在Port F(GPF)这一组里边
现在的问题是怎么把GPT4的引脚设置为输出低电平?
- GPT4配置为输出引脚,注意每个引脚都可以配置为输出引脚还是输入引脚
GPT4可以设置输入,也可以设置输出,也可以设置中断功能。
编程设置它的状态
这个是GPFCON和GPFDAT寄存器的地址:
设置GPFCON的[9:8]为0b01,那么GPF4设置为输出引脚,即把0x100写入GPFCON,就是写到地址0x56000050.
下面是GPFDATA寄存器的设置说明
GPFDATA的意思是设置对应引脚编号的位置为对应引脚输出的值。
比如是GPF4:
LED熄灭:那么就设置GPFDATA的第4位为1,即为输出高电平,把0x10写到地址0x56000054上;
LED点亮:设置GPFDATA的第4位为0,即为输出低电平,把0写到地址0x56000054上;
接下来就是写代码去配置,在写代码配置之前,需要先来了解下s3c2440框架和启动过程
启动过程如下:
nor启动:nor flash基地址为0,片内RAM地址为0x4000,0000
cpu读出nor上第一个指令(前4字节),执行。
cpu继续读出其他指令执行。nand启动:片内4Kram基地址为0,nor flash不可访问,2440硬件把nand前4K内容复制到片内RAM,然后cpu从0地址取出第一条指令执行。
另外,cpu操作寄存器的形式如下:
操作寄存器的几个汇编指令
4.1 LDR 指令
读内存,LDR R0,[R1] ,假设R1的值是x,读取地址x上的数据(4字节),保存到R0中。
4.2 STR指令
写内存指令,STR R0,[R1]
假设R1的值是x,把R0的值写到地址X(4字节)
4.3 B指令
跳转
4.4 MOV指令
MOV R0,R1 把R1的值赋值给R0,R0=R1;
MOV R0,#0x100,R0=0x100;
4.5 LDR R0,=0x12345678:R0=0x12345678
这是一条伪指令,它会被拆分为几条真正的RAM指令
如果我们用MOV R0,0x12345678,这样是错误的,不能正确执行,因为一条ARM指令长度是32位,除了数据意外,还需要表示指令本身以及其他变量。
所以引入伪指令 LDR R0,=0x12345678
4.6 加减法
add r0,r1,#4:r0=r1+4 ;
sub r0,r1,#4:r0=r1-4;
sub r0,r1,r2:r0=r1-r2;
4.7 bl指令
branch and link编写程序
我们写的第一个程序是汇编程序,不会上来就写c语言main函数。
新建文件led_on.S1
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
/*
* 点亮LED1: gpf4
*/
.text
.global _start
_start:
/* 配置GPF4为输出引脚
* 把0x100写到地址0x56000050
*/
ldr r1, =0x56000050
ldr r0, =0x100 /* mov r0, #0x100 */
str r0, [r1]
/* 设置GPF4输出高电平
* 把0写到地址0x56000054
*/
ldr r1, =0x56000054
ldr r0, =0 /* mov r0, #0 */
str r0, [r1]
/* 死循环 */
halt:
b halt将此文件放在linux系统编译:
5.1. 预编译
arm-linux-gcc -c -o led_on.o led_on.S
5.2. 连接
arm-linux-ld -Ttext 0 led_on.o -o led_on.elf
5.3. 得到bin文件
arm-linux-objcopy -o binary -S led_on.elf led_on.bin
这些命令写的时候容易出错,可以放在一个文件里边。
文件名为Makefile1
2
3
4
5
6
7
8all:
arm-linux-gcc -c -o led_on.o led_on.S
arm-linux-ld -Ttext 0 led_on.o -o led_on.elf
arm-linux-objcopy -O binary -S led_on.elf led_on.bin
arm-linux-objdump -D led_on.elf > led_on.dis
clean:
rm *.bin *.o *.elf
5.4. 然后使用make clean来清除,使用make命令来编译
烧写
将在linux下编译好的并文件copy到windows平台下
硬件接好烧写器
烧写命令:
oflash led_on.bin
一次输入0、1、0、0、0
接下来把烧写排线拔掉,关电,设置为nand启动,上电。
效果就是有一个灯亮了。
汇编与机器码
上边的makefile文件有一条 arm-linux-objdump -D led_on.elf > led_on.dis 这条命令是反汇编指令,会得到一个 led_on.dis文件
1 | led_on.elf: file format elf32-littlearm |
上边我们提到cpu可以直接访问的寄存器从R0到R15,pc是什么,pc是一个别名,有一张如下的映射表:
User 模式 SVC 模式 IRQ 模式 FIQ 模式 APCS
R0 ——- R0 ——- R0 ——- R0 a1
R1 ——- R1 ——- R1 ——- R1 a2
R2 ——- R2 ——- R2 ——- R2 a3
R3 ——- R3 ——- R3 ——- R3 a4
R4 ——- R4 ——- R4 ——- R4 v1
R5 ——- R5 ——- R5 ——- R5 v2
R6 ——- R6 ——- R6 ——- R6 v3
R7 ——- R7 ——- R7 ——- R7 v4
R8 ——- R8 ——- R8 R8_fiq v5
R9 ——- R9 ——- R9 R9_fiq v6
R10 —— R10 —— R10 R10_fiq sl
R11 —— R11 —— R11 R11_fiq fp
R12 —— R12 —— R12 R12_fiq ip
R13 R13_svc R13_irq R13_fiq sp
R14 R14_svc R14_irq R14_fiq lr
————- R15 / PC ————- pc
上边的汇编指令执行的时候哦,会是如下的方式:
字节序和位操作
C语言点亮LED
1 | int main() |
新建一个Start.S文件,用于为C语言文件设置内存,调用main函数:
1 |
|
C语言视角内存操作:
解析C程序的内部机制
上面点亮LED的代码,反编译为汇编指令:
1 |
|
在讲解这段指令之前,需要先解释下里边的一些陌生指令:
stmdb和ldmia指令。
下面对这段指令进行解析:
给C程序传递参数
修改Start.S文件:
1 |
|
修改ldc.c文件:
1 |
|
程序先让第一个灯亮,然后等了一段时间(delay函数),又让第二个灯亮,参数传递使用了r0寄存器,那么调用者和被调用者之前都是使用那些寄存器呢,下面是一套使用标准:
关闭看门狗
以上程序在运行的时候,2个灯被依次点亮,然后又会往复循环点亮,这是因为系统有保护机制,防止程序卡死,不能使用,会重新复位系统,内置了看门狗机制,我们可以关闭看门狗:
Start.S修改:
1 | .text |
程序兼容nor启动和nand启动
修改Start.S
1 |
|
修改led.c文件:
1 |
|
以上2个文件兼容nor和nand启动。
按钮控制led点亮
<<<<<<< HEAD
EINT0控制LED_1,EINT2控制LED_2,EINT11控制LED_4。查看原理图
寻找他们对应的引脚:
按键和引脚的对应关系:EINT0:GPF0,EINT2:GPF2,EINT11:GPG3;
想找到GPFCON的2个引脚配置:
GPFCON引脚的地址是:0x56000050
GPGCON引脚配置:
GPGCON引脚的地址是:0x56000060
GPFDATA和GPGDATA的地址分别是:0x56000054、0x56000064,这些都可以通过S3C2440芯片手册找到。
还有一个问题是,怎么判断按键是被按下或者松开?看下原理图:
这样,只要我们的程序只要检测到引脚GPF0是高电平意味着开关没有被按下,如果是低电平,意味着开关被按下。
然后就是确认3盏灯的控制引脚:
=======
EINT0控制LED_1,EINT2控制LED_2,EINT11控制LED_4。查看原理图
寻找他们对应的引脚:
按键和引脚的对应关系:EINT0:GPF0,EINT2:GPF2,EINT11:GPG3;
想找到GPFCON的2个引脚配置:
GPFCON引脚的地址是:0x56000050
GPGCON引脚配置:
GPGCON引脚的地址是:0x56000060
GPFDATA和GPGDATA的地址分别是:0x56000054、0x56000064,这些都可以通过S3C2440芯片手册找到。
还有一个问题是,怎么判断按键是被按下或者松开?看下原理图:
这样,只要我们的程序只要检测到引脚GPF0是高电平意味着开关没有被按下,如果是低电平,意味着开关被按下。
然后就是确认3盏灯的控制引脚:
db05d90729ee645598105d778a95820856c0bfea
分别是GPF4,GPF5,GPF6.
接下里就可以把led.c程序写出来:
1 | #include "s3c2440_soc.h" |
如果你觉得我的文章有用,可以打赏我一杯雀巢咖啡