EOS

Gestione del debouncing (antirimbalzo) nei microcontrollori

Gestione antirimbalzo

Il debouncing o antirimbalzo è un problema apparentemente banale ma che spesso è la causa di malfunzionamenti. Ecco come gestire il rimbalzo degli interruttori o dei pulsanti (debouncing) applicati all’ingresso di un microcontrollore.

Benché risultino praticamente impercettibili ai sensi umani, i rimbalzi meccanici degli interruttori possono provocare transizioni spurie degli ingressi ai quali sono applicati, equivalenti a pressioni consecutive del medesimo tasto. La figura 1 mostra la tipica connessione di un interruttore  in ingresso ad un microcontrollori  con la tipica resistenza di pull-up.

Figura 1. Rimbalzo nella commutazione tra livello alto e livello basso

Figura 1. Rimbalzo nella commutazione tra livello alto e livello basso

Tale resistenza ha un duplice scopo: uno è quello di mantenere a livello alto l’ingresso del micro quando il tasto è aperto (funzione di pull-up), l’altro  è quello di limitare la corrente durante la pressione del tasto. Nella stessa figura è riportato anche l’andamento  della tensione V ai capi dell’interruttore,  nell’ipotesi in cui questo venga chiuso all’istante 0. Si noti che la tensione in ingresso al microcontrollore non passa bruscamente dal valore alto a zero (come ci si aspetterebbe in una situazione ideale) ma oscilla tra i valori massimo e minimo prima di assestarsi sullo zero logico. Viene definito tempo di rimbalzo l’intervallo di tempo tra l’istante in cui il segnale passa per la prima volta a livello basso e quello in cui vi rimane senza alcuna ulteriore ambiguità. Nella figura 2 sono riportati alcune tipologie di rimbalzi ricavate dall’analisi all’oscilloscopio di diversi pulsanti.

Figura 2. Tipiche forme d’onda durante il bouncing

Figura 2. Tipiche forme d’onda durante il bouncing

Tipicamente il tempo di rimbalzo è dell’ordine di 10ms, un tempo impercettibile per l’utente, ma relativamente lungo per il microcontrollore.  Ecco alcune tecniche di programmazione firmware per ovviare al problema del rimbalzo.

Gestione dei ritardi

Un primo  modo  molto  semplice di gestione del rimbalzo per lo schema di  figura 1, è quello  di attendere che l’ingresso sia tornato a livello alto e che contemporaneamente sia trascorso un tempo di 20msec. Se l’ingresso torna a livello alto prima dello scadere dei 20msec è sicuro che si tratta di un rimbalzo quindi non dovrà essere considerato. Questa tecnica può essere implementata, per un PIC, con una macro il cui codice assembler è riportato nella Listato 1.

Debounce macro HiLo, Port, Bit
if HiLo == Lo
  btfss   Port,Bit         ; Pulsante premuto?
else
  btfsc   Port,Bit
endif
  goto    $ - 1            ; Si - attesa rilascio
  movlw InitDlay
  movwf   Dlay             ; Impostazione ritardo 20 msecs
  movlw    0
if HiLo == Lo
  btfss   Port,Bit         ; Se il pulsante è premuto attendi ancora
else
  btfsc   Port,Bit
endif
  goto    $ - 6
ifndef     Debug
  addlw   1                 ; Incremento contatore ritardo
  btfsc     STATUS, Z
else
  nop
  nop
endif
  decfsz  Dlay
  goto   $ - 5
endm
Listato 1
Pulsante macro porta, pin, statopremuto, ritardo, cicli, contatore, statofinale, indirizzo
   local        PulsanteEnd
   incf         contatore, w    ; Incrementa il contatore
 if ((statopremuto == 0) && (statofinale == 0))||(( statopremuto == 1) && (statofinale == 1))
   btfsc   Port, Pin            ; Se 0 allora lo stato è valido
 else
   btfss   Port, Pin            ; Se 1, allora lo stato è valido
 endif
   clrw                         ; non premuto, azzera contatore
   movwf   contatore            ; Salva il conteggio
   movlw   ritardo & 0x07F
   subwf   contatore, w         ; finito il debouncing?
   btfsc   STATUS, Z
   goto    indirizzo             ; Si, esci dal ciclo
 if    ((ritardo & 0x080) != 0)  ; Si è in modalità Autorepeat?
   btfsc   STATUS, C
   decf    contatore             ; No – Decrementa se maggiore di ‘ritardo’
 else
   btfss   STATUS, C
   goto    PulsanteEnd
   xorlw   cicli                 ; terminati i cicli in autorepat?
   btfsc   STATUS, Z
   goto    PulsanteEnd           ; No – Mantieni l’incremento
   movlw   ritardo               ; Si, resetta il contatore al valore precedente e ripeti
   movwf   contatore
   goto    indirizzo
 endif
