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

Re: Characterizing GPIO input pins

Sun Jan 31, 2016 11:01 am

I wrote another post about dealing with very slow moving voltages or ramps, and the solutions for all three edge based triggering functions.
viewtopic.php?f=28&t=134394&p=896208#p896208

Enjoy!

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

Re: Characterizing GPIO input pins

Sun Feb 14, 2016 2:56 pm

So what is happening to the edge recognition when you add a capacitance to the input ports?

Do you always need an extra Schmitt-Trigger device to clean-up the edges, or do we (students and hobbyists) get some leeway when we want to add buttons and switches to the Pi?

In earlier posts I already mentioned the fact that adding a little bit of capacitance (50pF) to an input pin can already cause mis-firings with the Rising edge detection of the RPi.GPIO library. Mis-firings because the number of edges reported is higher than is actually fed into the input.

Here is an analysis of that effect generating 100 edges (only incorrect resulting counts are shown):
Edge miscounts.gif
Edge miscounts.gif (12.66 KiB) Viewed 3625 times
Apart from the Rising Edge recognition, as long as you stay below 500 nF capacitor values, and you take some precautions in software, there are no strange artifacts happening.

The precautions in software are simple, actually.
The first statement in your call_back or ISR routine should be to disable the edge detection. At the end, you turn it back on. Alternatively, you can use bouncetime=10 to filter out edges for say 10 mSec after the first one.

Code: Select all

def isr(channel):
    GPIO.remove_event_detect(channel) # stop the edge detection
    # your code

    # turn the event detection back on
    GPIO.add_event_detect(channel, GPIO.FALLING, callback=isr)
    return
A more detailed analysis of just the Rising Edge detection shows what is actually going on :
RPiGPIO Rising Edge Fail.gif
RPiGPIO Rising Edge Fail.gif (15.07 KiB) Viewed 3625 times
So although the actual Rising Edge detection works as expected, there are also Falling edges detected. Here you can also see why some people believe that Rising Edge detection works the same as BOTH. Between 50nF and 440nF, the count is exactly double due to the erroneous Falling edge detection.

The hope is that this weird problem can be solved (it has been on the bug list for a while), but until that is fixed, we need to add some more software to work around this issue.

So far, in my input and edge analysis I have only worked with clean edges that were changed by an R/C combination. All clinical, regular and clean. A real debounce solution for switches and buttons however is a little more complex so we'll tackle that next.

Stay tuned for more...
Last edited by paulv on Tue Feb 16, 2016 7:47 am, edited 1 time in total.

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

Re: Characterizing GPIO input pins

Sun Feb 14, 2016 3:34 pm

What happens if you add a large capacitor (~100uF, preferably low-ESR electrolytic) between 3v3 and 0V - i.e. between pin 1 and 6 on the GPIO header.

Does this have any effect on false edge detection rate?
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

Sun Feb 14, 2016 5:09 pm

Hi jdb,

That's a very interesting suggestion!
I will start to work on that first thing tomorrow.
It will take me a while to very carefully redo several of my tests and measurements to see what the effects are, if any.

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

Re: Characterizing GPIO input pins

Mon Feb 15, 2016 9:24 am

jdb,

I tried to increase the 3V3 filtering with a 100uF(low ESR) capacitor but I could not detect any changes.

The system still generates a stream of interrupts in the "undefined" area, and the Falling edge detection still generates Rising edge detection as well.

I also tried it with a 220uF, and together with a 100nF or a 4n7F cap parallel to the electrolyte and made sure I have a single ground point and no loops by removing my scope leads. The leads that go from the P1 connector to my breadboard are 10cm. I guess we can eliminate 3V3 supply noise as a possible contributor?

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

Re: Characterizing GPIO input pins

Mon Feb 15, 2016 11:04 am

It was just a thought. The 3v3 is common to a lot of other devices on the Pi (notably SD card, which can draw 100s of mA when writing) which does cause the voltage to vary somewhat.
Rockets are loud.
https://astro-pi.org

User avatar
FTrevorGowen
Forum Moderator
Forum Moderator
Posts: 5196
Joined: Mon Mar 04, 2013 6:12 pm
Location: Bristol, U.K.
Contact: Website

Re: Characterizing GPIO input pins

Wed Feb 17, 2016 7:25 pm

jdb wrote:It was just a thought. The 3v3 is common to a lot of other devices on the Pi (notably SD card, which can draw 100s of mA when writing) which does cause the voltage to vary somewhat.
A related thought - given that the Pi's own 3v3 can only supply a limited current to external circuitry I sometimes use level-shifting modules that have their own 3v3 supply derived from the Pi's 5V supply**. I'd be interested in whether that type of configuration changes anything. (Since I don't have access to an oscilloscope etc. I can't investigate myself).
Trev.
** http://www.cpmspectrepi.webspace.virgin ... dules.html
Still running Raspbian Jessie or Stretch on some older Pi's (an A, B1, B2, B+, P2B, 3xP0, P0W, 2xP3A+, P3B+, P3B, B+, A+ and a B2) but Buster on the P4B's. See: https://www.cpmspectrepi.uk/raspberry_pi/raspiidx.htm

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

Re: Characterizing GPIO input pins

Wed Feb 17, 2016 9:15 pm

While trying to design a software solution that is 99.99% accurate in not missing events and not reporting incorrect numbers of events, I came across yet another interesting observation.

After all the precautions and tricks I knew, I was still getting sporadic incorrect event counts, and could not figure out where they came from. To make the whole process a bit more automatic, clean and reproducible, I used a little Python script to generate bursts of pulses that would feed directly into my script to deal with glitches and bounce noise coming from switches. The principle is easy and the same as I have used before. Just use one output pin to send pulses, couple that to an input pin through a resistor, and then count the pulses at the input.

I use a scope to measure the generated pulses with one channel, and use another channel to see when the actual edge detection takes place, and for how long. This is critical to know when you want to handle a bunch of bounce noise before and after the "real" button press.

It turns out that by using the 1:1 shielded cable from my scope to measure the pulses, the capacitive loading of the probe input (1MOhm @ 20pF) is enough to cause the edge detection for rising edges to fail at various times.

This is what I saw after trying for a long time to capture the elusive beast.
Regular glitch1.gif
Regular glitch1.gif (24.68 KiB) Viewed 3400 times
(red trace is the generated pulse [200mV/div], blue is the the time spent in the interrupt service routine [500mV/div]
I have used the following script to generate the pulses and to catch them. Any discrepancy is reported on the screen.

Here is what the program reports : (it only shows discrepancies, after finding one, it reports the finding and then resets the counters to start again)

Code: Select all

pulse counter :  32 edge counter :  33
pulse counter :  524 edge counter :  525
pulse counter :  153 edge counter :  154
pulse counter :  65 edge counter :  66
pulse counter :  26 edge counter :  27
pulse counter :  487 edge counter :  488
pulse counter :  174 edge counter :  175
pulse counter :  37 edge counter :  38
pulse counter :  18 edge counter :  19
pulse counter :  7 edge counter :  8
pulse counter :  58 edge counter :  59
pulse counter :  385 edge counter :  386
pulse counter :  19 edge counter :  20
pulse counter :  147 edge counter :  148
pulse counter :  9 edge counter :  10
pulse counter :  30 edge counter :  31
pulse counter :  21 edge counter :  22
pulse counter :  239 edge counter :  240
pulse counter :  5 edge counter :  6
pulse counter :  40 edge counter :  41
pulse counter :  19 edge counter :  20
pulse counter :  198 edge counter :  199
pulse counter :  221 edge counter :  222
pulse counter :  187 edge counter :  188
pulse counter :  210 edge counter :  211
And here is the program I wrote to do a thorough test:

Code: Select all

#!/usr/bin/env python2

from time import sleep, time
import RPi.GPIO as GPIO
from threading import Thread

# LOOP_IN is connected by a 1K resistor to LOOP_OUT, no caps used
LOOP_IN = 25    # GPIO-25, P1-22
LOOP_OUT = 23   # GPIO-23, P1-16
TRIGGER = 24    # GPIO-24, P1-18


def pulse_gen():
    # free running pulse generator
    global pulse_counter
    class pulse_gen_threadclass(Thread):
        def run(self):
            global pulse_counter

            pulse_counter = 0
            sleep(1) # wait for the edge detection setup
            while True:
                GPIO.output(LOOP_OUT, GPIO.HIGH)
                pulse_counter += 1 # count on leading edge for RISING
                sleep(0.05) # 50 mSec up
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.1) # 100 mSec pulse down

    pulse_gen = pulse_gen_threadclass()
    pulse_gen.setDaemon(True) # so a ctrl-C can terminate it
    pulse_gen.start()


def cb(LOOP_IN):
    global callback_count, pulse_counter

    # show when we enter
    GPIO.output(TRIGGER, GPIO.HIGH)
    # turn the edge detection off
    GPIO.remove_event_detect(LOOP_IN)
    sleep(.012) # total duration is about 15 mSec
    callback_count += 1 # counting edges
    if callback_count <> pulse_counter :
        print "pulse counter : ", pulse_counter, "edge counter : ", callback_count
        pulse_counter = 0
        callback_count = 0
    # turn edge detection back on
    GPIO.add_event_detect(LOOP_IN, GPIO.RISING, callback=cb)
    # show when we leave
    GPIO.output(TRIGGER, GPIO.LOW)
    return


def main():
    global callback_count, pulse_counter

    GPIO.setmode(GPIO.BCM)
    GPIO.setup(LOOP_IN, GPIO.IN)
    GPIO.setup(LOOP_OUT, GPIO.OUT)
    GPIO.setup(TRIGGER, GPIO.OUT, initial=GPIO.LOW)

    GPIO.add_event_detect(LOOP_IN, GPIO.RISING, callback=cb)
    # adding bouncetime makes no difference

    pulse_gen() # start the pulse train

    try:
        pulse_counter = 0
        callback_count = 0
        while True:
            sleep(0.5)


    except KeyboardInterrupt:
        pass
    finally:
        print "\nRelease the used pin(s)"
        GPIO.cleanup([LOOP_IN, TRIGGER, LOOP_OUT, ERROR])

if __name__ == '__main__':
    main()
At first I used pulses with 10uSec, but I wanted to be absolutely sure, so I went down to 50mSec pulses. The results were the same.

