Riflessioni sul bounce, che si prende gioco di me, povero nabbo :(

Ok, ho fatto qualche prova con un mio o-quasi. Che la R-pull sia di 1kΩ o di 1MΩ cambia nulla (almeno macroscopicamente). Ho qualche riserva sul campionamento analogico, nel senso che un ingresso digitale potrebbe interpretare diversamente (il mio o-quasi legge un ingresso digitale).

Un pulsantino simile a quello del primo post mi da dei rimbalzi (una volta su dieci, e solo in apertura) entro un millisecondo, il caso peggiore è stato un buco di 4ms. Resistenza da 10k e condensatore da 100nF (costante di tempo 1ms) sembra bastare, anche se per me la costante di tempo per sicurezza non dovrebbe mai scendere sotto i 10ms.

Schermata del 2019-07-07 17-38-45.png

Schermata del 2019-07-07 17-36-22.png

Schermata del 2019-07-07 17-38-09.png

Diverso è il caso di "filo infilato nella breadboard", in questo caso si può arrivare a oltre 50ms di "grattamento" (anche se la "norma" è 5..10ms):

Schermata del 2019-07-07 17-50-37.png

In questo caso può servire una costante di tempo molto più lunga, e si rischia lo stesso di vedere cose come questa, dove spariscono i picchi veloci ma può rimanere una componente a frequenza più bassa:

Schermata del 2019-07-07 17-45-20.png

Ho l'impressione che un debounce software da 20..30ms (accettazione segnale se stabile per almeno 20..30ms) permetta di non salire troppo con la costante di tempo richiesta da un debounce hardware.

L' o-quasi (4000 campioni da 25µs bitmappati in 500byte):

#define  RESOL   25
#define  SAMPL   4000
#define  LENDATA 500
uint32_t t;    // tempo in microsec
byte     in;   // valore 0/1 letto da ingresso
uint16_t n;
byte     d[LENDATA] = { 0 };
bool     basso      = true;
//_____________________________________________________________

void setup()
{
    pinMode(2, INPUT);
    pinMode(13, OUTPUT);
    Serial.begin(38400);
}
//_____________________________________________________________

void loop()
{
    in = (PIND >> 2) & 1;

    if (basso  and  in)  // fronte di salita
    {
        t = micros();
        d[0] = 0b10000000;
        n = 7;
        while (n < SAMPL)
        {
            while (micros() - t < RESOL);
            in = (PIND >> 2) & 1;
            n++;
            d[n >> 3] |= in << (n & 7); // bit da scrivere
            t += RESOL;
        }
        for (n = 0; n<LENDATA; n++)
        {
            Serial.write(d[n]);
            d[n] = 0;
        }
        basso = false;
    }

    else if (!basso  and  !in)  // fronte di discesa
    {
        t = micros();
        d[0] = 0b011111111;
        n = 7;
        while (n < SAMPL)
        {
            while (micros() - t < RESOL);
            in = (PIND >> 2) & 1;
            n++;
            d[n >> 3] |= in << (n & 7); // bit da scrivere
            t += RESOL;
        }
        for (n = 0; n<LENDATA; n++)
        {
            Serial.write(d[n]);
            d[n] = 0;
        }
        basso = true;
    }
}
//_____________________________________________________________

Il visualizzatore:

#! /usr/bin/env python3
import tkinter as tk
import serial
import time
##______________________________________________________________

PORTNAME = '/dev/ttyUSB0'
WID      = 500  ## pixel larghezza grafico
RESOL    = 25   ## 25 microsec.
SAMPLES  = 250  ## campioni da visualizzare, max 4000
FOSCA    = SAMPLES * (RESOL / 1000.)  ## fondo scala in ms
##______________________________________________________________

def crea_assi():  ## assi in millisecondi
    for n in 1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 100:
        x = (WID - 1.) * n / FOSCA
        canv1.create_line(x, 0, x, 300, fill='#a0a0a0')
##______________________________________________________________

def draw():
    canv1.delete('all')
    crea_assi()
    coords = []
    sx = (WID - 1.) / (SAMPLES - 1.)  ## step asse X
    n = 0
    while (n < SAMPLES):
        x = n * sx
        y = (g.d[n >> 3] >> (n & 7)) & 1
        coords.extend([x, 200 - 100*y])
        n += 1
    canv1.create_line(*coords, fill='red')
##______________________________________________________________

class g(object):  ## dati di lavoro globali
    fase = 0
    t = 0
    d = [0] * 500
    n = 0
##______________________________________________________________

def recall():  ## funzione attivata ogni 5 ms
    if (g.fase == 0):  ## attesa silenzio seriale 0.5 sec
        if (ser.inWaiting()):
            while (ser.inWaiting()): ser.read(1)
            g.t = time.time()
        elif (time.time()-g.t > 0.5):
            g.fase = 1;
            g.n = 0
    
    elif ((g.fase == 1) and ser.inWaiting()):  ## attesa dati
        g.t = time.time()
        g.fase = 2
        
    elif (g.fase == 2):  ## ricezione dati con timeout 0.5 sec
        while (ser.inWaiting()):
            g.d[g.n] = ser.read(1)[0]
            g.n += 1
            if (g.n == 500):
                draw()
                g.fase = 0
        if (time.time()-g.t > 0.5):  g.fase = 0;

    root.after(5, recall)
##______________________________________________________________

root = tk.Tk()
root.title('Bounce display - By C.Fin 2019')
root.resizable(False, False)

canv1 = tk.Canvas(root, width=WID, height=300,
                  background='#ffffff', highlightthickness=0)
canv1.pack(padx=16, pady=16)
crea_assi()

ser = serial.Serial(PORTNAME, 38400, 8, 'N', 1, timeout=0)

g.t = time.time()
root.after(5, recall)
root.mainloop()

Schermata del 2019-07-07 17-38-45.png

Schermata del 2019-07-07 17-36-22.png

Schermata del 2019-07-07 17-38-09.png

Schermata del 2019-07-07 17-50-37.png

Schermata del 2019-07-07 17-45-20.png