MPLAB X & XC8 – #2: Ingressi digitali e antirimbalzo

Utilizzo degli ingressi digitali e di varie tecniche di antirimbalzo per pulsanti.

 

 

 

 

 





INTRODUZIONE

In questo articolo vengono presentati gli ingressi digitali del microcontrollore. Viene analizzato il problema dell’effetto rimbalzo di pulsanti e interruttori e si introducono alcune possibili soluzioni.


LETTURA STATO PIN

I GPIO, cioè i pin di ingresso e uscita, permettono di interfacciare il microcontrollore e le periferiche al suo interno con il mondo esterno. Possono essere usati come output per inviare segnali all’esterno oppure come input e acquisirli dall’esterno.
Quando si usano i GPIO come input si legge lo stato del pin, cioè si legge il livello di tensione che determina il livello logico corrispondente:

  • se Vin ≤ VIL il segnale viene letto come basso e si associa il valore logico 0
  • se VIL < Vin < VIH il segnale può essere letto come 0 o come 1, pertanto è una zona da evitare
  • se Vin ≥ VIH il segnale viene letto come alto e si associa il valore logico 1

dove VIL è la Input Low Voltage ed è la massima tensione presente su un pin che viene letta come 0 logico, mentre VIH è la Input High Voltage ed è la minima tensione che viene letta come 1 logico. A seconda del microcontrollore si possono avere delle differenze nelle tensioni di soglia e nel caso del PIC18F252 si ha VIL = 0,8 V e VIH = 2,0 V.

È anche possibile usare i GPIO come ingressi analogici sfruttando il modulo ADC e poter leggere una tensione in ingresso associandole non il valore logico 0 od 1 ma un valore appartenente ad un range discreto. Vedere MPLAB X & XC8 – #4.

Per poter leggere lo stato di un pin bisogna agire sui registri TRIS e PORT.

Tramite i registri TRIS si setta la direzione del pin, che è impostato come input se il corrispondente bit vale ‘1’, oppure come output se vale ‘0’. Si ha un registro TRISx per ogni PORTx del microcontrollore, dove x indica il corrispondente nome del PORT. A seconda del tipo di microcontrollore si può andare da A ad E; per esempio per il PIC18F252 si ha TRISA, TRISB e TRISC, mentre per il PIC18F4520 si ha TRISA, TRISB, TRISC, TRISD e TRISE.   
Per la maggior parte dei microcontrollori della famiglia PIC12F si ha un solo PORT e il registro TRIS può essere TRISA o TRISIO, con i registri PORT che sono rispettivamente denominati PORTA o GPIO.

Nel caso dei PICMicro della famiglia 18 si ha, oltre al registro PORT, anche il registro LAT. Come riportato nel PICmicro 18C MCU Family Reference Manual il registro PORT permette di leggere lo stato sui pin mentre il registro LAT legge lo stato del data latch e non quello forzato esternamente sul pin. Per la lettura su un pin settato come input va quindi usato il registro PORT a cui si accede con il comando:

PORTxbits.Rx#

dove X indica il PORT e # il numero del pin. Per esempio, volendo leggere il pin 1 di PORTA si dovrebbe usare PORTAbits.RA1, mentre per il pin 3 di PORTC servirebbe PORTCbits.RC3.

Il comando per leggere lo stato del data latch di un particolare pin è

LATxbits.LATx#


PROBLEMA ANTIRIMBALZO

Lo stato del pin di un microcontrollore può essere forzato da un circuito esterno, per esempio un integrato o un transistor, oppure da un pulsante connesso tramite un resistore di pull-up o pull-down. Nel primo caso non si hanno problemi, mentre nel secondo si verifica il fenomeno del rimbalzo.    
Il fenomeno del rimbalzo dei pulsanti e interruttori è dovuto alla struttura con i quali sono realizzati, cioè da un conduttore metallico che effettua il collegamento tra i due terminali del pulsante. Nel momento in cui il pulsante viene premuto i due terminali vengono messi in contatto tra loro ma dopo un certo intervallo di tempo questi si scollegano per poi ricollegarsi, a causa delle oscillazioni del contatto metallico. A seconda del pulsante e di come avviene la pressione, le oscillazioni possono essere più o meno numerose e durature. Ciò che si vede ai capi del pulsante è una successione di aperture e chiusure del collegamento, che in campo digitale equivalgono ad una serie di livelli logici 1 e 0 alternati con un intervallo temporale variabile.