In an earlier post I reported that edges would start to fail consistently at 50pF (because I didn't have any capacitors on hand with a lower value) I now found that already at 20pF loading there are random edge failures.
Only a very clean and very fast rising edge (>5 nSec) will work for rising edge detection, therefore any capacitive loading from a test lead or probe is already a cause for edge detection failures.
Modifying the code to count only falling edges does not suffer from this, as I already showed earlier. It has been clean for more than an hour running.

Now that I know where the bad counts come from, I can finish the write up for a Python script that deals with accurate edge detection of buttons and switches.

Stay tuned!

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

Re: Characterizing GPIO input pins

Mon Feb 22, 2016 8:13 pm

I promised to come back with a complete solution for buttons and switches that would work reliably, counting all events without a mistake, and without missing events.

Well it took a lot longer than I expected, because I found yet another problem with the RPi.GPIO library that took me a long time to figure out.

First of all some fine print.
I'm not an expert on the debounce topic at all. What I know I gathered and learned the hard way, by experimenting and finding faults.
This is also how the topic for this post got started in the first place. I ran into undefined/undocumented territory and needed answers I could not find anywhere else. In all fairness, there will be many, many switch debounce solutions available that may do a much better job of what I will present here, although I must also point out that many of the ones I looked at where intended for the "wrong" platform, or the wrong application.


My solution is specifically aimed at the Raspberry Pi only, and then focusing on switches and buttons. Please keep that in mind.

Having said that, let's get started.
As we have seen, there are a few challenges that we need to overcome with this seemingly simple task.
The challenges of interfacing slow external devices to the Pi are:
1. The currently unreliable edge counts for rising edges from the RPi.GPIO library
2. The speed of the Pi, compared to many other kits like an Arduino. The Pi can be 10-100+ times faster in clock speed.
3. The "undefined" area in the edge and logic level recognition, where the chip blurts out interrupts.

So how do we go about this task.
On the one hand, the Pi is so fast that it will react to pulses or glitches in the nano second area. On the other hand we cannot add capacitance to filter them out because that will generate incorrect edge counts as I reported earlier. And lastly, using a larger capacitor to filter out switch bounce noise in the milli-second area will "open up" the "undefined" area and that will generate bursts of unintended interrupts. If you add all three factors together, I'm not at all surprised that so many of us, myself included, have seen or have been the victim of unexpected or false edge recognition's.

I have already showed in earlier posts that number 3 can be largely avoided by using R/C filters carefully that do not cause problems with this issue.
It can be further countered by using the bouncetime parameter, or by switching the edge recognition off for the duration of the interrupt service routine(ISR).

Number 2 is more difficult, because the Pi will recognize glitches or edges in the nano second area, maybe even in the sub-nano second area. Most of us will not have the equipment to even see these pulses, if you realize that the rule of thumb is that you need about 10 x the bandwith of the edge you need to measure. To filter out unwanted pulses with an R/C filter goes square against number 3. It can be a balance act.

Number 1 is a matter of some fairly simple code to determine the right edge and filter out the unwanted edge counts.

So where do we start. You can use Google to look for "switch debounce circuits" to get an appreciation of the difficulty. If it was simple, there would not be that many examples and solutions.

First of all it is important to know your particular switch or button. I'm not going to cover what others already did. Here is an extensive overview of switches and buttons and what switch noise is. The solutions that are offered are typical for what I call "arduino" type platforms.
http://www.ganssle.com/debouncing.htm

Second, it is important to know your application. You should not just throw a capacitor or other hardware to the problem if you don't know what it is or what the consequences are. What is important is to know what you want to do with the button or switch press. Do you press it only once in a while, to start or stop something, or are you interested in rapid push button presses or signals, like in a remote control, or even faster movements like a computer game. Is it acceptable to miss an edge every once in a while, or is it crucial to be correct? It is if your life depends on it (in a computer game of course).

For now let's assume we want to process rather rapid button presses in succession.
About the fastest I can produce is the kind of "double-click" as you do with a mouse. At best, that will produce about a 30-50 mSec pulse with about 130-150 mSec in between. To be more realistic we'll use 50 mSec. If we then select a pause period of 100 mSec in between pulses, we have a specification we can start to work from. In any case, this calls for a pretty responsive solution.

Eventually, with just about every "mechanical" input, you need to select a debounce filter circuit to filter the signals going to the Pi's input. Here are the 3 common R/C based types.
debounce-ckts.png
debounce-ckts.png (20.64 KiB) Viewed 3350 times
Do not use circuit-1 for a push button, it has a major design flaw !
The selection of the circuit depends largely on your hardware of course, but with a simple change, the scripts can be made to work with both circuit-2 and circuit-3. I personally prefer active high switch circuits and signals, because they avoid possible power-up issues.

For the purposes of designing and testing the software below, I use a simple test circuit I already used in these posts several times before. I use software scripts to drive a GPIO output pin which is connected through a resistor to a GPIO input pin. Filter capacitors can be then placed across the input pin to ground, and by using a breadboard, I can easily switch between various capacitor values. A 1K looping resistor in the test circuit has exactly the same effect on the capacitors as the 10K series resistor in both debounce circuits-2 and 3. This is due to the limited drive capability of the GPIO ouput pin compared to the strong 1K pull and the "short" of the button.

For the testing of my scripts, I designed and used two types of pulse generators. One is a simple and clean 50 mSec up/100 mSec down pulse generator, following the specification we set for ourselves earlier.
I also fabricated a pulse train/noise generator that looks a lot (well is supposed to) look like a (worse case) noisy button or switch.
It has several pulses ranging from 10 uSec all the way up to 5 mSec. Both the leading and trailing edge have the same treatment. The pulse is again 50mSec, the leading noise is about 10 mSec in duration, and so is the trailing noise. The period in-between is 80mSec to have a similar 50 on/100 off effect.
Here is the leading edge sample.
Leading Burst.gif
Leading Burst.gif (21.5 KiB) Viewed 3350 times
I then designed four different Python scripts that I will present below in the next post.

There are two simple ones and two elaborate ones.
Simple version A switches the event recognition off and on within the ISR and takes a logic level reading at a strategic moment to differentiate between a rising or falling edge. Simple version B uses the bouncetime parameter to "jump" over all the edge bounce, and also takes a logic level reading at a predetermined point in time. The more elaborate version C goes through a few more steps to make sure the correct edge is defined. It has more flexibility for various signals, because the first two need to be "tuned" for their purpose. Version D was designed after testing revealed yet another "system" generated glitch problem, it follows a complete active high or active low pulse.

During my testing, I first used the simple and clean pulse generator to generate functioning code. I then used the noise burst generator to further refine and test the three scripts under worse case conditions. In both pulse cases I tested falling and rising edge types, with no capacitors/no scope/no LA, then only the scope added (20pF load) and then I added various capacitor values from 50pF all the way up to 100uF to see the effects on the pulse and the code. I put my findings in a spreadsheet, it is listed at the very end of the next post.

Lastly, I tested all four methods with a lot of button presses, fast, slow, short and long and kept track of the statistics using different scripts, but based on the ones I will present here.

The four scripts are complete automatic test suites. I urge you to play around with them, if only to learn more about the effects capacitors and certain software features can have on the kind of switch you want to use. Of course, if you're only interested in the actual ISR, you can simply "lift" that portion from the code and take out all the debugging, reporting and testing stuff.

When I ran the tests after I completed the design, the first three of them showed edge errors that were completely random, and were (seemingly) not coming from the pulse generation. This baffled me for several days of testing, tinkering and soul-searching. I tried several ways to get around it, but could not find the cause and fix it. Only after I got my new Logic Analyzer out of the box, I saw the cause of the problem. (typical success story: After about 5 minutes of hooking up my never used LA for the very first time ever, I was able to capture the error and apply a fix)
system-glitch.gif
system-glitch.gif (24.14 KiB) Viewed 3350 times
Channel-0 is the pulse with leading and trailing glitches. Channel-1 is the duration of the ISR. Channel-3 is the system generated error signal. The pulse on Channel-2 indicates a wrong additional edge count in the ISR. You can see a small glitch within the ISR period.

I can only speculate what causes this extra edge recognition. My assumption is that it is caused when the edge recognition is turned back on inside the ISR. Solution B, that does not use the on/off switching does not produce these errors, further supporting my theory. I guess that Linux does something around this event switch off/on process in the RPi.GPIO library code that causes the problem, which also explains the randomness. I found that the time between the end of the legitimate ISR and the start of a fluke one is between 350uSec (this is the interrupt latency time, ie. the time between the event and the start of the ISR) and 8.6mSec. There is no pulse to be seen by my simple equipment going to the input anywhere, and higher capacitance values would have "killed" them, so I call this a "system" (Linux/Pi/RPi.GPIO library) generated edge.

I'm out of attachments to show the "system" errors, so I'll continue with a new post.
Last edited by paulv on Tue Feb 23, 2016 8:37 am, edited 2 times in total.

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

Re: Characterizing GPIO input pins

Mon Feb 22, 2016 8:33 pm

This is part 2.

Now that I knew that something like this was happening, fixing the "system" glitch happening after a pulse turned out to be a simple hack to scripts A and C. For completeness and comparing, it is also added to B, but not needed.

With the fix, the wrong edge rates now dropped significantly. There are however still situations where the hack does not work, as you can see in the following two situations.
System-Glitches-cont.gif
System-Glitches-cont.gif (49.37 KiB) Viewed 3323 times
In the top screen, the time spent within the ISR is extended before it returns. That will not be easy to fix and led to the design of script version D.
In the second screen, the pulse generation is severely messed up by Linux, also not easy to fix. Most of the time, the pulse I create is simply extended when Linux decides to do housekeeping, and this poses no problem. Why the pulses get messed-up as much as they do in this case, I don't know. I found that using the sleep function is more precise and stable than using time() for the pulse generation. For the moment however, these two kinds of errors continue to count as edge errors for A and C, but the system generated glitch extension is solved in D.

Here is another catch of a "system" generated edge interrupt. (sorry for the poor resolution forced by the file size limitation in this forum)
system-glitch2(lr).gif
system-glitch2(lr).gif (28.58 KiB) Viewed 3266 times
It is further proof to me that something is happening when the edge recognition is turned back on, starting a "false" new one that is representing an edge error, if you're looking for one. Script D avoids this from happening within the pulse period by no longer switching the edge recognition off after every edge like in script C, but tracking a complete interrupt cycle. So from a rising edge to a valid falling edge(active high), or from a falling edge to a valid rising edge(active low).

Here are the four automatic test scripts.

First a heads-up. The files are all large with a lot of code. Even the simple B version has 340 LOC. The majority of the code is dealing with the test suit, pulse generation and statistics and error reporting, but even so, it takes several lines of code to produce reliable results with the currently known issues.

Maybe these issues will get fixed in a new version of the RPi.GPIO library. I'll keep an eye out and report back here if there are developments that will help to fix this.

Here we go.
The first solution, A, uses the simple stop/start of the edge recognition within the ISR.
Here is the script :

Code: Select all

#!/usr/bin/env python2

from time import sleep, time
import RPi.GPIO as GPIO
from threading import Thread

__author__ = 'Paul Versteeg'

LOOP_IN = 25    # GPIO-25, P1-22
LOOP_OUT = 23   # GPIO-23, P1-16
TRIGGER = 24    # GPIO-24, P1-18 (for LA or scope use)
ERROR = 22      # GPIO-22, P1-15 (for LA or scope use)

# run time constants
RISING = True
BOUNCE_NOISE = True
PULSE_CNT = 100

#===============================================================================
# quick and ugly hack to automate the testing parameters
import sys
import os

if len(sys.argv) > 0:
    if str((sys.argv[1])) == "-?":
        print "\n\tusage : [rising|falling, count, errors, bounce|pulse|both, duration, largec, cleanup]\n"
        RISING = True
        BOUNCE_NOISE = True
        PULSE_CNT = 100
        ISR_DURATION = False
        os._exit(0)
    if str((sys.argv[1])) == "falling":
        RISING = False
    if str((sys.argv[1])) == "rising":
        RISING = True
else:
    RISING = True

if len(sys.argv) > 1 :
    PULSE_CNT = int(sys.argv[2])
else:
    PULSE_CNT = 6000 # about 15 minutes, 10,000 25 min.

if len(sys.argv) > 2 :
    if str(sys.argv[3]) == "errors":
        ERROR_REPORT = True
    else:
        ERROR_REPORT = False
else:
    ERROR_REPORT = False

if len(sys.argv) > 3 :
    if str(sys.argv[4]) == "bounce":
        BOUNCE_NOISE = True
        PULSE = False
    if str(sys.argv[4]) == "pulse":
        PULSE = True
        BOUNCE_NOISE = False
    if str(sys.argv[4]) == "both":
        PULSE = True
        BOUNCE_NOISE = True
else:
    BOUNCE_NOISE = True
    PULSE = True

if len(sys.argv) > 4 :
    if str(sys.argv[5]) == "duration":
        ISR_DURATION = True
    else:
        ISR_DURATION = False
else:
    ISR_DURATION = False

if len(sys.argv) > 5 :
    if str(sys.argv[6]) == "largec":
        LARGE_C = True
    else:
        LARGE_C = False
else:
    LARGE_C = False

if len(sys.argv) > 6 :
    if str(sys.argv[7]) == "cleanup":
        GPIO_CLEANUP = True
    else:
        GPIO_CLEANUP = False
else:
    GPIO_CLEANUP = True

print "cleanup = ", GPIO_CLEANUP
#===============================================================================


def pulse_gen():
    # free running pulse generator
    global pulse_count, total_count

    class pulse_gen_threadclass(Thread):
        def run(self):
            global pulse_count, total_count

#            pulse_count = 0
            sleep(1) # wait for the edge detection setup to finish

            while total_count < PULSE_CNT :
                GPIO.output(LOOP_OUT, GPIO.HIGH)
                if RISING :
                    pulse_count += 1 # count on leading edge for RISING
                sleep(0.05) # 50 mSec up
                GPIO.output(LOOP_OUT, GPIO.LOW)
                if not RISING :
                    pulse_count += 1 # count after falling edge for FALLING
                total_count += 1
                sleep(0.1) # 100mSec duration down
            exit # stop the thread

    pulse_gen = pulse_gen_threadclass()
    pulse_gen.setDaemon(True) # so a ctrl-C can terminate it
    pulse_gen.start() # start the thread


def bounce_noise_gen():
    # free running pulse generator
    global pulse_count, total_count

    class bounce_noise_gen_threadclass(Thread):
        def run(self):
            global pulse_count, total_count

#            pulse_count = 0
#            total_count = 0
            sleep(1) # wait for the edge detection setup to finish

            while total_count < PULSE_CNT :
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 1
                # no wait, about 10 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                # no wait, about 10 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 2
                sleep(0.00001) # about 150 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.00001) # about 50 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 3
                sleep(0.00005) # 50 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.00001) # 110 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 4
                sleep(0.0018) # 1.8 mSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.002) # 2 mSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 5
                # no wait, about 10 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.0005) # 50 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 6
                sleep(0.005) # 5 mSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.001) # 1 mSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # pulse

                if RISING :
                    pulse_count += 1 # count on leading edge for RISING
                    total_count += 1

                sleep(0.05) # 50 mSec pulse duration up

                if not RISING :
                    pulse_count += 1 # count after falling edge for FALLING
                    total_count += 1

                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.00005) # 50 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 1
                # no wait, about 10 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                # no wait, about 10 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 2
                sleep(0.00001) # about 150 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.00001) # about 50 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 3
                sleep(0.00005) # 50 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.00001) # 110 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 4
                sleep(0.0018) # 1.8 mSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.002) # 2 mSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 5
                # no wait, about 10 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.0005) # 50 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 6
                sleep(0.005) # 5 mSec
                GPIO.output(LOOP_OUT, GPIO.LOW)

                sleep(0.08) # 100mSec duration with noise
            exit # stop the thread

    bounce_noise_gen = bounce_noise_gen_threadclass()
    bounce_noise_gen.setDaemon(True) # so a ctrl-C can terminate it
    bounce_noise_gen.start() # start the thread


