4773|8

1560

帖子

24

TA的资源

五彩晶圆(初级)

楼主
 

BB Black 入门基础之GPIO中断 [复制链接]

本帖最后由 lonerzf 于 2014-1-18 21:25 编辑

有了上次 BB Black 入门基础之初识GPIO 的基础,今天的内容就轻松多了。咱们使用poll()来轮询设备,配置GPIO中断,接着控制LED灯。
参考资料是 derek molly 在YouTube上的视频介绍(不方便发链接,自己搜吧),以及BridgeRun的GPIO实现程序。
同样地,为了减少管理员的工作量,链接以截图形式给出。



前期准备

不管是面包板还是万用板还是印制电路板还是别的,只要有类似功能的都可以。
今天要做的就是读入按键部分的高低电平信号,并控制LED灯亮灭。


GPIO操作步骤大致总结如下:
1 在文件系统中配置内核GPIO支持。这点我们不需要做了。直接在/sys/class/gpio 可用。
如果你非知道怎么在sysfs中配置,我们再进一步学习交流。

elinux.org/GPIO上写着是这么配置的

Symbol: GPIO_SYSFS [=y]
  Prompt: /sys/class/gpio/... (sysfs interface)
    Defined at drivers/gpio/Kconfig:51
    Depends on: GPIOLIB && SYSFS && EXPERIMENTAL
     Location:
      -> Kernel configuration
        -> Device Drivers
         -> GPIO Support (GPIOLIB [=y])




当然,这部分现就不做展开了。别忘了咱们的目的,先入门再说吧。


2 导出我们需要操作并且可用的GPIO口。

3 配置GPIO的direction 为in 或 out。

4 配置GPIO作为中断源(如果有需要) 。需要配置上下沿触发rising, falling,或者both,即两者都触发中断。
注意:如果配置了GPIO作为中断源,那么程序对于该GPIO的value的读取会一直被阻塞,直到有相应的中断发生。

好了,下面介绍程序部分。
咱们的核心程序是poll()。这里就简单介绍下吧,深入的我也不会了。

//下面的都是个人总结,难免有误。如有错误请指正。

poll函数原型如下:
int poll(struct pollfd fds[], nfds_t nfds, int timeout);

返回值:
-1 表示有错误.一种可能是调用被中断。
0 表示在文件描述符就绪之前调用超时。
>0 表示一个或多个文件描述符就绪,在revents域中可以读取返回值.

参数:
参数fds  文件描述符数组。
它是一个如下结构体
struct pollfd
{
int fd;             /* File descriptor */
short events;   /* Requested events bit mask */
short revents;  /* Returned events bit mask */
};
参数nfds_t  unsigned int类型数据, 表示poll()中共需要轮询处理的文件描述符的个数。
参数timeout 超时时间, 单位毫秒。timeout = -1,则持续等待; timeout=0,直接运行而不等待;timeout>0,则等待timeout时间。

关于pollfd 结构体,也简单介绍下。
events 和revents这两个域都是 bit masks 操作,下表列举了每个域中的各种值。


第一组的几位和输入事件相关
第二组的几位和输出事件相关
第三组的几位返回的是文件描述符的附加信息。
最后一个位保留

这里可能对POLLIN和POLLPRI疑惑比较大,我当时也是,找了好多资料都没怎么讲到。
就近似这么理解吧:
POLLPRI用于高优先级数据输入,也就是紧急数据的读物,POLLIN处理的是普通数据。

补充:
blog.csdn.net/jnu_simba/article/details/8806654
这篇博客讲得不错,关于文件描述符的。下面摘录其中一部分:

用户程序不能直接访问内核中的文件描述符表,而只能使用文件描述符表的索引 (即0 1 2 3 ...),这些索引就称为文件描述符(File Descriptor),用int型变量保存。当调用open打开一个文件或创建一个新文件时,内核分配一个文件描述符并返回给用户程序,该文件描述符表项中的指针指向新打开的文件。当读写文件时,用户程序把文件描述符传给read 或write ,内核根据文件描述符找到相应的表项,再通过表项中的指针找到相应的文件。

默认情况下(没有重定向),每个进程的标准输入(stdin)、标准输出(stdout)和标准错误输出(stderr)都指向控制终端,因为在程序启动时(在main函数还没开始执行之前)会自动把控制终端打开三次,分别赋给三个FILE*指针stdin、stdout和stderr,这三个文件指针是libc中定义的全局变量,这三个文件的描述符分别是0、1、2,保存在相应的FILE结构体中。进程从标准输入读也就是读用户的键盘输入,进程往标准输出或标准错误输出写也就是输出到显示器上。头文件unistd.h 中有如下的宏定义来表示这三个文件描述符:
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2


