裸機程式二:GPIO點亮led燈 彙編
開發板: JZ2440 V3 + EasyOpenJTAG
參考資料:《CPU三星S3C2440A晶片手冊.pdf》
《JZ2440_V3電路圖.pdf》
使用匯編程式碼點亮led燈,主要是設定gpio GPCON和GPDAT暫存器,使用開發版資料中的原始碼:
@****************************************************************************** @ File:led_on.S @ 功能:LED點燈程式,點亮LED1 @****************************************************************************** .text .global _start _start: LDR R0,=0x56000050 @ R0設為GPFCON暫存器。此暫存器 @ 用於選擇埠B各引腳的功能: @ 是輸出、是輸入、還是其他 MOV R1,#0x00000100 STR R1,[R0] @ 設定GPF4為輸出口, 位[8:7] LDR R0,=0x56000054 @ R0設為GPBDAT暫存器。此暫存器 @ 用於讀/寫埠B各引腳的資料 MOV R1,#0x00000000 @ 此值改為0x00000010, @ 可讓LED1熄滅 STR R1,[R0] @ GPF4輸出0,LED1點亮 MAIN_LOOP: B MAIN_LOOP
然後是Makefile:
led_on.bin : led_on.S
#編譯但不連結
arm-linux-gcc -g -c -o led_on.o led_on.S
#連結並指定入口地址
arm-linux-ld -Ttext 0x0000000 -g led_on.o -o led_on_elf
arm-linux-objcopy -O binary -S led_on_elf led_on.bin
clean:
rm -f led_on.bin led_on_elf *.o
最後啟動openocd,並在另一個命令列中通過telnet登陸openocd,如下:
$ telnet localhost 4444 Trying ::1... telnet: connect to address ::1: Connection refused Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. Open On-Chip Debugger > halt //中斷裝置執行 > load_image ./led_on.bin //下載bin檔案 72 bytes written at address 0x00000000 downloaded 72 bytes in 0.005064s (13.885 KiB/s) > resume 0x0 //從地址0x0恢復裝置執行 > halt s3c2440.cpu: target state: halted target halted in ARM state due to debug-request, current mode: Supervisor cpsr: 0xf00000d3 pc: 0x00000014 MMU: disabled, D-Cache: disabled, I-Cache: disabled >
板子預設led1是亮的,修改上訴程式碼,關閉led1,重新燒寫執行,發現led1熄滅一會兒又會被點亮,這是由於我們沒有關閉看門狗,導致系統復位了
1、組合語言是否區分大小寫
組合語言的語句是不區分大小寫的,編譯軟體會自動識別這些語句,但是彙編程式中使用者定義的變數、標號等,將區分其大小寫,程式設計時要注意這些區別
2、指令ldr與mov的區別
ARM是RISC結構,資料從記憶體到CPU之間的移動只能通過L/S指令來完成,也就是ldr/str指令。
比如想把資料從記憶體中某處讀取到暫存器中,只能使用ldr:
ldr r0, 0x12345678
就是把0x12345678這個地址中的值存放到r0中。
而mov不能幹這個活,mov只能在暫存器之間移動資料,或者把立即數移動到暫存器中,這個和x86這種CISC架構的晶片區別最大的地方。
x86中沒有ldr這種指令,因為x86的mov指令可以將資料從記憶體中移動到暫存器中。
另外還有一個就是ldr偽指令,雖然ldr偽指令和ARM的ldr指令很像,但是作用不太一樣。ldr偽指令可以在立即數前加上=,以表示把一個地址寫到某暫存器中,比如:
ldr r0, =0x12345678
這樣,就把0x12345678這個地址寫到r0中了。所以,ldr偽指令和mov是比較相似的。只不過mov指令限制了立即數的長度為8位,也就是不能超過512。而ldr偽指令沒有這個限制。如果使用ldr偽指令時,後面跟的立即數沒有超過8位,那麼在實際彙編的時候該ldr偽指令是被轉換為mov指令的。
ldr偽指令和ldr指令不是一個同東西。
所以上述程式碼可以用str代替mov,如下:
LDR R0,=0x56000050 @ R0設為GPFCON暫存器。此暫存器
@ 用於選擇埠B各引腳的功能:
@ 是輸出、是輸入、還是其他
@MOV R1,#0x00000100</span>
STR R1,=0x00000100</span>
STR R1,[R0] @ 設定GPF4為輸出口, 位[8:7]
LDR R0,=0x56000054 @ R0設為GPBDAT暫存器。此暫存器
@ 用於讀/寫埠B各引腳的資料
@MOV R1,#0x00000000</span> @ 此值改為0x00000010,
STR R1,=0x00000000</span> @ 此值改為0x00000010,
3、ARM暫存器
ARM共有37個32位暫存器,其中31個為通用暫存器,6個為狀態暫存器.這些暫存器不能被同時訪問,但在任何時候,通用暫存器R0~R14,程式計數器PC,一個或兩個狀態暫存器都是可訪問的.
通用暫存器
通用暫存器包括R0~R15,可以分為3類:
(1)未分組暫存器R0~R7
(2)分組暫存器R8~R14
(3)程式計數器PC(R15)
暫存器R13在ARM指令中常用作堆疊指標, 使用者也可使用其他的暫存器作為堆疊指標,而在Thumb指令集中,某些指令強制性的要求使用R13作為堆疊指標.
暫存器R13在ARM指令中常用作堆疊指標,但這只是一種習慣用法,使用者也可使用其他的暫存器作為堆疊指標。而在Thumb指令集中,某些指令強制性的要求使用R13作為堆疊指標。
由於處理器的每種執行模式均有自己獨立的物理暫存器R13,在使用者應用程式的初始化部分,一般都要初始化每種模式下的R13,使其指向該執行模式的棧空間。這
樣,當程式的執行進入異常模式時,可以將需要保護的暫存器放入R13所指向的堆疊,而當程式從異常模式返回時,則從對應的堆疊中恢復,採用這種方式可以保證異常發生後程序的正常執行。
R14稱為連結暫存器(Link Register),當執行子程式呼叫指令(BL)時,R14可得到R15(程式計數器PC)的備份.
在每一種執行模式下,都可用R14儲存子程式的返回地址,當用BL或BLX指令呼叫子程式時,將PC的當前值複製給R14,執行完子程式後,又將R14的值複製回PC,即可完成子程式的呼叫返回。以上的描述可用指令完成。
暫存器R16
暫存器R16用作CPSR(Current
Program Status Register,當前程式狀態暫存器),CPSR可在任何執行模式下被訪問,它包括條件標誌位、中斷禁止位、當前處理器模式標誌位,以及其他一些相關的控制和狀態位。
每一種執行模式下又都有一個專用的物理狀態暫存器,稱為SPSR(Saved Program Status Register,備份的程式狀態暫存器),當異常發生時,SPSR用於儲存CPSR的當前值,從異常退出時則可由SPSR來恢復CPSR。
由於使用者模式和系統模式不屬於異常模式,它們沒有SPSR,當在這兩種模式下訪問SPSR,結果是未知的