1. 程式人生 > >嵌入式Linux應用開發完全手冊(二)GPIO

嵌入式Linux應用開發完全手冊(二)GPIO

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的哪個介面呢

Markdown

從上圖可以看到,這三條線分別連線到了2440的GPF4, GPF5,GPF6。
這樣的話,通過設定這三個埠到輸出埠,如果是高電平,LED燈沒有電勢差,不會亮;如果輸出0,那麼有電勢差,LED燈會亮。
* 點亮LED燈的步驟如下 *
- 設定GPIO F組的控制暫存器,GPF4到GPF6為輸出埠
- 控制GPF4到GPF6
- 1 高電平 LED不亮
- 0 低電平 LED亮

Markdown

從上圖查到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;
}