Se il pulsante venisse usato per accendere un LED non si avrebbero problemi in quanto l’alternanza degli stati 0 ed 1 sono limitati in numero e durata. Se invece il pulsante venisse usato in un circuito sensibile alle transizioni di livello ogni oscillazione del contatto verrebbe identificata come un’ulteriore pressione del pulsante, determinando un comportamento anomalo del circuito.


TECNICHE ANTIRIMBALZO

Per ovviare al problema del rimbalzo esistono diverse tecniche che permettono di mascherare o eliminare il fenomeno. Le varie soluzioni adottate sono funzionanti ma a seconda dei casi e degli utilizzi possono risultare più o meno valide.


Antirimbalzo Software

Osservando l’immagine prelevata dall’oscilloscopio si può osservare che i rimbalzi del pulsante si interrompono dopo un certo intervallo di tempo, trascorso il quale il valore è stabile. È possibile attendere finché il transitorio non si esaurisca, così da avere il valore stabile sul pin.
L’antirimbalzo software sfrutta questo principio e non fa altro che attendere un intervallo temporale adeguato con il quale si posticipa la lettura del pin fino a quando non sono terminati i rimbalzi del pulsante ed il suo stato è stabile. Questa operazione può essere ottenuta usando la funzione __delay_ms() impostata su un intervallo temporale superiore all’intervallo massimo di assestamento del pulsante; nella maggior parte dei pulsanti è sufficiente usare un ritardo di 10 – 20 ms.

L’antirimbalzo software viene implementato attraverso un doppio if statement. Il primo if controlla se il pulsante è stato premuto e attende un intervallo temporale, esaurito il quale si ha il secondo if che controlla se il pulsante risulta ancora premuto. In caso i due if sono verificati significa che il pulsante è premuto e non si hanno più rimbalzi, altrimenti il pulsante viene considerato non premuto o i rimbalzi non esauriti.

È possibile connettere i pulsanti con un resistore di pull-up, e quindi avere sul pin un segnale normalmente alto, oppure con un resistore di pull-down, e quindi avere un segnale normalmente basso.

Nel caso di connessione con resistore di pull-up gli if statement sono verificati se il GPIO è uguale a 0, quindi il codice è:

    if(PORTCbits.RC1==0){
__delay_ms(20);
if(PORTCbits.RC1==0){
...
}
}
else{
...
}

mentre nel caso di connessione con resistore di pull-down è verificato se è uguale ad 1, quindi il codice diventa:

    if(PORTCbits.RC1==1){
__delay_ms(20);
if(PORTCbits.RC1==1){
...
}
}
else{
...
}

Pro: non servono componenti aggiuntivi.
Contro: non è possibile proseguire con l’esecuzione del codice durante il delay.

Antirimbalzo RC

L’antirimbalzo hardware con circuito RC permette di filtrare il segnale sul pulsante e fornisce al microcontrollore un segnale ripulito dalle oscillazioni.

Il circuito RC è costituito da un resistore e da un capacitore che determinano una costante di tempo \(\tau\) = RC data dal prodotto della resistenza e della capacità. I valori dei componenti devono essere scelti in maniera tale da avere una costante di tempo superiore all’intervallo temporale in cui si verifica il rimbalzo del pulsante. Anche in questo caso è possibile usare un circuito con resistore di pull-up o di pull-down.

