前言
学习嵌入式已经快一年了,却是第一次正经的开始从ARM的裸板程序开始学习,有些汗颜……准备学习裸板程序的一部分,然后接着学习相应的设备驱动开发。第一次学博文,欢迎拍砖
第一课:GPIO操作——LED & 按键
环境:
软件:Ubuntu9.10 source insight3.5 oflash JTAG等驱动
硬件:ARM 2440 (韦东山配套教材)
参考教材:嵌入式Linux应用开发完全手册 韦东山著
一系列的环境搭建,操作步骤书中和视频教材解释也挺详细的
只是记录下学习过程的心得
按键点亮 LED (C语言 )
最终文件:
led_key.c :C程序文件,实现点亮led
crt0.s :裸板程序中的启动代码(汇编)
led_key.lds
Makefile :实现程序的预处理、编译、汇编和链接以及清除工作
实现:
此处GPIO的操作需要:
1.判断key
2.点亮/灭led
a.配置功能:输出/输入/其他
b.读key引脚,根据值来设置led引脚高电平LED熄灭,低电平LED点亮(参看电路图)
写程序之前先了解一下ARM2440的两种启动方式
1.Nand flash启动
a.在开发板上电启动的时候,CPU将Nand Flash开始的前4K数据复制到SRAM,即“Steppingstone”(此时内部RAM的起始地址为0);
b.CPU跳转地址0开始执行。
以上两步均为硬件执行
2.Nor flash启动
Nor flash可以像内存一样进行读操作,却不可想内存一样写
a.0地址指向Nor flash
b.CPU 从0地址取址执行
由于是裸板程序,因此需要在C语言文件之前实现一系列的初始化工作。
1)软件相关的初始化:
a.设置栈,即初始化SP指针,指向内存的某一块区域。如果是SRAM,可直接使用,如果是SDAM,还需要初始化(韦东山的板子都是直接映射到SRAM的,因此可以直接使用)
b.设置C语言中的main返回地址
c.调用main函数
(以上两步直接通过bl main可实现,返回地址保存到了LR寄存器)
d.清理工作
(通过死循环来实现的
halt_loop : b halt_loop )
2)硬件初始化:
a.关看门狗,关中断
b.初始化时钟
c.初始化SDAM
(此次程序只关了看门狗)
这两步是由crt0.s来实现的,之后调用main函数
@******************************************************************************
@ File:crt0.S
@ 功能:通过它转入C程序
@******************************************************************************
@.text部分是处理器开始执行代码的地方
@.global关键字用来让一个符号对链接器可见,可以供其他链接对象模块使用
@.global _start让_start符号成为链接器可见的标识符
@这样链接器就知道跳转到程序中的什么地方并开始执行
@linux寻找这个_start标签作为程序的默认进入点
@转载自:http://hi.baidu.com/ahauwangjie/item/10e988e740160ee5fb42baa5
.text
.global _start
_start:
ldr r0,= 0x53000000; @watchdog寄存器地址
mov r1,#0x0
str r1,[r0]
ldr sp,= 1024 * 4
bl main
halt_loop:
b halt_loop
程序的具体实现:
//引脚定义
#define GPFCON (*(volatile unsigned long *)0x56000050)
#define GPFDAT (*(volatile unsigned long *)0x56000054)
#define GPGCON (*(volatile unsigned long *)0x56000060)
#define GPGDAT (*(volatile unsigned long *)0x56000064)
//LED引脚初始化定义
#define GPF4_MSK (3<<(4*2))
#define GPF5_MSK (3<<(5*2))
#define GPF6_MSK (3<<(6*2))
//按键引脚初始化定义
#define GPF0_MSK (3<<(0*2))
#define GPF2_MSK (3<<(2*2))
#define GPG3_MSK (3<<(3*2))
//led引脚输出定义
#define GPF4_out (1<<(4*2))
#define GPF5_out (1<<(5*2))
#define GPF6_out (1<<(6*2))
int main()
{
unsigned long temp;
//先将led相应引脚置0,其他位保留
GPFCON &= ~(GPF4_MSK | GPF5_MSK |GPF6_MSK);
//定义led引脚为输出
GPFCON |= (GPF4_out | GPF5_out | GPF6_out);
//定义按键引脚为输入
GPFCON &= ~(GPF0_MSK | GPF2_MSK);
GPGCON &= ~(GPG3_MSK);
while(1)
{
//读取key值
temp = GPFDAT;
//key值为0说明按键按下,为1表示没有按下
if(temp & (1<<0))
GPFDAT |= (1<<4); //熄灭led
else
GPFDAT &= ~(1<<4); //点亮led
if(temp & (1<<2))
GPFDAT |= (1<<5);
else
GPFDAT &= ~(1<<5);
temp = GPGDAT;
if(temp & (1<<3))
GPFDAT |= (1<<6);
else
GPFDAT &= ~(1<<6);
}
return 0;
}
led_key.lds:
SECTIONS {
. = 0x00;
.text : { *(.text) }
.rodata ALIGN(4) : {*(.rodata)}
.data ALIGN(4) : { *(.data) }
.bss ALIGN(4) : { *(.bss) *(COMMON) }
}
Makefile:
led_key.bin : led_key.c crt0.s #依赖关系
arm-linux-gcc -g -c -o crt0.o crt0.s #编译crt0.s
arm-linux-gcc -g -c -o led_key.o led_key.c #编译led_key.c
arm-linux-ld -Tled_key.lds crt0.o led_key.o -o led_key_elf #通过连接脚本链接,生成led_key_elf文件
arm-linux-objcopy -O binary -S led_key_elf led_key.bin #生成二进制可执行文件
arm-linux-objdump -D -m arm led_key_elf > led_key.dis #生成反汇编文件
clean:
rm -f led_key_elf led_key.bin led_key.dis *.o *~ #make clean
至此按键控制led的裸板程序写好,中间还有不少迷惑的地方,诸如Makefile中诸多参数的定义,连接脚本的编写,启动汇编程序的完善……