Chinaunix首页 | 论坛 | 博客
  • 博客访问: 830929
  • 博文数量: 281
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2770
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-02 19:45
个人简介

邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛

文章分类
文章存档

2020年(1)

2018年(1)

2017年(56)

2016年(72)

2015年(151)

分类: LINUX

2017-02-20 13:21:29

2. GPIO 硬件编程
    相比于 Linux 2.4, 2.6 及以上的内核可以使用系统中的 GPIOLIB 模块在用户空间提供
    的 sysfs 接口,实现应用层对 GPIO 的独立控制。
    
2.1 GPIO 和 sysfs 操作接口
    Linux开发平台实现了通用 GPIO的驱动,用户通过 Shell命令或系统调用即能控制 GPIO
    的输出和读取其输入值。其属性文件均在/sys/class/gpio 目录下,如:
    root@EasyARM-iMX283 /# ls /sys/class/gpio/
    export       gpiochip128  gpiochip64   unexport
    gpiochip0    gpiochip32   gpiochip96

    属性文件有 export 和 unexport。其余五个文件为符号链接( gpiochip0, gpiochip32,
    gpiochip64, gpiochip96, gpiochip128),指向管理对应设备的目录,以 gpiochip0 为例,
    此目录下文件有:
    root@EasyARM-iMX283 /# ls /sys/class/gpio/gpiochip0
    base       label      ngpio      power      subsystem  uevent
    
    以上文件用途如表 2.1 所示。
    表 2.1 gpio 目录下默认属性文件用途
    
    文件名         路径                                     作用
    ---------------------------------------------------------------
    export         /sys/class/gpio/export                     导出 GPIO
    unexport     /sys/class/gpio/unexport                 将导出的 GPIO 从 sysfs 中清除
    gpiochipN
                /sys/class/gpio/gpiochipN/base             设备所管理的 GPIO 初始编号
                /sys/class/gpio/gpiochipN/label         设备信息
                /sys/class/gpio/gpiochipN/ngpio         设备所管理的 GPIO 总数
                /sys/class/gpio/gpiochipN/power         设备供电方面的相关信息
                /sys/class/gpio/gpiochipN/subsystem     符号链接,指向父目录
                /sys/class/gpio/gpiochipN/uevent         内核与 udev(自动设备发现程序)之间的通信接口
    
    向 export 文件写入需要操作的 GPIO 排列序号 N,就可以导出对应的 GPIO 设备目录。
    操作命令如下:
    # echo N > /sys/class/gpio/export
    
    例如,导出序号为 68 的 GPIO 的操作接口,在 Shell 下,可以用如下命令:
    # echo 68 > /sys/class/gpio/export
    
    通过以上操作后在/sys/class/gpio 目录下生成 gpioN 目录, 通过读写该目录下的属性文
    件就可以操作这个 GPIO 的输入和输出。以此类推可以导出其它 GPIO 设备目录。如果 GPIO
    已经被系统占用,导出时候会提示资源占用。
    以排列序号为 68 的 GPIO 为例, 设备目录下有如下属性文件:
    #ls /sys/class/gpio/gpio68/
    active_low edge subsystem value direction power uevent
    
    各个文件用途如表 2.2 所示。
    表 2.2 GPIO 属性文件用途
    
    文件名             路径                                 作用
    -----------------------------------------------------------------------------------------------
    active_low         /sys/class/gpio/gpioN/active_low    具有读写属性。用于决定 value 中的值是否翻转。
                                                        0 不翻    转, 1 翻转。
    edge             /sys/class/gpio/gpioN/edge             具有读写属性。设置 GPIO 中断,或检测中断是否发生。
    subsystem         /sys/class/gpio/gpioN/subsystem     符号链接,指向父目录。
    value             /sys/class/gpio/gpioN/value         具有读写属性。 GPIO 的电平状态设置或读取。
    direction         /sys/class/gpio/gpioN/direction     具有读写属性。用于查看或设置 GPIO 输入输出
    power             /sys/class/gpio/gpioN/power         设备供电方面的相关信息
    uevent             /sys/class/gpio/gpioN/uevent         内核与 udev(自动设备发现程序)之间的通信接口
    