Nel caso del circuito con resistore di pull-up si ha che quando il pulsante non è premuto il capacitore viene caricato attraverso la serie R1+R2 e si porta lentamente alla tensione di alimentazione con una costante di tempo \(\tau\) = (R1+R2)C. Quando il pulsante viene premuto il capacitore inizia a scaricarsi attraverso R2 con una costante di tempo ridotta pari a \(\tau\) = R2C.
Se si verifica un rimbalzo, cioè si verifica una sequenza di apertura e chiusura dei contatti, il condensatore risulta, per un breve intervallo di tempo, connesso a VDD tramite R1 e R2 e successivamente a massa attraverso R2. In questo modo la tensione sul pin del pulsante varia tra 0 V quando è premuto e VDD quando non è premuto, mentre sul condensatore si ha una variazione minore.
Ad ogni rimbalzo si ha lo stesso comportamento finché non si esauriscono i rimbalzi e il pulsante rimane stabilmente chiuso, scaricando completamente il condensatore.
Se i componenti sono correttamente dimensionati la variazione di tensione sul condensatore è sufficiente piccola da non provocare sbalzi tali da essere letti come una transizione tra 0 ed 1.  

Nel caso di circuito con resistore di pull-down si ha comportamento analogo ma con la carica del condensatore che avviene attraverso il resistore R2 e la scarica attraverso la serie R1+R2.

Per ripristinare il segnale a valori logici pieni 0 e 1 si usa un inverter che permette di invertire il segnale sul condensatore e di associarli una tensione VDD o GND. Per l’inverter si usa una porta logica NOT con Smith Trigger così da poter sfruttare l’isteresi e avere due soglie di tensione per la transizione da 1 a 0 e da 0 ad 1. Quando su una porta NOT senza trigger si impone un segnale in ingresso con un’oscillazione intorno alla soglia, in uscita si ottiene un segnale negato con stessa oscillazione, mentre se si usa una porta con trigger per avere in uscita lo stesso segnale è necessario che quello in ingresso superi entrambe le soglie. Il risultato che si ottiene è una maggiore robustezza a piccole variazioni di tensione.

Il principio di funzionamento di questo circuito antirimbalzo si basa sui circuiti RC. Applicando la legge di Kirchhoff al circuito e risolvendo un’equazione differenziale si determina la tensione sul condensatore.

Per la carica completa di un condensatore inizialmente scarico e forzato da un generatore VDD si ha:

\(V_{c}(t) = V_{DD}(1 – e^{\frac{-t}{\tau }})\)

mentre per la scarica completa di un condensatore inizialmente a tensione VDD si ha:

\(V_{c}(t) = V_{DD} e^{\frac{-t}{\tau }}\)

Le equazioni possono essere riordinate per determinare il valore di R nella fase di carica:

\(R = R_{1} + R_{2} = \frac{-t}{C\ ln\left (1 – \frac{V_{T+}}{V_{DD}}\right )}\)

e nella fase di scarica:

\( R = R_{2} = \frac{-t}{C\ ln \frac{V_{T-}}{V_{DD}}} \)

Dove VT+ e VT- sono rispettivamente la tensione di soglia per la transizione 0 → 1 e per 1 → 0.
Si ha così che in fase di carica per avere una transizione in uscita dall’inverte si deve avere sul condensatore una tensione superiore alla soglia superiore, quindi serve VC ≥ VT+, mentre in fase di scarica la tensione sul condensatore deve scendere sotto la soglia inferiore, quindi VC ≤ VT-.
A causa delle variazioni di processo le soglie per ogni integrato oscillano intorno a valori tipici, che per l’integrato utilizzato in questo esempio, cioè la porta logica NOT SN74LS14, sono VT+ = 1,6 V e VT- = 0,8 V.
Per poter calcolare correttamente i valori dei resistori R1 e R2, si devono considerare i worst cases delle due soglie. Per la soglia VT+ il caso peggiore è quello minimo, infatti se la tensione di soglia dovesse risultare superiore al valore minimo si avrebbe una costante di tempo superiore a quella attesa, rendendo il circuito RC più robusto ai rimbalzi. In maniera analoga per la soglia VT- il caso peggiore è quello massimo, così che se la tensione di soglia dovesse risultare inferiore si avrebbe anche in questo caso una costante RC superiore.
Per l’integrato SN74LS14 si trova nel datasheet che i worst cases sono VT+ = 1,4 V e VT- = 1,0 V.

