Although I've been an avid user of the Pi for a number of years now, and have used the GPIO pins extensively in my designs and tinkering, I'm still learning and discovering things I didn't know.
Based on two recent post from users that again reported problems using the GPIO input pins, I decided to spend some more time characterizing them, and then use the information to write a detailed HOW-TO. Sounds overkill when you're just using a button as an input, but you only have to Google a bit, and you'll see that there are numerous issues people have that they can't get their arms around.
Here are the two posts where several of us collectively worked on:
Before diving into the intricacies and start to offer solutions and workarounds, we first need to know and share more about the electrical specifications of the input pins. Unfortunately, there seems to be no electrical specifications from Broadcom, the chip supplier, or from the Foundation.
This means we're largely on our own, and need to figure out as much as we can from actual measurements and other empirical observations. We need facts, not fiction or beliefs, nor opinions and hearsay. Please!
What is pretty much agreed upon is that the inputs have a pull_up/pull_down resistor that is somewhere around 50-70K. We also know that the input level should be no higher than 3.3Volt (3V3). We also know, through measurements, that there seems to be a clamping diode from the input to ground, limiting the input voltage to a maximum of -0.3 to -0.6V. There seems to be no clamping diode to the 3V3 supply, but something is clamping the input voltage to a maximum of just over 5.6V, as has been proven by actual measurements. However, it is prudent to limit the input levels to 3V3.
So what about the threshold levels? When is a 0 a zero and a 1 a one? The BROADCOM designers will have followed the JEDEC standard for 3V3 devices (LVTTL & LVCMOS), but that recommends the maximum and minimum levels. How the designers implemented that is largely unknown to you and me.
The JESD8C.01 JEDEC interface standard for 3.3V devices states that:
By a simple measurement, using a pot-meter between 3V3 and ground, and the wiper attached to an input pin, you can fairly easily determine when the system switches from a 0 to a 1, and back, and from a 1 to a 0, and back.for inputs:
VIH = 2.0V (everything >= will be a logical "1")
VIL = 0.8V (everything <= will be a logical "0")
VOH = 2.4V (a "1" will be => )
VOL = 0.4V (a "0" will be <= )
I measured the rising voltage level to change state at 1.3V, and back again at 1.1V. A hysteresis of about 0.2V A falling voltage level went from a 1 to a zero at 1.1V and back to a 1 at 1.3V. The values are probably +/- 50mV due to the accuracy of my simple (and not calibrated to a standard) measurement and tools, but they must be pretty close.
Here is a copy of the code I used, so you can check it out yourself.Logic Level Detection for Inputs
State Changes & Threshold Levels
Rising voltage level or edge:
- From 0 to 1 : 1.3V
- From 1 to 0 : 1.1V
Falling voltage level or edge:
- From 1 to 0 : 1.3V
- From 0 to 1 : 1.1V
Hysteresis : 200mV
Code: Select all
#!/usr/bin/env python2.7 # Topic: Detecting the input threshold levels # # file : input_test.py import RPi.GPIO as GPIO from time import sleep GPIO.setmode(GPIO.BCM) Potmeter = 23 # GPIO-23, pin 16 GPIO.setup(Potmeter, GPIO.IN) try: print "Starting" while True: print GPIO.input(Potmeter) except KeyboardInterrupt: pass finally: print "\nRelease our used channel(s)" GPIO.cleanup([Potmeter]) def main(): pass if __name__ == '__main__': main()
There have been many reports about difficulties with the GPIO.add_event_detect and callback method. To be fair, there is at least one anomaly in the GPIO code for falling edges, and also a much miss-understood feature, that of the bouncetime. We'll not get there yet, lets go back to basics again.
When you simply use a logical input read, chances are you will never detect anything strange. When you use interrupts to look for edges, chances are much larger that you will see unexpected events.
Many users have reported "mis-firings" or multiple hits when they used the add_event_detection. This was also the topic of the two posts above. We're getting a little closer to the problems, but lets us first determine the exact voltage levels when the BCM2835 determines to go from a 0 to a 1 and from a 1 to a 0.
As I just measured myself, the input threshold for a rising edge is 1.3V. Gert van Loo in a post on this forum mentions that the BCM2835 changes state at 1.3V, so that matches. However, when you do some more detailed measurements on the add_event_detection, there is more to it.
Let's take a step at a time to learn more.
Here is the little script I used to test the latency time between a button press and the start of the, what I would call Interrupt Service Routine (ISR), otherwise known as the "my_callback" routine.
Code: Select all
#!/usr/bin/env python2.7 # latency test for GPIO input & add_event_detect(ion) import RPi.GPIO as GPIO from time import sleep GPIO.setmode(GPIO.BCM) Trigger = 24 # Used to send timing reference signals to the scope GPIO.setup(Trigger, GPIO.OUT) Button = 23 GPIO.setup(Button, GPIO.IN, pull_up_down=GPIO.PUD_UP) # input for the button def my_callback(channel): GPIO.output(Trigger, GPIO.HIGH) # signal that we're entering sleep(0.0001) # 100uSec GPIO.output(Trigger, GPIO.LOW) # signal that we're leaving return GPIO.add_event_detect(Button, GPIO.RISING, callback=my_callback) #GPIO.add_event_detect(Button, GPIO.BOTH, callback=my_callback, bouncetime=1) # bouncetime is specified in milliseconds print "Raspberry Pi Model B Rev.", GPIO.RPI_INFO['P1_REVISION'] print "GPIO Version = ", GPIO.VERSION print "Waiting for a button press" try: while True: pass except KeyboardInterrupt: pass finally: print "\nRelease the used pin(s)" GPIO.cleanup([Trigger, Button])
Here is a screenshot of what seems to be a normal and average situation: The vertical scale is 500mV/Div, and the horizontal scale is 100uSec/Div. The red trace is the button, the blue trace the Trigger activity. I use no switch de-bounce capacitor.
So what is evident here is that the ISR latency (the time from the event happening - the rising edge of the level from the button - until we enter the ISR) is around 340 uSec in this shot. The ISR duration itself is about 320 uSec, of which the Sleep takes up 100 uSec. With Linux being Linux, we can expect the latency to change based on other priority tasks. I saw a variation of between 300 uSec up to more than 10mSec. It can vary a lot.
When you hit the button enough times and take a measurement every time, you can see some variations to the above, and that was what I was expecting. The latency varied from 300-400uSec, with an occasional (1 out of 50 or so) much longer one.
What you can also expect to see is switch bounce: This should be no surprise, but hang on, what happened to our 320uSec ISR duration? It went off the screen. The reason is most likely that the ISR got interrupted by the next edge, and that maybe several times. The first pulse is significantly below the threshold level of 1.3V, so that would not have caused a trigger, right? It turns out that the BCM2835 can recognize edges in the single nano sec range. My simple scope cannot register that, the Pi can, so be aware.
I was surprised to see this however : The first ISR happened around 480 nSec after the rising edge from the button, but this is no surprise, probably Linux doing some housekeeping. What is a total surprise is the second ISR. Yes there is a glitch 300uSec after the rising edge, but it is still significantly above the 1.3V threshold! This "should" not have caused a trigger, but it did.
My simple scope cannot register the whole spike, maybe yours can't either.
For now, be aware that switch bounce glitches can be in the low nano second region and the Pi will register that.
More to follow...
[Edited for correctness and more measurement precision]