2.2 GPIO 基本操作
    在应用层我们可以通过 Shell 命令操作 GPIO。通过以下步骤,就可以控制 GPIO 输入输
    出。 下面步骤是以 GPIO 的输入输出功能进行介绍。
    
    1. 输入输出设置
        GPIO 导出后默认为输入功能。向 direction 文件写入“ in”字符串,表示设置为输入功能;
        向 direction 文件写入“ out”字符串,表示设置为输出功能。读 direction 文件,会返回
        in/out 字符串, in 表示当前 GPIO 作为输入, out 表示当前 GPIO 作为输出。
        方向查看和设置命令如下:
        # cat /sys/class/gpio/gpioN/direction                 #查看方向
        # echo out > /sys/class/gpio/gpioN/direction         #设置为输出
        # echo in > /sys/class/gpio/gpioN/direction         #设置为输入
        例如, 查看排列序号为 68 的 GPIO 的方向, 在 Shell 下, 可以用如下命令:
        # cat /sys/class/gpio/gpio68/direction
    
    2. 输入读取
        当 GPIO 被设为输入时, value 文件记录 GPIO 引脚的输入电平状态:
            1 表示输入的是高电平;
            0 表示输入的是低电平。
        通过查看 value 文件可以读取 GPIO 的电平,查看命令如下:
        # echo in > /sys/class/gpio/gpioN/direction     #设置 GPIO 排列序号为 N 的 GPIO 方向为输入
        # cat /sys/class/gpio/gpioN/value                 #查看 GPIO 排列序号为 N 的 GPIO 电平
        
        例如, 查看排列序号为 68 的 GPIO 的电平状态, 在 Shell 下, 可以用如下命令:
        # echo in > /sys/class/gpio/gpio68/direction
        # cat /sys/class/gpio/gpio68/value
    
    3. 输出控制
        当 GPIO 被设为输出时,通过向 value 文件写入 0 或 1( 0 表示输出低电平; 1 表示输出
        高电平)可以设置输出电平的状态,输出命名如下:
        # echo out > /sys/class/gpio/gpioN/direction     #设置 GPIO 排列序号为 N 的 GPIO 方向为输出
        # echo 0 > /sys/class/gpio/gpioN/value             #输出低电平
        # echo 1 > /sys/class/gpio/gpioN/value             #输出高电平
        
        例如, 设置排列序号为 68 的 GPIO 的电平为高电平, 在 Shell 下, 可以用如下命令:
        # echo out > /sys/class/gpio/gpio68/direction
        # echo 0 > /sys/class/gpio/gpio68/value
        
2.3 在 C 程序中操作 GPIO
    使用系统调用实现 GPIO 输入输出操作时, 首先需要使用 export 属性文件导出 GPIO,
    #define EXPORT_PATH "/sys/class/gpio/export" //GPIO 设备导出设备
    #define GPIO "68" //GPIO2_4
    int fd_export = open(EXPORT_PATH, O_RDWR); //打开 GPIO 设备导出设备
    ...
    write(fd_export, GPIO, strlen(GPIO)); //向 export 文件写入 GPIO 排列序号字符串
    
    可以调用 write 函数向 direction 设备写入方向 in/out 字符串,将 GPIO 设置为输入(输出), 如:
    #define DIRECT_PATH "/sys/class/gpio/gpio68/direction" //GPIO 输入输出控制设备
    int fd_dir, ret ;
    fd_dir = open(DIRECT_PATH, O_RDWR); //打开 GPIO 输入输出控制设备
    ...
    ret = write(fd_dir, direction, sizeof(direction)); //写入 GPIO 输入( in)输出( out)方向
    
    GPIO 设置为输入时使用 read 系统调用读取 value 属性文件,就可以读取 GPIO 电平值。
    GPIO 设置为输出时, 使用 write 系统调用向 value 属性文件写入 0 或 1 字符串,就可以设置
    GPIO 电平值。如:
    #define DEV_PATH "/sys/class/gpio/gpio68/value" //输入输出电平值设备
    int fd_dev, ret ;
    fd_dev = open(DEV_PATH, O_RDWR); //打开输入输出电平值设备
    ...
    ret = read(fd_dev, buf, sizeof(buf)); //读取 GPIO 输入电平值
    
