There are several things going on in your code. I suspect that what's really causing the problem is that you need to exit the interrupt handler before another interrupt callback can be triggered...but there is also a confusing mix of callback-based handlers and the GPIO.event_detected method.
I think you can simplify things by performing less manipulation of your interrupt configuration. Just have a state variable that starts at 0, increment it to 1 on the first interrupt, so the next time the interrupt method is called you know it's the second interrupt. No need to try setting multiple handlers like that.
Keeping in mind that I don't actually know what you're trying to do...I imagine something like this:
import RPi.GPIO as GPIO
import time
state = 0
GPIO.setmode(GPIO.BCM)
GPIO.setup(26, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(19, GPIO.IN, pull_up_down=GPIO.PUD_UP)
def interrupt_handler(channel):
global state
print("interrupt handler")
if channel == 19:
if state == 1:
state = 0
print("state reset by event on pin 19")
elif channel == 26:
if state == 0:
state = 1
print("state set by event on pin 26")
GPIO.add_event_detect(26, GPIO.RISING,
callback=interrupt_handler,
bouncetime=200)
GPIO.add_event_detect(19, GPIO.RISING,
callback=interrupt_handler,
bouncetime=200)
while (True):
time.sleep(0)
Answer from larsks on Stack OverflowThere are several things going on in your code. I suspect that what's really causing the problem is that you need to exit the interrupt handler before another interrupt callback can be triggered...but there is also a confusing mix of callback-based handlers and the GPIO.event_detected method.
I think you can simplify things by performing less manipulation of your interrupt configuration. Just have a state variable that starts at 0, increment it to 1 on the first interrupt, so the next time the interrupt method is called you know it's the second interrupt. No need to try setting multiple handlers like that.
Keeping in mind that I don't actually know what you're trying to do...I imagine something like this:
import RPi.GPIO as GPIO
import time
state = 0
GPIO.setmode(GPIO.BCM)
GPIO.setup(26, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(19, GPIO.IN, pull_up_down=GPIO.PUD_UP)
def interrupt_handler(channel):
global state
print("interrupt handler")
if channel == 19:
if state == 1:
state = 0
print("state reset by event on pin 19")
elif channel == 26:
if state == 0:
state = 1
print("state set by event on pin 26")
GPIO.add_event_detect(26, GPIO.RISING,
callback=interrupt_handler,
bouncetime=200)
GPIO.add_event_detect(19, GPIO.RISING,
callback=interrupt_handler,
bouncetime=200)
while (True):
time.sleep(0)
That sleep(0) just causes the process to idle there while waiting for switch interrupts. Since nothing in there programmatically ends the process, doing a Ctl-C will stop it.
WiringPi: using interrupts with Python - Raspberry Pi Stack Exchange
python - Fastest Hardware Interrupt software? - Raspberry Pi Stack Exchange
Interrupt for python - Raspberry Pi Forums
Use interrupts with python with Raspberry Pi B+ - Stack Overflow
Videos
Python may not be the best choice if you have high or sustained data rates. You would be much better off using C.
Try the following Python. It should capture all the interrupts although if you have sustained high interrupt rates it may take time for them all to be processed.
#!/usr/bin/env python
import time
import pigpio # http://abyz.me.uk/rpi/pigpio/python.html
pi = pigpio.pi()
if not pi.connected:
exit(0)
cb = pi.callback(21)
while True:
print(cb.tally())
cb.reset_tally()
time.sleep(1)
You can handle high speed interrupts on the R'Pi using Python easily if you make some configuration changes:
- Constrain operation to cpu 0,1,2 for the 'Pi
- Never do prints in the interrupt routines
- Set syscheckinterval to a large value to reduce overhead
- When you start your Python app, remap to cpu 3 and set the priority to realtime (-20)
This will allow you to get quite good response out to 5+kHz interrupt rates with no delays.
Here's some sample code:
#! /usr/bin/python2
##Interupt driven x1, x2 up/down encoder counter
##Jack Creasey
from RPi import GPIO
import os
import sys
##*********************************
#Define pin usage for encoder
#**********************************
phase_a = 21
phase_a_dash= 16
phase_b = 20
##*********************************
#Define pin usage for PWM
#**********************************
pwm_out = 12 #connect pin 12 to pin 24 to create a pwm timer interrupt
pwm_in = 24
#**********************************
#Setup GPIO
#**********************************
GPIO.setwarnings(False)
GPIO.cleanup()
GPIO.setmode(GPIO.BCM)
GPIO.setup(phase_a, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(phase_a_dash, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(phase_b, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(pwm_in, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(pwm_out, GPIO.OUT) # Set GPIO pin 12 to output mode.
pwm = GPIO.PWM(pwm_out, 200) # Initialize PWM pin and frequency
pwm.start(5) # Start PWM with 5% duty cycle
#**********************************
#Global variables
#**********************************
global counter
global lastcounter
global pwmticks
global lastpwmticks
def setup():
#Find out our pid so we can remap to cpu 3
mypid=os.getpid()
myppid=os.getppid()
print("My pid is "), mypid
print('Setting cpu affinity to cpu 3')
select_cpu="sudo taskset -cp 3 "
print('Setting priority to -20')
set_priority="sudo renice -n -20 -p "
#Call out to the os to remap the cpu and set priority high
ret=os.system(set_priority+str(mypid))
ret=os.system(select_cpu+str(mypid))
sys.setcheckinterval(1000); ##no Python threading so just let Python run for a long time
def my_callback(channel): #x1 sensing
global counter
if GPIO.input(phase_b):
counter += 1
else:
counter -= 1
def my_callback1(channel): #x2 sensing
global counter
if GPIO.input(phase_b):
counter -= 1
else:
counter += 1
def my_callback2(channel): #PWM interrupt
global pwmticks
pwmticks += 1
setup()
counter = 0
lastcounter=counter
pwmticks=0
lastpwmticks=pwmticks
GPIO.add_event_detect(phase_a, GPIO.FALLING , callback=my_callback) #x1
GPIO.add_event_detect(phase_a_dash, GPIO.RISING , callback=my_callback1) #x2
GPIO.add_event_detect(pwm_in, GPIO.RISING , callback=my_callback2)
try:
while True: #busy work for Python main loop to do
if counter != lastcounter:
lastcounter=counter
sys.stdout.write("\r" + str(counter) + " \r")
sys.stdout.flush()
if pwmticks >= (lastpwmticks + 1000):
lastpwmticks=pwmticks
sys.stdout.write("\n Ticks= " + str(lastpwmticks) + " \n")
sys.stdout.flush()
except KeyboardInterrupt:
print("Ctl C pressed - ending program")