OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 4713|回复: 0

《I.MX6U嵌入式Linux C应用编程指南 V1.1》第十七章 GPIO应用编程

[复制链接]

1118

主题

1129

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4672
金钱
4672
注册时间
2019-5-8
在线时间
1224 小时
发表于 2021-8-24 15:48:56 | 显示全部楼层 |阅读模式
1)实验平台:正点原子阿尔法Linux开发板
2)  章节摘自【正点原子】《I.MX6U嵌入式Linux C应用编程指南 V1.1》

3)购买链接:https://detail.tmall.com/item.htm?id=609033604451
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/arm-linux/zdyz-i.mx6ull.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子阿尔法Linux交流群:1027879335 QQ群.png

原子哥.jpg

微信公众号.png




第十七章 GPIO应用编程

本章介绍应用层如何控制GPIO,譬如控制GPIO输出高电平、或输出低电平。

1.1应用层如何操控GPIO
与LED设备一样,GPIO同样也是通过sysfs方式进行操控,进入到/sys/class/gpio目录下,如下所示:
第十七章 GPIO应用编程122.png
图 17.1.1 /sys/class/gpio目录
可以看到该目录下包含两个文件export、unexport以及5个gpiochipX(X等于0、32、64、96、128)命名的文件夹。
gpiochipX:当前SoC所包含的GPIO控制器,我们知道I.MX6UL/I.MX6ULL一共包含了5个GPIO控制器,分别为GPIO1、GPIO2、GPIO3、GPIO4、GPIO5,在这里分别对应gpiochip0、gpiochip32、gpiochip64、gpiochip96、gpiochip128这5个文件夹,每一个gpiochipX文件夹用来管理一组GPIO。随便进入到其中某个目录下,可以看到这些目录下包含了如下文件:
第十七章 GPIO应用编程483.png
图 17.1.2 gpiochip0目录下的文件
在这个目录我们主要关注的是base、label、ngpio这三个属性文件,这三个属性文件均是只读、不可写。
base:与gpiochipX中的X相同,表示该控制器所管理的这组GPIO引脚中最小的编号。每一个GPIO引脚都会有一个对应的编号,Linux下通过这个编号来操控对应的GPIO引脚。
第十七章 GPIO应用编程699.png
图 17.1.3 base属性文件
label:该组GPIO对应的标签,也就是名字。
第十七章 GPIO应用编程787.png
图 17.1.4 label属性文件
ngpio:该控制器所管理的GPIO引脚的数量(所以引脚编号范围是:base ~ base+ngpio-1)。
第十七章 GPIO应用编程907.png
图 17.1.5 ngpio属性文件
对于给定的一个GPIO引脚,如何计算它在sysfs中对应的编号呢?其实非常简单,譬如给定一个GPIO引脚为GPIO4_IO16,那它对应的编号是多少呢?首先我们要确定GPIO4对应于gpiochip96,该组GPIO引脚的最小编号是96(对应于GPIO4_IO0),所以GPIO4_IO16对应的编号自然是96 + 16 = 112;同理GPIO3_IO20对应的编号是64 + 20 = 84。
export:用于将指定编号的GPIO引脚导出。在使用GPIO引脚之前,需要将其导出,导出成功之后才能使用它。注意export文件是只写文件,不能读取,将一个指定的编号写入到export文件中即可将对应的GPIO引脚导出,譬如:
  1. echo 0 > export                # 导出编号为0的GPIO引脚(对于I.MX6UL/I.MX6ULL来说,也就是GPIO1_IO0)
复制代码


第十七章 GPIO应用编程1353.png
图 17.1.6 导出成功
导出成功之后会发现在/sys/class/gpio目录下生成了一个名为gpio0的文件夹(gpioX,X表示对应的编号),如图 16.1.7所示。这个文件夹就是导出来的GPIO引脚对应的文件夹,用于管理、控制该GPIO引脚,稍后再给大家介绍。
第十七章 GPIO应用编程1558.png
图 17.1.7 GPIO引脚对应的文件夹
unexport:将导出的GPIO引脚删除。当使用完GPIO引脚之后,我们需要将导出的引脚删除,同样该文件也是只写文件、不可读,譬如:
  1. echo 0 > unexport                # 删除导出的编号为0的GPIO引脚
