首页 > 代码库 > 起航,第一个程序——还是LED灯

起航,第一个程序——还是LED灯

如同学基本语言一样,helloworld是很多语言的第一个程序。在嵌入式开发中,点亮LED灯也是各种架构和开发板的第一个程序,其中很多东西是和单片机例如stm32是类似的,只是,现在我们没有了库函数,我们要自己完成一些东西。

先说启动文件,st官方已结给我们做好了,但是jz2440开发板没有统一的启动文件,需要自己编写,那么,基础的arm汇编就得有所熟悉,在之后的学习中,遇到一个指令就学习一个。

(汇编)指令是CPU机器指令的助记符,经过编译后会得到一串1、0组成的机器码,可以由CPU读取执行。 (汇编)伪指令本质上不是指令(只是和指令一起写在代码中),它是编译器环境提供的,目的是用来指导编译过程,经过编译后伪指令最终不会生成机器码。

ARM官方的ARM汇编风格:指令一般用大写、Windows中IDE开发环境(如ADS、MDK等)常用。如: LDR R0, [R1] GNU风格的ARM汇编:指令一般用小写字母、linux中常用。如:ldr r0, [r1]

ARM采用RISC架构,CPU本身不能直接读取内存,而需要先将内存中内容加载入CPU中通用寄存器中才能被CPU处理。 ldr(load register)指令将内存内容加载入通用寄存器。 str(store register)指令将寄存器内容存入内存空间中。 ldr/str组合用来实现 ARM CPU和内存数据交换。

先看流水灯的一段启动代码:

 1 @******************************************************************************
 2 @ File:crt0.S
 3 @ 功能:通过它转入C程序
 4 @******************************************************************************       
 5 
 6 .text
 7 .global _start
 8 _start:
 9             ldr     r0, =0x53000000     @ WATCHDOG寄存器地址
10             mov     r1, #0x0                     
11             str   r1, [r0]              @ 写入0,禁止WATCHDOG,否则CPU会不断重启
12             
13             ldr     sp, =1024*4         @ 设置堆栈,注意:不能大于4k, 因为现在可用的内存只有4K
14                                         @ nand flash中的代码在复位后会移到内部ram中,此ram只有4K
15             bl      main                @ 调用C程序中的main函数
16 halt_loop:
17             b       halt_loop

预备知识:摘自 http://blog.csdn.net/qq506124204/article/details/7952966

mov   r1, #0x53000000   //立即数寻址方式 
mov   r2, #0x0 
str   r2, [r1]        
立即数寻址方式,立即数要求以“#”作前缀,对于十六进制的数,还要求在#后面加上0x或者&。STR是比较重要的指令了,跟它对应的是LDR。 ARM指令集是加载/存储型的,也就是说它只处理在寄存器中的数据。那么对于系统存储器的访问就经常用到STR和LDR了。STR是把寄存器上的数据传输 到指定地址的存储器上。它的格式我个人认为很特殊:
STR(条件) 源寄存器,<存储器地址>
比如 STR R0, [R1] ,意思是R0-> [R1],它把源寄存器写在前面,跟MOV、LDR都相反。
LDR应该是非常常见了。LDR就是把数据从存储器传输到寄存器上。而且有个伪指令也是LDR,因此我有个百思不得其解的问题。看这段代码:
mov r1, #GPIO_CTL_BASE 
add   r1, r1, #oGPIO_F 
ldr   r2,=0x55aa   // 0x55aa是个立即数啊,前面加个=干什么? 
对于当中的ldr 那句,我就不明白了,如果你把=去掉,是不能通过编译的。我查了一些资料,个人感觉知道了原因:这个=应该表示LDR不是ARM指令,而是伪指令。作为伪指令的时候,LDR的格式如下:
LDR 寄存器, =数字常量/Label
它的作用是把一个32位的地址或者常量调入寄存器。嗬嗬,那大家可能会问,
“MOV r2,#0x55aa”也可以啊。应该是这样的。不过,LDR是伪指令啊,也就是说编译时编译器会处理它的。怎么处理的呢?——规则如下:如果该数字常量 在MOV指令范围内,汇编器会把这个指令作为MOV。如果不在MOV范围中,汇编器把该常量放在程序后面,用LDR来读取,PC和该常量的偏移量不能超过 4KB。

然后说一下跳转指令。ARM有两种跳转方式。
(1) mov pc <跳转地址〉
这种向程序计数器PC直接写跳转地址,能在4GB连续空间内任意跳转。
(2)通过 B BL BLX BX 可以完成在当前指令向前或者向后32MB的地址空间的跳转(为什么是32MB呢?寄存器是32位的,此时的值是24位有符号数,所以32MB)。
B是最简单的跳转指令。要注意的是,跳转指令的实际值不是绝对地址,而是相对地址——是相对当前PC值的一个偏移量,它的值由汇编器计算得出。
BL非常常用。它在跳转之前会在寄存器LR(R14)中保存PC的当前内容。

起航,第一个程序——还是LED灯