• 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)。
    配置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。
    1 #define GPFCON (*(volatile unsigned long *) 0x56000050)
    2 #define GPFDAT (*(volatile unsigned long *) 0x56000054)
    3 #define GPF4_out    (1 << 4*2)
    4 GPFCON = GPF4_out;    //GPF4引脚设为输出
    5 GPFDAT &= ~(1 << 4);    //GPF4输出低电平
    GPF4 配置
        实例分为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
    led_on.S
        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
        注意: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
    crt0.S
        上面设置堆栈指针后,就可以调用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 }
    led_on_c.c
        最后来看看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
    Makefile
        操作步骤如下:
        (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 }
    key_led_on.c
        操作步骤如下:
        (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
  • 相关阅读:
    element-ui 刷新页面不能自动打开对应的菜单
    cookie
    cdn
    为已有文件添加 d.ts 声明
    WiFi 漫游过程
    Wifi 4 way handshake 四次握手
    WiFi association request/response
    WiFi beacon
    WiFi Auth/Deauth帧
    WiFi probe request/response
  • 原文地址:https://www.cnblogs.com/sz189981/p/7709579.html
Copyright © 2020-2023  润新知