转自Hugo Zhu:http://hugozhu.myalert.info/2013 ... pio-pins.html#toc_0
背景介绍树莓派的GPIO引脚不仅可以输出高低电平,也可以当做输入端口(可以想象成键盘输入),当GPIO接入的是高电平,GPIO的值可以认为是1,如果是低电平则是0。如下图所示,可以使用一个Push Button开关按键来控制GPIO 25(BCM Numbering)的高低电平以达到控制的目的。
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 #
wiringPiwiringPi库封装了一个简单的接口,传入一个回调函数,当事件发生时传入的函数将被调用。 - 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.GPIOPython的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
参考链接
|