好了,有了前面的铺垫,上代码。
改编自BridgeRun的源程序。
源程序在这里 Gpio-int-test.rar (2.03 KB, 下载次数: 40)

以下是改过的程序,多少有点差别。但是人家的思想很好,很有借鉴意义,是吧?嘿嘿。

#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define POLL_TIMEOUT    (3 * 1000) //超时时间定为3s
#define MAX_BUF   64
typedef unsigned char uint8_t;

//注意以下的几个函数,都做成了独立可复用的函数模块。这个思想很值得我们学习借鉴的。

//gpio export function
int gpioExport(uint8_t gpioPin)
{
int fd, len;
char buf[MAX_BUF];
fd = open( "/sys/class/gpio/export", O_WRONLY);
if (fd < 0)
{
  perror("gpio/export");
  return fd;
}
len = snprintf(buf, sizeof(buf), "%d", gpioPin);
write(fd, buf, len);
close(fd);
return 0;
}

// gpio unexport function
int gpioUnexport(uint8_t gpioPin)
{
int fd, len;
char buf[MAX_BUF];
fd = open( "/sys/class/gpio/unexport", O_WRONLY);
if (fd < 0)
{
  perror("gpio/export");
  return fd;
}
len = snprintf(buf, sizeof(buf), "%d", gpioPin);
write(fd, buf, len);
close(fd);
return 0;
}

//gpio set direction function
int gpioSetDirection(uint8_t gpioPin, uint8_t outdir)
{
int fd, len;
char buf[MAX_BUF];
len = snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/direction", gpioPin);
fd = open(buf, O_WRONLY);
if (fd < 0)
{
  perror("gpio/direction");
  return fd;
}
if (outdir)
{
  strcpy(buf, "out");
}
else
{
  strcpy(buf, "in");
}
len = strlen(buf);
write(fd, buf, len);
close(fd);
return 0;
}

// gpio set value function
int gpioSetValue(uint8_t gpioPin, uint8_t value)
{
int fd, len;
char buf[MAX_BUF];
len = snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpioPin);
fd = open(buf, O_WRONLY);
if (fd < 0)
{
  perror("gpio/set-value");
  return fd;
}
if (value)
{
  strcpy(buf, "1");
}
else
{
  strcpy(buf, "0");
}
len = strlen(buf);
write(fd, buf, len);
close(fd);
return 0;
}

//gpio get value function
int gpioGetValue(uint8_t gpioPin, uint8_t *value)
{
int fd;
char buf[MAX_BUF];
char ch;
snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpioPin);
fd = open(buf, O_RDONLY);
if (fd < 0)
{
  perror("gpio/get-value");
  return fd;
}
read(fd, &ch, 1);
if (ch != '0')
{
  *value = 1;
}
else
{
  *value = 0;
}
close(fd);
return 0;
}

//gpio set edge function
int gpioSetEdge(uint8_t gpioPin, const char *edge)
{
int fd;
char buf[MAX_BUF];
snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/edge", gpioPin);
fd = open(buf, O_WRONLY);
if (fd < 0)
{
  perror("gpio/set-edge");
  return fd;
}
write(fd, edge, strlen(edge) + 1);
close(fd);
return 0;
}

// gpio fd open function
int gpioFdOpen(uint8_t gpioPin)
{
int fd;
char buf[MAX_BUF];
snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpioPin);
fd = open(buf, O_RDONLY | O_NONBLOCK );
if (fd < 0)
{
  perror("gpio/fd_open");
}
return fd;
}

// close a gpio with the file descriptor we have known.
int gpioFdClose(int fd)
{
return close(fd);
}

// 到此为止就是几个函数模块的声明与实现。
//下面就是主函数。最重要的当然要看poll()了。

