GPIO – Event / Using interrupt-driven GPIO in Raspian

6. April 2013 at 06:59
Print Friendly, PDF & Email

http://wiki.gumstix.org/index.php?title=GPIO_Event_Driver

https://pypi.python.org/pypi/RPi.GPIO

Below you will find a version of using interrupts with Quick2Wire library.

You want to look at wiringPI, which is offering a method for interrupts: click.

 

 http://quick2wire.com/2012/11/using-interrupt-driven-gpio-in-raspian/

Quick2Wire’s Python GPIO library allows your program to be notified when something interesting has happened to a device attached to a GPIO pin. Instead of repeatedly checking the pin, an application can wait for the pin to trigger an event as soon as its state changes. Interrupt-driven GPIO also works with the Python epoll library, so you can integrate your GPIO code with other core system objects such as network sockets and files.

Polling for changes

Traditional GPIO programming works by polling the state of the pins. You write a “busy loop”, continually checking the status of a pin until you see the signal you want.

pin = pins.pin(0, direction=In)
while True:
     if pin.value == 1:
         handle_pin_1()

This code is fine for dedicated devices, such as an Arduino, but is not appropriate for the Pi because it monopolizes the Pi’s processor. Linux will struggle to give enough processor time to other tasks, such as serving web pages or reading from the command line. Swapping between tasks will result in degraded performance for the entire system.

An obvious solution is to check the value of the pin less frequently:

pin = pins.pin(1, direction=In) 
while True: 
    while pin.value == 0: # Are we there yet? 
        sleep(1) # Take a quick nap 
        handle_pin_1()

This improves the responsiveness of the Pi, but it’s still problematic. How long should it sleep? Too little and there will still be performance problems, too long and the application might not respond promptly to a pin event. More interestingly, what if you’re on waiting for more than one input? You’ll need to expand your loop to check each pin in turn:

pin1 = pins.pin(0, direction=In) 
pin2 = pins.pin(1, direction=In) 
while True: 
    if pin1.value == 1 
        handle_pin_1() 
    if pin2.value == 0 
        handle_pin_2() 
    sleep(1)

There’s a better solution, but it requires a new programming model:

Using Python’s epoll API with GPIO

GPIO interrupts allow an application to use epoll to wait for a GPIO events instead of continually checking the value of the pin in a busy loop  Epoll is a Linux facility for efficiently handling event notifications. Quick2Wire’s GPIO Pins integrate into Python’s epoll library with a little extra code:

import select 
pin1 = pins.pin(0, direction=In, interrupt=Rising)
pin2 = pins.pin(1, direction=In, interrupt=Falling)

epoll = select.epoll() 
epoll.register(pin1, select.EPOLLIN | select.EPOLLET) 
epoll.register(pin2, select.EPOLLIN | select.EPOLLET) 
with pin1, pin2
    while True: 
        events = epoll.poll() 
        for fileno, event in events: 
            if fileno == pin1.fileno(): 
                handle_pin_1() 
            if fileno == pin2.fileno(): 
                handle_pin_2()

This might look confusing if you aren’t used to epoll, so we’ll step through the code:

pin1 = pins.pin(0, direction=In, interrupt=Rising)
pin2 = pins.pin(1, direction=In, interrupt=Falling)

Notice the new parameter to the Pin constructor. Adding this parameter enables interrupts on the pin, which will be used to wake your application. The pin can be configured to wake when high (‘Rising’), low (‘Falling’) or either (‘Both’).

epoll = select.epoll()
epoll.register(pin1, select.EPOLLIN | select.EPOLLET) 
epoll.register(pin2, select.EPOLLIN | select.EPOLLET)

Here we create an epoll object and register our pins. The flags tell epoll to wake up whenever there is something to read, but only on “edge triggers”. Unlike the older Linux functions select and poll, epoll can be configured to wake up with either “level triggers” or “edge triggers”. Level triggers (the default) will cause epoll.poll() to return whenever the condition is true. Edge triggers will cause epoll.poll() to return only once after a transition from low-to-high or high-to-low.

while True: 
    events = epoll.poll() 
    for fileno, event in events: 
        ...

Here’s our main loop. Each call to epoll.poll() returns a list of events, one event for each of the registered objects that have been triggered since the previous call. Each event is a pair of the file number for the object being triggered and flags describing the type of event that happened (for example EPOLLIN for an input).

Adding more input types

Python will allow you to register any file descriptor with epoll. Because “everything is a file” in Linux, we can, for example, listen for network sockets and the keyboard in the same loop as our pin.

sock = initialize_socket() 
pin = pins.pin(0, direction=In, interrupt=Both) 
pin.open()
epoll = select.epoll()
epoll.register(sock, select.EPOLLIN)
epoll.register(pin, select.EPOLLIN | select.EPOLLET)
epoll.register(sys.stdin, select.EPOLLIN) 
while True: 
    events = epoll.poll() 
    for fileno, event in events: 
        if fileno == sys.stdin.fileno(): 
            # read from the keyboard (or stdin) 
        if fileno == pin.fileno(): 
            # read from the pin 
        if fileno == sock.fileno(): 
            # read from the socket

For another example of GPIO and epoll, take a look at the Quick2Wire Python epoll example in GitHub.