Per poter dimensionare correttamente i valori dei resistori si deve avere una stima del tempo impiegato dal pulsante per esaurire i rimbalzi, che per molti pulsanti si assesta al di sotto di 10 ms, e fissare una capacità per il condensatore, che può essere 10 µF, ottenendo:

\(R_{2} = \frac{-10\times 10^{-3}}{10\times 10^{-6}\ ln \frac{1,0\;V}{5\;V}}\simeq 621\;\Omega\)

\(R_{1} + R_{2} = \frac{-10\times 10^{-3}}{10\times 10^{-6}\ ln\left (1 – \frac{1,4\;V}{5\;V}\right )}\simeq 3040\;\Omega\)

che approssimate ai valori standard diventano R2 = 620 \(\Omega\) e quindi R1 = 2,4 \(k\Omega\).

Per poter effettuare la lettura dello stato del GPIO si usa uno if statement che deve tener conto della negazione introdotta dalla porta NOT, quindi nel caso di circuito con pull-up si ha:

    if(PORTCbits.RC2==1){
...
}
else{
...
}

mentre con il circuito di pull-down si ha:

    if(PORTCbits.RC2==0){
...
}
else{
...
}

Pro: non servono risorse software.
Contro: servono componenti aggiuntivi.

Antirimbalzo Flip-Flop (o SPDT)

L’antirimbalzo hardware con pulsante SPDT e flip-flop permette di rilevare solo la prima transizione 0 → 1 o 1 → 0 e bloccare tutte le altre, rendendo così il circuito perfettamente immune alle transizioni indesiderate dovute ai rimbalzi.

Il circuito è realizzato da un pulsante SPDT, il cui contatto comune è connesso a massa e gli altri due sono connessi a VDD tramite resistori di pull-up e agli ingressi S ed R del flip-flop. È possibile scegliere un integrato che abbia al suo interno un flip-flop SR oppure usare un integrato con porte logiche NAND, per esempio CD4093, e connetterle come in foto.

Il flip-flop SR permette di settare ad 1, resettare a 0 e memorizzare lo stato delle uscite sulla base dei valori forniti agli ingressi S ed R. In tabella è riassunto il comportamento di un flip-flop SR realizzato con porte NAND:

SRQ(t)Qn(t)Stato flip-flop
0
101Reset
1010Set
11Q(t – \(\Delta\)t)Qn(t – \(\Delta\)t)Memorizzazione
0011Inconsistenza

I due resistori connessi a VDD permettono di evitare che entrambi gli ingressi siano connessi a massa e quindi che si possa verificare lo stato di inconsistenza.
Si connette il pulsante in maniera tale da avere il contatto normalmente chiuso su S e quello normalmente aperto su R. Quando il pulsante non è premuto si ha S = 0 e R = 1, il flip-flop si trova nella condizione di Reset e sul GPIO si ha 0 logico.
Quando il pulsante viene premuto si ha S = 1 e R = 0, il flip-flop passa nella condizione di Set e sul GPIO si ha 1 logico. 
Nella fase di commutazione del pulsante si verifica, per un certo intervallo temporale, che il contatto comune non è connesso a nessuno dei due terminali e gli ingressi valgono entrambi 1, portando così il flip-flop in memorizzazione; sul GPIO si ha quindi il livello logico che si aveva prima che iniziasse la commutazione del pulsante.

Durante il rimbalzo i contatti metallici si aprono e chiudono diverse volte in successione. Nel caso in cui il pulsante venga premuto si passa dallo stato S = 0 e R = 1 a quello S = 1 e R = 0. Il contatto normalmente chiuso, cioè quello su S, viene sconnesso e il contatto metallico viene spostato sul contatto normalmente aperto, cioè su R. Solo su R si verificano le piccole oscillazioni del contatto mentre su S si ha stabilmente il livello logico alto. Il flip-flop si porta in Set e l’uscita Q assume valore logico alto. Nel momento del rimbalzo non si ha connessione e entrambi gli ingressi sono ad 1 e il flip-flop è in memorizzazione mantenendo Q = 1 e non si hanno commutazioni. In questo modo si sono ignorati i rimbalzi su R.
Caso analogo si ha quando il pulsante viene rilasciato con le oscillazioni che si verificano solo su S. Si passa quindi da S = 1 e R = 0 a S = 0 e R = 1, il flip-flop si porta in Reset e Q assume valore logico basso. Quando si verifica il rimbalzo si ha S = 1 e R = 1 e il flip-flop mantiene memorizzato Q a 0. Anche sul rilascio del pulsante si sono così ignorati i rimbalzi.

