Characterizing GPIO input pins

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Characterizing GPIO input pins

I fully realize that I may be opening a Pandora Box here, but with the amount of users (not only on this Forum) that report "issues" and difficulties that are related to the GPIO input pins, I wanted to start to characterize them better.

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:
viewtopic.php?f=28&t=131440
viewtopic.php?f=32&t=133111

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:
for inputs:
VIH = 2.0V (everything >= will be a logical "1")
VIL = 0.8V (everything <= will be a logical "0")
for outputs:
VOH = 2.4V (a "1" will be => )
VOL = 0.4V (a "0" will be <= )
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.
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.
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
Here is a copy of the code I used, so you can check it out yourself.

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()``````
Why is this important? Well, if you want to design a method to eliminate switch bounce, you need to know where the cut-off levels are.

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.

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

# 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])``````
The code is pretty much straight forward. I setup the event detection, and within the ISR, I set an output high, have a little delay, and set the output low again. On a digital scope, I trigger on a button press on one channel, and use the other channel to measure the output activity of the manipulated output pin.
Here is a screenshot of what seems to be a normal and average situation:
ISR Latency.gif (11.59 KiB) Viewed 15848 times
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:
Switch bounce2.gif (11.63 KiB) Viewed 15848 times
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 :
Switch Bounce-4.gif (14.21 KiB) Viewed 15848 times
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]
Last edited by paulv on Mon Feb 15, 2016 10:49 am, edited 9 times in total.

joan
Posts: 14473
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Characterizing GPIO input pins

If your scope can't see the whole spike why do you assume the input hasn't dropped to zero volts?

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Characterizing GPIO input pins

Good point joan, but how small does the pulse/glitch/spike have to be before it is no longer recognized by the hardware?
glitch-3.png (10.3 KiB) Viewed 15765 times
This is at 200uSec/Div and with 50KSamples.
I know that I'm using a very simple scope, but this is all I have access to at the moment.

This test is so simple to repeat that I hope somebody with a high bandwidth scope can give us more information. Can you?

joan
Posts: 14473
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Characterizing GPIO input pins

I don't know. I've assumed a few nanoseconds for ordinary edge interrupts, less for asynchronous interrupts. Purely an assumption on my part.

Last edited by joan on Sat Jan 23, 2016 9:20 pm, edited 1 time in total.

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Characterizing GPIO input pins

Joan, I don't think this is the document you wanted to send, but I already looked all over the BCM2835_PDF_Datasheet one.

On page 97 is this:
The asynchronous falling edge detect enable registers define the pins for which a
asynchronous falling edge transition sets a bit in the event detect status registers
(GPEDSn). Asynchronous means the incoming signal is not sampled by the system
clock. As such falling edges of very short duration can be detected.
And on page 98 is this:
The falling edge detect enable registers define the pins for which a falling edge
transition sets a bit in the event detect status registers (GPEDSn). When the relevant
bits are set in both the GPRENn and GPFENn registers, any transition (1 to 0 and 0
to 1) will set a bit in the GPEDSn registers. The GPFENn registers use synchronous
edge detection. This means the input signal is sampled using the system clock and
then it is looking for a “100” pattern on the sampled signal. This has the effect of
suppressing glitches.
There is no definite timing information, but I take it your guess is based on a lot of work in this area, so most likely pretty good.

I'm working on a next post that shows how weird and unexpected some things are when you look at them in detail, and I will also cover the possible de-bouncing solutions that will eliminate the false hits.

Funny how complicated things can get by adding a simple push button...

Please keep an eye on this post, I (we) can use your inputs and critique.

jdb
Raspberry Pi Engineer & Forum Moderator
Posts: 2161
Joined: Thu Jul 11, 2013 2:37 pm

Re: Characterizing GPIO input pins

There's another factor in play which may result in re-entrant IRQ handling.

When AXI writes are posted, there's a little optimisation in the arbiter that returns write responses as complete before they've gone out across the bus. This is known as "early bResp" in the docs.