def button_press_isr(LOOP_IN):
    global edge_count, pulse_count, error_count
    global glitch_error_timing, glitch_error

    # remove the edge detection for the duration of this ISR
    GPIO.remove_event_detect(LOOP_IN)

    # begin trigger pulse when we enter
    GPIO.output(TRIGGER, GPIO.HIGH)

    timestart = time() # used to measure total duration time and report error time

    # Hack to prevent "system" generated glitches to falsify the results.
    # This probably happens when the edge recognition is turned back on.
    # The period between the end of the ISR and the restart is 350uSec-11.5mSec,
    # so let's prevent that from causing erroneous edge counts.
    if (time() - glitch_error_timing) < 0.015 : # 15 mSec
        glitch_error += 1
        if ERROR_REPORT :
            print "*** system glitch: ", glitch_error
        glitch_error_timing = time() # start again
        # turn the event detection back on
        if RISING :
            GPIO.add_event_detect(LOOP_IN, GPIO.RISING, callback=button_press_isr)
        else:
            GPIO.add_event_detect(LOOP_IN, GPIO.FALLING, callback=button_press_isr)
        return

    # wait for the maximum bounce time
    sleep(0.025) # 25 mSec + latency (300uS) and other execution time
    # actual reading will be approx. 25 mSec from edge detection
    if GPIO.input(LOOP_IN) == RISING : # 0/False for Falling, 1/True for Rising
        edge_count += 1 # we have a valid edge
        if edge_count <> pulse_count :
            error_count += 1
            # send out an error indication to the LA
            GPIO.output(ERROR, GPIO.HIGH)
            sleep(0.001)
            GPIO.output(ERROR, GPIO.LOW)

            if ERROR_REPORT :
                print "pulses : {0}\tedges : {1}\t total : {2}\trun time : {3:.2f} sec."\
                    .format(pulse_count, edge_count, total_count, (time() - start_time))
                pulse_count = 0
                edge_count = 0

    glitch_error_timing = time() # start the timer again

    # end trigger pulse when we leave
    GPIO.output(TRIGGER, GPIO.LOW)

    # at the very last, turn the event detection back on
    if RISING :
        GPIO.add_event_detect(LOOP_IN, GPIO.RISING, callback=button_press_isr)
    else:
        GPIO.add_event_detect(LOOP_IN, GPIO.FALLING, callback=button_press_isr)
    return


def main():
    global edge_count, pulse_count, start_time, error_count, total_count
    global glitch_error_timing, glitch_error

    if GPIO_CLEANUP :
        GPIO.setwarnings(True)
    else:
        GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(LOOP_IN, GPIO.IN) # do not use pull parameter with 10K looping
    GPIO.setup(LOOP_OUT, GPIO.OUT)
    GPIO.setup(TRIGGER, GPIO.OUT, initial=GPIO.LOW)
    GPIO.setup(ERROR, GPIO.OUT, initial=GPIO.LOW)

    print "testing edges with simple start-stop edge detection : {edge} {count}x"\
        .format(edge="rising edge" if RISING else "falling edge", count = PULSE_CNT)

    if RISING :
        GPIO.add_event_detect(LOOP_IN, GPIO.RISING, callback=button_press_isr)
    else:
        GPIO.add_event_detect(LOOP_IN, GPIO.FALLING, callback=button_press_isr)

    try:
        if PULSE :
            glitch_error_timing = 0.01
            glitch_error = 0
            pulse_count = 0
            edge_count = 0
            error_count = 0
            total_count = 0
            start_time = time() # used in the ISR
            pulse_gen() # start the pulse generator
            print "normal edges"
            while total_count < PULSE_CNT :
                sleep(0.5) # or your code

            if error_count > 0 : print "\n"
            tFile = open('/sys/class/thermal/thermal_zone0/temp')
            temp = float(tFile.read())/1000
            print "total count: {0}   errors: {1}   {2:.2f}%   run_time: {3:.2f} min.  cpu temp: {4:.2f}C  glitches: {5}"\
                .format(total_count, error_count, \
                (1-(float(error_count)/float(total_count)))*100, \
                (time() - start_time)/60, temp, glitch_error)



            sleep(1) # let the thread terminate before we starts again or exit

        if BOUNCE_NOISE :
            glitch_error_timing = 0.01
            glitch_error = 0
            pulse_count = 0
            edge_count = 0
            error_count = 0
            total_count = 0
            start_time = time() # used in the ISR
            bounce_noise_gen() # start the pulse generator with bounce noise
            print "\nedges with bounce noise"
            while total_count < PULSE_CNT :
                sleep(0.5) # or your code

            if error_count > 0 : print "\n"
            tFile = open('/sys/class/thermal/thermal_zone0/temp')
            temp = float(tFile.read())/1000

            print "total count: {0}   errors: {1}   {2:.2f}%   run_time: {3:.2f} min.  cpu temp: {4:.2f}C  glitches: {5}"\
                .format(total_count, error_count, \
                (1-(float(error_count)/float(total_count)))*100, \
                (time() - start_time)/60, temp, glitch_error)

            sleep(1) # let the thread terminate before we exit

    except KeyboardInterrupt:
        print "\nterminated"
    except Exception as e:
        print "exception : ", e
    finally:
        # in batch mode it's better not to do the clean-up so the LA continues
        # to look for triggers. A cleanup resets the levels from low to float
        # and this will be seen as a high by the LA, causing a trigger at the end
        # of every run
        if GPIO_CLEANUP :
            GPIO.cleanup([LOOP_IN, TRIGGER, LOOP_OUT, ERROR])
        print "\ndone"

if __name__ == '__main__':
    main()
The downside to this solution is that it needs to be "tuned" for it's application.

Script B uses the debounce parameter solution. This is a brute force solution.

Code: Select all

#!/usr/bin/env python2

from time import sleep, time
import RPi.GPIO as GPIO
from threading import Thread

__author__ = 'Paul Versteeg'

LOOP_IN = 25    # GPIO-25, P1-22
LOOP_OUT = 23   # GPIO-23, P1-16
TRIGGER = 24    # GPIO-24, P1-18 (for LA or scope use)
ERROR = 22      # GPIO-22, P1-15 (for LA or scope use)

# runtime constants
RISING = True
BOUNCE_NOISE = True
PULSE_CNT = 100

#===============================================================================
# quick and ugly hack to automate the testing parameters
import sys
import os

if len(sys.argv) > 0:
    if str((sys.argv[1])) == "-?":
        print "\n\tusage : [rising|falling, count, errors, bounce|pulse|both, duration, largec, cleanup]\n"
        RISING = True
        BOUNCE_NOISE = True
        PULSE_CNT = 100
        ISR_DURATION = False
        os._exit(0)
    if str((sys.argv[1])) == "falling":
        RISING = False
    if str((sys.argv[1])) == "rising":
        RISING = True
else:
    RISING = True

if len(sys.argv) > 1 :
    PULSE_CNT = int(sys.argv[2])
else:
    PULSE_CNT = 6000 # about 15 minutes, 10,000 25 min.

if len(sys.argv) > 2 :
    if str(sys.argv[3]) == "errors":
        ERROR_REPORT = True
    else:
        ERROR_REPORT = False
else:
    ERROR_REPORT = False

if len(sys.argv) > 3 :
    if str(sys.argv[4]) == "bounce":
        BOUNCE_NOISE = True
        PULSE = False
    if str(sys.argv[4]) == "pulse":
        PULSE = True
        BOUNCE_NOISE = False
    if str(sys.argv[4]) == "both":
        PULSE = True
        BOUNCE_NOISE = True
else:
    BOUNCE_NOISE = True
    PULSE = True

if len(sys.argv) > 4 :
    if str(sys.argv[5]) == "duration":
        ISR_DURATION = True
    else:
        ISR_DURATION = False
