第一個部分中,徹底使用ARM彙編來控制LED,徹底使用匯編來編寫大的系統存在理論可行性(固然現實中也有徹底使用匯編實現的操做系統),可是彙編理解起來太困難,編寫起來很複雜,太瑣碎,因此確定會想要使用理解性更好的C語言來編寫大部分代碼,那麼哪些代碼用匯編編寫?哪些用C語言編寫呢?linux
從工程實踐中能夠得出一些結論:函數
1.跟具體硬件緊密相關的功能,對這部分功能,無需保持可移植性。優化
2.對C語言編譯後的代碼沒有信心,或者必須進行特別優化時,必須依靠人工優化代碼時。spa
3.相對的是,若是須要保持可移植性,那麼必須把平臺無關的功能經過底層的部分彙編代碼進行隔絕,其餘代碼經過C語言來編寫。操作系統
功能原理和電路圖見第一篇。code
代碼:blog
彙編代碼完成硬件初始化,包括內存基地址和大小設定,WatchDog關閉,設置棧,而後跳轉到C代碼中。內存
start.S開發
1 // 啓動代碼 2 .global _start 3 4 _start: 5 6 // 把外設的基地址告訴CPU 7 ldr r0, =0x70000000 //對於6410來講,內存(0x00000000~0x60000000),外設(0x70000000-0x7fffffff) 8 orr r0, r0, #0x13 //外設大小:256M 9 mcr p15,0,r0,c15,c2,4 //把r0的值(包括了外設基地址+外設大小)寫給cpu 10 11 // 關看門狗 12 ldr r0, =0x7E004000 13 mov r1, #0 14 str r1, [r0] 15 16 // 設置棧,0x0c002000內存地址映射到SoC內部的SRAM中,速度較快 17 ldr sp, =0x0c002000 18 19 // 調用C函數點燈 20 bl main 21 22 halt: 23 b halt
C語言用來實現具體的流水燈功能:編譯
main.c
1 // 功能:c實現流水燈 2 3 // 延時 4 void delay() 5 { 6 volatile int i = 0x100000; 7 while (i--); 8 } 9 10 int main() 11 { 12 int i = 0x01; 13 14 // 配置引腳 15 volatile unsigned long *gpkcon0 = (volatile unsigned long *)0x7F008820; 16 volatile unsigned long *gpkdat = (volatile unsigned long *)0x7F008824; 17 18 *gpkcon0 = 0x00001111; 19 20 // 跑馬燈 21 while (1) 22 { 23 *gpkdat = i; 24 i = i << 1; 25 if (i == 0x100) 26 i = 0x01; 27 delay(); 28 } 29 30 return 0; 31 }
1 led.bin: start.o main.o 2 arm-linux-ld -Ttext 0x50000000 -o led.elf $^ 3 arm-linux-objcopy -O binary led.elf led.bin 4 arm-linux-objdump -D led.elf > led_elf.dis 5 %.o : %.S 6 arm-linux-gcc -nostdlib -o $@ $< -c 7 8 %.o : %.c 9 arm-linux-gcc -nostdlib -o $@ $< -c 10 11 clean: 12 rm *.o *.elf *.bin *.dis -rf
編譯後下載到開發板中,運行,具體運行方法見第一篇。運行效果能夠看到四個LED燈依次熄滅,而後點亮。
總結:
1.彙編代碼完成必要的硬件初始化功能,從C語言也會被編譯爲彙編來講,C語言理論上也能完成這項工做,可是卻沒有這樣作的必要,由於這部分代碼無需可移植性,而且彙編比C代碼更可控,效率更高。
2.在上層功能上來講,C代碼可讀性優秀,比彙編代碼可理解性要好,並且可移植性也更好,若是要把本文代碼移植到另一個SoC上去,只需修改彙編部分,C基本無需修改。
3.彙編代碼中設置了棧,這部分是爲C代碼的運行準備好條件,由於C語言的代碼編譯後的局部變量,參數等都依賴棧纔可以工做。這也是和第一篇純彙編代碼有所區別的地方。