Il debouncing è una FAQ. Dovresti essere in grado di trovare ... un numero pressoché illimitato di pagine Web sull'argomento. Smith ha anche commentato il PDF ampiamente letto di Jack Ganssle sull'argomento. E con tutte queste risposte hai entrambi i metodi hardware e software.
Aggiungerò a questa "letteratura" solo un po 'parlando principalmente di idee che non sono già state trattate bene. Ma prima di me, un punto o due:
- Il debounc in hardware analogico può ottenere risultati che non è possibile ottenere da uno switch "osservato" solo digitalmente su base periodica mediante polling o persino da eventi di cambio pin hardware. Ma puoi fare "abbastanza bene" a tutti gli effetti, in modo digitale. Quasi nessuno oggigiorno utilizza soluzioni di debouncing analogico esterno. Ma ho usato di tutto, dall'allungamento del polso usando gli one-shot (74121) alle tecniche menzionate da Jack Ganssle qui .
- Per coloro che eseguono solo la programmazione integrata e non sono affatto interessati all'apprendimento dell'elettronica, il rimbalzo degli switch è probabilmente uno dei due set di competenze di base necessari. Il funzionamento dei LED è probabilmente l'altro. E con questo, non intendo avere una sola abilità in questi. Intendo essere in grado di farlo in diversi modi. Così realmente fare bisogno di completamente apprehend quanto scrive Jack Ganssle circa, e più ancora, per quanto riguarda gli interruttori.
Da quando ho citato lo stiramento del polso usando un 74121 e poiché Jack Ganssle non lo menziona, e nessuno lo fa ancora qui, potrei anche fornire questo link aggiuntivo come ulteriore lettura suggerita sull'uso del 74121 o 555 come un colpo solo timer per interruttori di rimbalzo.
Ora, continuiamo a farlo attraverso l'osservazione con un microcontrollore.
Di solito uso una macchina a stati per gestire il rimbalzo. Questo è quasi sempre guidato da un normale "battito cardiaco" che ho impostato su8SM, dove possibile. (In genere NON uso eventi di interruzione triggerati da bordi per diversi motivi.)
La macchina a stati si presenta così:
simula questo circuito - Schema creato usando CircuitLab
Il valore di DEBOUNCED per lo switch potrebbe assumere i valori "inattivo", "attivo" e "sconosciuto". In questo modo, è possibile assicurarsi che il software sia in attesa fino a quando il valore dell'interruttore non si stabilizza dopo l'inizializzazione. Ma di solito non mi preoccupo di questo. Sostituisco il valore "sconosciuto" con un valore predefinito e utilizzo invece un sistema di valori binari.
La macchina a stati viene inserita impostando prima il valore debounce sul suo valore predefinito e quindi inserendo lo stato "CAMBIAMENTO" della macchina a stati. Ad ogni intervallo di tempo (in genere8SMse riesco a cavarmela), leggerò il valore di commutazione corrente ed eseguirò un aggiornamento dello stato corrente e possibilmente del valore rimbalzato. Quindi esco. Il codice di alto livello accede quindi solo allo stato rimbalzato.
Se per me è importante, posso anche mantenere uno stato di rimbalzo precedente. In questi casi, quando aggiorno lo stato rimbalzato stesso, copierò innanzitutto tale stato in uno "stato rimandato precedente". Posso quindi utilizzare la coppia di valori per determinare se c'è stata una transizione rimbalzata. A volte, non mi importa delle transizioni. Qualche volta lo faccio. Quindi dipende. Ma in tutti i casi, voglio solo conoscere le transizioni che sono state rimbalzate. Non mi preoccupo mai delle transizioni runt . Quindi il codice di alto livello non utilizza mai lo stato interno utilizzato dalla macchina a stati per il proprio lavoro.
Una delle cose belle di questo metodo è che posso rimbalzare un'intera porta di switch, contemporaneamente. E posso farlo anche senza un singolo ramo nel codice di interruzione. Ciò significa un codice di debouncing molto veloce e breve fino alla larghezza della porta del microcontrollore (generalmente 8 bit di larghezza.) Un esempio di Atmel AT90 mostra come questo si ottiene utilizzando un evento di interruzione Timer0:
.equ SWPORTPINS = PINB
.def SwRawCurr = r4
.def SwRawPrev = r5
.def SwState = r6
.def SwDebCurr = r7
.def SwDebPrev = r8
; Debounce the input switches.
mov SwRawPrev, SwRawCurr
in SwRawCurr, SWPORTPINS
mov Timer0Tmp1, SwRawCurr
eor Timer0Tmp1, SwRawPrev
mov Timer0Tmp0, Timer0Tmp1
or Timer0Tmp1, SwState
mov SwState, Timer0Tmp0
mov Timer0Tmp0, Timer0Tmp1
com Timer0Tmp0
and Timer0Tmp1, SwDebCurr
and Timer0Tmp0, SwRawCurr
or Timer0Tmp1, Timer0Tmp0
mov SwDebPrev, SwDebCurr
mov SwDebCurr, Timer0Tmp1
Ora, questo esempio mostra l'intero affare, inclusi i valori di switch debounce precedenti e attuali. Ed esegue anche tutte le transizioni di stato necessarie. Non mostro l'inizializzazione di questo codice. Ma quanto sopra capisce quanto sia facile far funzionare la macchina a stati e quanto poco codice è necessario per farlo. È abbastanza veloce e semplice e non richiede ramificazioni (che a volte comporta cicli aggiuntivi e spazio di codice aggiuntivo).
Preferisco usare 8SMtempismo perché lunghi, lunghi test con una varietà di persone diverse che utilizzano attrezzature su cui ho lavorato in passato mi hanno portato lì. Ho provato periodi più lunghi e quando lo faccio, inizio a convincere la gente a dirmi che la "reattività" non è "vivace". (Al giorno d'oggi, con i bambini che crescono a lavorare in tempo reale "sparagli", potrei anche accorciarlo ulteriormente. Si lamenteranno amaramente anche dei lievi ritardi causati dai moderni televisori digitali nell'installazione e nella visualizzazione di un frame.)
Alcune persone avranno sentimenti molto chiari su quanto dovrebbe essere nitido e reattivo un sistema. Fresco e reattivo significa campionare più spesso, non meno. Ma personalmente, trovo20SMperiodi di osservazione accettabili. (Io non trovo volte più a lungo abbastanza buono anche per me, però.)
Si noti che la macchina a stati che ho citato deve prima entrare nello stato SETTAGGI e quindi rimanere lì per un altro tempo di campionamento prima che il valore di DEBOUNCED venga aggiornato. Quindi premere un pulsante e tenerlo premuto, anche nelle migliori circostanze, richiederà queste transizioni:
- passare da SETTLED a CHANGING
- passare da MODIFICA a IMPOSTATO
- rimanere in SETTLED, aggiornando DEBOUNCED
Pertanto, per ottenere un nuovo stato rimbalzato sono necessari almeno 3 periodi di campionamento.
Un pulsante richiederà almeno 6 volte di campionamento per passare da inattivo, a attivo, e quindi di nuovo a inattivo.
Ho citato i dettagli di cui sopra in modo che sia assolutamente chiaro che un tempo di campionamento di 8SM significa che da qualche parte in mezzo 16ms <t≤24SMper passare da inattivo a un risultato rimbalzato attivo riconosciuto. E ci vorrà un altro24SMprima che lo stato possa tornare inattivo. Questo è un minimo di40ms <t≤48SM per passare attraverso un intero ciclo di pulsanti.
L'uso di tempi di campionamento più lunghi avrà periodi di conseguenza più lunghi. Usando il20SM Ho già detto "accettabile" per me, quindi significa da qualche parte in giro 100ms <t≤120SMper un intero ciclo di pulsanti. E che sta ottenendo esattamente su nella zona dove la gente non tendono a notare. Certamente non mi piace la "sensazione" se si allunga.
Se segui questa strada, non essere sprezzante nell'utilizzare tempi di campionamento più lunghi. Se è necessario, penso che anche tu debba fare molti test con utenti / consumatori.
E se stai sviluppando codice per una tastiera di battitura, usa tempi più brevi. Il record per una dattilografa è stato stabilito decenni fa a 217 wpm. Ciò si traduce in circa una chiave ogni45SM. I dattilografi del genere colpiscono più chiavi in un ordine controllato. Per ottenere buone prestazioni per dattilografi molto veloci utilizzando un sistema di commutazione relè reed bagnato con mercurio, ho scoperto che2SM ha funzionato bene.