else:
    ISR_DURATION = False

if len(sys.argv) > 5 :
    if str(sys.argv[6]) == "largec":
        LARGE_C = True
    else:
        LARGE_C = False
else:
    LARGE_C = False

if len(sys.argv) > 6 :
    if str(sys.argv[7]) == "cleanup":
        GPIO_CLEANUP = True
    else:
        GPIO_CLEANUP = False
else:
    GPIO_CLEANUP = True

print "cleanup = ", GPIO_CLEANUP
#===============================================================================


def pulse_gen():
    # free running pulse generator
    global pulse_count, total_count

    class pulse_gen_threadclass(Thread):
        def run(self):
            global pulse_count, total_count

            sleep(2) # wait for the edge detection setup to finish
            while total_count < PULSE_CNT :
                GPIO.output(LOOP_OUT, GPIO.HIGH)
                if RISING :
                    pulse_count += 1 # count on leading edge for RISING
                sleep(0.05) # 50 mSec up
                GPIO.output(LOOP_OUT, GPIO.LOW)
                if not RISING :
                    pulse_count += 1 # count after falling edge for FALLING
                sleep(0.1) # 100 mSec pulse
                total_count += 1
            exit # stop the thread

    pulse_gen = pulse_gen_threadclass()
    pulse_gen.setDaemon(True) # so a ctrl-C can terminate it
    pulse_gen.start() # start the thread



def bounce_noise_gen():
    # free running pulse generator with noise burst
    global pulse_count, total_count

    class bounce_noise_gen_threadclass(Thread):
        def run(self):
            global pulse_count, total_count

            sleep(2) # wait for the edge detection setup to finish

            while total_count < PULSE_CNT :
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 1
                # no wait, about 10 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                # no wait, about 10 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 2
                sleep(0.00001) # about 150 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.00001) # about 50 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 3
                sleep(0.00005) # 50 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.00001) # 110 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 4
                sleep(0.0018) # 1.8 mSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.002) # 2 mSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 5
                # no wait, about 10 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.0005) # 50 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 6
                sleep(0.005) # 5 mSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.001) # 1 mSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # pulse

                if RISING :
                    pulse_count += 1 # count on leading edge for RISING
                    total_count += 1

                sleep(0.05) # 50 mSec pulse duration up

                if not RISING :
                    pulse_count += 1 # count after falling edge for FALLING
                    total_count += 1

                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.00005) # 50 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 1
                # no wait, about 10 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                # no wait, about 10 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 2
                sleep(0.00001) # about 150 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.00001) # about 50 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 3
                sleep(0.00005) # 50 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.00001) # 110 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 4
                sleep(0.0018) # 1.8 mSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.002) # 2 mSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 5
                # no wait, about 10 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.0005) # 50 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 6
                sleep(0.005) # 5 mSec
                GPIO.output(LOOP_OUT, GPIO.LOW)

                sleep(0.08) # 100mSec duration with noise
            exit # stop the thread

    bounce_noise_gen = bounce_noise_gen_threadclass()
    bounce_noise_gen.setDaemon(True) # so a ctrl-C can terminate it
    bounce_noise_gen.start() # start the thread



def button_press_isr(LOOP_IN):
    global edge_count, pulse_count, start_time, error_count
    global glitch_error_timing, glitch_error

    # hack to prevent system generated glitches to falsify the results
    # this probably happens when the edge recognition is turned back on
    # the period between the end of the ISR and the restart is 350uSec-1.8mSec,
    # so let's prevent that from causing erroneous edge counts.
    if (time() - glitch_error_timing) < 0.002 : # 2 mSec
        glitch_error += 1
        if ERROR_REPORT :
            print "*** system glitch: ", glitch_error
        glitch_error_timing = time() # start again
        return

    # begin trigger pulse when we really enter
    GPIO.output(TRIGGER, GPIO.HIGH)

    timestart = time() # save the start time for the maximum duration and errors

    # wait until we are in the middle of the pulse, or clearly after it,
    # with or without edge bounce but still before the bouncetime runs out.
    # This needs to be tuned for other applications.
    sleep(0.025)

    if GPIO.input(LOOP_IN) == RISING : # 0/False for Falling, 1/True for Rising
        edge_count += 1 # we have a valid button press

        if edge_count <> pulse_count :
            error_count += 1
            # send out an error indication to the LA
            GPIO.output(ERROR, GPIO.HIGH)
            sleep(0.001)
            GPIO.output(ERROR, GPIO.LOW)

            if ERROR_REPORT :
                print "pulses : {0}\tedges : {1}\t total : {2}\trun time : {3:.2f} sec."\
                    .format(pulse_count, edge_count, total_count, (time() - start_time))
                pulse_count = 0
                edge_count = 0

    glitch_error_timing = time() # start the timer

    # end trigger pulse when we leave
    GPIO.output(TRIGGER, GPIO.LOW)
    return


def main():
    global edge_count, pulse_count, start_time, error_count, total_count
    global glitch_error_timing, glitch_error

    if GPIO_CLEANUP :
        GPIO.setwarnings(True)
    else:
        GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(LOOP_IN, GPIO.IN) # do not use pull parameter with 10K looping
    GPIO.setup(LOOP_OUT, GPIO.OUT)
    GPIO.setup(TRIGGER, GPIO.OUT, initial=GPIO.LOW)
    GPIO.setup(ERROR, GPIO.OUT, initial=GPIO.LOW)

    print "testing edges with bouncetime edge detection : {edge} {count}x"\
        .format(edge="rising edge" if RISING else "falling edge", count = PULSE_CNT)

    # This is really a brute force measure.
    # Bouncetime is set to encompass the leading and trailing noise plus the
    # pulse itself. (10+50+10 mSec)
    # The bouncetime parameter should be less than the repetition rate,
    # which is 80 mSec. This needs to be tuned for other applications.
    if RISING :
        GPIO.add_event_detect(LOOP_IN, GPIO.RISING, callback=button_press_isr, bouncetime=70)
    else:
        GPIO.add_event_detect(LOOP_IN, GPIO.FALLING, callback=button_press_isr, bouncetime=70)

    try:
        if PULSE :
            glitch_error_timing = 0.01
            glitch_error = 0
            pulse_count = 0
            edge_count = 0
            error_count = 0
            total_count = 0
            start_time = time()
            pulse_gen() # start the pulse generator
            print "normal edges"
            while total_count < PULSE_CNT :
                sleep(0.5) # or your code

            if error_count > 0 : print "\n"
            tFile = open('/sys/class/thermal/thermal_zone0/temp')
            temp = float(tFile.read())/1000
            print "total count: {0}   errors: {1}   {2:.2f}%   run_time: {3:.2f} min.  cpu temp: {4:.2f}C  glitches: {5}"\
                .format(total_count, error_count, \
                (1-(float(error_count)/float(total_count)))*100, \
                (time() - start_time)/60, temp, glitch_error)

            sleep(1) # let the thread terminate before we start again or exit

        if BOUNCE_NOISE :
            glitch_error_timing = 0.01
            glitch_error = 0
            pulse_count = 0
            edge_count = 0
            error_count = 0
            total_count = 0
            start_time = time() # used in the ISR
            bounce_noise_gen() # start the pulse generator with bounce noise
            print "\nedges with bounce noise"
            while total_count < PULSE_CNT :
                sleep(0.5) # or your code

            if error_count > 0 : print "\n"
            tFile = open('/sys/class/thermal/thermal_zone0/temp')
            temp = float(tFile.read())/1000
            print "total count: {0}   errors: {1}   {2:.2f}%   run_time: {3:.2f} min.  cpu temp: {4:.2f}C  glitches: {5}"\
                .format(total_count, error_count, \
                (1-(float(error_count)/float(total_count)))*100, \
                (time() - start_time)/60, temp, glitch_error)

            sleep(1) # let the thread terminate before we exit


    except KeyboardInterrupt:
        print "\nterminated"
    except Exception as e:
        print "exception : ", e
    finally:
        # in batch mode it's better not to do the clean-up so the LA continues
        # to look for triggers. A cleanup resets the levels from low to float
        # and this will be seen as a high by the LA, causing a trigger at the end
        # of every run
        if GPIO_CLEANUP :
            GPIO.cleanup([LOOP_IN, TRIGGER, LOOP_OUT, ERROR])
        print "\ndone"


if __name__ == '__main__':
    main()
This one also needs to be "tuned" for the application.

Script C is more elaborate and also much more flexible. It does not need any tuning, you can throw any pulse to it that is longer then say 20mSec, and as long as input glitches are not longer than 13mSec or so, it will be correct for 99.9%.

Code: Select all

#!/usr/bin/env python2

from time import sleep, time
import RPi.GPIO as GPIO
from threading import Thread

__author__ = 'Paul Versteeg'

LOOP_IN = 25    # GPIO-25, P1-22
LOOP_OUT = 23   # GPIO-23, P1-16
TRIGGER = 24    # GPIO-24, P1-18 (for LA or scope use)
ERROR = 22      # GPIO-22, P1-15 (for LA or scope use)

RISING = True
BOUNCE_NOISE = True
PULSE_CNT = 100
ISR_DURATION = True

#===============================================================================
# quick and ugly hack to automate the testing parameters
import sys
import os

if len(sys.argv) > 0:
    if str((sys.argv[1])) == "-?":
        print "\n\tusage : [rising|falling, count, errors, bounce|pulse|both, duration, largec, cleanup]\n"
        RISING = True
        BOUNCE_NOISE = True
        PULSE_CNT = 100
        ISR_DURATION = False
        os._exit(0)
    if str((sys.argv[1])) == "falling":
        RISING = False
    if str((sys.argv[1])) == "rising":
        RISING = True
else:
    RISING = True

if len(sys.argv) > 1 :
    PULSE_CNT = int(sys.argv[2])
else:
    PULSE_CNT = 6000 # about 15 minutes, 10,000 25 min.

if len(sys.argv) > 2 :
    if str(sys.argv[3]) == "errors":
        ERROR_REPORT = True
    else:
        ERROR_REPORT = False
else:
    ERROR_REPORT = False

if len(sys.argv) > 3 :
    if str(sys.argv[4]) == "bounce":
        BOUNCE_NOISE = True
        PULSE = False
    if str(sys.argv[4]) == "pulse":
        PULSE = True
        BOUNCE_NOISE = False
    if str(sys.argv[4]) == "both":
        PULSE = True
        BOUNCE_NOISE = True
else:
    BOUNCE_NOISE = True
    PULSE = True

if len(sys.argv) > 4 :
    if str(sys.argv[5]) == "duration":
        ISR_DURATION = True
    else:
        ISR_DURATION = False
else:
    ISR_DURATION = False

if len(sys.argv) > 5 :
    if str(sys.argv[6]) == "largec":
        LARGE_C = True
    else:
        LARGE_C = False
else:
    LARGE_C = False

if len(sys.argv) > 6 :
    if str(sys.argv[7]) == "cleanup":
        GPIO_CLEANUP = True
    else:
        GPIO_CLEANUP = False
else:
    GPIO_CLEANUP = True

print "cleanup = ", GPIO_CLEANUP
#===============================================================================


