第二个示例程序:按响BEEP
这次的程序主要练习ARM的GPIO口的输入功能,并以此来按响BEEP。
比较严密的说法应该是:按了按键,然后CPU得知,然后CPU再输出相应的内容。
所以过程很简单,程序不断地读取GPIO口的值,当发现有不同输入的时候,做出相应的反应。
硬件环境:
w3c2440,GPF0,2,3,4接4个按键。并且接上拉电阻。 //我一直在想,GPF口本身自带上拉电阻,如果不接上拉电阻应该也行。
当某一个键按下的时候,对应的GPF口数据位输入0。没有按下的时候,依靠上拉电阻输入1。
BEEP接三极管的集电极,GPB0口接基极,发射极接地 //简单得说,就是三极管当做开关,GPB0输出1就接通BEEP。电子方面有谬误还请指出来。
汇编代码:
1 AREA BEEPSAMPLE,CODE,READONLY 2 ENTRY 3 4 LDR R1, =0x56000058 ;GPFUP_addr 5 MOV R2, #0x000000FF ;GPFUP_value=disable 6 STR R2, [R1] 7 8 LDR R1, =0x56000018 ;GPBUP_addr 9 LDR R2, =0x00000FFF ;GPBUP_value=disable 10 STR R2, [R1] 11 12 LDR R1, =0x56000050 ;GPFCON_addr 13 LDR R2, =0x0000FC0C ;GPFCON(0,2,3,4)_value=input 14 STR R2, [R1] 15 16 LDR R2, =0x56000014 ;GPBDAT_addr 17 18 LDR R3, =0x56000010 ;GPBCON_addr 19 LDR R4, =0x00FFFFFD ;GPBCON(0)_value=output 20 STR R4, [R3] 21 22 START 23 LDR R5, =0x56000054 ;GPFDAT_addr 24 LDR R6, [R5] ;read GPFDAT 25 ORR R6, R6, #0xE2 26 27 CMP R6, #0xFF 28 BEQ BEEPDOWN 29 30 BEEPUP 31 LDR R7, =0xFFF 32 STR R7, [R2] ;set GPBDAT(0), output=1 33 B START 34 35 BEEPDOWN 36 LDR R6, =0xFFE 37 STR R6, [R2] ;set GPBDAT(1), output=0 38 B START 39 40 END
注解及笔记:
04~10:禁止GPF,GPB的上拉电阻
12~20:配置GPF口,功能设置为输入。而GPB口则设置为输出
23~24:读取GPF口的值。
在这里,23行的LDR是一条伪指令,而24的LDR才是真正的ARM指令而非伪指令。
23行:把内存GPF的内存地址存入R5(伪指令:把一个“常量”存到寄存器)
24行:从内存地址 0x56000054 (保存在R5中的地址)处读取数据,存入R6(ARM指令:从内存传输数据到寄存器)
25:将读取到的值(保存在R6)和(1110 0010)做或运算。
这样做的目的是简化操作,把GPF0,2,3,4以外的数据位都设置为1,方便比较。
27:CMP比较指令。比较两个数,并设置相关CPSR(程序状态寄存器)的相关标志位。
标志位相对复杂,现阶段暂且不深究。
简单得说,通过CMP比较并设置相关标志位后,指令可以通过条件码选择执行:
EQ:当(刚才的比较)相等时执行; NE:当不相等时执行。
28:BEQ,就是 B (跳转指令)+ EQ(条件码:相等时执行)。
连起来就是当相等时执行跳转。
30~33:打开BEEP。
方法很简单,就是向GPB0输出1而已。
输出结束后跳回检测GPF输入值的代码段(START)
35~38:关闭BEEP。
向GPB0输出0。
注意点:
1:GPF口是一个0位口,所以读到的数据也就是8位二进制,所以CMP对比的时候和0xFF对比即可。
2:汇编和高级编程语言不同,不能像C一样直接对某个IO口寄存器赋值(比如:GPB=0xFFF)
必须要分三步:载入IO口寄存器地址,载入需要输出的数据,通过LDR将数据送入IO口寄存器。