厦门知名做企业网站设计的公司,邯郸做网站的博客,个人工作室创业项目,北辰做网站公司目录应用层如何操控GPIOGPIO 应用编程之输出GPIO 应用编程之输入GPIO 应用编程之中断在开发板上测试GPIO 输出测试GPIO 输入测试GPIO 中断测试本章介绍应用层如何控制GPIO#xff0c;譬如控制GPIO 输出高电平、或输出低电平。应用层如何操控GPIO
与LED 设备一样#xff0c;G…
目录应用层如何操控GPIOGPIO 应用编程之输出GPIO 应用编程之输入GPIO 应用编程之中断在开发板上测试GPIO 输出测试GPIO 输入测试GPIO 中断测试本章介绍应用层如何控制GPIO譬如控制GPIO 输出高电平、或输出低电平。应用层如何操控GPIO
与LED 设备一样GPIO 同样也是通过sysfs 方式进行操控进入到/sys/class/gpio 目录下如下所示 可以看到该目录下包含两个文件export、unexport 以及5 个gpiochipXX 等于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。随便进入到其中某个目录下可以看到这些目录下包含了如下文件 在这个目录我们主要关注的是base、label、ngpio 这三个属性文件这三个属性文件均是只读、不可写。 base与gpiochipX 中的X 相同表示该控制器所管理的这组GPIO 引脚中最小的编号。每一个GPIO引脚都会有一个对应的编号Linux 下通过这个编号来操控对应的GPIO 引脚。 label该组GPIO 对应的标签也就是名字。 ngpio该控制器所管理的GPIO 引脚的数量所以引脚编号范围是base ~ basengpio-1。 对于给定的一个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 引脚导出譬如
echo 0 export # 导出编号为0 的GPIO 引脚对于I.MX6UL/I.MX6ULL 来说也就是
GPIO1_IO0导出成功之后会发现在/sys/class/gpio 目录下生成了一个名为gpio0 的文件夹gpioXX 表示对应的编号如图16.1.7 所示。这个文件夹就是导出来的GPIO 引脚对应的文件夹用于管理、控制该GPIO 引脚稍后再给大家介绍。 ⚫ unexport将导出的GPIO 引脚删除。当使用完GPIO 引脚之后我们需要将导出的引脚删除同样该文件也是只写文件、不可读譬如
echo 0 unexport # 删除导出的编号为0 的GPIO 引脚删除成功之后之前生成的gpio0 文件夹就会消失
以上就给大家介绍了/sys/class/gpio 目录下的所有文件和文件夹控制GPIO 引脚主要是通过export 导出之后所生成的gpioXX 表示对应的编号文件夹在该文件夹目录下存在一些属性文件可用于控制GPIO引脚的输入、输出以及输出的电平状态等。
Tips需要注意的是并不是所有GPIO 引脚都可以成功导出如果对应的GPIO 已经在内核中被使用了那便无法成功导出打印如下信息
那也就是意味着该引脚已经被内核使用了譬如某个驱动使用了该引脚那么将无法导出成功
gpioX 将指定的编号写入到export 文件中可以导出指定编号的GPIO 引脚导出成功之后会在/sys/class/gpio目录下生成对应的gpioXX 表示GPIO 的编号文件夹以前面所生成的gpio0 为例进入到gpio0 目录该目录下的文件如下所示 我们主要关心的文件是active_low、direction、edge 以及value 这四个属性文件接下来分别介绍这四个属性文件的作用
⚫ direction配置GPIO 引脚为输入或输出模式。该文件可读、可写读表示查看GPIO 当前是输入还是输出模式写表示将GPIO 配置为输入或输出模式读取或写入操作可取的值为out输出模式和in输入模式如下所示 ⚫ value在GPIO 配置为输出模式下向value 文件写入0控制GPIO 引脚输出低电平写入1则控制GPIO 引脚输出高电平。在输入模式下读取value 文件获取GPIO 引脚当前的输入电平状态。譬如
# 获取GPIO 引脚的输入电平状态
echo in direction
cat value# 控制GPIO 引脚输出高电平
echo out direction
echo 1 value⚫ active_low这个属性文件用于控制极性可读可写默认情况下为0譬如
# active_low 等于0 时
echo 0 active_low
echo out direction
echo 1 value #输出高
echo 0 value #输出低# active_low 等于1 时
$ echo 1 active_low
$ echo out direction
$ echo 1 value #输出低
$ echo 0 value #输出高由此看出active_low 的作用已经非常明显了对于输入模式来说也同样适用。 ⚫ edge控制中断的触发模式该文件可读可写。在配置GPIO 引脚的中断触发模式之前需将其设置为输入模式 非中断引脚echo “none” edge 上升沿触发echo “rising” edge 下降沿触发echo “falling” edge 边沿触发echo “both” edge
当引脚被配置为中断后可以使用poll()函数监听引脚的电平状态变化在后面的示例中将向大家介绍。
GPIO 应用编程之输出
上一小节已经向大家介绍了如何通过sysfs 方式控制开发板上的GPIO 引脚本小节我们编写一个简单地测试程序控制开发板上的某一个GPIO 输出高、低不同的电平状态其示例代码如下所示
本例程源码对应的路径为开发板光盘-11、Linux C 应用编程例程源码-16_gpio-gpio_out.c。
#include stdio.h
#include stdlib.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include unistd.h
#include string.hstatic char gpio_path[100];static int gpio_config(const char *attr, const char *val)
{char file_path[100];int len;int fd;sprintf(file_path, %s/%s, gpio_path, attr);if (0 (fd open(file_path, O_WRONLY))) {perror(open error);return fd;}len strlen(val);if (len ! write(fd, val, len)) {perror(write error);close(fd);return -1;}close(fd); //关闭文件return 0;
}int main(int argc, char *argv[])
{/* 校验传参 */if (3 ! argc) {fprintf(stderr, usage: %s gpio value\n, argv[0]);exit(-1);}/* 判断指定编号的GPIO是否导出 */sprintf(gpio_path, /sys/class/gpio/gpio%s, argv[1]);if (access(gpio_path, F_OK)) {//如果目录不存在 则需要导出int fd;int len;if (0 (fd open(/sys/class/gpio/export, O_WRONLY))) {perror(open error);exit(-1);}len strlen(argv[1]);if (len ! write(fd, argv[1], len)) {//导出gpioperror(write error);close(fd);exit(-1);}close(fd); //关闭文件}/* 配置为输出模式 */if (gpio_config(direction, out))exit(-1);/* 极性设置 */if (gpio_config(active_low, 0))exit(-1);/* 控制GPIO输出高低电平 */if (gpio_config(value, argv[2]))exit(-1);/* 退出程序 */exit(0);
}执行程序时需要传入两个参数argv[1]指定GPIO 的编号、argv[2]指定输出电平状态0 表示低电平、1 表示高电平。
上述代码中首先使用access()函数判断指定编号的GPIO 引脚是否已经导出也就是判断相应的gpioX目录是否存在如果不存在则表示未导出则通过/sys/class/gpio/export文件将其导出导出之后先配置了GPIO 引脚为输出模式也就是向direction 文件中写入out接着再配置极性通过向active_low 文件中写入0不用配置也可以最后再控制GPIO 引脚输出相应的电平状态通过对value 属性文件写入1或0来使其输出高电平或低电平。
使用交叉编译工具编译应用程序如下所示 GPIO 应用编程之输入
本小节我们编写一个读取GPIO 电平状态的测试程序其示例代码如下所示 本例程源码对应的路径为开发板光盘-11、Linux C 应用编程例程源码-16_gpio-gpio_in.c。
#include stdio.h
#include stdlib.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include unistd.h
#include string.hstatic char gpio_path[100];static int gpio_config(const char *attr, const char *val)
{char file_path[100];int len;int fd;sprintf(file_path, %s/%s, gpio_path, attr);if (0 (fd open(file_path, O_WRONLY))) {perror(open error);return fd;}len strlen(val);if (len ! write(fd, val, len)) {perror(write error);close(fd);return -1;}close(fd); //关闭文件return 0;
}int main(int argc, char *argv[])
{char file_path[100];char val;int fd;/* 校验传参 */if (2 ! argc) {fprintf(stderr, usage: %s gpio\n, argv[0]);exit(-1);}/* 判断指定编号的GPIO是否导出 */sprintf(gpio_path, /sys/class/gpio/gpio%s, argv[1]);if (access(gpio_path, F_OK)) {//如果目录不存在 则需要导出int len;if (0 (fd open(/sys/class/gpio/export, O_WRONLY))) {perror(open error);exit(-1);}len strlen(argv[1]);if (len ! write(fd, argv[1], len)) {//导出gpioperror(write error);close(fd);exit(-1);}close(fd); //关闭文件}/* 配置为输入模式 */if (gpio_config(direction, in))exit(-1);/* 极性设置 */if (gpio_config(active_low, 0))exit(-1);/* 配置为非中断方式 */if (gpio_config(edge, none))exit(-1);/* 读取GPIO电平状态 */sprintf(file_path, %s/%s, gpio_path, value);if (0 (fd open(file_path, O_RDONLY))) {perror(open error);exit(-1);}if (0 read(fd, val, 1)) {perror(read error);close(fd);exit(-1);}printf(value: %c\n, val);/* 退出程序 */close(fd);exit(0);
}执行程序时需要传入一个参数argv[1]指定要读取电平状态的GPIO 对应的编号。 上述代码中首先使用access()函数判断指定编号的GPIO 引脚是否已经导出若未导出则通过 “/sys/class/gpio/export文件将其导出导出之后先配置了GPIO 引脚为输入模式也就是向direction 文件中写入in”接着再配置极性、设置GPIO 引脚为非中断模式向edge 属性文件中写入none。 最后打开value 属性文件读取GPIO 的电平状态并将其打印出来。 使用交叉编译工具编译应用程序如下所示
GPIO 应用编程之中断
在应用层可以将GPIO 配置为中断触发模式譬如将GPIO 配置为上升沿触发、下降沿触发或者边沿触发本小节我们来编写一个测试程序将GPIO 配置为边沿触发模式并监测中断触发状态。其示例代码如下所示 本例程源码对应的路径为开发板光盘-11、Linux C 应用编程例程源码-16_gpio-gpio_intr.c。
#include stdio.h
#include stdlib.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include unistd.h
#include string.h
#include poll.hstatic char gpio_path[100];static int gpio_config(const char *attr, const char *val)
{char file_path[100];int len;int fd;sprintf(file_path, %s/%s, gpio_path, attr);if (0 (fd open(file_path, O_WRONLY))) {perror(open error);return fd;}len strlen(val);if (len ! write(fd, val, len)) {perror(write error);return -1;}close(fd); //关闭文件return 0;
}int main(int argc, char *argv[])
{struct pollfd pfd;char file_path[100];int ret;char val;/* 校验传参 */if (2 ! argc) {fprintf(stderr, usage: %s gpio\n, argv[0]);exit(-1);}/* 判断指定编号的GPIO是否导出 */sprintf(gpio_path, /sys/class/gpio/gpio%s, argv[1]);if (access(gpio_path, F_OK)) {//如果目录不存在 则需要导出int len;int fd;if (0 (fd open(/sys/class/gpio/export, O_WRONLY))) {perror(open error);exit(-1);}len strlen(argv[1]);if (len ! write(fd, argv[1], len)) {//导出gpioperror(write error);exit(-1);}close(fd); //关闭文件}/* 配置为输入模式 */if (gpio_config(direction, in))exit(-1);/* 极性设置 */if (gpio_config(active_low, 0))exit(-1);/* 配置中断触发方式: 上升沿和下降沿 */if (gpio_config(edge, both))exit(-1);/* 打开value属性文件 */sprintf(file_path, %s/%s, gpio_path, value);if (0 (pfd.fd open(file_path, O_RDONLY))) {perror(open error);exit(-1);}/* 调用poll */pfd.events POLLPRI; //只关心高优先级数据可读中断read(pfd.fd, val, 1);//先读取一次清除状态for ( ; ; ) {ret poll(pfd, 1, -1); //调用pollif (0 ret) {perror(poll error);exit(-1);}else if (0 ret) {fprintf(stderr, poll timeout.\n);continue;}/* 校验高优先级数据是否可读 */if(pfd.revents POLLPRI) {if (0 lseek(pfd.fd, 0, SEEK_SET)) {//将读位置移动到头部perror(lseek error);exit(-1);}if (0 read(pfd.fd, val, 1)) {perror(read error);exit(-1);}printf(GPIO中断触发value%c\n, val);}}/* 退出程序 */exit(0);
}执行程序时需要传入一个参数argv[1]指定要读取电平状态的GPIO 对应的编号。 上述代码中首先使用access()函数判断指定编号的GPIO 引脚是否已经导出若未导出则通过 /sys/class/gpio/export文件将其导出。 对GPIO 进行配置配置为输入模式、配置极性、将触发方式配置为边沿触发。 打开value 属性文件获取到文件描述符接着使用poll()函数对value 的文件描述符进行监视这里为什么要使用poll()监视、而不是直接对文件描述符进行读取操作这里简单的描述一下。 13.2.3 小节给大家详细介绍了poll()函数这里不再重述poll()函数可以监视一个或多个文件描述符上的I/O 状态变化譬如POLLIN、POLLOUT、POLLERR、POLLPRI 等其中POLLIN 和POLLOUT 表示普通优先级数据可读、可写而POLLPRI 表示有高优先级数据可读取中断就是一种高优先级事件当中断触发时表示有高优先级数据可被读取。当然除此之外还可使用13.4 小节所介绍的异步I/O 方式来监视 GPIO 中断触发。 使用交叉编译工具编译应用程序如下所示
在开发板上测试
前面我们编写了3 个测试程序并编译得到了对应的可执行文件本小节一个一个进行测试。在测试之前选择一个测试引脚这里笔者以板子上的GPIO1_IO01 引脚为例该引脚在底板上已经引出如下所示 Mini 开发板可以通过背面丝印标注的名称或原理图进行确认。
GPIO 输出测试
将示例代码16.2.1 编译的到的可执行文件拷贝到开发板Linux 系统用户家目录下执行该应用程序控制开发板上的GPIO1_IO01 引脚输出高或低电平
./testApp 1 1 #控制GPIO1_IO01 输出高电平
./testApp 1 0 #控制GPIO1_IO01 输出低电平执行相应的命令后可以使用万用表或者连接一个LED 小灯进行检验以验证实验结果
GPIO 输入测试
将示例代码16.3.1 编译的到的可执行文件拷贝到开发板Linux 系统用户家目录下执行该应用程序以读取GPIO1_IO01 引脚此时的电平状态是高电平还是低电平 首先通过杜邦线将GPIO1_IO01 引脚连接到板子上的3.3V 电源引脚上接着执行命令读取GPIO 电平状态 打印出的value 等于1表示读取到GPIO 的电平确实是高电平接着将GPIO1_IO01 引脚连接到板子上的GND 引脚上执行命令 打印出的value 等于0表示读取到GPIO 的电平确实是低电平测试结果与实际相符合
GPIO 中断测试
将示例代码16.4.1 编译的到的可执行文件拷贝到开发板Linux 系统用户家目录下执行该应用程序可以监测GPIO 的中断触发。 执行应用程序监测GPIO1_IO01 引脚的中断触发情况如下所示
./testApp 1 # 监测GPIO1_IO01 引脚中断触发当执行命令之后我们可以使用杜邦线将GPIO1_IO01 引脚连接到GND 或3.3V 电源引脚上来回切换使得GPIO1_IO01 引脚的电平状态发生由高到低或由低到高的状态变化以验证GPIO 中断的边沿触发情况当发生中断时终端将会打印相应的信息如上图所示。
Tips测试完成后按CtrlC 退出程序