One of the consequences of this is in interrupt handlers - when you write to a register to clear an interrupt, you may do a return-from-interrupt (subs pc, lr, #4) instruction immediately afterwards. If the AXI write is still in-flight, you may re-enter the IRQ because the interrupt condition has not been cleared yet.

One of the ways around this is to issue a write to a peripheral register then a read to a peripheral register in the same 4kiB segment. Reads and writes happen in-order to individual peripherals so this technique can serve as an absolute memory barrier.
Rockets are loud.
https://astro-pi.org

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Characterizing GPIO input pins

Thank you jdb for that insight.
I guess that this is more for the designer of the code, Ben Croston, not for ordinary (:-)) users.
I hope he's watching this too.
Last edited by paulv on Sun Jan 24, 2016 10:34 am, edited 1 time in total.

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Characterizing GPIO input pins

Here is another part that deals with the input characterization, but now in a more practical sense.

I should mention here that I use a Raspberry Pi Model B Rev.2 stock for my profiling, with the latest software version of kernel and packages.

We've seen that there is a real possibility of glitches and false triggers, especially when you use relatively slow input devices like a push button.

Relatively slow, because the fastest button press I could manage is in the 50-60 millisec range, but the Pi Model B Rev 2 runs at 700MHz, and that means running at clock cycles of 1.4 nanosec. It's the speed of the processor that gives us some hart burn dealing with slow events.

If what joan offers is indeed the case, a glitch in nano seconds can already trigger an interrupt. This means that you HAVE to use de-bouncing or glitch filtering techniques.

When you're dealing with these kind of situations, it is very common to "slap" a, typically, 100nF capacitor across the button and that's it. Well, maybe not.
Here are three typical de-bounce circuits [updated] :
debounce-ckts.png (28.08 KiB) Viewed 14652 times
The most common one would be Circuit-1. There are actually two problems with this circuit. First it only provides a filter function for rising edges, nothing at all for falling edges, which is what you get when you press the button. The other one is that when you short the capacitor, "sparks will fly" and that can cause other unintended issues. If you've ever shorted a charged electrolyte with a piece of metal you know what I mean.

The general "engineering" recommendation is to follow the R/C de-bounce circuit with a Schmitt-Trigger circuit to bring the slower slope back to hi-speed and feed that to the input. (Google for "debounce circuit images")
There are other solutions too, but then you need to know a bit more about the input pin characteristics, and how to deal with them. Hobbyists like me are driven by other motives, so if we can do it differently, why not experiment and learn from it?

The correct "Active Low" de-bounce filter would be Circuit-2. As a bonus, that circuit will also provide about half the amount of filtering for the rising edge with the shown resistor values. If the series resister is increased to 10K, as an example, both edges will receive about the same "treatment". It all depends on the amount of switch noise you have, so it's important to know your switch. If you can, use a scope to measure it.

If you're after a singular button press, while you're program is doing something else, it's better to use an interrupt, rather than polling. Polling not only uses the processor a lot, it is also tricky, because you may miss the event when "you're not looking".

The GPIO library has two interrupt features. One is event_detected. It will capture the event so you will not loose it, but it is still used within a loop.

To completely make a button edge detection independent of all other things you're doing, like pressing the button to start a shutdown or reset, or shooting a gun in your game, or getting your robot a warning about an approaching obstacle, the best option is probably to use add_event_detect. This is an edge detection done by the BCM2835, with a accompanying service routine (the callback) written by you. I rather use the more traditional name for it myself, the Interrupt Service Routine or ISR.

There is only one issue with the RPi.GPIO falling edge detection, when you add a capacitance of 50pF or more to the input, both falling and rising edges are registered. You can only use the rising edge detection reliably with the de-bounce circuits above, unless you do something extra in software. In any case, I recommend that you should only use Circuit-2 or 3 type circuits. With Circuit-2, when the button is not pressed, C2 is pulled high through R2 and R3, and a tiny bit by the internal pull_up resistor. This pull setting is optional with Circuit-2 and 3.

In any case, when the button is pressed down, the capacitor will "slowly" discharge through R3 and the button to ground. When the button is released, the capacitor will charge through R2 and R3 to 3V3. This bonus time constant (R2+R3/C2) is half that of just R3/C2 with the values shown, or about 60-40 when R2 is increased to 10K.

OK, so far so good? Yes, but here is a warning. Don't try to play safe by using a larger capacitor, with the idea that larger is better. It's absolutely not. The much slower slope of dis-charging/charging will wreak havoc on the pulse recognition, because the Pi is so fast. And, there is an issue in the "undefined" area between 1.13 to 1.0V for Falling edges and between 1.3 and 1.33 for Rising edges. More about that later. Here is a screenshot to show what I mean with this :
Too much debounce-ckt-2.gif (12.41 KiB) Viewed 15564 times
I replaced the 100nF capacitor with a 1uF one, only a factor 10 more. Oops! Schmitt-Trigger to the rescue? Not yet!

