JZ2440 第5章 GPIO接口

本章目標:

    掌握嵌入式開發的步驟:編程、編譯、燒寫程序、運行
    通過GPIO的操作了解軟件如何控制硬件

5.1 GPIO硬件介紹

    S3C2440A有130個多功能輸入/輸出口引腳,分爲A~J共9組:GPA、GPB、...、GPH、GPJ。

5.1.1 管腳相關的寄存器

    對於這幾組GPIO引腳,它們的寄存器是相似的:
① GPxCON:用於選擇管腳功能;
    x爲A、B、...、H、J 
    PORTA與PORTB~PORTJ在功能選擇上有所不同,GPACON中每一位對應一根引腳(共23根引腳)。
    當某位被設爲0時,對應的引腳爲輸出引腳;
    當某位被設爲1時,對應的引腳爲地址線或用於地址控制,此時GPADAT無用。
    一般而言,GAPCON通常被設爲全1,以方便訪問外部存儲器件。本章不使用PORTA。
    PORTB~PORTJ在寄存器操作方面完全相同。GPxCON中每兩位控制一根引腳。
00:表示輸入、01:表示輸出、10:表示特殊功能、11:表示保留不用。
② GPxDATA:用於讀寫管腳數據;
    x爲A、B、...、H、J
    當引腳被設爲輸入時,讀此寄存器可知對應引腳的電平狀態是高還是低;
    當引腳被設爲輸出時,寫此寄存器對應位,可令此引腳輸出高電平或低電平。
③ GPxUP    :用於確定是否使用內部上拉電阻。
    x爲B、...、H、J,沒有GPAUP寄存器。
    某位爲1時,相應引腳無內部上拉電阻;某位爲0時,相應引腳使用內部上拉電阻

5.1.2 怎麼使用軟件來訪問硬件

1.訪問單個引腳
    操作種類:輸出高/低電平、檢測引腳狀態、中斷。
    如下圖所示,JZ2440原理圖中LED和按鍵的連接。
  

     可以設置GPFCON寄存器將GPF4、GPF5、GPF6設爲輸出功能,然後寫GPFDAT寄存器的
相應位使得這3個引腳輸出高電平或低電平。
    還可以設置GPFCON寄存器將GPF0、GPF2、GPG3、GPG11設置爲輸入功能,然後通過讀
出GPFDAT/GPGDAT寄存器並判斷相應位電平狀態來確定各個按鍵是否按下。
    訪問寄存器的方法:通過軟件,讀寫它們的地址。
    比如,S3C2440的GPBCON、GPBDAT寄存器地址是0x5600 0010、0x56000014,可以通
過如下指令讓GPF4輸出低電平,點亮LED(D10)。
#define GPFCON (*(volatile unsigned long *) 0x56000050)
#define GPFDAT (*(volatile unsigned long *) 0x56000054)
#define GPF4_out    (1 << 4*2)
GPFCON = GPF4_out;      //GPF4引腳設爲輸出
GPFDAT &= ~(1 << 4);    //GPF4輸出低電平

5.2 GPIO操作實例:LED和按鍵

5.2.1 硬件設計

    如上圖所示。

5.2.2 程序設計及代碼詳解

     本小節有3個實例,通過讀寫GPIO寄存器來驅動LED、獲取按鍵狀態。先使用匯編程序編寫
一個簡單的點亮LED的程序,然後使用C語言實現了更復雜的功能。
    1.實例1:使用匯編代碼點亮1個LED
    源程序爲/work/hardware/led_on/led_on.S。它只有7條指令,簡單地點亮LED。
    實例分爲4個步驟:編譯源程序、生成可執行程序、燒寫程序、運行程序。
    先看看源程序:led_on.S: 
1
.text
2
.global _start
3
_start:
4
    LDR R0,=0x56000050     @R0設爲GPFCON寄存器
5
    MOV R1, #0x00000100    @0b 01 0000 0000
6
    STR R1,[R0]            @設置GPF4爲輸出口,位[9:8] = 01
7
    LDR R0,=0x56000054     @R0設置GPFDAT寄存器