def pulse_gen():
    # free running pulse generator
    global pulse_count, total_count

    class pulse_gen_threadclass(Thread):
        def run(self):
            global pulse_count, total_count

            sleep(1) # wait for the edge detection setup to finish
            if RISING:
                while pulse_count < PULSE_CNT : # active high
                    GPIO.output(LOOP_OUT, GPIO.HIGH)
                    pulse_count += 1 # count on leading edge for RISING
                    if LARGE_C:
                        sleep(1) # 1Sec up
                    else:
                        sleep(0.05) # 50 mSec up
                    GPIO.output(LOOP_OUT, GPIO.LOW)
                    if LARGE_C:
                        sleep(5) # 5 Sec pulse down
                    else:
                        sleep(0.1) # 100 mSec pulse down
                    total_count += 1
                exit # stop the thread
            else:
                while pulse_count < PULSE_CNT : # active low
                    GPIO.output(LOOP_OUT, GPIO.LOW)
                    pulse_count += 1 # count on leading edge for FALLING
                    if LARGE_C:
                        sleep(1) # 1Sec up
                    else:
                        sleep(0.05) # 50 mSec up
                    GPIO.output(LOOP_OUT, GPIO.HIGH)
                    total_count += 1
                    if LARGE_C:
                        sleep(5) # 5 Sec pulse down
                    else:
                        sleep(0.1) # 100 mSec pulse down
                exit # stop the thread

    pulse_gen = pulse_gen_threadclass()
    pulse_gen.setDaemon(True) # so a ctrl-C can terminate it
    pulse_gen.start() # start the thread


def bounce_noise_gen():
    # free running pulse generator
    global pulse_count, total_count

    class bounce_noise_gen_threadclass(Thread):
        def run(self):
            global pulse_count, total_count

            if RISING : # start with a low level
                GPIO.output(LOOP_OUT, GPIO.LOW)
            else: # falling: start with a high
                GPIO.output(LOOP_OUT, GPIO.HIGH)
            sleep(1) # wait for the edge detection setup to finish

            while total_count < PULSE_CNT :

                if RISING:
                    GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 1
                    sleep(.001)# about 1 mSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                # no wait, about 10 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 2
                sleep(0.00001) # about 150 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.00001) # about 50 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 3
                sleep(0.00005) # 50 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.00001) # 110 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 4
                sleep(0.0018) # 1.8 mSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.002) # 2 mSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 5
                # no wait, about 10 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.0005) # 50 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 6
                sleep(0.005) # 5 mSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.001) # 1 mSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # pulse

                if RISING : # end with a high
                    pulse_count += 1 # count on leading edge for RISING
                    total_count += 1
                else : # end with a low
                    sleep(0.0001) # end with a low
                    GPIO.output(LOOP_OUT, GPIO.LOW)
                    pulse_count += 1 # count on leading edge for FALLING
                    total_count += 1

                if LARGE_C :
                    sleep(1) # 1 Sec for caps 10uF and above
                else:
                    sleep(0.05) # 50 mSec pulse duration down

                if RISING :
                    GPIO.output(LOOP_OUT, GPIO.LOW)
                    sleep(0.00005) # 50 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 1
                # no wait, about 10 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                # no wait, about 10 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 2
                sleep(0.00001) # about 150 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.00001) # about 50 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 3
                sleep(0.00005) # 50 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.00001) # 110 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 4
                sleep(0.0018) # 1.8 mSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.002) # 2 mSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 5
                # no wait, about 10 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.0005) # 50 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 6
                sleep(0.005) # 5 mSec
                GPIO.output(LOOP_OUT, GPIO.LOW)

                if RISING : # end with a low
                    sleep(0.001)
                else: # end with a high
                    GPIO.output(LOOP_OUT, GPIO.HIGH)

                if LARGE_C:
                    sleep(5) # 5 Sec for caps 10uF and above
                else:
                    sleep(0.08) # 80 mSec but total is 100 mSec duration with noise

            exit # stop the thread

    bounce_noise_gen = bounce_noise_gen_threadclass()
    bounce_noise_gen.setDaemon(True) # so a ctrl-C can terminate it
    bounce_noise_gen.start() # start the thread



def button_press_isr(LOOP_IN):
    global edge_count, pulse_count, error_count
    global glitch_error_timing, glitch_error

    # remove the edge detection for the duration of this ISR
    GPIO.remove_event_detect(LOOP_IN)

    # begin trigger pulse when we enter
    GPIO.output(TRIGGER, GPIO.HIGH)

    edge_start = time() # used to measure total duration time and report error time

    # Hack to prevent "system" generated glitches to falsify the results.
    # This probably happens when the edge recognition is turned back on.
    # The period between the end of the ISR and the restart is 350uSec-11.5mSec,
    # so let's prevent that from causing erroneous edge counts.
    # Make sure that the delay is less than the pulse width with some margin.
    if (time() - glitch_error_timing) < 0.015 : # 15 mSec
        glitch_error += 1
        if ERROR_REPORT :
            print "*** system glitch: ", glitch_error
        glitch_error_timing = time() # start again
        # turn the event detection back on
        if RISING :
            GPIO.add_event_detect(LOOP_IN, GPIO.RISING, callback=button_press_isr)
        else:
            GPIO.add_event_detect(LOOP_IN, GPIO.FALLING, callback=button_press_isr)
        return

    # Do we have 7 correct consecutive logic levels within a 35 mSec period?
    cnt = 0

    while (time() - edge_start) <= 0.035 : # 35 mSec max, 15mSec margin
        # we have detected the first rising edge, now start to count the logic
        # levels by sampling 7 x roughly every 1.5 mSec = 10.5 mS, which is
        # then also the largest glitch we can skip.
        sleep(0.0015) # 1.5 mSec
        if GPIO.input(LOOP_IN) == RISING : # 0/False for Falling, 1/True for Rising
            cnt += 1
        else: # when we have captured a glitch, start all over again
            cnt = 0

        # With 7 consecutive correct logic levels, or when the max time is up, leave.
        # Max time is from first edge onwards. Stay below min. pulse width of
        # 50 mSec when there is no noise.
        if cnt == 7 : # 7? lucky number
            break
    if cnt == 7 :
        edge_count += 1 # we have a valid edge

        if edge_count <> pulse_count : # incorrect edge count
            error_count += 1
            # send out an error indication to the LA
            GPIO.output(ERROR, GPIO.HIGH)
            sleep(0.001)
            GPIO.output(ERROR, GPIO.LOW)

            if ERROR_REPORT :
                print "pulses : {0}\tedges : {1}\t total : {2}\trun time : {3:.2f} sec."\
                    .format(pulse_count, edge_count, total_count, (time() - start_time))
                # restart the counters so we can see the next mismatch happening
                pulse_count = 0
                edge_count = 0

    # we can measure the time we take within the ISR to see the "Linux" effects
    if ISR_DURATION :
        edge_end = time() - edge_start
        if BOUNCE_NOISE:
            if edge_end > 0.040 : # 40 mSec due to the excessive noise time.
                print "isr duration extended 40 mSec. : {0:.2f}".format(edge_end*1000)
        else :
            if edge_end > 0.020 : # 20 mSec
                print "isr duration extended 20 mSec. : {0:.2f}".format(edge_end*1000)

    # end trigger pulse when we leave
    GPIO.output(TRIGGER, GPIO.LOW)

    glitch_error_timing = time() # start the glitch timer again

    # at the very last, turn the event detection back on
    if RISING :
        GPIO.add_event_detect(LOOP_IN, GPIO.RISING, callback=button_press_isr)
    else:
        GPIO.add_event_detect(LOOP_IN, GPIO.FALLING, callback=button_press_isr)
    return



def main():
    global edge_count, pulse_count, start_time, error_count, total_count
    global glitch_error_timing, glitch_error

    if GPIO_CLEANUP :
        GPIO.setwarnings(True)
    else:
        GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(LOOP_IN, GPIO.IN) # do not use pull parameter with 10K looping
    GPIO.setup(LOOP_OUT, GPIO.OUT)
    GPIO.setup(TRIGGER, GPIO.OUT, initial=GPIO.LOW)
    GPIO.setup(ERROR, GPIO.OUT, initial=GPIO.LOW)

    print "testing edges with extended start-stop edge detection : {edge} {count}x"\
        .format(edge="rising edge" if RISING else "falling edge", count = PULSE_CNT)

    if RISING :
        GPIO.add_event_detect(LOOP_IN, GPIO.RISING, callback=button_press_isr)
    else:
        GPIO.add_event_detect(LOOP_IN, GPIO.FALLING, callback=button_press_isr)

    try:
        if PULSE :
            glitch_error_timing = 0.01
            glitch_error = 0
            pulse_count = 0
            edge_count = 0
            error_count = 0
            total_count = 0
            start_time = time() # used in the ISR
            pulse_gen() # start the pulse generator
            print "normal edges"
            while total_count < PULSE_CNT :
                sleep(0.5) # or your code

            if error_count > 0 : print "\n"
            tFile = open('/sys/class/thermal/thermal_zone0/temp')
            temp = float(tFile.read())/1000
            print "total count: {0}   errors: {1}   {2:.2f}%   run_time: {3:.2f} min.  cpu temp: {4:.2f}C  glitches: {5}"\
                .format(total_count, error_count, \
                (1-(float(error_count)/float(total_count)))*100, \
                (time() - start_time)/60, temp, glitch_error)

            sleep(1) # let the thread terminate before we start again or exit

        if BOUNCE_NOISE :
            glitch_error_timing = 0.01
            glitch_error = 0
            pulse_count = 0
            edge_count = 0
            error_count = 0
            total_count = 0
            start_time = time() # used in the ISR
            bounce_noise_gen() # start the pulse generator with bounce noise
            print "\nedges with bounce noise"
            while total_count < PULSE_CNT : # about 15 minutes
                sleep(0.5) # or your code

            if error_count > 0 : print "\n"
            tFile = open('/sys/class/thermal/thermal_zone0/temp')
            temp = float(tFile.read())/1000
            print "total count: {0}   errors: {1}   {2:.2f}%   run_time: {3:.2f} min.  cpu temp: {4:.2f}C  glitches: {5}"\
                .format(total_count, error_count, \
                (1-(float(error_count)/float(total_count)))*100, \
                (time() - start_time)/60, temp, glitch_error)

            sleep(1) # let the thread terminate before we exit

    except KeyboardInterrupt:
        print "\nterminated"
    except Exception as e:
        print "exception : ", e
    finally:
        # in batch mode it's better not to do the clean-up so the LA continues
        # to look for triggers. A cleanup resets the levels from low to float
        # and this will be seen as a high by the LA, causing a trigger at the end
        # of every run
        if GPIO_CLEANUP :
            GPIO.cleanup([LOOP_IN, TRIGGER, LOOP_OUT, ERROR])
        print "\ndone"


if __name__ == '__main__':
    main()
I ran out of text room, so the rest is in a third post.
Last edited by paulv on Tue Feb 23, 2016 9:42 am, edited 5 times in total.

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

Re: Characterizing GPIO input pins

Mon Feb 22, 2016 8:44 pm

This is part 3

Script D is based on script C, but completely follows the active high or active low pulse, no matter how long or short it is.

Code: Select all

#!/usr/bin/env python2

from time import sleep, time
import RPi.GPIO as GPIO
from threading import Thread

__author__ = 'Paul Versteeg'

LOOP_IN = 25    # GPIO-25, P1-22
LOOP_OUT = 23   # GPIO-23, P1-16
TRIGGER = 24    # GPIO-24, P1-18 (for LA or scope use)
ERROR = 22      # GPIO-22, P1-15 (for LA or scope use)

RISING = True
BOUNCE_NOISE = True
PULSE_CNT = 100
ISR_DURATION = True

