一,说到做ARM的裸机程序,很多人马上就会联想到一个名为ADS的开发工具,但是我们在linux下同样也可以做ARM的裸机程序,下面来说说其具体实施过程:
步骤一:编辑代码,这个没什么好说的。
步骤二:编译代码,编译代码分为三个方面的内容:1.链接脚本 2.用命令行确定链接时的文件顺序 3.用命令行编译。这三部分的内容可以全部写成一个Makefile文件,编译的时候执行make命令就可以了。
步骤三:把编译后的".bin"文件烧写进开发板,重新上电观察效果。
二,TQ2440按键点亮LED灯裸机程序分析:
1.启动文件:
ARM是不能直接执行C语言程序的,必须通过一个名为crt0.S的汇编程序完成一些初始化的工作后才能转而执行C语言程序,这个名为crt0.S的文件我们把其称之为--启动文件。
启动文件目的是做一些初始化的工作,为最终执行C语言程序做好准备,前面所说的初始化工作包含两个方面的内容:1.软件初始化,具体流程是 设置栈-->设置返回地址 --> 调用main函数 --> 做一些调用完后的清理工作。 2.硬件初始化,具体流程是 关闭看门狗 --> 初始化时钟 --> 初始化SDRAM。
TQ2440按键点亮LED灯裸机程序的启动文件:
@****************************************************************************** @ File:crt0.S @ 功能:通过它转入C程序 @****************************************************************************** .text .global _start _start: ldr r0, =0x53000000 @ WATCHDOG寄存器地址 mov r1, #0x0 str r1, [r0] @ 写入0,禁止WATCHDOG,否则CPU会不断重启 ldr sp, =1024*4 @ 设置堆栈,注意:不能大于4k, 因为现在可用的内存只有4K @ nand flash中的代码在复位后会移到内部ram中,此ram只有4K bl main @ 调用C程序中的main函数 halt_loop: b halt_loop
2.Makefile :
key_led.bin : crt0.S key_led.c arm-linux-gcc -g -c -o crt0.o crt0.S arm-linux-gcc -g -c -o key_led.o key_led.c arm-linux-ld -Ttext 0x0000000 -g crt0.o key_led.o -o key_led_elf arm-linux-objcopy -O binary -S key_led_elf key_led.bin arm-linux-objdump -D -m arm key_led_elf > key_led.dis clean: rm -f key_led.dis key_led.bin key_led_elf *.o
Makefile的规则:
目标文件:依赖文件1 依赖文件2 .....
Tab键 命令
Makefile命令能够被执行的条件:目标文件不存在;目标文件的依赖文件已经更新;以上两个条件满足其中一个便能够执行Makefile的命令
3.TQ2440按键点亮LED灯裸机程序的源代码:
//led的控制寄存器和数据寄存器 #define GPBCON (*(volatile unsigned long *)0x56000010) #define GPBDAT (*(volatile unsigned long *)0x56000014) //Key的控制寄存器和数据寄存器 #define GPFCON (*(volatile unsigned long *)0x56000050) #define GPFDAT (*(volatile unsigned long *)0x56000054) /* * LED1,LED2,LED3,LED4对应GPB5、GPB6、GPB7、GPB8 */ #define GPB5_out (1<<(5*2)) #define GPB6_out (1<<(6*2)) #define GPB7_out (1<<(7*2)) #define GPB8_out (1<<(8*2)) #define GPB5_msk (3<<(5*2)) #define GPB6_msk (3<<(6*2)) #define GPB7_msk (3<<(7*2)) #define GPB8_msk (3<<(8*2)) /* * K1,K2,K3,K4对应GPF1、GPF4、GPF2、GPF0 */ #define GPF1_in (0<<(1*2)) #define GPF4_in (0<<(4*2)) #define GPF2_in (0<<(2*2)) #define GPF0_in (0<<(0*2)) #define GPF1_msk (3<<(1*2)) #define GPF4_msk (3<<(4*2)) #define GPF2_msk (3<<(2*2)) #define GPF0_msk (3<<(0*2)) int main() { unsigned long dwDat; // LED1,LED2,LED3,LED4对应的4根引脚设为输出 GPBCON &= ~(GPB5_msk | GPB6_msk | GPB7_msk | GPB8_msk); //将4个寄存器的对应位设为 00 GPBCON |= GPB5_out | GPB6_out | GPB7_out | GPB8_out; // K1,K2,K3,K4对应的4根引脚设为输入 GPFCON &= ~(GPF1_msk | GPF4_msk | GPF2_msk | GPF0_msk); //将4个寄存器的对应为设为 00 GPFCON |= GPF1_in | GPF4_in | GPF2_in | GPF0_in; while(1){ //若Kn为0(表示按下),则令LEDn为0(表示点亮) dwDat = GPFDAT; // 读取GPF管脚电平状态 if (dwDat & (1<<1)) // K1 没有按下 GPBDAT |= (1<<5); // LED1熄灭 else GPBDAT &= ~(1<<5); // LED1点亮 if (dwDat & (1<<4)) // K2 没有按下 GPBDAT |= (1<<6); // LED2熄灭 else GPBDAT &= ~(1<<6); // LED2点亮 if (dwDat & (1<<2)) //K3 没有被按下 GPBDAT |= (1<<7); //LED3熄灭 else GPBDAT &= ~(1<<7); //LED3点亮 if (dwDat & (1<<0)) // K4 没有按下 GPBDAT |= (1<<8); // LED4熄灭 else GPBDAT &= ~(1<<8); // LED4点亮 } return 0; }
具体原理和硬件的分析这里就不多说了,我以前写过一边名为《TQ2440按键点亮LED驱动程序》 这里面有分析,不同之处在于linux下的按键点亮LED是通过中断实现的,而这里的裸机程序并没有通过按键中断实现,而只是把按键所接的引脚当作一个普通的GPIO口来看待,通过判断按键是否被按下时其对应管脚电平的不同来设置LED管脚的电平的高低,从而控制LED灯的亮与灭。