8
    MOV R1, #0x0000000     @此值改爲0x00000010(0001 0000)可以讓LED全熄滅
9
    STR R1,[R0]            @GPF4輸出0,點亮LED
10
MAIN_LOOP:
11
    B MAIN_LOOP
    Makefile內容如下:
1
led_on.bin:led_on.S                                        @make指令比較led_on.bin和led_on.S的時間,決定是否執行下面的命令
2
    arm-linux-gcc -g -c -o led_on.o led_on.S               @編譯
3
    arm-linux-ld -Ttext 0x0000 -g led_on.o -o led_on_elf   @鏈接
4
    arm-linux-objcopy -O binary -S led_on_elf led_on.bin   @把ELF格式的可執行文件led_on_elf轉換成二進制格式文件led_on.bin
5
clean:
6
    rm -f led_on.bin led_on_elf *.o
    注意:Makefile文件中相應的命令行前一定要有一個製表符
    2.實例2:使用C語言代碼點亮1個LED
    源程序爲/work/hardware/led_on_c目錄下。
    C語言執行的第一條指令並不在main函數中。生成一個C程序的可執行文件時,編譯器通常會在我們
的代碼中加上幾個被稱爲啓動文件的代碼——crtl.o、crti.o、crtend.o、crtn.o等,它們是標準庫文件。
這些代碼設置C程序的堆棧等,然後調用main函數。它們依賴於操作系統,在裸板上這些代碼無法執行,
需要自己寫一個。
    這段代碼很簡單,只有6條指令。自己編寫的crt0.S文件內容如下:
1
@************************************
2
@File:crt0.S
3
@功能:通過它轉入C程序
4
@************************************
5
.text
6
.global _start
7
_start:
8
    ldr r0, =0x56000010    @WATCHDOG寄存器地址
9
    mov r1, #0x0
10
    str r1, [r0]           @寫入0,禁止WATCHDOG
11
    
12
    ldr sp, =1024*4        @設置堆棧,注意不能大於4k,因爲現在可用內存只有4kB
13
                           @NAND Flash中的代碼在復位後會移到內部ram(只有4kB)
14
    bl main
15
halt_loop:
16
    b  halt_loop
    上面設置堆棧指針後,就可以調用C函數main了。C函數執行前,必須設置棧。
    現在可以很容易寫出控制LED的程序了。main函數在led_on_c.c文件中,代碼如下:
1
#define GPFCON (*(volatile unsigned long *) 0x56000050)
2
#define GPFDAT (*(volatile unsigned long *) 0x56000054)
3
#define GPF4_out    (1 << 4*2)
4
5
int main()
6
{
7
    GPFCON = GPF4_out;    //GPF4引腳設爲輸出
8
    GPFDAT &= ~(1 << 4);    //GPF4輸出低電平
9
    
10
    return 0;
11
}
    最後來看看Makefile:
1
led_on_c.bin:crt0.S led_on_c.c
2
    arm-linux-gcc -g -c -o crt0.o crt0.S
3
    arm-linux-gcc -g -c -o led_on_c.o led_on_c.c
4
    arm-linux-ld -Ttext 0x0000000 -g crt0.o led_on_c.o -o led_on_c_elf
5
    arm-linux-objcopy -O binary -S led_on_c_elf led_on_c.bin
6
    arm-linux-objdump -D -m arm led_on_c_elf > led_on_c.dis
7
clean:
8
    rm -f led_on_c.dis led_on_c.bin led_on_c_elf *.o
    操作步驟如下:
    (1)進入led_on_c目錄後,執行如下命令可生成可執行文件led_on_c.bin;
    $make
    (2)使用dnw把led_on_c.bin寫入NAND Flash;
    (3)把開發板撥爲NAND啓動,給開發板上電,可看見LED被點亮。
2.實例3:使用按鍵來控制LED 
    目錄/work/hardware/key_led中的程序功能爲:當K1~K3中某個按鍵被按下時,點亮D10~D12
(v2版電路板只有3個燈)中相應的LED。
    key_led.c代碼如下:
1
#define GPFCON      (*(volatile unsigned long *)0x56000050)
2
#define GPFDAT      (*(volatile unsigned long *)0x56000054)
3
4
#define GPGCON      (*(volatile unsigned long *)0x56000060)
5
#define GPGDAT      (*(volatile unsigned long *)0x56000064)
6
7
/*
8
 * LED1,LED2,LED4對應GPF4、GPF5、GPF6
9
 */
10
#define GPF4_out    (1<<(4*2))
11
#define GPF5_out    (1<<(5*2))
12
#define GPF6_out    (1<<(6*2))
13
14
#define GPF4_msk    (3<<(4*2))
15
#define GPF5_msk    (3<<(5*2))
16
#define GPF6_msk    (3<<(6*2))
17
18
/*
19
 * S2,S3,S4對應GPF0、GPF2、GPG3
20
 */
21
#define GPF0_in     (0<<(0*2))
22
#define GPF2_in     (0<<(2*2))
23
#define GPG3_in     (0<<(3*2))
24
25
#define GPF0_msk    (3<<(0*2))
26
#define GPF2_msk    (3<<(2*2))
27
#define GPG3_msk    (3<<(3*2))
28
29
int main()
30
{
31
        unsigned long dwDat;
32
        // LED1,LED2,LED4對應的3根引腳設爲輸出
33
        GPFCON &= ~(GPF4_msk | GPF5_msk | GPF6_msk);
34
        GPFCON |= GPF4_out | GPF5_out | GPF6_out;
35
        
36
        // S2,S3對應的2根引腳設爲輸入
37
        GPFCON &= ~(GPF0_msk | GPF2_msk);
38
        GPFCON |= GPF0_in | GPF2_in;
39
40
        // S4對應的引腳設爲輸入
41
        GPGCON &= ~GPG3_msk;
42
        GPGCON |= GPG3_in;
43
44
        while(1){
45
            //若Kn爲0(表示按下),則令LEDn爲0(表示點亮)
46
            dwDat = GPFDAT;             // 讀取GPF管腳電平狀態
47
        
48
            if (dwDat & (1<<0))        // S2沒有按下
49
                GPFDAT |= (1<<4);       // LED1熄滅
50
            else    
51
                GPFDAT &= ~(1<<4);      // LED1點亮
52
                
53
            if (dwDat & (1<<2))         // S3沒有按下
54
                GPFDAT |= (1<<5);       // LED2熄滅
55
            else    
56
                GPFDAT &= ~(1<<5);      // LED2點亮
57
    
58
            dwDat = GPGDAT;             // 讀取GPG管腳電平狀態
59
            
60
            if (dwDat & (1<<3))         // S4沒有按下
61
                GPFDAT |= (1<<6);       // LED3熄滅
62
            else    
63
                GPFDAT &= ~(1<<6);      // LED3點亮
64
    }
65
66
    return 0;
67
}
    操作步驟如下:
    (1)進入key_led目錄後,執行make命令,即可生成可執行文件key_led.bin;
    (2)使用dnw把key_led.bin寫入NAND Flash;
    (3)把開發板撥爲NAND啓動,給開發板上電,可看見LED被點亮。

5.2.3 實例測試

    在燒寫程序是要燒到NAND Flash中去的原因:2440中有被稱爲“Steppingstone”的
4KB內部RAM,當選擇從NAND Flash啓動CPU時,CPU會通過內部的硬件將NAND 
Flash開始的4KB字節數據複製到這4KB的內部RAM中(此時,內部RAM的起始地址爲0),
然後跳到地址0開始執行。
    NOR Flash雖然可以像內存一樣進行讀操作,但不能像內存一樣進行寫操作,所以從
NOR Flash啓動時,一般先在代碼的開始部分使用匯編指令初始化外接的內存器件(外存),
然後將代碼複製到外存中,最後跳到外存中繼續執行。
    對於小程序,一般將它燒入NAND Flash中,藉助CPU內部RAM直接運行。
附:代碼:
鏈接: https://pan.baidu.com/s/1kV24a9L 密碼: tfab
發佈了20 篇原創文章 · 獲贊 35 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章