#===============================================================================
# quick and ugly hack to automate the testing parameters
import sys
import os

if len(sys.argv) > 0:
    if str(sys.argv[1]) == "-?":
        print "\n\tusage : [rising|falling, count, errors, bounce|pulse|both, duration, largec, cleanup]\n"
        RISING = True
        BOUNCE_NOISE = True
        PULSE_CNT = 100
        ISR_DURATION = False
        os._exit(0)
    if str((sys.argv[1])) == "falling":
        RISING = False
    if str((sys.argv[1])) == "rising":
        RISING = True
else:
    RISING = True

if len(sys.argv) > 21:
    PULSE_CNT = int(sys.argv[2])
else:
    PULSE_CNT = 6000 # about 15 minutes, 10,000 25 min.

if len(sys.argv) > 2 :
    if str(sys.argv[3]) == "errors":
        ERROR_REPORT = True
    else:
        ERROR_REPORT = False
else:
    ERROR_REPORT = False

if len(sys.argv) > 3 :
    if str(sys.argv[4]) == "bounce":
        BOUNCE_NOISE = True
        PULSE = False
    if str(sys.argv[4]) == "pulse":
        PULSE = True
        BOUNCE_NOISE = False
    if str(sys.argv[4]) == "both":
        PULSE = True
        BOUNCE_NOISE = True
else:
    BOUNCE_NOISE = True
    PULSE = True

if len(sys.argv) > 4 :
    if str(sys.argv[5]) == "duration":
        ISR_DURATION = True
    else:
        ISR_DURATION = False
else:
    ISR_DURATION = False

if len(sys.argv) > 5 :
    if str(sys.argv[6]) == "largec":
        LARGE_C = True
    else:
        LARGE_C = False
else:
    LARGE_C = False

if len(sys.argv) > 6 :
    if str(sys.argv[7]) == "cleanup":
        GPIO_CLEANUP = True
    else:
        GPIO_CLEANUP = False
else:
    GPIO_CLEANUP = True
print "cleanup = ", GPIO_CLEANUP
#===============================================================================


def pulse_gen():
    # free running pulse generator
    global pulse_count, total_count

    class pulse_gen_threadclass(Thread):
        def run(self):
            global pulse_count, total_count

            sleep(1) # wait for the edge detection setup to finish
            if RISING:
                while pulse_count < PULSE_CNT : # active high
                    GPIO.output(LOOP_OUT, GPIO.HIGH)
                    pulse_count += 1 # count on leading edge for RISING
                    if LARGE_C:
                        sleep(1) # 1Sec up
                    else:
                        sleep(0.05) # 50 mSec up
                    GPIO.output(LOOP_OUT, GPIO.LOW)
                    if LARGE_C:
                        sleep(5) # 5 Sec pulse down
                    else:
                        sleep(0.1) # 100 mSec pulse down
                    total_count += 1
                exit # stop the thread
            else:
                while pulse_count < PULSE_CNT : # active low
                    GPIO.output(LOOP_OUT, GPIO.LOW)
                    pulse_count += 1 # count on leading edge for FALLING
                    if LARGE_C:
                        sleep(1) # 1Sec up
                    else:
                        sleep(0.05) # 50 mSec up
                    GPIO.output(LOOP_OUT, GPIO.HIGH)
                    total_count += 1
                    if LARGE_C:
                        sleep(5) # 5 Sec pulse down
                    else:
                        sleep(0.1) # 100 mSec pulse down
                exit # stop the thread

    pulse_gen = pulse_gen_threadclass()
    pulse_gen.setDaemon(True) # so a ctrl-C can terminate it
    pulse_gen.start() # start the thread



def bounce_noise_gen():
    # free running pulse generator
    global pulse_count, total_count

    class bounce_noise_gen_threadclass(Thread):
        def run(self):
            global pulse_count, total_count

            if RISING : # start with a low level
                GPIO.output(LOOP_OUT, GPIO.LOW)
            else: # falling: start with a high
                GPIO.output(LOOP_OUT, GPIO.HIGH)
            sleep(1) # wait for the edge detection setup to finish

            while total_count < PULSE_CNT :

                if RISING:
                    GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 1
                    sleep(.001)# about 1 mSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                # no wait, about 10 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 2
                sleep(0.00001) # about 150 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.00001) # about 50 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 3
                sleep(0.00005) # 50 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.00001) # 110 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 4
                sleep(0.0018) # 1.8 mSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.002) # 2 mSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 5
                # no wait, about 10 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.0005) # 50 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 6
                sleep(0.005) # 5 mSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.001) # 1 mSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # pulse

                if RISING : # end with a high
                    pulse_count += 1 # count on leading edge for RISING
                    total_count += 1
                else : # end with a low
                    sleep(0.0001) # end with a low
                    GPIO.output(LOOP_OUT, GPIO.LOW)
                    pulse_count += 1 # count on leading edge for FALLING
                    total_count += 1

                if LARGE_C :
                    sleep(1) # 1 Sec for caps 10uF and above
                else:
                    sleep(0.05) # 50 mSec pulse duration down

                if RISING :
                    GPIO.output(LOOP_OUT, GPIO.LOW)
                    sleep(0.00005) # 50 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 1
                # no wait, about 10 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                # no wait, about 10 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 2
                sleep(0.00001) # about 150 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.00001) # about 50 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 3
                sleep(0.00005) # 50 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.00001) # 110 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 4
                sleep(0.0018) # 1.8 mSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.002) # 2 mSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 5
                # no wait, about 10 uSec
                GPIO.output(LOOP_OUT, GPIO.LOW)
                sleep(0.0005) # 50 uSec
                GPIO.output(LOOP_OUT, GPIO.HIGH) # glitch 6
                sleep(0.005) # 5 mSec
                GPIO.output(LOOP_OUT, GPIO.LOW)

                if RISING : # end with a low
                    sleep(0.001)
                else: # end with a high
                    GPIO.output(LOOP_OUT, GPIO.HIGH)

                if LARGE_C:
                    sleep(5) # 5 Sec for caps 10uF and above
                else:
                    sleep(0.08) # 80 mSec but total is 100 mSec duration with noise

            exit # stop the thread

    bounce_noise_gen = bounce_noise_gen_threadclass()
    bounce_noise_gen.setDaemon(True) # so a ctrl-C can terminate it
    bounce_noise_gen.start() # start the thread



def button_press_isr(LOOP_IN):
    '''
    This ISR is used to recognize either active high or active low pulses.

    Active high pulses need to have the RISING constant set to True
    Active low pulses need to have the RISING constant set to False
    '''
    global edge_count, pulse_count, error_count
    global glitch_error_timing, glitch_error

    # remove the edge detection for the duration of this ISR
    GPIO.remove_event_detect(LOOP_IN)

    # begin trigger pulse when we enter
    GPIO.output(TRIGGER, GPIO.HIGH)

    edge_start = time() # used to measure total duration time and report error time

    # Hack to prevent "system" generated glitches to falsify the results.
    # This probably happens when the edge recognition is turned back on.
    # The period between the end of the ISR and the restart is 350uSec-11.5mSec,
    # so let's prevent that from causing erroneous edge counts.
    # Make sure that the delay is less than the pulse width with some margin.
    if (time() - glitch_error_timing) < 0.015 : # 15 mSec
        glitch_error += 1
        if ERROR_REPORT :
            print "*** system glitch: ", glitch_error
        glitch_error_timing = time() # start again
        # turn the event detection back on
        if RISING :
            GPIO.add_event_detect(LOOP_IN, GPIO.RISING, callback=button_press_isr)
        else:
            GPIO.add_event_detect(LOOP_IN, GPIO.FALLING, callback=button_press_isr)
        return

    # Do we have 7 correct consecutive logic levels within a 35 mSec period?
    cnt = 0

    while (time() - edge_start) <= 0.035 : # 35 mSec max, 15mSec margin
        # we have detected the first rising edge, now start to count the logic
        # levels by sampling 7 x roughly every 1.5 mSec = 10.5 mS, which is
        # then also the largest glitch we can skip.
        sleep(0.0015) # 1.5 mSec
        if GPIO.input(LOOP_IN) == RISING : # 0/False for Falling, 1/True for Rising
            cnt += 1
        else: # when we have captured a glitch, start all over again
            cnt = 0

        # With 7 consecutive correct logic levels, or when the max time is up, leave.
        # Max time is from first edge onwards. Stay below min. pulse width of
        # 50 mSec when there is no noise.
        if cnt == 7 : # 7? lucky number
            break
    if cnt == 7 :
        edge_count += 1 # we have a valid edge

        if edge_count <> pulse_count : # incorrect edge count
            error_count += 1
            # send out an error indication to the LA
            GPIO.output(ERROR, GPIO.HIGH)
            sleep(0.001)
            GPIO.output(ERROR, GPIO.LOW)

            if ERROR_REPORT :
                print "pulses : {0}\tedges : {1}\t total : {2}\trun time : {3:.2f} sec."\
                    .format(pulse_count, edge_count, total_count, (time() - start_time))
                # restart the counters so we can see the next mismatch happening
                pulse_count = 0
                edge_count = 0

    # we have a valid edge detected, now wait for the reverse edge to happen.
    # Do we have 7 correct consecutive logic levels?
    cnt = 0

    while True:
        # now start to count the logic for the reverse edge
        # levels by sampling 7 x roughly every 1.5 mSec = 10.5 mS, which is
        # then also the largest glitch we can skip.
        sleep(0.0015) # 1.5 mSec
        if GPIO.input(LOOP_IN) == (not RISING) : # 0/False for Falling, 1/True for Rising
            cnt += 1
        else: # when we have captured a glitch, start all over again
            cnt = 0

        # With 7 consecutive correct logic levels, leave.
        # Max time is from first edge onwards. Stay below min. pulse width of
        # 50 mSec when there is no noise.
        if cnt == 7 : # 7? lucky number
            break
    # start to finish the cycle

    # we can measure the time we take within the ISR to see the "Linux" effects
    # or to measure the total pulse duration from leading edge to falling edge
    # plus an additional 10.5mSec, which then needs to be subtracted
    if ISR_DURATION :
        edge_end = time() - edge_start
        if BOUNCE_NOISE:
            if edge_end > 0.085 : # 10+50+10+10.5mSec. with 4.5 mSec margin
                print "isr duration extended 75 mSec. : {0:.2f}".format(edge_end*1000)
        else :
            if edge_end > 0.065 : # 50+10.5 mSec. with 4.5 mSec margin
                print "isr duration extended 55 mSec. : {0:.2f}".format(edge_end*1000)

    # end trigger pulse when we leave
    GPIO.output(TRIGGER, GPIO.LOW)

    glitch_error_timing = time() # start the glitch timer again

    # at the very last, turn the event detection back on
    if RISING :
        GPIO.add_event_detect(LOOP_IN, GPIO.RISING, callback=button_press_isr)
    else:
        GPIO.add_event_detect(LOOP_IN, GPIO.FALLING, callback=button_press_isr)
    return