int main(int argc, char **argv, char **envp)
{
struct pollfd fdset[2];
int nfds = 2;
int gpio_fd, timeout, rc;
char *buf[MAX_BUF];
uint8_t gpioPin, ledPin;
bool isLEDFlashing = false;

if (argc < 3)
{
  printf("Usage: HelloBBB \n");
  printf("Waits for a change in the GPIO pin voltage level or input on stdin\n");
  exit(-1);
}
gpioPin = atoi(argv[1]); // string to int
    ledPin = atoi(argv[2]);
gpioExport(gpioPin);
gpioSetDirection(gpioPin, 0); //input
gpioSetEdge(gpioPin, "rising");
gpio_fd = gpioFdOpen(gpioPin); //the open file descriptor for input gpio.
gpioExport(ledPin);
gpioSetDirection(ledPin, 1); //led output
timeout = POLL_TIMEOUT;
while(1)
{
  memset((void *)fdset, 0, sizeof(fdset) ); //fill with 0, clear the memory.
  fdset[0].fd = STDIN_FILENO;
  fdset[0].events = POLLIN;  //Data other than high-priority data can be read
  fdset[1].fd = gpio_fd;
  fdset[1].events = POLLPRI; //High-priority data can be read
  rc = poll(fdset, nfds, timeout);
  if (rc < 0) // if poll is unsuccessful, returns a negative value.
  {
   printf("\npoll() failed!\n");
   return -1;
  }
  if (rc == 0) //timeout, loop again.
  {
   printf("#");
  }
  if (fdset[1].revents & POLLPRI) // if the button was pressed, high-priority value
  {
   read(fdset[1].fd, buf, MAX_BUF);
   printf("\n poll() GPIO %d interrupt occurred\n", gpioPin);
   if(isLEDFlashing)
   {
    gpioSetValue(ledPin, 1);
   }
   else
   {
    gpioSetValue(ledPin, 0);
   }
   isLEDFlashing = !isLEDFlashing;
  }
  if (fdset[0].revents & POLLIN)
  {
   (void)read(fdset[0].fd, buf, 1);
   printf("\n poll() stdin read 0x%2.2X\n", (unsigned int) buf[0]);
  }
  fflush(stdout);
}
gpioFdClose(gpio_fd);
return 0;
}

看看运行是什么下效果吧。这里的参数参数 GPIO口为什么是26 和44就不用再解释了吧。
结果还算满意。怎么样,很有成就感吧?


运行视频见附件
VID_LED_INT.rar (11.67 MB, 下载次数: 142)

好啦。今天就到这里吧,程序部分个人感觉还算好,就是开始接触Linux系统接口会有那么一个过程。
其他的暂时没什么要注意的了吧。今天的知识点不多,但足够消化一会儿啦。





最新回复

看了都很给力  详情 回复 发表于 2016-8-4 15:11
点赞 关注(1)
个人签名这孩子,成熟的象征,理智的典范。
 

回复
举报

2万

帖子

71

TA的资源

管理员

沙发
 
嘿嘿  看到楼主的帖 盼着看下一集了
加EE小助手好友,
入技术交流群
EE服务号
精彩活动e手掌握
EE订阅号
热门资讯e网打尽
聚焦汽车电子软硬件开发
认真关注技术本身

点评

谢谢。我发帖总要改几次,显得比较慢。不过,又进步了一点。  详情 回复 发表于 2014-1-15 19:24
个人签名

加油!在电子行业默默贡献自己的力量!:)

 
 

回复

24

帖子

0

TA的资源

一粒金砂(中级)

板凳
 
给力的楼主 哈哈

点评

谢谢支持。  详情 回复 发表于 2014-1-15 18:34
 
 
 

回复

1560

帖子

24

TA的资源

五彩晶圆(初级)

4
 
wk123 发表于 2014-1-15 17:25
给力的楼主 哈哈

谢谢支持。
个人签名这孩子,成熟的象征,理智的典范。
 
 
 

回复

1560

帖子

24

TA的资源

五彩晶圆(初级)

5
 
soso 发表于 2014-1-15 10:19
嘿嘿  看到楼主的帖 盼着看下一集了

谢谢。我发帖总要改几次,显得比较慢。不过,又进步了一点。
个人签名这孩子,成熟的象征,理智的典范。
 
 
 

回复

1173

帖子

3

TA的资源

五彩晶圆(初级)

6
 
 
 

回复

2002

帖子

24

TA的资源

五彩晶圆(高级)

7
 
你是干劲十足啊,可惜我是归心似箭

点评

我这也是把之前学的东西趁着放假前整理出来,年后就真的什么都忘了。哈哈。  详情 回复 发表于 2014-1-15 21:44
 
 
 

回复

1560

帖子

24

TA的资源

五彩晶圆(初级)

8
 
shower.xu 发表于 2014-1-15 20:10
你是干劲十足啊,可惜我是归心似箭

我这也是把之前学的东西趁着放假前整理出来,年后就真的什么都忘了。哈哈。
个人签名这孩子,成熟的象征,理智的典范。
 
 
 

回复

12

帖子

0

TA的资源

一粒金砂(中级)

9
 
看了都很给力
 
 
 

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

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/6 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表