PulsanteEnd
 endm
Listato 2
Ciclo
   btfss Hi        ; Attesa che l’ingresso sia stato riconosciuto a livello alto
   goto $ - 1
   btfss Lo        ; Attesa che l’ingresso sia stato riconosciuto a livello basso
   goto $ - 1
   btfsc PORTB, 2   ; è stato premuto il pulsante?
   goto NoPress     ; No, azzera il bit 2 della porta B
   bsf PORTB, 2     ;pulsante premuto
   goto Ciclo
NoPress            ;pulsante non premuto
  bcf PORTB, 2
  goto Ciclo
Listato 3

L’uso di questa macro permette di monitorare qualsiasi pin del PIC purché configurato come ingresso, e funziona qualunque sia il valore del clock a patto di ricalcolare la costante InitDelay che tiene conto dei 20msec di attesa per il debounce. Tale costante può essere calcolata con la formula:

InitDelay=1+ (Td*F/4)/(256*7)

Dove Td è il tempo di debounce (20msec) ed F è la frequenza del clock del micro (il quarzo esterno o il valore impostato per l’oscillatore RC). Chiaramente il valore ottenuto  dalla formula precedente dovrà essere arrotondato all’intero più vicino. Volendo quindi un tempo di debounce di circa 20msec  si  dovrà  impostare  a  12  il  valore  di InitDelay.

Una tecnica Interrupt-like

La gestione dei ritardi  può  essere  effettuata con una tecnica molto  simile a quella degli interrupt. Tale tecnica consiste nel lanciare una macro passandogli  alcuni  parametri  generici  necessari al monitoraggio  di uno specifico ingresso. La macro viene richiamata ricorsivamente in un ciclo interrotto solo alla fine del debouncing. Il possibile ciclo che richiama la macro Pulsante con i dovuti parametri, potrebbe essere quello del listato 4.

ciclo
  Pulsante porta, pin, statopremuto, ritardo, cicli, contatore, statofinale, indirizzo
  Delay
  goto ciclo
Indirizzo: ;indirizzo al quale si salta dopo il debouncing
Listato 4
org 4
Int
  movwf _w                  ; Salvataggio del contesto
  movf STATUS, w
  movwf _status

  movlw 256 - 0x09C         ; Impostazione del Timer0 per un timeout di 20msec
  movwf TMR0
  btfss INTCON, INTF        ; è stato premuto il tasto?
  goto IntTimerOF           ;no, o timeout di Timer0
IntTMR0Reset                ;reset del flag di interrupt per Timer0

  bcf INTCON, INTF
  bsf STATUS, RP0           ;cambia il fronte attivo di generazione interruzione
  movlw 0x040
  xorwf OPTION_REG ^ 0x080
  bcf STATUS, RP0

  bcf Lo                     ;Azzeramento dei flag
  bcf Hi

  goto IntEnd
  
IntTimerOF                    ;Timeout di Timer0 o Debounce completato

  bcf INTCON, T0IF            ; Reset del flag interrupt per il timer

  bsf Lo ; Gestione dei flag
  bcf Hi
  movlw 1
  btfsc Button
  addwf flags

IntEnd
  movf _status, w             ; Ripristino del contesto
  movwf STATUS
  swapf _w
  swapf _w, w

  retfie                      ; Fine Interrupt
Listato 5

Come si può notare dal codice del listato 4, la macro Pulsante  viene chiamata con una serie di parametri:

  • porta, pin – è l’ingresso su cui è collegato il pulsante (es. PORTB, 1);
  • statopremuto – lo stato dell’ingresso quando il pulsante è premuto;
  • ritardo – il numero massimo di iterazioni prima di uscire dal ciclo (max. 127). Se impostato a 0 viene effettuato il salto all’indirizzo di uscita non appena viene rilevata la pressione del tasto (quindi  senza eseguire il debouncing). Se il bit 7 di ritardo viene messo ad 1 si disabilita la modalità auto-repeats della macro quindi  non viene considerato il parametro cicli;
  • cicli – se la macro è in modalità auto-repeats (bit 7 di ritardo a 0) è il numero di cicli prima di uscire dalla ricorsione;
  • contatore – variabile che tiene conto del numero di ritorsioni effettuate;
  • statofinale – lo stato (dell’ingresso) a cui la macro risponde (valori possibili: 0 o 1);
  • indirizzo – l’indirizzo al quale si salta in uscita dal ciclo.

Nel listato 2 una possibile implementazione della macro Pulsante.

Gestione mediante interrupt

Uso di interruzioni su RB0/INT

