RPi.GPIO IRQ Handling

  • Operation confirmed with our ODROID C1+/C2/XU4/N2.

We can simply implement GPIO IRQ handler with Python 2/3.

In this guide, we will use Python 3 to code with.

Before getting started, obviously, we have to install RPi.GPIO for ODROID. Please see the Wiki page for this and install that: RPi.GPIO for ODROID

Let me show you an example of Python 3 codes.

#!/usr/bin/env python3
 
import sys
import time
import RPi.GPIO as GPIO
 
# https://wiki.odroid.com/odroid-xu4/application_note/gpio/rpi.gpio#about_bcm_numbering
IRQ_GPIO_PIN = 25
IRQ_EDGE = GPIO.FALLING
count = 0
 
def handler(channel):
    global count
 
    count += 1
 
def print_status():
    global count
 
    print(count)
    count = 0
 
if __name__ == '__main__':
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(IRQ_GPIO_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
    GPIO.add_event_detect(IRQ_GPIO_PIN, IRQ_EDGE, callback=handler)
 
    print('Press Ctrl-C to exit')
    try:
        while True:
            time.sleep(1)
            print_status()
    except KeyboardInterrupt:
        GPIO.cleanup()
        sys.exit(0)

This is a very basic template script for using GPIO IRQ handler. It counts how many interrupt requested within 1 second and print that count number. If a user presses Ctrl + C, then the script will be closed.

There's a handler() function, that is received a parameter channel. This parameter is necessary since the library requires it when registering the handler function internally. This function increases count number by 1 when the interrupt received.

And print_status() function shows that the count number and initialize count variable to 0.

If the script file is the main executed file, which means it is the first file of the that Python project, RPi.GPIO initially configured using GPIO.setmode(). This function clears the user uses BCM numbering for selecting a GPIO pin.

In GPIO.setup(), the selected GPIO pin is set as an interrupt source in this timing to use on the way the user intended. We should put 3 parameters, which are GPIO pin number in BCM numbering, signal direction, and pull mode. We should set to input direction and pull up mode to receive GPIO interrupt in this guide.

Then we have to add an event handler function using GPIO.add_event_detect(). This gets 3 parameters, which are GPIO pin number in BCM numbering, IRQ edge mode, and handler function pointer as its name. RPi.GPIO library will register the handler for the GPIO pin to the something kind of an internal loop. We specify interrupt edge mode to falling using this function.

Lastly, there're exception handling codes that detect keyboard interrupts (SIGINT). If the interrupt received, the RPi.GPIO cleans itself with GPIO.cleanup() function and that program will be closed. If not, the infinite loop runs and calls print_status() every 1 second.

So we can clean up these long descriptions.

TL;DR

  • def handler()
    • An interrupt handler. Increase count number by 1 when the interrupt occurs. At least 1 parameter required.
  • def print_status()
    • Shows current count number and initialize that to 0.
  • GPIO.setmode()
    • Initializes RPi.GPIO with a pin number guide which is to be used.
  • GPIO.setup()
    • Sets the GPIO pin as an interrupt source. Also set signal direction and pull mode.
  • GPIO.add_event_detect()
    • Set the GPIO pin which is set up before, with interrupt edge mode and handler function.
  • remains
    • Runs print_status function every 1 second and detects keyboard interrupt to close the program appropriately.

Environments

To test its IRQ handling performance, I used a set of test devices.

  • ODROID C1/C2/XU4/N2
  • Function generator
  • Microscope

I set the Function generator up to generate 1 KHz square wave, and check that wave using Microscope. And I will change the amplitude to target to 1.8V or 3.0V every time I changed the board.

Then I chose the pins, #22 for the interrupt source and #20 for ground. The physical pin #22 is equivalent to #25 in BCM numbering. All the three models have the same form factor on the GPIO header.

Then, wire the cables as the concept. So the overall environment is like the following photo.

Run

Let's test with C1+, first.

Just run the script without any changes. As the script includes, it should show numbers about 1000 every second since it is set to handle falling-edge interrupts and the function generator is set to generate 1 KHz square wave.

To make sure, I also check the current kernel version.

root@odroid:~# uname -a
Linux odroid 3.10.107-13 #2 SMP PREEMPT Wed Jun 19 02:31:43 -03 2019 armv7l armv7l armv7l GNU/Linux
root@odroid:~# python3 test.py
Press Ctrl-C to exit
1000
1003
1000
1000
1000
1001
1001
987
1001
1001

This shows that number as we expected. This couldn't be exactly 1000 in every moment, because there're many of [un]foreseen factors which affect detecting interrupts.

I run this with the other boards.

In C2,

root@odroid:~# uname -a
Linux odroid 3.16.68-41 #1 SMP PREEMPT Tue Jun 18 15:06:16 -03 2019 aarch64 aarch64 aarch64 GNU/Linux
root@odroid:~# python3 test.py
Press Ctrl-C to exit
981
993
993
984
985
996
977
995
1001
981

In XU4, which uses pin #26 for the interrupt source and #28 for ground,

root@odroid:~# uname -a
Linux odroid 4.14.120-160 #1 SMP PREEMPT Fri May 17 01:18:14 -03 2019 armv7l armv7l armv7l GNU/Linux
root@odroid:~# python3 test.py
Press Ctrl-C to exit
1005
1007
1014
1005
1008
1003
1005
1001
1005
1004

And in N2,

root@odroid:~# uname -a
Linux odroid 4.9.182-31 #1 SMP PREEMPT Tue Jun 18 14:45:56 -03 2019 aarch64 aarch64 aarch64 GNU/Linux
root@odroid:~# python3 test.py
Press Ctrl-C to exit
1000
1002
1002
1003
1000
1000
1000
999
1000
1000

All the models work well on GPIO IRQ handling with RPi.GPIO. Note that the showing number in the acceptable error range is not that matter. It is continuously changed.

We can change the edge detecting type of the pin.

There're three edge types.

  • Falling
  • Rising
  • Both

This can be changed when you set GPIO IRQ handler in the codes using GPIO.add_event_detect() function.

These are defined as,

  • GPIO.FALLING
  • GPIO.RISING
  • GPIO.BOTH

, respectively.

So if you want to detect using both-edge mode, replace exist IRQ_EDGE value in line #9 to the following one.

# From
IRQ_EDGE = GPIO.FALLING
 
# To
IRQ_EDGE = GPIO.BOTH

Let me give a shot to check if it works. With C1+, the modified script outputs like this with 1 KHz square wave.

root@odroid:~# python3 test.py
Press Ctrl-C to exit
1994
1997
1994
1998
1999
1980
2002
2001
2001
2002

It shows about 2000 because the handler reacts for both edge moments, which means it works flawlessly.