First of all, lets figure out why this is happening.
Another input characterization observation:
This shows that the signal recognition for the edge detection seems to be a bit different from what you expect from the specification as I showed in my earlier post
What is happening is that the BCM2835 will go "through" an undefined region "on it's way" to a logical 1 or 0 detection. This is what I measured.
For rising edges (going from 0 to a 1)
The "undefined" area starts at 1.3V and ends at approx. 1.33V
(this is significantly below the specification of 2.0V.

For falling edges (going from 1 to a 0)
The "undefined" area starts at 1.13V and ends at approx. 1.04V
(this is above the specification of 800mV)
If the input value is in the "undefined" area, some unexpected things can happen. We'll come back to this later.

I will actually use this bad example to show a de-bouncing method without using a Schmitt-Trigger circuit, just using software. We really only want one pulse, even if our hardware de-bounce is not optimum.

So what can we do? We want the first detected edge, but not the others. Well there is a (much mis-understood - also by me) parameter for the add_event_detect, the bouncetime option. The idea behind this option is described on the GPIO website, but the example given (300 mSec delay) leads you in the wrong direction. Again, more is not better!
We're dealing with a few milliseconds of bounce problems, so if we use just 5 mSec, that's more than enough.

Code: Select all

``GPIO.add_event_detect(Button, GPIO.FALLING, callback=my_callback, bouncetime=5)``
This will recognize the first edge, but "filter out" the others for a duration of 5 mSec. Perfect!

So what can we do to make this even more reliable? Especially if all you're interested in is that very first edge. Well, we can stop the edge recognition as soon as we have seen the first one, and turn them back on when we have finished our task.

Code: Select all

``````def my_callback(Button):
GPIO.remove_event_detect(Button)
#    do what you need to do
return # not needed, just for clarity``````
Note that the ISR takes a little longer due to the two instructions to turn-off and back on of the event recognition.

Wrapping-up, if you want to use a push button press to start a simple shutdown/poweroff of the Pi, Circuit-2 in combination with the following rudimentary and simple code should do the trick.

Code: Select all

``````#!/usr/bin/env python2.7

# Push Button Input - Shutdown Pi

import RPi.GPIO as GPIO
import subprocess

GPIO.setmode(GPIO.BCM)

Button = 23 # GPIO-23
GPIO.setup(Button, GPIO.IN, pull_up_down=GPIO.PUD_UP)

def my_callback(Button):
subprocess.call(['poweroff'], shell=True, \
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
return # not needed, just for clarity

try:
while True:
pass # replace with any of your code or just wait here

except KeyboardInterrupt:
pass
finally:
print "\nRelease the used channel(s)"
GPIO.cleanup([Button])

def main():
pass

if __name__ == '__main__':
main()
``````
I hope I have been able to clarify this whole button thing a bit more. It looks so easy, but it is more complicated when you start to look under the hood. But, there is more to learn...

Enjoy!

[Edited for correctness, readability & better accuracy measurements]
Last edited by paulv on Mon Feb 15, 2016 10:54 am, edited 17 times in total.

mattmiller
Posts: 2138
Joined: Thu Feb 05, 2015 11:25 pm

Re: Characterizing GPIO input pins

def my_callback(Button):
GPIO.remove_event_detect(Button)
# do what you need to do
I'd never thought of doing that - such a simple idea
Matthew

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Characterizing GPIO input pins

Here is another observation for the inputs.

During the tinkering in the previous post, it already looked to me that the hysteresis for the input seemed to be different from using it with edge detection in combination with an interrupt vs. a normal logic level input.

I just did some more analysis to see if I could substantiate that and found this:
An "edge" seems to be defined as a transition from high to low, or vice-versa, no matter how slow that transition is. After the first recognition, so within the "undefined" area, the level can be stationary. i.e. no change in the input voltage and still generate interrupts.

Hysteresis and Level Changes :
Rising edge detection:
This setting will start to recognize "edges" starting from approx. 1.3V all the way up to approx. 1.33V, a range of about 20mV. The 1.3V is pretty consistent, the 1.33V varies a little.
Falling edge detection:
This setting will start to recognize "edges" starting from approx. 1.13V down to approx. 1.04V, a range of about 100mV. The 1.13V is pretty consistent, the 1.04V varies a little.

As soon as the "undefined" range has been entered, and as long as the voltage level at the input stays within this range, edge detection will continue. This results in a continuous stream of interrupts as fast as the system can manage as soon as the range is entered.
This is the reason for the seemingly weird behavior captured in the following screenshot. (I already used it in the post above) :
Too much debounce-ckt-2.gif (12.41 KiB) Viewed 15481 times
It also proves that by using an incorrect R/C combination in an attempt to get rid of switch bounce will make it worse, not better.

This observation can be easily duplicated by attaching a 10K potmeter via a 4K7 resistor to the 3V3 and to ground and the wiper via a 10K series resistor (to avoid loading) to the GPIO channel. The following script will give you a visual indication of what is going on.

Code: Select all

``````#!/usr/bin/env python2.7

# hysteresis test for GPIO inputs with event interrupts

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) # input for the button

event = 1

def my_callback(channel):
global event
GPIO.output(Trigger, GPIO.HIGH) # signal that we're detecting an edge
print "edge detected", event
event += 1
GPIO.output(Trigger, GPIO.LOW) # signal that we're leaving the ISR
return

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])``````
To switch from Rising to the Falling detection, just change the edge parameter in the add_event_detection statement.
One channel of my scope was connected to the Button pin (only connected to the wiper of the pot-meter), the other channel on the Trigger pin.

This observation of the behavior in the undefined area seems to be typical for the range that can be found on more processors and chips. What I don't know is if the Pi is the only one that sends a continuous stream of interrupts. In the case of RPi.GPIO this could be the contributor to the strange mis-firings of rising edges when detecting only falling edges with only a little bit of capacitance (50pF) added to slow the fall time. On the other hand, the BOTH setting works correctly. Go figure.

Take care!

[Edited for correctness and precision]
[EDITED : I have improved the load to the GPIO input with a 10K series resistor, and eliminated the pull_up/down setting. The voltage levels reflect a better measurement accuracy.]
Last edited by paulv on Mon Feb 15, 2016 10:57 am, edited 17 times in total.

stevech
Posts: 144
Joined: Sun Jul 15, 2012 11:53 pm

Re: Characterizing GPIO input pins

Logic levels for input... just follow the specifications for the chip from the manufacturer. Use their spec sheet test config. since the levels vary a bit with what external impedance exists.

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Characterizing GPIO input pins

Still tinkering and trying to understand...

Here is another weird observation that I cannot explain.
I was using the same, but somewhat improved setup with a variable voltage coming from a potmeter, feeding into a GPIO input.

As I reported earlier, as soon as the voltage entered the "undefined" edge detection input range (from 1.3 until 1.33V for a rising edge, and from 1.13 down to 1.0V for a falling edge, interrupts were continuously generated, even when "seemingly" the voltage stayed at one level.

In an attempt to try to see if I could somehow fix this I changed to ISR to switch off the event recognition, and turn it back on at the end of the ISR.

I was actually more amazed then delighted to see that now only one edge was detected as soon the the voltage entered the "undefined" range. This worked for the falling edge detection, with only one ISR call while slowly turning the pot-meter all the way down from 3V3 to 0V. Because of the issue in the GPIO code, the rising edge detection produced not only one trigger, on "the way up" from 0 to 3V3, but also one "one the way down", from 3V3 to 0 again.

It seems a little strange to me that turning the event detection off and on once, would "fix" the rather weird continuous streaming. I can't explain it, can you?

In any case, it seems that in order to avoid multiple hits on slowly dropping or rising voltage levels, you need to use this ISR code :

Code: Select all

``````#!/usr/bin/env python2.7

# hysteresis test for GPIO inputs with event interrupts

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) # input for the button

event = 1

def my_callback(Button):
global event

GPIO.output(Trigger, GPIO.HIGH) # signal that we're entering the ISR
GPIO.remove_event_detect(Button) # turn the recognition off
print "edge detected", event
event += 1
GPIO.add_event_detect(Button, GPIO.FALLING, callback=my_callback, bouncetime=5) # turn it back on
GPIO.output(Trigger, GPIO.LOW) # signal that we're leaving the ISR

return

print "Raspberry Pi Model B Rev.", GPIO.RPI_INFO['P1_REVISION']
print "GPIO Version = ", GPIO.VERSION

try:
while True:
pass
except KeyboardInterrupt:
pass
finally:
print "\nRelease the used pin(s)"
GPIO.cleanup([Trigger, Button])``````

[EDITED] Found sometimes two bounces on the Rising, Falling and Both edge detections, adding bouncetime=5 fixed that. I assume it's caused by a glitch coming from the potmeter wiper while I turn it. I was able to fix it by adding a 100nF cap to the wiper, before the 10K series resistor so that seems to prove my assumption. I updated the code above. Updated with better measurement accuracy

This is the setup I used :
Hysteresis Measurement.png (9.16 KiB) Viewed 15243 times
Last edited by paulv on Mon Feb 15, 2016 10:59 am, edited 10 times in total.

mattmiller
Posts: 2138
Joined: Thu Feb 05, 2015 11:25 pm

Re: Characterizing GPIO input pins

Im getting a bit confused
this code
def my_callback(Button):
global event

GPIO.output(Trigger, GPIO.HIGH) # signal that we're entering the ISR
GPIO.remove_event_detect(Button) # turn the recognition off
print "edge detected", event
event += 1
GPIO.add_event_detect(Button, GPIO.FALLING, callback=my_callback) # turn it back on
GPIO.output(Trigger, GPIO.LOW) # signal that we're leaving the ISR

return
seems to be essential the same as your previous suggestion
def my_callback(Button):
GPIO.remove_event_detect(Button)
# do what you need to do
But now without the bouncetime=5

Is bouncetime out now?

Matthew

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Characterizing GPIO input pins

Matthew,

With this particular test, with only a very slowly ramping up or down of the input voltage, there is no switch, and therefore no bounce, so you don't need to filter out the bounce glitches.

OK?

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Characterizing GPIO input pins

Well Matthew,

While I was replying to your post, I was testing the setup once more because I happened to see a double hit sometimes (1 out of 20 or so), on the Falling, Rising and Both edge settings. Adding the bouncetime parameter fixed this, so I assume there is a tiny glitch coming from the potmeter while I turn it.

Code: Select all

``    GPIO.add_event_detect(Button, GPIO.BOTH, callback=my_callback, bouncetime=5)``
This fixed it, so it looks like bouncetime is going to stay.

Enjoy!
Attachments
Hysteresis Measurement.png (9.16 KiB) Viewed 15243 times

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Characterizing GPIO input pins

This diagram cannot be absent from this post...
GPIO Block Diagram.gif (38.34 KiB) Viewed 15236 times
It is from page 89 of the Broadcom BCM 2835 ARM Peripherals document.

It clearly shows that the input pins can be configured for edge detection (for interrupts), and also for logic level detection.

On page 96, where it describes the GPIO Event Detect Status Register, there is a synopsis that reads as follows:
The event detect status registers are used to record level and edge events on the GPIO pins. ...
... The interrupt controller can be programmed to interrupt the processor when any of the status bits are set...
Here is an edited summary of some more information from this datasheet:
Edge detection can take place in a synchronous mode, meaning it is sampled at processor clock edges, providing some sort of "filtering", suppressing glitches.
I don't know if the processor actually runs at 700MHz, because that would mean a 1.4nSec sampling rate.
The edge detection can also be configured to take place in an asynchronous mode, meaning not sampled at clock edges, allowing edge detection of very short duration.
So I presume even shorter than 1.4nSec. This is what joan eluded to. You will only see pulses with this speed with very good equipment, so it's easily missed as a cause for mysterious interrupt firings.

Keep this in mind when you attach devices like, buttons, switches or other input devices that work in the mSec range, or unexpected things may happen.
Last edited by paulv on Wed Jan 27, 2016 8:23 am, edited 3 times in total.

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Characterizing GPIO input pins

So I have found a "fun" way to use level-based event recognition that only fires one interrupt when reaching the hysteresis range.
OK, but is that a bug or can we turn it into a feature?

YES, we can! But watch out, this is not the proper way to use slow edges and do something with it.

It's possible, it works for me within my constraints, in this particular setup and for this particular purpose.
As an example, consider the situation where you need to monitor a battery level while it is discharging to a critical level. Let's also assume that you use this cell to provide backup power (UPS) for your Pi to bridge brown-outs of the mains. Can't do that monitoring with a Pi, unless you add an Analog to Digital Converter (DC), right?

Well, with what we know now, there is a trick we can play and get this solved in a very simple way.

Let's look at this circuit :
Measuring Discharge Level.png (12.75 KiB) Viewed 15167 times
In this case I use a 3.7V rechargeable Lithium Polymer cell as an example, but it really can be any rechargeable technology or any voltage level.

A 3V7 cell should not be used below 3V, for one, the discharge will be very rapidly below that level, and it is also not healthy for the cell too. We also know now that the GPIO.add_event_detect function will "fire" an interrupt when the voltage at the GPIO pin drops below 1V13. So, we can use a simple voltage divider to generate a voltage of 1V13 when the cell has discharged to slightly below 3V2.

If that interrupt fires, you can execute a powerdown to make sure there is enough juice left before there is a power related crash. Here is the code to do that :

Code: Select all

``````#!/usr/bin/env python2.7

# cell discharge test with GPIO input

import RPi.GPIO as GPIO
from time import sleep
import subprocess

GPIO.setmode(GPIO.BCM)

Cell_Voltage_Pin = 23
GPIO.setup(Cell_Voltage_Pin, GPIO.IN) # input for the cell voltage divider

def my_callback(Cell_Voltage_Pin):

GPIO.remove_event_detect(Cell_Voltage_Pin)
if GPIO.input(Cell_Voltage_Pin) == 0: # only falling levels
print "citical cell voltage level"
subprocess.call(['poweroff'], shell=True, \
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
sleep(10) # wait out the powerdown here, the rest is not needed

return

try:
while True:
pass
except KeyboardInterrupt:
pass
finally:
print "\nRelease the used pin(s)"
GPIO.cleanup([Cell_Voltage_Pin])
``````
I will try add this feature to my own automatic or backup based supplies. So there will be a version 5 for this one if I can get it to work reliably enough : viewtopic.php?f=37&t=132201

I hope this gets your creative juices flowing, because you can also use the rising edge for charging levels, or both for whatever changing voltage level you would like to "crudely" monitor.

Enjoy!
Last edited by paulv on Mon Feb 15, 2016 11:00 am, edited 5 times in total.

jdb
Raspberry Pi Engineer & Forum Moderator
Posts: 2161
Joined: Thu Jul 11, 2013 2:37 pm

Re: Characterizing GPIO input pins

There's another possibility not yet considered:

The signal from the input buffer may be oscillating. When you bias a CMOS input in the linear region (0.7VDD > Vin > 0.3VDD), the output invariably ends up in an unstable state as the output can couple back to the input.

The GPIO pad will have an ESD protection network and level shifter in it that translates from the VDD_GPIO power domain to the internal VDD_CORE power domain. If there's an inversion there, then with parasitic coupling from output to input you can get a nice free-running oscillator.

The key thing that would tell you that this is happening is if you enable both falling and rising edge detect and see what happens if you vary the voltage through the point where edge detection starts occuring.
Attachments
100 hours in mspaint.png (10.87 KiB) Viewed 15081 times
Rockets are loud.
https://astro-pi.org

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Characterizing GPIO input pins

Hi jdb,

Interesting theory and possible cause.
Two things. You show two diodes going to the 3V3 supply, but there is no documentation that supports that. Empirical measurements have show that these diodes do not seem to exist, but that there is "something" that clamps the input pin to a maximum of 5V6.

Next, I did enable the edge detection to "BOTH", and "RISING" edge detection (same result) and found no evidence of something that would support oscillation. Depending on the capacitance on the input, Falling edges were recognized once on the way down, and varying Rising edges on the way up. But I don't have fast enough scope to back your theory up. Maybe somebody else can?

Thanks for the contribution though, every little bit adds up to more understanding in the absence of trusty or official documentation.
Last edited by paulv on Fri Feb 12, 2016 9:37 am, edited 1 time in total.

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Characterizing GPIO input pins

Some housekeeping.
I did not ignore the posting from stevech from a few days ago.
Because I did not fully understand what he was referring to, I asked him in a private message to elaborate on his rather cryptic (for me) post.
Unfortunately, I have not received an answer to my request for elaboration or on the documentation he seems to be referring to.

Too bad, because his input is pretty much worthless to me without more information or an explanation.

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Characterizing GPIO input pins

For the benefit of those following this topic, I have edited several posts for correctness.
I must admit that up until now, I misunderstood the concept of "edge" detection for what it really seems to be.

In my vocabulary, an "edge" is a rather fast transition.
It turns out however that an "edge" is a transition from high to low or vice-versa, however slow or with what level (milli Volts) it may be.

My bad, I now stand corrected, but I may not be the only one with this misconception, so be aware.
So here it is. Mea Culpa.

I have changed my posts accordingly.
Last edited by paulv on Fri Feb 12, 2016 9:39 am, edited 1 time in total.

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Characterizing GPIO input pins

Continuing with my quest to detail the input characterizations.

You would assume that the wait_for_edge uses the same settings to setup the input port edge detection, similar to what I have already documented for the add_event_detect.

Well, not quite.
Edge based Triggering using wait_for_edge()

Hysteresis and Level Changes :
Rising edge detection:
- This setting recognized an "edge" at approx. 1.3V.
Falling edge detection:
- This setting recognized "edges" continuously starting from approx. 1.13V down to 1.04V
Both edge detection:
- A combination of falling and rising
There is a strange observation however.
When using the rising edge, and slowly increasing the voltage, there is a single edge detected when we enter the range at 1.3V, and nothing more either within the range or above the range. This seems OK.
However, when I reduce the voltage again, and the input voltage passes the 1.3V level, a stream of interrupts will start, and continue until the level is below 1.04V. Bummer! Is this rising edge broken too?
Testing the falling edge then. As soon as the input voltage reaches 1.13V, there is a stream of interrupts that will continue until the voltage is lower than 1.0V. Going up again produces no edge detection, as it should.
Testing the "both" edge detection. Only one edge detected on the way up at 1.3V. On the way down, at 1.13V there is the stream of interrupts, until we are below 1.0V.

Here is the code I used:

Code: Select all

``````#!/usr/bin/env python2.7

# hysteresis test for GPIO inputs with event interrupts

import RPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BCM)