Per poter leggere lo stato sul pin si usa l’if statement:

    if(PORTCbits.RC3==1){
...
}
else{
...
}

Pro: immunità perfetta ai rimbalzi.
Contro: servono componenti aggiuntivi.

CODICE ESEMPIO

Viene proposto un codice di esempio che, grazie ad un microcontrollore PIC18F252, permette di leggere lo stato dei pin forzati da 4 pulsanti, uno soggetto a rimbalzi (connesso su RA0), uno con antirimbalzo software (su RA1), uno con circuito RC (su RA2) e uno con flip-flop e pulsante SPDT (su RA3). Lo schema elettrico è il seguente:

Per realizzare il progetto si segue la stessa procedura utilizzata in XC8 – #1.

Header File

Il file config.h è scritto utilizzando la Configuration Bits accessibile dalla toolbar selezionando Window >> Target Memory Views >> Configuration Bits. Seguire la procedura riportata in XC8 – #1 che è valida anche in questo caso.

Il file config.h appare così:

#define _XTAL_FREQ 8000000  

// PIC18F252 Configuration Bit Settings 
// 'C' source line config statements 

// CONFIG1H 
#pragma config OSC = HS         // Oscillator Selection bits (HS oscillator) 
#pragma config OSCS = OFF       // Oscillator System Clock Switch Enable bit (Oscillator system clock switch option is disabled (main oscillator is source)) 

// CONFIG2L 
#pragma config PWRT = OFF       // Power-up Timer Enable bit (PWRT disabled) 
#pragma config BOR = OFF        // Brown-out Reset Enable bit (Brown-out Reset disabled) 
#pragma config BORV = 20        // Brown-out Reset Voltage bits (VBOR set to 2.0V) 

// CONFIG2H 
#pragma config WDT = OFF        // Watchdog Timer Enable bit (WDT disabled (control is placed on the SWDTEN bit)) 
#pragma config WDTPS = 128      // Watchdog Timer Postscale Select bits (1:128) 

// CONFIG3H 
#pragma config CCP2MUX = OFF    // CCP2 Mux bit (CCP2 input/output is multiplexed with RB3) 

// CONFIG4L 
#pragma config STVR = OFF       // Stack Full/Underflow Reset Enable bit (Stack Full/Underflow will not cause RESET) 
#pragma config LVP = OFF        // Low Voltage ICSP Enable bit (Low Voltage ICSP disabled) 

// CONFIG5L 
#pragma config CP0 = OFF        // Code Protection bit (Block 0 (000200-001FFFh) not code protected) 
#pragma config CP1 = OFF        // Code Protection bit (Block 1 (002000-003FFFh) not code protected) 
#pragma config CP2 = OFF        // Code Protection bit (Block 2 (004000-005FFFh) not code protected) 
#pragma config CP3 = OFF        // Code Protection bit (Block 3 (006000-007FFFh) not code protected) 

// CONFIG5H 
#pragma config CPB = OFF        // Boot Block Code Protection bit (Boot Block (000000-0001FFh) not code protected) 
#pragma config CPD = OFF        // Data EEPROM Code Protection bit (Data EEPROM not code protected) 

// CONFIG6L 
#pragma config WRT0 = OFF       // Write Protection bit (Block 0 (000200-001FFFh) not write protected) 
#pragma config WRT1 = OFF       // Write Protection bit (Block 1 (002000-003FFFh) not write protected) 
#pragma config WRT2 = OFF       // Write Protection bit (Block 2 (004000-005FFFh) not write protected) 
#pragma config WRT3 = OFF       // Write Protection bit (Block 3 (006000-007FFFh) not write protected) 

