查看: 14141|回复: 1

[教程] Raspberry Pi的GPIO中断编程

[复制链接]
  • TA的每日心情
    奋斗
    2020-9-28 10:10
  • 签到天数: 1018 天

    连续签到: 1 天

    [LV.10]以坛为家III

    发表于 2013-4-11 09:52:47 | 显示全部楼层 |阅读模式
    分享到:
    转自Hugo Zhu:http://hugozhu.myalert.info/2013 ... pio-pins.html#toc_0
    背景介绍

    树莓派的GPIO引脚不仅可以输出高低电平,也可以当做输入端口(可以想象成键盘输入),当GPIO接入的是高电平,GPIO的值可以认为是1,如果是低电平则是0。如下图所示,可以使用一个Push Button开关按键来控制GPIO 25(BCM Numbering)的高低电平以达到控制的目的。

    13 11-25 PM.png

    GPIO 25和VCC(3.3V)之间通过R1(10K欧姆)和R2(1K欧姆)上拉电阻相连,当按键未被按下时,GPIO 25上拉到VCC,程序可以读到1,当按键按下时,GPIO 25被下拉电阻R2拉到GND(0V),程序可以读到0。如果不加R1,而GPIO 25不小心被设置成输出低电平时,将直接和VCC相连而造成短路,这样可能会烧掉这个引脚,所以加上限流电阻R1后,即使发生这样的情况,也不会出现短路情况。

    应用

    如果我们需要根据GPIO 25的值来控制树莓派,比如按下按钮时希望点亮某个LED或在液晶上显示当前时间,就需要通过程序来获取状态的变化。

    一种常见的做法是在循环里不断读取该引脚的状态,当发生对应的变化的时执行控制逻辑,但显而易见,这种做法很消耗CPU,如果在循环增加sleep(1000)这样的调用,又很容易错过按键变化。较好的做法则是通过中断来实现。

    最新的树莓派Raspbian和Arch Linux内核都已经包含了GPIO的中断处理支持。但使用前需要将指定GPIO引脚输出,方法如下:

    首先可以通过命令echo 25 > /sys/class/gpio/export导出GPIO 25端口,执行成功后在相应的目录下看到以下文件,得益于Linux下一切都是文件的设计理念,GPIO的状态可以通过value文件来获取,这样就可以利用Linux的poll/epoll来获取value文件的变化(这点和Linux高性能网络编程是类似的)。

    • root@raspberrypi2 ~/projects/interrupt_test # ls -l /sys/class/gpio/gpio25/
    • total 0
    • -rw-r--r-- 1 root root 4096 Apr  8 23:56 active_low
    • -rw-r--r-- 1 root root 4096 Apr  8 22:29 direction
    • -rw-r--r-- 1 root root 4096 Apr  8 22:29 edge
    • drwxr-xr-x 2 root root    0 Apr  8 23:56 power
    • lrwxrwxrwx 1 root root    0 Apr  8 23:56 subsystem -> ../../../../class/gpio
    • -rw-r--r-- 1 root root 4096 Apr  8 22:08 uevent
    • -rw-r--r-- 1 root root 4096 Apr  8 22:29 value
    • root@raspberrypi2 ~/projects/interrupt_test #
    wiringPi

    wiringPi库封装了一个简单的接口,传入一个回调函数,当事件发生时传入的函数将被调用。

    • int wiringPiISR (int pin, int edgeType,  void (*function)(void)) ;

    其最主要的部分的实现代码是:

    • int waitForInterruptSys (int pin, int mS)
    • {
    •   int fd, x ;
    •   uint8_t c ;
    •   struct pollfd polls ;
    •   if ((fd = sysFds [pin & 63]) == -1)
    •     return -2 ;
    • // Setup poll structure
    •   polls.fd     = fd ;
    •   polls.events = POLLPRI ;      // Urgent data!
    • // Wait for it ...
    •   x = poll (&polls, 1, mS) ;
    • // Do a dummy read to clear the interrupt
    • //      A one character read appars to be enough.
    •   (void)read (fd, &c, 1) ;
    •   return x ;
    • }
    示例代码
    • #include <stdio.h>
    • #include <string.h>
    • #include <errno.h>
    • #include <stdlib.h>
    • #include <wiringPi.h>
    • // What GPIO input are we using?
    • //      This is a wiringPi pin number
    • #define BUTTON_PIN      6
    • static volatile int globalCounter = 0 ;
    • void myInterrupt (void)
    • {
    •   ++globalCounter ;
    • }
    • int main (void)
    • {
    •   int myCounter = 0 ;
    •   if (wiringPiSetup () < 0)
    •   {
    •     fprintf (stderr, "Unable to setup wiringPi: %s\n", strerror (errno)) ;
    •     return 1 ;
    •   }
    •   if (wiringPiISR (BUTTON_PIN, INT_EDGE_FALLING, &myInterrupt) < 0)
    •   {
    •     fprintf (stderr, "Unable to setup ISR: %s\n", strerror (errno)) ;
    •     return 1 ;
    •   }
    •   for (;;)
    •   {
    •     printf ("Waiting ... ") ; fflush (stdout) ;
    •     while (myCounter == globalCounter)
    •       delay (100) ;
    •     printf (" Done. counter: %5d\n", globalCounter) ;
    •     myCounter = globalCounter ;
    •   }
    •   return 0 ;
    • }
    RPi.GPIO

    Python的PRI.GPIO库也对中断进行了封装,以下是使用例子,差别在于wiringPi支持多线程,允许在等待中断的时候干点别的。

    • #!/usr/bin/env python2.7  
    • # script by Alex Eames http://RasPi.tv/  
    • # http://raspi.tv/2013/how-to-use- ... rry-pi-and-rpi-gpio  
    • import RPi.GPIO as GPIO  
    • GPIO.setmode(GPIO.BCM)  
    •   
    • # GPIO 25 set up as input. It is pulled up to stop false signals  
    • GPIO.setup(25, GPIO.IN, pull_up_down=GPIO.PUD_UP)  
    •   
    • print "Make sure you have a button connected so that when pressed"  
    • print "it will connect GPIO port 23 (pin 16) to GND (pin 6)\n"  
    • raw_input("Press Enter when ready\n>")  
    •   
    • print "Waiting for falling edge on port 23"  
    • # now the program will do nothing until the signal on port 23   
    • # starts to fall towards zero. This is why we used the pullup  
    • # to keep the signal high and prevent a false interrupt  
    •   
    • print "During this waiting time, your computer is not"   
    • print "wasting resources by polling for a button press.\n"  
    • print "Press your button when ready to initiate a falling edge interrupt."  
    • while 1:
    •     try:  
    •         GPIO.wait_for_edge(25, GPIO.FALLING)  
    •         print "\nFalling edge detected. Now your program can continue with"  
    •         print "whatever was waiting for a button press."  
    •     except KeyboardInterrupt:  
    •         GPIO.cleanup()       # clean up GPIO on CTRL+C exit  
    • GPIO.cleanup()           # clean up GPIO on normal exit  
    参考链接
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2012-11-23 16:50
  • 签到天数: 15 天

    连续签到: 1 天

    [LV.4]偶尔看看III

    发表于 2013-4-11 11:02:24 | 显示全部楼层
    RPI.GPIO库0.4的版本好像就支持 GPIO.wait_for_edge函数了,但是不知道函数底层是如何实现的,总觉得这不是中断,而是像软件一直在循环检测直到沿的触发,而不是硬件中断返回
    回复 支持 反对

    使用道具 举报

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

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条



    手机版|小黑屋|与非网

    GMT+8, 2024-11-19 08:38 , Processed in 0.124260 second(s), 18 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.