Un’altra tecnica per la gestione dei tasti è sfruttare la possibilità offerta dai PIC di  generare una interruzione al cambiamento di stato di uno degli ingressi della porta B. Il primo esempio, riportato nel listato 6, illustra la gestione di una interruzione su RB0/INT.

org 4
Int

   movwf _w ; Salvataggio del contesto
   movf STATUS, w
   movwf _status
   movlw 256 - 0x09C ; Impostazione del Timer0 per un timeout di 20msec
   movwf TMR0
   btfss INTCON, RBIF ; è stato premuto il tasto?
   goto IntTimerOF ;no, timeout di Timer0

IntTMR0Reset
   movf PORTB, w
   bcf INTCON, RBIF ; reset del flag di interrupt per Timer0
   clrf flagLo ; Azzeramento dei flag
   clrf flagHi
   goto IntEnd

IntTimerOF
   bcf INTCON, T0IF
   movf PORTB, w ; acquisizione dello stato del pulsante
   xorlw 0x0F0
   andlw 0x0F0
   movwf flagLo ; impostazione flag Low
   movf PORTB, w ; impostazione flag High
   andlw 0x0F0
   movwf flagHi

IntEnd

   movf _status, w ; ripristino del contesto
   movwf STATUS
   swapf _w
   swapf _w, w

   retfie ; fine interruzione
Listato 6

In questo caso l’handler gestisce il debouncing e imposta il valore di due flag (Hi e Lo) che verranno analizzati in polling dal programma principale. Il programma avrà dunque la struttura riportata nel listato 3.

Figura 3. Debouncing hardware con rete RC e buffer triggerato

Figura 3. Debouncing hardware con rete RC e buffer triggerato

L’handler all’indirizzo 0x04 che andrà in esecuzione a seguito dell’interruzione avrà la struttura del listato 5. Come si può notare, la routine di interruzione del listato 5 prevede la modifica del registro OPTION al fine di  attivare alternativamente l’interruzione sul  fronte  di  salita e  sul  fronte  di  discesa del segnale su RB0. L’handler  può  essere dunque ottimizzato utilizzando le interruzioni sulla porta B (e non solo su RB0) come illustrato di seguito.

Uso di interruzioni su PORTB

Usando le interruzioni sulla porta B (quindi gestendo il flag di interruzione RBIF)  non solo si ottimizza il codice, ma è possibile gestire quattro pulsanti con la stessa routine. In questo caso l’interrupt  handler dovrà provvedere alle seguenti operazioni: salvataggio del contesto, inizializzazione di Timer0 per il ritardo  di debouncing,  impostazione dei flag a notificare il tasto premuto, ripristino del contesto. Nel listato 6 l’handler da utilizzare in questo caso. Ovviamente per la corretta esecuzione della routine sarà necessario definire  i  flag  “Hi”  e  “Lo” anche usando la direttiva #DEFINE.

Un cenno all’hardware  debouncing

Oltre al debouncing software, è possibile gestire il fenomeno anche con  accorgimenti  hardware.  Il più semplice è quello riportato  nella figura 3 che prevede l’inserimento di una capacità in parallelo allo switch. Resistenza e capacità vanno dimensionate in modo che la costante tempo della rete (data dal prodotto  RC) sia dell’ordine del tempo di debounce (assunto pari a 20ms). Per rendere netto il fronte del segnale è possibile anche inserire un buffer a trigger di Schmitt tra il circuito di debouncing  e l’ingresso del microcontrollore. Poiché il ritardo di debouncing varia a seconda del  tipo  di  switch  utilizzato,  usare la  soluzione precedente implica calibrare la rete RC in base al tipo di rimbalzo del tasto. Per ovviare a questo inconveniente è possibile inserire un latch RS come mostrato in figura 4.

Figura 4. Debouncing hardware con latch RS

Figura 4. Debouncing hardware con latch RS

Nella stessa figura è riportata anche la temporizzazione dell’uscita in cui si nota l’evidente filtraggio del rimbalzo. Il sistema sfrutta il fatto che il latch RS è caratterizzato da uno stato di conservazione in cui l'uscita permane al valore precedente. Nell'ottica del risparmio di componenti e semplificazione dello sbroglio dell'eventuale circuito stampato, spesso si preferisce adottare la soluzione firmware. Questa scelta consente inoltre di modificare il comportamento dell'applicazione senza intervenire sull'hardware.

Leggi anche:

Elettronica Digitale (per principianti) parte seconda

4 Commenti

  1. Emanuele Bonanni Emanuele 18 novembre 2015
  2. dfacchin 18 novembre 2015
  3. Ernesto Sorrentino 18 novembre 2015
  4. Emanuele Bonanni Emanuele 18 novembre 2015

Scrivi un commento

EOS