Nella Quarta Parte della Guida si Parlerà degli Ingressi Digitali del PIC. Si Faranno Esempi con Pulsanti e Ingressi Provenienti da Altri Integrati…
INTRO
Oltre al tempo, hanno un ruolo molto importante gli ingressi, infatti, in funzione dello stato logico degli ingressi del PIC le uscite possono evolvere in modi diversi. Per prima cosa iniziamo a dire come si comunica al PIC che un determinato pin deve essere un’ingresso, questo è molto semplice e si scrive con una semplice riga di codice:
Nell’esempio1 facciamo in modo che tutti i pin del registro TRISA siano impostati come ingresso scrivendo “PORTA = 0b11111111;“ mentre i pin del registro TRISB sono stati impostati tramite un valore esadecimale. Inumeri esadecimali si analizzano a due a due; la prima cifra dopo la “x” rappresenta in decimale i 4 bit più significativi, la seconda cifra rappresenta i 4 bit meno significativi. Quindi la prima cifra identifica i pin RB7:RB4 mentre l’altra i pin RB3:RB0, la prima cifra ha un valore esadecimale uguale a 0 che in binario diventa 0000, la seconda cifra in esadecimale è 2 mentre in binario 0010. Scrivendo quindi “PORTB = 0x02;” andiamo ad impostare i pin RB0 ed RB1 come ingressi mentre tutti gli altri pin del registro TRISB sono impostati come uscite.
Nell’esempio2 invece avremo RA2, RA3, RA4, RA5, RA6, RA7 come ingressi per il registro TRISA, mentre 0x28 in binario diventa 0b00111000 ( 2 = 0010 , 8 = 1000 ) quindi avremo RB5, RB4, RB3 ingressi mentre gli altri pin saranno configurati come uscite.
Lo stato degli ingressi nel programma si può usare per diversi scopi:
1) Come espressione di controllo in un istruzione iterativa;
2) Come variabile;
3) Per incrementare il valore di una variabile.
Andiamo a vedere tre esempi di programma per i tre casi sopracitati.
ACCENSIONE DI UN LED TRAMITE PULSANTE
#define led PORTB.F1 //led PORTB RB1
#define pulsante PORTB.F0 //pulsante PORTB RB0
void main() { //inizio programma
TRISB = 0x01; //config. pin PORTB, RB0 in, RB1:RB7 out
led = 0; //inizializziamo il led spento
while(1) { //ciclo infinito
if (pulsante){ // se il pulsante è a livello logico alto
led=1; //il led si accende
}
else led=0; //altrimenti rimane spento
}
}
Il primo programma serve ad accendere un led quando viene premuto un pulsante e rimane acceso finchè il pulsante è premuto, quindi l’ingresso è a livello alto. Le prime due righe di codice “#define led PORTB.F1” e “#define pulsante PORTB.F0” servono a nominare RB1 “led” ed RB0 sarà nominato “pulsante”. Segnato l’inizio del programma con la stringa “void main() {” andiamo ad imporre RB0 come ingresso, RB1 come uscita e led spento. “while(1) {” segna l’inizio di un ciclo infinito che accende il led se pulsante è a livello alto, altrimenti lo mantiene spento.
Lo schema per il programma accensione led è il seguente, il PIC è alimentato da una tensione continua di 5Volt, il quarzo è da 4MHz e il circuito di oscillazione è terminato da due condensatori da 22pF. R1 serve a mantenere a livello alto il pin MCLR, quindi a non resettare il PIC, R2 serve invece a mantenere a livello basso ingresso RB0 finche il pulsante non viene premuto, se non ci fosse tale resistenza l’ingresso sarebbe flottante ovvero ne a livello basso ne a livello alto quindi indeterminato. Sull’uscita RB1 c’è una resistenza di protezione e un led blu a basso consumo.
REGISTRO PISO CON PIC16F84A
#define led PORTA.F0 //definiamo l'uscita del led
#define in1 PORTB.F0 //definiamo gli ingressi
#define in2 PORTB.F1
#define in3 PORTB.F2
#define in4 PORTB.F3
#define in5 PORTB.F4
#define in6 PORTB.F5
#define in7 PORTB.F6
#define in8 PORTB.F7
int in[8]; //vettore che contiene lo stato degli ingressi
int i; //indice di scansione ingressi
void main() { //inizio programma
TRISA = 0x00; //definiamo ingressi e uscite
TRISB = 0xff;
while(1){ //ciclo infinito
in[0]=in1; //inseriamo nel vettore il valore degli ingressi
in[1]=in2;
in[2]=in3;
in[3]=in4;
in[4]=in5;
in[5]=in6;
in[6]=in7;
in[7]=in8;
for(i=0;i<8;i++){ //scansioniamo il vettore degli ingressi
led = in[i]; //led assume il valore dell'ingresso i-esimo
delay_ms(300); //ogni 300ms
}
}
}
Un registro piso è un sistema digitale che riceve in ingresso 8 o più bit in ingressi paralleli, quindi riceve 8 ingressi contemporaneamente, e toglie fuori un bit alla volta dall’uscita ogni n secondi, secondo un segnale di Clock. Per il nostro esempio utilizziamo 8 pulsanti, ma possono essere usati ingressi provenienti da altri integrati. Il programma inizia dando un nome agli ingressi e all’uscita, con la funzione “#define“, inizializzando un vettore di interi di 8 elementi che andrà a contenere lo stato degli otto ingressi che chiamiamo “in” e inizializziamo anche un’indice “i” che va a scansionare il vettore. Fisato l’inizio del programma con la stringa “void main() {” e configurati ingressi e uscite inseriamo un ciclo infinito. Andiamo quindi ad inserire il valore dell’i-esimo ingresso nella posizione i del vettore “in”, in questo modo avremo il valore degli 8 ingressi all’interno del vettore. Con il ciclo “for(i=0;i<8;i++){” e le istruzioni in esso contenute andiamo a porre in uscita il valore i-esimo del vettore “in” ogni 300ms, quindi all’inizio avremo il valore di in1 in uscita e dopo 300ms quello di in2 e così via, tutti gli ingressi sono trasferiti in uscita dopo 300*8=2400ms, potendo così aggiornare il valore degli ingressi.
Lo schema è molto simile al primo, gli ingressi sono inviati alle porta del registro PORTB se i microswitch sono su ON l’ingresso è altro, se sono allo stato OFF le resistenze R2 portano a livello basso gli ingressi. L’uscita è formata sempre dal led e dalla resistenza di protezione mentre la rete di oscillazione è sempre composta dal quarzo da 4MHz e da 2 condensatori ceramici da 22pF. Il pin MCLR è sempre a livello alto per evitare il reset.
CONTATORE UP & DOWN CON PIC16F84A
#define piu PORTA.F0
#define meno PORTA.F1
void main() { //inizio programma
unsigned int conta=0; //iniz. conta, il n° di pressioni
int numero[6] = {0x00, 0x01, 0x02, //iniz. numero, la config. out
0x04, 0x08, 0x10};
TRISA = 0x03; //RA0 e RA1 ingressi
TRISB = 0x00; //RB0:RB7 uscite
PORTB = 0x00; //inizializziamo le uscite a 0
while(1){ //ciclo infinito
PORTB = numero[conta]; //PORTB sarà l'elemento conta di num
if((piu)&(conta<5)){ //piu true e conta è <5 esegue...
conta++; //incrementa conta
while(piu){ //ciclo antirimbalzo
delay_ms(10); //aspetta 10ms dp la press. del stasto
}
}
if((meno)&(conta>0)){ //meno true e conta è >0 esegue...
conta--; //decrementa conta
while(meno){ //ciclo antirimbalzo
delay_ms(10); //aspetta 10ms dp la press. del stasto
}
}
}
}
Il terzo esempio è un contatore con un pulsante per incrementare il conteggio e un contatore per decrementarlo. Diamo il nome “piu” all’ingresso RB0 e il nome “meno” all’ingresso RB1, questi ingressi sono i due pulsanti. Iniziamo il programma con la solita stringa “void main() {” e inizializziamo un intero senza segno chiamato “conta”, questa variabile memorizza il numero di pressione dei pulsanti, un’altra variabile inizializzata è il vettore “numero” che contiene la configurazione che devono assumere le uscite in base alla variabile “conta”. Andiamo quindi a configurare ingressi e uscite: RB0:RB7 come uscite e RA0 e RA1 ingressi, infatti sulle uscite PORTB andiamo a collegare resistenza di protezione e i led mentre su RA0 il pulsante che incrementa il conteggio e su RA1 il pulsante che lo decrementa.
Andiamo quindi ad implementare un ciclo infinito con la stringa “while(1){” all’interno di questo ciclo andiamo a dare alle uscite il valore contenuto nella posizione i-esima del vettore numero, dove i è la variabile conta. All’interno del ciclo infinito abbiamo anche una serie di stringhe che vanno ad analizzare gli ingressi, se queste passano da livello logico basso a livello alto, la variabile viene incrementata o decrementata. La stringa è la seguente:
if((piu)&(conta<5)){
conta++;
if(piu){
delay_ms(10); }
}
Se “piu” è a livello logico alto e la variabile “conta” è minore di 5 viene incrementata la variabile “conta” inoltre è presente un ciclo if all’interno del primo if, una volta entrati nel primo if se “piu” è a livello logico alto vi sarà un ritardo di 10ms per evitare il problema del rimbalzo. Il problema del rimbalzo consiste nel fatto che premuto un pulsante si può avere una fase di transizione in cui il segnale rimbalza da livello basso a quello alto un paio o più volte, come in figura:
Il secondo if con il delay blocca il conteggio dei fronti di salita o discesa indesiderati. Per quanto riguarda il decremento della variabile conta avremo:
if((meno)&(conta>0)){
conta–;
if(meno){
delay_ms(10);
}
}
In questo caso se “meno” è a livello logico alto e la variabile “conta” è maggiore di zero la variabile “conta” viene decrementata e poi si ha il secondo if per la funzione antirimbalzo.
Lo schema è simile agli altri, vengono usati 5 led per le uscite e due pulsanti come ingressi, il quarzo è sempre da 4MHz e il pin MCLR è a livello alto per non avere il reset del PIC. E’ possibile implementare un sistema antirimbalzo hardware, basta inserire in parallelo a R2 un condensatore (e un altro condensatore anche in serie a R3) con un valore di circa 10nF, in questo modo si smorza il fenomeno del rimbalzo del segnale.
DOWNLOAD PROGRAMMI SCRITTI E COMPILATI
1)ACCENSIONE DI UN LED TRAMITE PULSANTE CON PIC16F84A
3)CONTATORE UP & DOWN CON PIC16F84A
Ciao scusa, stavo leggendo un po’ di tutorial vari sui pic e mi sono imbattuto in questo, vi segnalo un errorino:
2 in binario è 0010 non 0011 (= 3)
“la prima cifra ha un valore esadecimale uguale a 0 che in binario diventa 0000, la seconda cifra in esadecimale è 2 mentre in binario 0011. Scrivendo quindi “PORTB = 0x02;” andiamo ad impostare i pin RB0 ed RB1 come ingressi mentre tutti gli altri pin del registro TRISB sono impostati come uscite.”
Grazie mille, ho subito corretto 🙂