目录
一、poll函数介绍
1. poll函数events标志
本实验为GPIO中断触发实验,所以需要一个机制能够检测中断的触发,中断的优先级高于主程序运行的优先级,而poll函数刚好提供了一个监听高优先级事件的标志,所以本实验采用poll函数去监听IO口有无触发中断。
2.struct pollfd结构体
struct pollfd {
int fd;
short events;
short revents;
};
tips:
fd 是一个文件描述符,struct pollfd 结构体中的 events 和 revents 都是位掩码,调用者初始化 events 来指定需要为文件描述符 fd 做检查的事件。当 poll()函数返回时,revents 变量由 poll()函数内部进行设置,用于说明文件描述符 fd 发生了哪些事件(注意,poll()没有更改 events 量),我们可以对 revents 进行检查,判断文件描述符 fd 发生了什么事件。
3.events和revents标志
![](https://i-blog.csdnimg.cn/blog_migrate/b5b678cb592875da3a356be7d8cf0fc5.png)
tips:POLLIN、POLLRDNORM、POLLRDBAND、POLLPRI、POLLRDHUP与数据可读相关;POLLOUT、POLLWRNORM、POLLWRBAND与可写数据相关;POLLERR、POLLHUP、POLLNVAL)是设定在 revents 变量中用来返回有关文件描述符的附加信息,如果在 events 变量中指定了这三个标志,则会被忽略。
二、GPIO中断程序编写
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <sys/stat.h>
#include <sys/types.h>
static char gpio_path[100];
static int gpio_config(const char *attr,const char *val)
{
char file_path[100];
int len,fd;
//配置gpio电气属性
sprintf(file_path,"%s/%s",gpio_path,attr);
fd = open(file_path,O_WRONLY);//只写方式打开文件
if(fd < 0)
{
fprintf(stderr,"打开%s文件失败!!!\n",file_path);
return -1;
}
len = strlen(val);
if(write(fd,val,len) != len) //配置模式参数
{
perror("write error");
close(fd);
return -1;
}
close(fd);
return 0;
}
int main(int argc,char *argv[])
{
struct pollfd pfd;
char file_path[100];
int ret;
char val;
//检验命令行传入参数
if(argc != 2)
{
fprintf(stderr,"usage:%s<gpio>\n",argv[0]);
exit(-1);
}
//判断需要操作的GPIO是否导出
sprintf(gpio_path,"/sys/class/gpio/gpio%s",argv[1]);
ret = access(gpio_path,F_OK);
if(ret != 0) //目录不存在
{
int fd,len;
fd = open("/sys/class/gpio/export",O_WRONLY);//只写方式打开文件
if(fd < 0)
{
perror("open error");
exit(-1);
}
len = strlen(argv[1]);
//导出gpio
if(write(fd,argv[1],len) != len)
{
fprintf(stderr,"导出gpio失败\n");
close(fd);
exit(-1);
}
close(fd); //关闭export文件
}
//配置为输入模式
ret = gpio_config("direction","in");
if(ret < 0)
{
fprintf(stderr,"配置输入模式出错\n");
exit(-1);
}
//配置极性
ret = gpio_config("active_low","0");
if(ret < 0)
{
fprintf(stderr,"配置极性出错\n");
exit(-1);
}
//配置中断出发方式,上升沿和下降沿(边沿触发)
ret = gpio_config("edge","both");
if(ret < 0)
{
fprintf(stderr,"配置触发方式出错\n");
exit(-1);
}
//打开value属性文件
sprintf(file_path,"%s/%s",gpio_path,"value");
pfd.fd = open(file_path,O_RDONLY);
if(pfd.fd < 0)
{
fprintf(stderr,"打开value属性文件出错\n");
exit(-1);
}
//poll监听事件
pfd.events = POLLPRI;//指定监听事件,只关心高优先级数据可读
ret = read(pfd.fd,&val,1); //先读取一次清除标志
if(ret < 0)
{
fprintf(stderr,"读取中断事件出错\n");
exit(-1);
}
for(;;)
{
ret = poll(&pfd,1,-1);//阻塞监听知道有事件发生
if(ret < 0)
{
fprintf(stderr,"poll error");
exit(-1);
}
//返回 0 表示该调用在任意一个文件描述符成为就绪态之前就超时了。
else if(ret == 0)
{
fprintf(stderr,"poll timeout\n");
continue;//回到for循环继续执行
}
//检查高优先级数据是否可读
if(pfd.revents & POLLPRI)
{
ret = lseek(pfd.fd,0,SEEK_SET);//将光标位置移到头部
if(ret < 0)
{
perror("lseek error");
exit(-1);
}
//往value属性文件里面读取一个数据
ret = read(pfd.fd,&val,1);
if(ret < 0)
{
perror("read error");
exit(-1);
}
printf("GPIO中断触发<value=%c>\n",val);
}
}
exit(0);
}
三、运行可执行程序
1.交叉编译源文件
arm-linux-gnueabihf-gcc gpio_exti.c -o gpio_extiAPP
2.运行可执行文件
./gpio_extiAPP 1
tips:”gpio_extiAPP“为可执行文件程序名。”1“为gpio端口
![](https://i-blog.csdnimg.cn/blog_migrate/002453a8302f5c77e7a37e31a8a6277c.png)
tips:本实验为GPIO边沿触发中断实验,可以将gpio1交替连接到3.3V和GND端口模拟上升沿和下降沿,查看GPIO端口电平变化状态。
四、作者自述
记录生活。