嵌入式Linux應用開發完全手冊(二)GPIO
阿新 • • 發佈:2018-11-12
5 GPIO介面
5.1 GPIO硬體介面介紹
- GPIO General Purpose I/O Port,通用輸入、輸出埠。簡單說就是這個埠可以配成輸入的(讀電平訊號),也可以配成輸出的(設定電平訊號)
- 無論是輸入還是輸出都是通過暫存器來實現的
- 輸入 通過讀某個暫存器來確定引腳電平是高還是低,是1還是0
- 輸出 通過寫入某個暫存器讓這個引腳輸出高電平或者低電平,1或者0
- 具體的暫存器設定需要看硬體手冊,這裡以2440為例
- GPxCON暫存器
- GPxDAT暫存器
- GPxUP暫存器
以上是GPACON GPADAT暫存器的手冊說明。
從中可以看到A組GPIO的控制和資料暫存器的地址,編碼格式。
- GPACON和GPADAT都是4位元組,32位的暫存器,地址分別是0x56000000 0x56000004
- GPACON的有效配置位是0到24,分別對應GPA0到GPA24,每個埠一位
- GPACON的每一個埠,配置成0表示用作output埠,配置為1代表不同的控制訊號,用作各種專門的用途
- GPADAT的0到24位,對應GPA0到24,用作output埠時,管腳的電平根據暫存器的設定來輸出;用作其他用途的時候根據各自的用途,晶片自動設定
- GP A組的管腳作用特殊,我們可以看下B組的控制暫存器格式,B組是通用的
這是B組的GPIO暫存器格式,用3個暫存器控制。GPBCON的控制配置,GPBDAT的資料配置,GPBUP的狀態配置。 - 與A組不同,GPBCON每2位控制一個GPIO管腳,可以有4種工作方式
- 00 輸入
- 01 輸出
- 10 特殊功能
- 11 保留
- GPBDAT
- 對應管腳是輸入管腳的時候,通過對應的暫存器位判斷輸入訊號
- 對應管腳是輸出管腳的時候,通過設定對應的暫存器位,輸出訊號
- GPBUP
- 0 對應的管腳使用上拉電阻
- 1 對應管腳不使用上拉電阻
- 關於上拉和下拉,指的是管腳懸空的時候,保持高電平狀態還是低電平狀態,可以簡單的這裡理解
- 暫存器的操作 *
- 暫存器的地址和格式定義都已經瞭解了,下面就是怎麼操作
- 暫存器地址型別轉換為 volatile unsigned long 指標
- 對這個指標進行間接定址操作,位操作,清零,置1
#define GPBCON (*(volatile unsigned long *)0x56000010)
#define GPBDAT (*(volatile unsigned long *)0x56000014)
#define GPB5_out (1 << (5 * 2))
GPBCON |= GPB5_out; // GPB5管腳設定為輸出管腳,其他部分不變
GPBDAT &= ~(1 << 5); // GPB5輸出低電平
5.2 例項 點亮LED
彙編實現
- 先看電路圖,找到LED的連線
能看到一共有3個LED燈,電源3.3V,分別連線了連線nLED1, nLED2, nLED4
那麼著條線分別連線到了2440的哪個介面呢
從上圖可以看到,這三條線分別連線到了2440的GPF4, GPF5,GPF6。
這樣的話,通過設定這三個埠到輸出埠,如果是高電平,LED燈沒有電勢差,不會亮;如果輸出0,那麼有電勢差,LED燈會亮。
* 點亮LED燈的步驟如下 *
- 設定GPIO F組的控制暫存器,GPF4到GPF6為輸出埠
- 控制GPF4到GPF6
- 1 高電平 LED不亮
- 0 低電平 LED亮
從上圖查到GPF的暫存器地址。
[led_on.s]
.text
.global _start
_start:
LDR R0,=0x56000050 @ R0設為GPBCON暫存器,選擇F組GPIO引腳功能
MOV R1,#0x00000500
STR R1,[R0] @ 這3個指令是存入某個暫存器一個給定值的套路
LDR R0,=0x56000054
MOV R1,#0x00000000
STR R1,[R0] @ GPF4,5輸出0,點亮LED1,2 保持LED3不亮
MAIN_LOOP: @ 程式碼部分注意不要在中文輸入模式,例如這句的冒號,如果是中文冒號,會顯示“無效指令 main_loop:”
B MAIN_LOOP
[Makefile]
led_on.bin : led_on.s
arm-linux-gcc -c -o led_on.o led_on.s
arm-linux-ld -Ttext 0x00000000 -o led_on_elf led_on.o
arm-linux-objcopy -O binary -S led_on_elf led_on.bin
clean :
rm *.o led_on_elf led_on.bin
生成的bin檔案燒入開發板的0地址,即可觀察結果。
C語言實現
- C語言應用程式的入口是main,但是在基於作業系統的C語言程式中,呼叫main是作業系統完成的。在呼叫main之前還呼叫了crtl.o crti.o crtend.o ctrn.o這幾個啟動檔案。裸板程式無法依賴這些啟動檔案,因此需要自己編寫啟動檔案,啟動之後再呼叫main函式。 *
- 編寫啟動程式碼
[crt0.s]
.text
.global _start
_start:
ldr r0,=0x53000000 @ 第一步關閉看門狗
mov r1,#0
str r1,[r0]
ldr sp,=1024*4 @ 設定堆疊
bl main @ 呼叫C語言程式main函式
halt_loop:
b halt_loop
- 編寫C語言程式
[led_on_c.c]
#define GPFCON (*(volatile unsigned long *)0x56000050)
#define GPFDAT (*(volatile unsigned long *)0x56000054)
#define GPF_OUT(x) (1 << (2 * (x)))
#define GPF_SET(x) (1 << (x))
int main()
{
/* 設定GPF4 5 6為輸出埠,GPF4 GPF6為高電平,LED2亮,LED1和3不亮 */
GPFCON = GPF_OUT(4) | GPF_OUT(5) | GPF_OUT(6);
GPFDAT = GPF_SET(4) | GPF_SET(6);
return 0;
}
- 編寫Makefile
[Makefile]
led_on_c.bin : led_on_c.c crt0.s
arm-linux-gcc -c led_on_c.c -o led_on_c.o
arm-linux-gcc -c crt0.s -o crt0.o
arm-linux-ld -Ttext 0x00000000 crt0.o led_on_c.o -o led_on_c_elf
arm-linux-objcopy -O binary -S led_on_c_elf led_on_c.bin
clean :
rm *.o led_on_c_elf led_on_c.bin
- 擴充套件,讓LED燈按照2進位制計數器的方式閃爍
#define GPFCON (*(volatile unsigned long *)0x56000050)
#define GPFDAT (*(volatile unsigned long *)0x56000054)
#define GPF_OUT(x) (1 << (2 * (x)))
#define LED_NUM(x) (~(((x) & ~((~0) << 3)) << 4))
int main()
{
unsigned int i;
unsigned long j;
/* led to count, in binary */
GPFCON = GPF_OUT(4) | GPF_OUT(5) | GPF_OUT(6);
for (i = 0;;i = ++i % 8)
{
GPFDAT = LED_NUM(i);
for (j = 0; j < 1000000; j++)
;
}
return 0;
}