def main():
    global edge_count, pulse_count, start_time, error_count, total_count
    global glitch_error_timing, glitch_error

    if GPIO_CLEANUP :
        GPIO.setwarnings(True)
    else:
        GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(LOOP_IN, GPIO.IN) # do not use pull parameter with 10K looping
    if RISING: # active high
        GPIO.setup(LOOP_OUT, GPIO.OUT, initial=GPIO.LOW)
    else: # active low
        GPIO.setup(LOOP_OUT, GPIO.OUT, initial=GPIO.HIGH)
    GPIO.setup(TRIGGER, GPIO.OUT, initial=GPIO.LOW)
    GPIO.setup(ERROR, GPIO.OUT, initial=GPIO.LOW)

    print "testing edges with extended start-stop edge detection : {edge} {count}x"\
        .format(edge="rising edge" if RISING else "falling edge", count = PULSE_CNT)

    if RISING :
        GPIO.add_event_detect(LOOP_IN, GPIO.RISING, callback=button_press_isr)
    else:
        GPIO.add_event_detect(LOOP_IN, GPIO.FALLING, callback=button_press_isr)

    try:
        if PULSE :
            glitch_error_timing = 0.01
            glitch_error = 0
            pulse_count = 0
            edge_count = 0
            error_count = 0
            total_count = 0
            start_time = time() # used in the ISR
            pulse_gen() # start the pulse generator
            print "normal edges"
            while total_count < PULSE_CNT : # about 15 minutes
                sleep(0.5) # or your code

            if error_count > 0 : print "\n"
            tFile = open('/sys/class/thermal/thermal_zone0/temp')
            temp = float(tFile.read())/1000
            print "total count: {0}   errors: {1}   {2:.2f}%   run_time: {3:.2f} min.  cpu temp: {4:.2f}C  glitches: {5}"\
                .format(total_count, error_count, \
                (1-(float(error_count)/float(total_count)))*100, \
                (time() - start_time)/60, temp, glitch_error)

            sleep(1) # let the thread terminate before we start again or exit

        if BOUNCE_NOISE :
            glitch_error_timing = 0.01
            glitch_error = 0
            pulse_count = 0
            edge_count = 0
            error_count = 0
            total_count = 0
            start_time = time() # used in the ISR
            bounce_noise_gen() # start the pulse generator with bounce noise
            print "\nedges with bounce noise"
            while total_count < PULSE_CNT :
                sleep(0.5) # or your code

            if error_count > 0 : print "\n"
            tFile = open('/sys/class/thermal/thermal_zone0/temp')
            temp = float(tFile.read())/1000
            print "total count: {0}   errors: {1}   {2:.2f}%   run_time: {3:.2f} min.  cpu temp: {4:.2f}C  glitches: {5}"\
                .format(total_count, error_count, \
                (1-(float(error_count)/float(total_count)))*100, \
                (time() - start_time)/60, temp, glitch_error)

            sleep(1) # let the thread terminate before we exit

    except KeyboardInterrupt:
        print "\nterminated"
    except Exception as e:
        print "exception : ", e
    finally:
        # in batch mode it's better not to do the clean-up so the LA continues
        # to look for triggers. A cleanup resets the levels from low to float
        # and this will be seen as a high by the LA, causing a trigger at the end
        # of every run
        if GPIO_CLEANUP :
            GPIO.cleanup([LOOP_IN, TRIGGER, LOOP_OUT, ERROR])
        print "\ndone"


if __name__ == '__main__':
    main()
Here is an elaborate test report from all four scripts with various capacities loaded. Only the burst generator with glitches was used.
Glitch-Report1.gif
Glitch-Report1.gif (36.16 KiB) Viewed 3285 times
Glitch-Report2.gif
Glitch-Report2.gif (42.81 KiB) Viewed 3285 times
As you can see, script D works really well for fast and very slow moving edges. The tests with capacitors above 1uF were made with 1 sec. on / 5 sec off pulses. This gives the capacitor time to charge and decharge. The script worked flawlessly with a 100% accuracy. The rising edges for script C also work pretty good up to 100uF but the falling edges do not go above 1uF without adding the code D already has.

Scripts C and D will be the ones I will select for my own applications. Script C is great for a falling or rising edge detection like coming from a switch or slow level changes like a dropping input voltage. Script D is great for button presses and also slow level changes. Both can be easily tailored, are very reliable and more forgiving on different switch or event parameters then script A or B.

So how fast can the pulse be with script D without modifying it? I tested with a clean pulse of 25mS on/25mS off, and the ISR took 36mS in total, no errors.
I also tested the bounce pulse, by reducing the on pulse period to 15mS. This produces a pulse from first edge to last of 51 mSec. The down was lowered to 25mS. Also no errors.
If you need to go a bit faster, you can tweak the time between the logic level reads and change them from 1.5 mS to 1 mS, which saves about 7mS in total, but the largest glitch that can be recognized is now 7 mSec + overhead.

Here is another example of the "system" related pulses that cause false edges to be recognized.
System-Glitches(low res).gif
System-Glitches(low res).gif (50.77 KiB) Viewed 3258 times
They only happen every once in a while, like 1-5 in a 100, but they do cause edge errors.

I hope that after I have shown that there is an additional problem, future enhancements to the RPi.GPIO libary, or maybe in the kernel, can put a stop to this annoying situation.

To finalize, after I investigated precisely what is going on, and now knowing what causes the erroneous edge triggers, I was able to get four scripts working with an excellent accuracy even under worse case conditions. And that without slapping a bounce filter or any additional hardware like Schmitt-Triggers, flip-flops or gates to the "challenge".

The "acid test" for such a "challenge" is, in my opinion, to use a potmeter to feed a slow moving voltage level to the ISR, and see if it copes with those signals. Both scripts C and D pass with flying colors. With that result I can declare the need for a flip-flop or Schmitt-Trigger as "busted".

So why would you still use a bounce filter if these scripts already work reliably without? I still suggest that you try to filter the high speed spikes and glitches going to the inputs of the Pi. It just makes common sense to do it. My recommendation is to use a 100nF-4u7F capacitor for typical push buttons and switches. Any other "mechanical" inputs or external level changes will need to be tried and tested with any of the four scripts.

Enjoy!

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

Re: Characterizing GPIO input pins

Wed Feb 24, 2016 10:20 am

Here is a version of the button debounce script D with just the bare minimum to run a button test yourself. The script still has the test and statistics code in, so you can measure the number of presses and the duration of the press, and the total time.
It also has the "hooks" for a scope or logic analyzer in so you can follow what is going on.

The code is for a button configured as active high, as in circuit-3, it works without a capacitor.

Code: Select all

#!/usr/bin/env python2

# this button test uses an active high pulse (circuit-3)
# as a worst-case test, there is no need for a capacitor on the input pin

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

__author__ = 'Paul Versteeg'

LOOP_IN = 25    # GPIO-25, P1-22
LOOP_OUT = 23   # GPIO-23, P1-16
TRIGGER = 24    # GPIO-24, P1-18 (for LA or scope use)
ERROR = 22      # GPIO-22, P1-15 (for LA or scope use)

# run-time constants
PULSE_CNT = 10
ISR_DURATION = True
ERROR_REPORT = True
BOUNCE_NOISE = True


def button_press_isr(LOOP_IN):
    global total_count
    global glitch_error_timing, glitch_error

    GPIO.remove_event_detect(LOOP_IN)
    GPIO.output(TRIGGER, GPIO.HIGH)
    edge_start = time() # used to measure total duration time and report error time
    if (time() - glitch_error_timing) < 0.015 : # 15 mSec
        glitch_error += 1
        GPIO.output(ERROR, GPIO.HIGH)
        sleep(0.0001)
        GPIO.output(ERROR, GPIO.LOW)
        if ERROR_REPORT :
            print "*** system glitch: ", glitch_error
        glitch_error_timing = time() # start again
        # turn the event detection back on
        GPIO.add_event_detect(LOOP_IN, GPIO.RISING, callback=button_press_isr)
        # skip this edge
        return
    cnt = 0
    while (time() - edge_start) <= 0.035 : # 35 mSec max, 15mSec margin
        sleep(0.0015) # 1.5 mSec
        if GPIO.input(LOOP_IN) == 1 : # 0/False for Falling, 1/True for Rising
            cnt += 1
        else: # when we have captured a glitch, start all over again
            cnt = 0
        if cnt == 7 : # 7? lucky number
            break
    if cnt == 7 :
        total_count += 1
    if ERROR_REPORT :
        print "button pressed\t total : {0}\trun time : {1:.2f} sec."\
            .format(total_count, (time() - start_time))
    cnt = 0
    while True:
        sleep(0.0015) # 1.5 mSec
        if GPIO.input(LOOP_IN) == 0 : # 0/False for Falling, 1/True for Rising
            cnt += 1
        else: # when we have captured a glitch, start all over again
            cnt = 0
        if cnt == 7 : # 7? lucky number
            break
    if ISR_DURATION :
        edge_end = time() - edge_start
        if BOUNCE_NOISE:
            print "button released\t isr duration time : {0:.2f} mSec.".format(edge_end*1000)
    GPIO.output(TRIGGER, GPIO.LOW)
    glitch_error_timing = time() # start the glitch timer again
    GPIO.add_event_detect(LOOP_IN, GPIO.RISING, callback=button_press_isr)
    return


def main():
    global start_time, total_count
    global glitch_error_timing, glitch_error

    GPIO.setwarnings(True)
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(LOOP_IN, GPIO.IN) # do not use pull parameter
    GPIO.setup(LOOP_OUT, GPIO.OUT, initial=GPIO.LOW)
    GPIO.setup(TRIGGER, GPIO.OUT, initial=GPIO.LOW)
    GPIO.setup(ERROR, GPIO.OUT, initial=GPIO.LOW)
    print "testing button presses with extended start-stop edge detection (D) : active high  {0}x"\
        .format(PULSE_CNT)
    GPIO.add_event_detect(LOOP_IN, GPIO.RISING, callback=button_press_isr)
    try:
        glitch_error_timing = 0.01
        glitch_error = 0
        total_count = 0
        start_time = time() # used in the ISR
        print "count button presses\n"
        while total_count < PULSE_CNT :
            sleep(0.5) # or your code

        tFile = open('/sys/class/thermal/thermal_zone0/temp')
        temp = float(tFile.read())/1000
        print "\ntotal count: {0}   run_time: {1:.2f} min.  cpu temp: {2:.2f}C  glitches: {3}"\
            .format(total_count, (time() - start_time)/60, temp, glitch_error)

    except KeyboardInterrupt:
        print "\nterminated"
    except Exception as e:
        print "exception : ", e
    finally:
        GPIO.cleanup([LOOP_IN, TRIGGER, LOOP_OUT, ERROR])
        print "\ndone"

if __name__ == '__main__':
    main()

momoaux
Posts: 11
Joined: Fri Aug 19, 2016 11:37 am

Re: Characterizing GPIO input pins

Fri Aug 19, 2016 12:06 pm

Hi, I love the D version but how can I react on other inputs while waiting for one button to be released.

Button pressed = falling
Button released = rising

Example:
2 People press two buttons.

A presses button and holds it.
B presses button and releases.
A releases.

Count == 1 not 2

Do you have a idea how to manage this?

All versions that I came up with are getting false falling flanks from time to time when releasing the button.

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

Re: Characterizing GPIO input pins

Sat Aug 20, 2016 8:13 am

Hi momoaux,

Unfortunately, I cannot figure out what it is you need help with.
In essence, you need two separate versions of the interrupt service routines (ISR) that check the button presses (one for A and one for B), and use a "main" piece to test for the relationship between them. One way of doing that is by setting a global flag within each ISR which shows the main routine what the button status is.

If this is no help, can you post the diagram of your button circuitry and a sample of your code?

Success!