// CONFIG6H 
#pragma config WRTC = OFF       // Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) not write protected) 
#pragma config WRTB = OFF       // Boot Block Write Protection bit (Boot Block (000000-0001FFh) not write protected) 
#pragma config WRTD = OFF       // Data EEPROM Write Protection bit (Data EEPROM not write protected) 

// CONFIG7L 
#pragma config EBTR0 = OFF      // Table Read Protection bit (Block 0 (000200-001FFFh) not protected from Table Reads executed in other blocks) 
#pragma config EBTR1 = OFF      // Table Read Protection bit (Block 1 (002000-003FFFh) not protected from Table Reads executed in other blocks) 
#pragma config EBTR2 = OFF      // Table Read Protection bit (Block 2 (004000-005FFFh) not protected from Table Reads executed in other blocks) 
#pragma config EBTR3 = OFF      // Table Read Protection bit (Block 3 (006000-007FFFh) not protected from Table Reads executed in other blocks) 

// CONFIG7H 
#pragma config EBTRB = OFF      // Boot Block Table Read Protection bit (Boot Block (000000-0001FFh) not protected from Table Reads executed in other blocks) 

// #pragma config statements should precede project file includes. // Use project enums instead of #define for ON and OFF. 


Source File

Il file main.c inizia con le include di xc.h che contiene le informazioni relative al microcontrollore necessarie al compilatore, la libreria stdint.h e l’header file:

#include <xc.h>
#include <stdint.h>
#include "config.h"

Si hanno poi le dichiarazioni della variabile count che conteggia gli impulsi voluti premendo il pulsante e i rimbalzi indesiderati, e le variabili tempX che permettono di contare solo le transizioni di livello, impedendo che la variabile count venga incrementata anche quando il pulsante è stabilmente premuto e non si hanno rimbalzi. Le variabili sono dichiarate usando uint8_t che indica una variabile unsigned ad 8 bit. Per poter usare questa notazione mnemonica si fa uso della libreria stdint.h inclusa nelle righe precedenti.

uint8_t count = 0; 
uint8_t temp1, temp2, temp3, temp4 = 0;

Segue poi la funzione di configurazione dei GPIO del microcontrollore. I GPIO relativi a PORTA vengono settati tutti come ingressi digitali, sia quelli utilizzati come ingressi (RA0:RA3) sia quelli non utilizzati, quelli relativi a PORTB da RB0 a RB3 vengono usati per indicare la pressione dei pulsanti e vengono settati come uscite, mentre da RB4 a RB7 vengono impostati come ingressi digitali non utilizzati, infine i GPIO relativi alla PORTC sono usati per visualizzare gli incrementi dovuti alla pressione dei pulsanti e ai rimbalzi, e vengono quindi tutti impostati come uscite digitali. I GPIO non utilizzati vengono imposti come ingressi per evitare che in caso di corticirciti ci sia un passaggio di corrente elevato da poterli danneggiare.

void CFG_Init(void){
    TRISA = 0b11111111;
    TRISB = 0b11110000;
    TRISC = 0b00000000;

    PORTA = 0b00000000;
    PORTB = 0b00000000;
    PORTC = 0b00000000;
} 

I GPIO relativi alla PORTA sono associati ai canali del modulo ADC che in questo caso non si vuole utilizzare. Va quindi disabilitato il modulo ADC e impostati gli ingressi come digitali. Questo è possibile agendo sui registri relativi all’ADC che per il PIC18F252 sono ADCON0 e ADCON1.
Nel registro ADCON0 è sufficiente settare a ‘0’ il bit ADON che disabilita il modulo ADC, mentre per il registro ADCON1 si devono impostare i bit PCFG che determinano quali ingressi vengono impostati come digitali o analogici. Nel caso in esempio vengono impostati tutti come ingressi digitali. La configurazione del modulo ADC e degli ingressi della PORTA vengono inseriti nella funzione ADC_Init().

void ADC_Init(void){
    ADCON0 = 0b00000000;
    ADCON1 = 0b00000110;
}

