自己學驅動11——簡單GPIO操作
阿新 • • 發佈:2018-12-29
1.對於GPIO的操作
對於GPIO的操作,通常是通過讀寫其相應的暫存器來實現的,S3C2440也是如此。比如,S3C2440的GPBCON和GPBDAT暫存器的地址分別是0x56000010和0x56000014,可以通過如下的指令讓GPB5輸出低電平。
#define GPBCON (*(volatile unsigned long *)0x56000010)
#define GPBDAT (*(volatile unsigned long *)0x56000014)
GPBCON = (1<<(5*2)); //GPB5設定為輸出
GPBDAT &= ~(1<<5); //GPB5輸出低電平
對於暫存器的讀與此類此,不舉例說明。
2.NOR Flash操作注意點
假設開發板上NOR Flash的片選訊號使用2440的nGCS0,當CPU發出的地址訊號處於0x00000000~0x07ffffff之間時,nGCS0訊號有效,NOR Flash被選中。需要注意的是NOR Flash在實際使用中多數以16位為單位讀寫,即2440的ADDR1~ADDR20與NOR Flash的A0~A19對應相連。
注意以下幾種操作:
(1)地址對齊的16位讀操作。
unsigned short pwAddr = (unsigned short *)0x2;
unsigned short wVal;
wVal = *pwAddr;
這段程式碼2440將0x2傳送給NOR Flash時會讀取到NOR Flash中的0x1處16位的資料(NOR Flash設定為word模式,每個地址對應的資料為16位),所以當2440要讀取0x0~0x1均讀取的是NOR Flash中0x0的資料,而當2440要讀取0x2~0x3的資料時實際讀取的是NOR Flash中0x1地址的資料。
(2)地址不對齊的16位讀操作。
當2440傳入的地址為0x1類似的地址時,由於地址不是按2對齊的,所以會導致異常。一般可以通過設定異常處理函式來處理這種情況,在異常函式中,使用0x0和0x2發起兩次讀操作,然後將兩個結果各取一位元組組合起來得到需要的資料。
(3)8位讀操作。
unsigned char pwAddr = (unsigned char *)0x2;
unsigned char wVal;
wVal = *pwAddr;
CPU讀取資料之後會自動丟棄D1,假設讀到的資料為D0和D1。
(4)32位讀操作。
unsigned int pwAddr = (unsigned int *)0x2;
unsigned int wVal;
wVal = *pwAddr;
CPU會首先使用地址0x2對NOR Flash發起一次讀操作,然後使用0x4對NOR Flash發起一次讀操作,最後將兩次讀取到的資料合併並且賦值給wVal,這個過程完全由處理器硬體機制完成對於程式設計師來說是不可見的。
(5)16位寫操作。
由於NOR Flash的特性,使得對NOR Flash的寫操作比較複雜——比如要先發出特定的地址訊號通知NOR Flash準備接收資料,然後才能發出資料等。不過,其總線上的電訊號與軟體指令的關係與讀操作類似,只是資料的傳輸方向相反。
unsigned short *pwAddr = (unsigned shrot *)0x6;
*pwAddr = 0x1234;
3.最簡單的點燈程式(程式均燒寫到Nand Flash中)
(1)彙編版
led.S原始碼:
.text
.global _start
_start:
LDR R0, =0x56000010
MOV R1, #0x00000400
STR R1, [R0]
LDR R0, =0x56000014
MOV R1, #0x00000000
STR R1, [R0]
MAIN_LOOP:
B MAIN_LOOP
可以看出,程式中所做的工作就是將GPB5設定為輸出,然後再輸出0,最後是一個死迴圈。
以上彙編程式碼對應的Makefile(編譯連線等命令放於其中)為:
led.bin:led.S
arm-linux-gcc -g -c -o led.o led.S
arm-linux-ld -Ttext 0x0000000 -g led.o -o led_elf
arm-linux-objcopy -O binary -S led_elf led.bin
clean:
rm -f led.bin led_elf *.o
注:其中arm-linux-gcc指令中的-g選項表示生成本地除錯資訊,而-c選項表示只完成預處理、編譯和彙編,不做連線。
(2)C語言版
C語言版的LED操作需要從彙編程式碼跳轉到C語言中去完成,首先是一段必要的彙編程式碼完成關閉看門狗、設定堆疊以及跳轉到C入口程式碼等操作。對應的彙編程式碼原始檔內容如下(crt0.S):
.text
.global _start
_start:
ldr r0, =0x560000XX @0x560000XX代表具體的看門狗暫存器地址
mov r1, #0x0
str r1, [r0] @禁止看門狗
ldr sp, =1024*4 @設定堆疊,不能大於4KB
bl main @呼叫C程式中的main函式
halt_loop:
b halt_loop
而還有一個相應的C語言原始碼檔案如下(led.c):
#define GPBCON (*(volatile unsigned long *)0x56000010)
#define GPBDAT (*(volatile unsigned long *)0x56000014)
int main()
{
GPBCON = 0x00000400;
GPBDAT = 0x00000000;
return 0;
}
這兩段程式碼所對應的Makefile檔案內容為:
led.bin:crt0.S led.c
arm-linux-gcc -g -c -o crt0.o crt0.S
arm-linux-gcc -g -c -o led.o led.c
arm-linux-ld -Ttext 0x0000000 -g crt0.o led.o -o led_elf
arm-linux-objcopy -O binary -S led_elf led.bin
arm-linux-objdump -D -m arm led_elf > led.dis
clean:
rm -f led.dis led.bin led_elf *.o
注意:arm-linux-objdump指令將結果轉換為彙編程式碼以供檢視。2440的OM1和OM0引腳用於設定啟動位置。NOR Flash可以像記憶體一樣進行讀操作,卻不可以像記憶體一樣進行寫操作,所以從NOR Flash啟動時,一般先在程式碼的開始部分使用匯編指令初始化外接的記憶體,然後將程式碼複製到外存中,最後跳轉到外存中繼續執行。NAND Flash中前4KB的內容會自動的被複制到2440內部RAM中,所以小實驗程式可以藉助此性質比較方便的完成。
對於GPIO的操作,通常是通過讀寫其相應的暫存器來實現的,S3C2440也是如此。比如,S3C2440的GPBCON和GPBDAT暫存器的地址分別是0x56000010和0x56000014,可以通過如下的指令讓GPB5輸出低電平。
#define GPBCON (*(volatile unsigned long *)0x56000010)
#define GPBDAT (*(volatile unsigned long *)0x56000014)
GPBCON = (1<<(5*2)); //GPB5設定為輸出
GPBDAT &= ~(1<<5); //GPB5輸出低電平
對於暫存器的讀與此類此,不舉例說明。
2.NOR Flash操作注意點
假設開發板上NOR Flash的片選訊號使用2440的nGCS0,當CPU發出的地址訊號處於0x00000000~0x07ffffff之間時,nGCS0訊號有效,NOR Flash被選中。需要注意的是NOR Flash在實際使用中多數以16位為單位讀寫,即2440的ADDR1~ADDR20與NOR Flash的A0~A19對應相連。
注意以下幾種操作:
(1)地址對齊的16位讀操作。
unsigned short pwAddr = (unsigned short *)0x2;
unsigned short wVal;
wVal = *pwAddr;
這段程式碼2440將0x2傳送給NOR Flash時會讀取到NOR Flash中的0x1處16位的資料(NOR Flash設定為word模式,每個地址對應的資料為16位),所以當2440要讀取0x0~0x1均讀取的是NOR Flash中0x0的資料,而當2440要讀取0x2~0x3的資料時實際讀取的是NOR Flash中0x1地址的資料。
(2)地址不對齊的16位讀操作。
當2440傳入的地址為0x1類似的地址時,由於地址不是按2對齊的,所以會導致異常。一般可以通過設定異常處理函式來處理這種情況,在異常函式中,使用0x0和0x2發起兩次讀操作,然後將兩個結果各取一位元組組合起來得到需要的資料。
(3)8位讀操作。
unsigned char pwAddr = (unsigned char *)0x2;
unsigned char wVal;
wVal = *pwAddr;
CPU讀取資料之後會自動丟棄D1,假設讀到的資料為D0和D1。
(4)32位讀操作。
unsigned int pwAddr = (unsigned int *)0x2;
unsigned int wVal;
wVal = *pwAddr;
CPU會首先使用地址0x2對NOR Flash發起一次讀操作,然後使用0x4對NOR Flash發起一次讀操作,最後將兩次讀取到的資料合併並且賦值給wVal,這個過程完全由處理器硬體機制完成對於程式設計師來說是不可見的。
(5)16位寫操作。
由於NOR Flash的特性,使得對NOR Flash的寫操作比較複雜——比如要先發出特定的地址訊號通知NOR Flash準備接收資料,然後才能發出資料等。不過,其總線上的電訊號與軟體指令的關係與讀操作類似,只是資料的傳輸方向相反。
unsigned short *pwAddr = (unsigned shrot *)0x6;
*pwAddr = 0x1234;
3.最簡單的點燈程式(程式均燒寫到Nand Flash中)
(1)彙編版
led.S原始碼:
.text
.global _start
_start:
LDR R0, =0x56000010
MOV R1, #0x00000400
STR R1, [R0]
LDR R0, =0x56000014
MOV R1, #0x00000000
STR R1, [R0]
MAIN_LOOP:
B MAIN_LOOP
可以看出,程式中所做的工作就是將GPB5設定為輸出,然後再輸出0,最後是一個死迴圈。
以上彙編程式碼對應的Makefile(編譯連線等命令放於其中)為:
led.bin:led.S
arm-linux-gcc -g -c -o led.o led.S
arm-linux-ld -Ttext 0x0000000 -g led.o -o led_elf
arm-linux-objcopy -O binary -S led_elf led.bin
clean:
rm -f led.bin led_elf *.o
注:其中arm-linux-gcc指令中的-g選項表示生成本地除錯資訊,而-c選項表示只完成預處理、編譯和彙編,不做連線。
(2)C語言版
C語言版的LED操作需要從彙編程式碼跳轉到C語言中去完成,首先是一段必要的彙編程式碼完成關閉看門狗、設定堆疊以及跳轉到C入口程式碼等操作。對應的彙編程式碼原始檔內容如下(crt0.S):
.text
.global _start
_start:
ldr r0, =0x560000XX @0x560000XX代表具體的看門狗暫存器地址
mov r1, #0x0
str r1, [r0] @禁止看門狗
ldr sp, =1024*4 @設定堆疊,不能大於4KB
bl main @呼叫C程式中的main函式
halt_loop:
b halt_loop
而還有一個相應的C語言原始碼檔案如下(led.c):
#define GPBCON (*(volatile unsigned long *)0x56000010)
#define GPBDAT (*(volatile unsigned long *)0x56000014)
int main()
{
GPBCON = 0x00000400;
GPBDAT = 0x00000000;
return 0;
}
這兩段程式碼所對應的Makefile檔案內容為:
led.bin:crt0.S led.c
arm-linux-gcc -g -c -o crt0.o crt0.S
arm-linux-gcc -g -c -o led.o led.c
arm-linux-ld -Ttext 0x0000000 -g crt0.o led.o -o led_elf
arm-linux-objcopy -O binary -S led_elf led.bin
arm-linux-objdump -D -m arm led_elf > led.dis
clean:
rm -f led.dis led.bin led_elf *.o
注意:arm-linux-objdump指令將結果轉換為彙編程式碼以供檢視。2440的OM1和OM0引腳用於設定啟動位置。NOR Flash可以像記憶體一樣進行讀操作,卻不可以像記憶體一樣進行寫操作,所以從NOR Flash啟動時,一般先在程式碼的開始部分使用匯編指令初始化外接的記憶體,然後將程式碼複製到外存中,最後跳轉到外存中繼續執行。NAND Flash中前4KB的內容會自動的被複制到2440內部RAM中,所以小實驗程式可以藉助此性質比較方便的完成。