复制代码


删除成功之后,之前生成的gpio0文件夹就会消失!
以上就给大家介绍了/sys/class/gpio目录下的所有文件和文件夹,控制GPIO引脚主要是通过export导出之后所生成的gpioX(X表示对应的编号)文件夹,在该文件夹目录下存在一些属性文件可用于控制GPIO引脚的输入、输出以及输出的电平状态等。
Tips:需要注意的是,并不是所有GPIO引脚都可以成功导出,如果对应的GPIO已经在内核中被使用了,那便无法成功导出,打印如下信息:
第十七章 GPIO应用编程1953.png
图 17.1.8 导出失败
那也就是意味着该引脚已经被内核使用了,譬如某个驱动使用了该引脚,那么将无法导出成功!
gpioX
将指定的编号写入到export文件中,可以导出指定编号的GPIO引脚,导出成功之后会在/sys/class/gpio目录下生成对应的gpioX(X表示GPIO的编号)文件夹,以前面所生成的gpio0为例,进入到gpio0目录,该目录下的文件如下所示:
第十七章 GPIO应用编程2187.png
图 17.1.9 gpioX文件夹
我们主要关心的文件是active_low、direction、edge以及value这四个属性文件,接下来分别介绍这四个属性文件的作用:
direction:配置GPIO引脚为输入或输出模式。该文件可读、可写,读表示查看GPIO当前是输入还是输出模式,写表示将GPIO配置为输入或输出模式;读取或写入操作可取的值为"out"(输出模式)和"in"(输入模式),如下所示:
第十七章 GPIO应用编程2436.png
图 17.1.10 direction属性文件
value:在GPIO配置为输出模式下,向value文件写入"0"控制GPIO引脚输出低电平,写入"1"则控制GPIO引脚输出高电平。在输入模式下,读取value文件获取GPIO引脚当前的输入电平状态。譬如:
  1. # 获取GPIO引脚的输入电平状态
  2. echo "in" > direction
  3. cat value

  4. # 控制GPIO引脚输出高电平
  5. echo "out" > direction
  6. echo "1" > value
复制代码


active_low:这个属性文件用于控制极性,可读可写,默认情况下为0,譬如:
  1. # active_low等于0时
  2. echo "0" > active_low
  3. echo "out" > direction
  4. echo "1" > value                #输出高
  5. echo "0" > value                #输出低

  6. # active_low等于1时
  7. $ echo "1" > active_low
  8. $ echo "out" > direction
  9. $ echo "1" > value                #输出低
  10. $ echo "0" > value                #输出高
复制代码