Segue la funzione main che richiama le funzioni di configurazione CFG_Init() e ADC ADC_Init() e crea un while infinito all’interno del quale si esegue il codice di lettura dei GPIO.

Il primo pulsante è quello che viene letto senza far uso di tecniche di antirimbalzo. Questo pulsante è connesso sul pin RA0 e il pin di stato è RB0 che ne segnala l’attivazione tramite un LED.
Il pulsante è connesso a VDD tramite un resistore di pull-up e quando risulta premuto si porta il pin RA0 a massa, quindi la condizione da verificare nell’if è che sia uguale a 0. Quando la condizione è verificata si accende il LED posto su RB0 e si esegue un secondo if che è verificato in quanto la variabile temp1 è inizializzata a 0. Viene così incrementato il valore di count e forzata ad 1 la variabile temp1 così da impedire ulteriori incrementi di count finché non sia rilasciato il pulsante e si entri nell’else, resettando a 0 temp1 e spegnendo il LED.

        /* Pulsante 1 - Nessun Antirimbalzo*/
        if(PORTAbits.RA0==0){
            PORTBbits.RB0=1;
            if(temp1==0){
                count++;
                temp1 = 1;             
            }
        }
        else{
            PORTBbits.RB0=0;
            temp1 = 0;
        } 

Il secondo pulsante è quello che viene letto facendo uso dell’antirimbalzo software. Questo pulsante è connesso sul pin RA1 e il pin di stato è RB1 che ne segnala l’attivazione tramite un LED.
Il pulsante, analogamente a quanto fatto per quello su RA0, è connesso a VDD tramite un resistore di pull-up e quando risulta premuto si porta il pin a massa, quindi la condizione da verificare nell’if è che sia uguale a 0. Quando la condizione è verificata si esegue il comando __delay_ms(20) che mette in pausa l’esecuzione del codice per 20 ms, al termine dei quali si esegue un secondo if con la stessa condizione di controllo del primo. Se anche questo if è verificato significa che il transitorio è esaurito e il pulsante è stabilmente chiuso e può essere acceso il LED su RB1 e incrementato count. Anche in questo caso l’incremento della variabile count viene eseguito con un if che controlla il valore della varibile temp2 così da impedire ulteriori incrementi finché non si rilasci il pulsante e si entra nell’else, resettando a 0 temp2 e spegnendo il LED.

        /* Pulsante 2 - Antirimbalzo Software*/
        if(PORTAbits.RA1==0){
             __delay_ms(20);           
             if(PORTAbits.RA1==0){      
                 PORTBbits.RB1=1; 
                 if(temp2==0){ 
                     count++;       
                     temp2 = 1;
                 }           
             }      
        }      
        else{     
            PORTBbits.RB1=0;  
            temp2 = 0; 
        } 

Il terzo pulsante è quello che viene letto facendo uso dell’antirimbalzo hardware con circuito RC. Questo pulsante è connesso sul pin RA2 e il pin di stato è RB2 che ne segnala l’attivazione tramite un LED.
Il pulsante a VDD tramite un resistore di pull-up e quando risulta premuto si porta il nodo a massa che, risultando connesso al pin RA2 per mezzo di una porta NOT, impone che la condizione di controllo nell’if sia uguale ad 1. Il blocco di codice risulta identico a quello del caso del pulsante 1 senza antirimbalzo (ma come se fosse di tipo pull-down), poiché la procedura che ripulisce il segnale viene effettuata all’esterno del microcontrollore e ciò che si ha sul GPIO è un segnale già condizionato e pronto da usare.

        /* Pulsante 3 - Antirimbalzo RC*/
        if(PORTAbits.RA2==1){
            PORTBbits.RB2=1;
                if(temp3==0){
                    count++;
                    temp3 = 1;
                }
        }
        else{
            PORTBbits.RB2=0;
            temp3 = 0;
        } 