momoaux
Posts: 11
Joined: Fri Aug 19, 2016 11:37 am

Re: Characterizing GPIO input pins

Mon Aug 22, 2016 7:27 am

I have 8 Buttons Connected to the GPIOs and ground.
Each GPIO is also connected via 1,8k pullup to 3.3V

Im using your D routine and just added a Quene.add(LOOP_IN) to next to the total_count += 1
https://docs.python.org/3/library/queue.html

At the moment all Buttons use the same ISR.

GPIO.add_event_detect(18, GPIO.FALLING, callback=button_press_isr)
GPIO.add_event_detect(4, GPIO.FALLING, callback=button_press_isr)
GPIO.add_event_detect(27, GPIO.FALLING, callback=button_press_isr)
GPIO.add_event_detect(17, GPIO.FALLING, callback=button_press_isr)
GPIO.add_event_detect(23, GPIO.FALLING, callback=button_press_isr)
GPIO.add_event_detect(22, GPIO.FALLING, callback=button_press_isr)
GPIO.add_event_detect(25, GPIO.FALLING, callback=button_press_isr)
GPIO.add_event_detect(24, GPIO.FALLING, callback=button_press_isr)

Each button press adds the port number (LOOP_IN) to a Queue that is processed in the main loop and triggers an requests.post() call.

Does that mean I would have to copy the ISR method 8 Times (ISR[1..8]()) to get seperated threads for the routines?

Isn't there a more elegant way for doing that?

Thank you very much for your help.

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

Re: Characterizing GPIO input pins

Mon Aug 22, 2016 7:41 am

Sorry, the way the ISR is written, to avoid the mishaps and issues with the RPi.GPIO code, you have to copy it for every button.

momoaux
Posts: 11
Joined: Fri Aug 19, 2016 11:37 am

Re: Characterizing GPIO input pins

Mon Aug 22, 2016 10:31 am

So I changed to:
GPIO.add_event_detect(18, GPIO.FALLING, callback=button_press_isr1)
GPIO.add_event_detect(4, GPIO.FALLING, callback=button_press_isr2)

But using your D: if Button1 is pressed and not released, it is in the while loop waiting for the RISING. (I'm watching for Falling and check input for 0 first)
So Button2 Presses are ignored until I release button1.

Is there anything I can do to capture Button presses on other Pins while waiting for the RISING on the first pin?

If I got it right, there is one additional thread for all callbacks, not one for each callback.
So I can't wait with a loop, but I need to wait for the release of the button, else I get false hits on release.

I also get an segmentation fault when calling request.post() in the main loop while keep on clicking the buttons. (If I replace the request.post with a sleep(1) no segfault occurs.)
Also if I call the function multiple times via the code (without clicking Buttons) not segfault occurs.

Here is a minimal example that gives me a segfault on the first button press:

Code: Select all

import datetime
import time
from time import mktime, time, sleep
import RPi.GPIO as GPIO
import requests
import queue

queue = queue.Queue()

def button_press_isr1(channel):
    global glitch_error_timing1
    GPIO.remove_event_detect(18)
    sleep(0.0001)
    edge_start1 = time() # used to measure total duration time and report error time
    if (time() - glitch_error_timing1) < 0.015 : # 15 mSec
        sleep(0.0001)
        if ERROR_REPORT :
            print ("*** system glitch ***")
        glitch_error_timing1 = time() # start again
        # turn the event detection back on
        GPIO.add_event_detect(18, GPIO.FALLING, callback=button_press_isr1)
        # skip this edge
        return
    cnt = 0
    while (time() - edge_start1) <= 0.035 : # 35 mSec max, 15mSec margin
        sleep(0.0015) # 1.5 mSec
        if GPIO.input(18) == 0 : # 0/False for Falling, 1/True for Rising
            cnt += 1
        else: # when we have captured a glitch, start all over again
            cnt = 0
        if cnt == 7 : # 7? lucky number
            break
    if cnt == 7 :
        queue.put(18)
    cnt = 0
    while True:
        sleep(0.0015) # 1.5 mSec
        if GPIO.input(18) == 1 : # 0/False for Falling, 1/True for Rising
            cnt += 1
        else: # when we have captured a glitch, start all over again
            cnt = 0
        if cnt == 7 : # 7? lucky number
            break
    glitch_error_timing1 = time() # start the glitch timer again
    GPIO.add_event_detect(18, GPIO.FALLING, callback=button_press_isr1)


def button_press_isr2(channel):
    global glitch_error_timing2
    GPIO.remove_event_detect(4)
    sleep(0.0001)
    edge_start2 = time() # used to measure total duration time and report error time
    if (time() - glitch_error_timing2) < 0.015 : # 15 mSec
        sleep(0.0001)
        glitch_error_timing2 = time() # start again
        # turn the event detection back on
        GPIO.add_event_detect(4, GPIO.FALLING, callback=button_press_isr2)
        # skip this edge
        return
    cnt = 0
    while (time() - edge_start2) <= 0.035 : # 35 mSec max, 15mSec margin
        sleep(0.0015) # 1.5 mSec
        if GPIO.input(4) == 0 : # 0/False for Falling, 1/True for Rising
            cnt += 1
        else: # when we have captured a glitch, start all over again
            cnt = 0
        if cnt == 7 : # 7? lucky number
            break
    if cnt == 7 :
        queue.put(4)
    cnt = 0
    while True:
        sleep(0.0015) # 1.5 mSec
        if GPIO.input(4) == 1 : # 0/False for Falling, 1/True for Rising
            cnt += 1
        else: # when we have captured a glitch, start all over again
            cnt = 0
        if cnt == 7 : # 7? lucky number
            break
    glitch_error_timing2 = time() # start the glitch timer again
    GPIO.add_event_detect(4, GPIO.FALLING, callback=button_press_isr2)



def buttonPress(channel):
    response = requests.post(url="http://192.168.1.5:8080")

def init():
  global glitch_error_timing1, glitch_error_timing2

  glitch_error_timing1 = 0.01
  glitch_error_timing2 = 0.01

  GPIO.setwarnings(True)
  GPIO.setmode(GPIO.BCM)
  GPIO.setup(18, GPIO.IN)
  GPIO.setup(4, GPIO.IN)

  GPIO.add_event_detect(18, GPIO.FALLING, callback=button_press_isr1)
  GPIO.add_event_detect(4, GPIO.FALLING, callback=button_press_isr2)

def main():
  try:
    while True:
      if not queue.empty():
        fireChannel = queue.get() 
        try:
            buttonPress(fireChannel)
        except requests.exceptions.RequestException as e:
            print(e)

        print("button pressed: %s" % fireChannel )
      sleep(0.03)

  except KeyboardInterrupt:
      # exit with CTRL+C
      print("CTRL+C used to end Program")
  finally: 
    GPIO.cleanup()


if __name__ == "__main__":
    init()
    main()

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

Re: Characterizing GPIO input pins

Mon Aug 22, 2016 2:13 pm

Well, it seems you're between a rock and a hard place.

If you've followed my post, then you know about the two major issues with RPi.GPIO.
There is little you can do about the "OS" glitches, but they don't happen that often.

You can avoid the double edge-triggering on the falling edge triggering in the ISR only by making sure you have absolutely no(!) capacitance on the input pin. So if you decide to use the "real" Falling edge interrupts (so not using the loop within the ISR), you have to use a (hardware) Schmitt-Trigger gate to clean-up the edge triggering and avoid bounce noise at the same time.

I hope this helps!
Paul

momoaux
Posts: 11
Joined: Fri Aug 19, 2016 11:37 am

Re: Characterizing GPIO input pins

Tue Aug 23, 2016 9:55 am

I think I found a solution: :D
Waiting for the release of the button in another thread. :o

And I tested a little bit with using the same ISR for all Buttons. Looks like it is working without copying the function multiple times.

It would be great if someone could also test it and tell me his results.
Thank you.

Hardware for test:
Just hook up 2 Pushbuttons between the IOPins and GND and add a >2k Resistor between each IOPin and 3.3V as pullup
button_test.png

Code: Select all

import datetime
import time
from time import mktime, time, sleep
import RPi.GPIO as GPIO
import requests
import queue
from threading import Thread 

queue = queue.Queue()

def button_press_isr(channel):
    global glitch_error_timing
    GPIO.remove_event_detect(channel)
    sleep(0.0001)
    edge_start = time() # used to measure total duration time and report error time
    if (time() - glitch_error_timing[channel]) < 0.015 : # 15 mSec
        sleep(0.0001)
        glitch_error_timing[channel] = time() # start again
        # turn the event detection back on
        GPIO.add_event_detect(channel, GPIO.FALLING, callback=button_press_isr)
        # skip this edge
        return
    cnt = 0
    while (time() - edge_start) <= 0.035 : # 35 mSec max, 15mSec margin
        sleep(0.0015) # 1.5 mSec
        if GPIO.input(channel) == 0 : # 0/False for Falling, 1/True for Rising
            cnt += 1
        else: # when we have captured a glitch, start all over again
            cnt = 0
        if cnt == 7 : # 7? lucky number
            break
    if cnt == 7 :
        queue.put(channel)
    glitch_error_timing[channel] = time() # start the glitch timer again
    thread = Thread(target=waitForFlank, args=(channel,button_press_isr))
    thread.start()

def waitForFlank(channel, callbackFunction):
    print("waitForFlank started for channel: {0} with callback {1!s}".format(channel,callbackFunction))
    cnt = 0
    while True:
        sleep(0.0015) # 1.5 mSec
        if GPIO.input(channel) ==  1: # 0/False for Falling, 1/True for Rising
            cnt += 1
        else: # when we have captured a glitch, start all over again
            cnt = 0
        if cnt == 7 : # 7? lucky number
            break
    GPIO.add_event_detect(channel, GPIO.FALLING, callback=callbackFunction)
    print("waitForFlank finished for channel: %s" % channel)

def buttonPress(channel):
    response = requests.post(url="http://192.168.1.5:8080")

def init():
  global glitch_error_timing
  glitch_error_timing={18:0.01, 4:0.01}

  GPIO.setwarnings(True)
  GPIO.setmode(GPIO.BCM)
  GPIO.setup(18, GPIO.IN)
  GPIO.setup(4, GPIO.IN)

  GPIO.add_event_detect(18, GPIO.FALLING, callback=button_press_isr)
  GPIO.add_event_detect(4, GPIO.FALLING, callback=button_press_isr)

def main():
  try:
    while True:
      if not queue.empty():
        fireChannel = queue.get() 
        try:
            buttonPress(fireChannel)
        except requests.exceptions.RequestException as e:
            print(e)

        print("button pressed: %s" % fireChannel )
      sleep(0.03)

  except KeyboardInterrupt:
      # exit with CTRL+C
      print("CTRL+C used to end Program")
  finally: 
    GPIO.cleanup()


if __name__ == "__main__":
    init()
    main()

momoaux
Posts: 11
Joined: Fri Aug 19, 2016 11:37 am

Re: Characterizing GPIO input pins

Mon Aug 29, 2016 11:31 am

And I found a workaround to the remove_event_detect Segfault.

Just leave the event detect enabled but use a variable to ignore them:

Code: Select all

    #GPIO.remove_event_detect(channel)
    #Instead use a variable to set the callback on ignore
    if ignoreCalls==1:
        return
Complete Example here:
http://schlierf.info/raspberry/pi/gpio/ ... Fault.html

Return to “Advanced users”