由此看出,active_low的作用已经非常明显了,对于输入模式来说也同样适用。
edge:控制中断的触发模式,该文件可读可写。在配置GPIO引脚的中断触发模式之前,需将其设置为输入模式:
非中断引脚:echo "none" > edge
上升沿触发:echo "rising" > edge
下降沿触发:echo "falling" > edge
边沿触发:echo "both" > edge
当引脚被配置为中断后可以使用poll()函数监听引脚的电平状态变化,在后面的示例中将向大家介绍。
1.2编写应用程序
本例程源码对应的路径为:开发板光盘->11、Linux C应用编程例程源码->17_gpio->gpio.c。
上一小节已经向大家介绍了如何通过sysfs方式控制开发板上的GPIO引脚,本小节我们编写一个简单地测试程序,控制开发板上的某一个GPIO输出PWM波形,其示例代码如下所示:
示例代码 17.2.1 控制GPIO输出PWM
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include <fcntl.h>
  6. #include <unistd.h>
  7. #include <string.h>

  8. #define  EXPORT     "/sys/class/gpio/export"

  9. int main(int argc, char *argv[])
  10. {
  11.     char gpio_dir[50] = "/sys/class/gpio/gpio";
  12.     char file_path[50] = {0};
  13.     int fd;

  14.     /* 校验传参 */
  15.     if (2 != argc) {
  16.         fprintf(stderr, "usage: %s <gpio>\n", argv[0]);
  17.         exit(-1);
  18.     }

  19.     /* 判断指定编号的GPIO是否导出 */
  20.     strcat(gpio_dir, argv[1]);
  21.     if (0 > access(gpio_dir, F_OK)) {//如果目录不存在 则需要导出
  22.         if (0 > (fd = open(EXPORT, O_WRONLY))) {
  23.             perror("open error");
  24.             exit(-1);
  25.         }

  26.         if (0 > write(fd, argv[1], strlen(argv[1]))) {//导出gpio
  27.             perror("write error");
  28.             exit(-1);
  29.         }

  30.         close(fd);  //关闭文件
  31.     }

  32.     /* 配置为输出模式 */
  33.     strcpy(file_path, gpio_dir);
  34.     strcat(file_path, "/direction");
  35.     if (0 > (fd = open(file_path, O_WRONLY))) {
  36.         perror("open error");
  37.         exit(-1);
  38.     }

  39.     if (0 > write(fd, "out", 3)) {  //配置为输出模式
  40.         perror("write error");
  41.         exit(-1);
  42.     }

  43.     close(fd);

  44.     /* 极性设置 */
  45.     memset(file_path, 0x0, sizeof(file_path));
  46.     strcpy(file_path, gpio_dir);
  47.     strcat(file_path, "/active_low");
  48.     if (0 > (fd = open(file_path, O_WRONLY))) {
  49.         perror("open error");
  50.         exit(-1);
  51.     }

  52.     if (0 > write(fd, "0", 1)) {
  53.         perror("write error");
  54.         exit(-1);
  55.     }

  56.     close(fd);

  57.     /* 控制GPIO输出高低电平 */
  58.     memset(file_path, 0x0, sizeof(file_path));
  59.     strcpy(file_path, gpio_dir);
  60.     strcat(file_path, "/value");
  61.     if (0 > (fd = open(file_path, O_WRONLY))) {
  62.         perror("open error");
  63.         exit(-1);
  64.     }

  65.     for ( ; ; ) {   //产生PWM  50Hz
  66.         write(fd, "1", 1);                  //拉高
  67.         usleep(10 * 1000);          //休眠10ms
  68.         write(fd, "0", 1);                  //拉低
  69.         usleep(10 * 1000);          //休眠10ms
  70.     }
  71. }
复制代码


执行程序时需要传入一个参数,该参数指定一个编号,程序会控制指定编号的GPIO引脚产生PWM。上述代码中首先使用access()函数判断指定编号的GPIO引脚是否已经导出,也就是判断相应的gpioX目录是否存在,如果不存在则表示未导出,则通过"/sys/class/gpio/export"文件将其导出;导出之后先配置了GPIO引脚为输出模式,也就是向direction文件中写入"out";接着再配置极性,通过向active_low文件中写入"0";最后再控制GPIO引脚输出相应的高低电平,以使其产生PWM,通过对value文件写入"1"或"0"来拉高或拉低GPIO。
使用交叉编译工具编译应用程序,如下所示:
第十七章 GPIO应用编程5659.png
图 17.2.1 编译应用程序
1.3在开发板上测试
将上小节编译得到的可执行文件testApp拷贝到开发板根文件系统中,譬如拷贝到开发板Linux系统的家目录下,接着执行应用程序:
第十七章 GPIO应用编程5793.png
图 17.3.1 执行应用程序
执行应用程序时,笔者指定的编号为1,也就是对应I.MX6UL/I.MX6ULL的GPIO1_IO01,在阿尔法开发板底板上已经将该引脚引出了,如下所示:
第十七章 GPIO应用编程5931.png
图 17.3.2 GPIO1_IO01引脚
程序执行之后,大家可以使用示波器查看该引脚是否输出了PWM波形,下图是笔者使用示波器所测试的结果:
第十七章 GPIO应用编程6048.png
图 17.3.3 测试结果
从图中可以看出,由应用程序所控制产生的PWM波其周期为50Hz,占空比为50%,与程序实际设置的情况是相符合的。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-25 17:24

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表