Il quarto pulsante è quello che viene letto facendo uso dell’antirimbalzo hardware con flip-flop e pulsante SPDT. Questo pulsante è connesso sul pin RA3 e il pin di stato è RB3 che ne segnala l’attivazione tramite un LED.
Il pulsante è connesso avendo il contatto normalmente chiuso su S e quello normalmente aperto su R, e viene letto dall’uscita Q del flip-flop, pertanto quando il pulsante è premuto Q si porta a livello logico 1 e la condizione da verificare nell’if è che sia uguale ad 1. Anche in questo caso, come per l’antirimbalzo hardware con circuito RC, il segnale fornito al GPIO è già ripulito dai rimbalzi e il microcontrollore deve solo effettuare la lettura, quindi il blocco di codice è lo stesso di quello del pulsante senza antirimbalzo.

        /* Pulsante 4 - Antirimbalzo FF*/
        if(PORTAbits.RA3==1){
            PORTBbits.RB3=1;
                if(temp4==0){
                    count++;
                    temp4 = 1;
                }
        }
        else{
            PORTBbits.RB3=0;
            temp4 = 0;
        } 

Il codice è quindi terminato e prima di chiudere il ciclo while si scrive la variabile su PORTC così da poter visualizzare il valore conteggiato dalla variabile count:

    PORTB = count;

Questa variabile tiene conto delle pressioni dei vari pulsanti e dei rimbalzi avvenuti, così da poter controllare se alcuni rimbalzi non sono stati correttamente eliminati e valutarne l’efficacia in confronto al pulsante su RA0.

Il codice main.c completo risulta quindi:

 #include <xc.h>
 #include <stdint.h> 
 #include "config.h" 
  
 uint8_t count = 0; 
 uint8_t temp1, temp2, temp3, temp4 = 0; 
  
 void CFG_Init(void){     
     TRISA = 0b11111111;         // Pin TRISA settati come Input
     TRISB = 0b11110000;         // Pin RB0:RB3 Output, RB4:RB7 Input
     TRISC = 0b00000000;         // Pin TRISC settati come Output
     
     PORTA = 0b00000000;              
     PORTB = 0b00000000;              
     PORTC = 0b00000000;          
 } 

 void ADC_Init(void){
     ADCON0 = 0b00000000;
     ADCON1 = 0b00000110;
 }

 void main(void) {     
     CFG_Init();
     ADC_Init();  
     
     while(1){
         /* Pulsante 1 - Nessun Antirimbalzo*/
         if(PORTAbits.RA0==0){
             PORTBbits.RB0=1;
             if(temp1==0){
                 count++;
                 temp1 = 1;             
             }
         }
         else{
             PORTBbits.RB0=0;
             temp1 = 0;
         }   
  
         /* Pulsante 2 - Antirimbalzo Software*/
         if(PORTAbits.RA1==0){
              __delay_ms(20);           
              if(PORTAbits.RA1==0){      
                  PORTBbits.RB1=1; 
                  if(temp2==0){ 
                      count++;       
                      temp2 = 1;
                  }           
              }      
         }      
         else{     
             PORTBbits.RB1=0;  
             temp2 = 0; 
         }   
   
         /* Pulsante 3 - Antirimbalzo RC*/
         if(PORTAbits.RA2==1){
             PORTBbits.RB2=1;
                 if(temp3==0){
                     count++;
                     temp3 = 1;
                 }
         }
         else{
             PORTBbits.RB2=0;
             temp3 = 0;
         }  
  
         /* Pulsante 4 - Antirimbalzo FF*/
         if(PORTAbits.RA3==1){
             PORTBbits.RB3=1;
                 if(temp4==0){
                     count++;
                     temp4 = 1;
                 }
         }
         else{
             PORTBbits.RB3=0;
             temp4 = 0;
         }    
  
     PORTC = count; 
     } 
 } 


PROGRAMMAZIONE

Per programmare il microcontrollore usando un programmatore Microchip, per esempio tramite il PICkit3, si può usare l’ambiente di sviluppo MPLAB X IDE oppure MPLAB X IPE. Per i vari step da seguire si rimanda a XC8 – #1.

DOWNLOAD

Al seguente link è possibile scaricare i file .c e .h utilizzati: DOWNLOAD.

   

[Voti Totali: 0 Media Voti: 0]
Segui la Nostra Pagina Facebook:
Facebook

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *