第二個示例程序:按響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口寄存器。