Button = 23
GPIO.setup(Button, GPIO.IN) # input for the button

event = 1

print "Raspberry Pi Model B Rev.", GPIO.RPI_INFO['P1_REVISION']
print "GPIO Version = ", GPIO.VERSION

try:
while True:
GPIO.wait_for_edge(Button, GPIO.BOTH)
print "edge detected", event
event += 1

except KeyboardInterrupt:
pass
finally:
print "\nRelease the used pin(s)"
GPIO.cleanup([Button])``````
So also this event detect with edges has issues.
Next up is event_detected. Stay tuned for more.
Last edited by paulv on Mon Feb 15, 2016 11:03 am, edited 3 times in total.

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Characterizing GPIO input pins

The add_event_detected is used for edge detection within a loop. It is better than using an input read, because the edge is "latched" by an interrupt and is then available for testing within the loop. It makes sure there are no missed edges because the program "blinked".

This event detection is used in two parts. One to setup the event detection, and one to read the result in the loop construct. Here is the code I used :

Code: Select all

``````#!/usr/bin/env python2.7

# hysteresis test for GPIO inputs with event interrupts

import RPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BCM)

Button = 23
GPIO.setup(Button, GPIO.IN) # input for the button

event = 1

print "Raspberry Pi Model B Rev.", GPIO.RPI_INFO['P1_REVISION']
print "GPIO Version = ", GPIO.VERSION

try:
while True:
if GPIO.event_detected(Button) == True:
print "edge detected", event
event += 1

except KeyboardInterrupt:
pass
finally:
print "\nRelease the used pin(s)"
GPIO.cleanup([Button])``````
As far as the measurement results and observations go, they are the same as with wait_for_edge. As could be expected, this setup uses the same settings.

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Characterizing GPIO input pins

I have created a new post with a summary that hopefully is easy to find now and later for many users.
It also presents software scripts that avoid false hits with the three different RPi.GPIO event detection functions.
Have a look here: viewtopic.php?uid=52264&f=28&t=134394&start=0

Enjoy!

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Characterizing GPIO input pins

Question for Ben Croston and joan.

Ben, could this "automatic firing range" behavior between 1.13 and 1.0V be the reason for the false edge triggering in the Rising Edge detection?

Joan, do you see this behavior with your system too? If not, what is different?

Tks!
Last edited by paulv on Mon Feb 15, 2016 11:04 am, edited 1 time in total.