2.4 EasyARM-i.MX283A GPIO 应用编程
    1. GPIO 资源汇总
        下面介绍在 EasyARM-i.MX283A 开发板上操作 GPIO 的示例。 EasyARM-i.MX283A 底
        板 GPIO 通过扩展接口 1/2 引出。
        EasyARM-i.MX283A 没有直接给出 GPIO 排列序号,需要通过计算得出。 其序号计算
        公式如下:
            GPIO排列序号 = BANK × 32 + N
        BANK 为 GPIO 引脚所在的 BANK, N 为引脚所在的 BANK 的序号。比如管脚:P2.4为例,
        BANK 值为 2,N 为 4。其排列序号为 2*32+4=68。
            
    2. Shell 命令操作范例
        按照通用 GPIO 操作方法,我们以 EasyARM-i.MX283A 排列序号为 68 的 GPIO 为例进
        行 GPIO 输出操作,步骤如下:
        导出对应的 GPIO 设备目录:
        root@EasyARM-iMX283x # cd /sys/class/gpio
        root@EasyARM-iMX283x /sys/class/gpio# echo 68 >export
        输出方向设置:
        root@EasyARM-iMX283x /sys/devices/virtual/gpio/gpio68# echo out >direction
        通过向 value 文件写入 0 或 1( 0 表示输出低电平; 1 表示输出高电平)可以设置输出电
        平的状态:
        root@EasyARM-iMX283x /sys/devices/virtual/gpio/gpio68# echo 0 >value
        root@EasyARM-iMX283x /sys/devices/virtual/gpio/gpio68# echo 1 >value    
        
    3. C 代码操作范例
    范例代码以 GPIO2_4 为例,实现 GPIO 的输入读取。首先通过 open()和 write()系统调用
    导出 GPIO,然后设置 GPIO 为输入,读取 GPIO 的输入值。操作范例如所程序清单 2.1所示。    
    
    程序清单 2.1 C 语言操作 GPIO 输入示例
    

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <fcntl.h>
  7. #include <termios.h>
  8. #include <errno.h>
  9. #include <string.h>

  10. #define DEV_PATH        "/sys/class/gpio/gpio68/value"        //输入输出电平值设备
  11. #define EXPORT_PATH     "/sys/class/gpio/export"            //GPIO 设备导出设备
  12. #define DIRECT_PATH     "/sys/class/gpio/gpio68/direction"    //GPIO 输入输出控制设备
  13. #define OUT             "out"
  14. #define IN                "in"
  15. #define GPIO            "68"        // GPIO2_4
  16. #define HIGH_LEVEL        "1"
  17. #define LOW_LEVEL        "0"

  18. int main(int argc, char ** argv)
  19. {
  20.     static int fd_dev, fd_export, fd_dir, ret;
  21.     char buf[10], direction[4];
  22.     fd_export = open(EXPORT_PATH, O_WRONLY); //打开 GPIO 设备导出设备
  23.     if(fd_export < 0) {
  24.         perror("open export:");
  25.         return -1;
  26.     }
  27.     // 相当于输入命令: echo 68 > /sys/class/gpio/export
  28.     // 导出GPIO68
  29.     write(fd_export, GPIO, strlen(GPIO));
  30.     
  31.     fd_dev = open(DEV_PATH, O_RDWR); //打开输入输出电平值设备
  32.     if(fd_dev < 0) {
  33.         perror("export write:");
  34.         return -1;
  35.     }
  36.     
  37.     fd_dir = open(DIRECT_PATH, O_RDWR); //打开 GPIO 输入输出控制设备
  38.     if(fd_dir < 0) {
  39.         perror("export write:");
  40.         return -1;
  41.     }
  42.     
  43.     // 读取默认的gpio输入输出方向
  44.     ret = read(fd_dir, direction, sizeof(direction)); //读取 GPIO2_4 输入输出方向
  45.     if(ret < 0) {
  46.         perror("dir read:");
  47.         close(fd_export);
  48.         close(fd_dir);
  49.         close(fd_dev);
  50.         return -1;
  51.     }    
  52.     printf("default directions: %s", direction);
  53.     
  54.     // 设置管脚的方向:输入引脚
  55.     strcpy(buf, IN);
  56.     ret = write(fd_dir, buf, strlen(IN));
  57.     if(ret < 0) {
  58.         perror("dir read:");
  59.         close(fd_export);
  60.         close(fd_dir);
  61.         close(fd_dev);
  62.         return -1;
  63.     }
  64.     // 读取管脚输入输出方向,看是否设置成功
  65.     ret = read(fd_dir, direction, sizeof(direction));
  66.     if(ret < 0) {
  67.         perror("dir read:");
  68.         close(fd_export);
  69.         close(fd_dir);
  70.         close(fd_dev);
  71.         return -1;
  72.     }
  73.     
  74.     // 读取管脚的电平值
  75.     ret = read(fd_dev, buf, sizeof(buf)); //读取 GPIO2_4 输入电平值
  76.     if(ret < 0) {
  77.         perror("dir read:");
  78.         close(fd_export);
  79.         close(fd_dir);
  80.         close(fd_dev);
  81.         return -1;
  82.     }
  83.     printf("now directions:%sinput level:%s", direction, buf);
  84.     close(fd_export);
  85.     close(fd_dir);
  86.     close(fd_dev);
  87.     return 0;
  88. }
    编译目标代码:
    root@ubuntu:~# arm-fsl-linux-gnueabi-gcc test.c -o gpio_test
    将编译好的代码下载到开发板,运行代码前,先使用杜邦线将开发板 GND 或 Vcc 接入
    GPIO2_4,然后运行代码,代码会在串口打印当前输入电平值。
    
    测试:
    (1) GPIO2_4接上高电平:
        root@EasyARM-iMX283 /mnt/app_for_hardware/2nd_gpio# ./gpio
        default directions: in
        now directions:in
        input level:1
    (2) GPIO2_4接上低电平:
        root@EasyARM-iMX283 /mnt/app_for_hardware/2nd_gpio# ./gpio
        default directions: in
        now directions:in
        input level:0


阅读(1307) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~