fisertek_300
 
Lezione 6 
 
Impariamo ad usare le porte analogiche dei PIC
 
 
barra rossa2 
 
In questa pagina impareremo ad utilizzare le porte A/D dei PIC, in modo da poter acquisire grandezze analogiche, oltre che digitali. Ciò ci servirà, ad esempio, per collegare al pic delle sonde termometriche, fare misure di tensione o di corrente, leggere la posizione di un potenziometro, etc. etc.Ricordo che le porte analogiche non sono di serie in tutti i PIC. Per esempio, il 16F84 non ne è dotato.Quindi, in questa pagina parleremo del 16F876-16F877 che sono i PIC più comuni, dotati di porte analogiche. Come al solito, chi vuole approfondire, potrà ricorrere ai datasheet.
 
barra rossa2 
 
L'A/D dei PIC 
Alcuni PIC di uso comune, come il 16F876/877, sono dotati di alcuni pin che possono essere utilizzati sia in modalità digitale che analogica. Questo lo dobbiamo al fatto che questi PIC sono dotati, al loro interno, di un convertitore analogico/digitale.Cosa è un convertitore analogico/digitale???Per noi, robottari e non, è un dispositivo che ci permette di rilevare delle grandezze lineari (che hanno valori intermedi compresi tra un minimo e un massimo) e renderli compatibili con il sistema digitaleLavorare con l'analogico in ambito digitale, ci pone di per se dei problemi di ordine mentale perché si ha a che fare con due cose che apparentemente entrano in contrasto tra loro. Oltretutto, se non si fa un pò di sperimentazione, il datasheet sembra più complicato che mai.In realtà, usare gli A/D dei PIC, è una cosa piuttosto semplice, tanto che non serve neanche fare delle routine apposite per la loro gestione.C'è solo da capire alcune cosette per sapere dove mettere le mani quando facciamo un programma. 
Cominciamo… 
Tutto il discorso delle porte A/D ruota attorno a 4 registri del PIC, vediamo quali sono: 
ADRESH 
ADRESL 
ADCON0 
ADCON1 
I primi due registri (ADRESH e ADRESL) sono i registri nel quale il PIC deposita il valore digitale della grandezza analogica appena misurata. I registri usati per il deposito del risultato sono due perché mamma Microchip ha implementato nei suoi PIC degli A/D a 10 bit. Effettivamente, se utilizziamo solo il registro ADRESL avremo una risoluzione di 8 bit. I due bit mancanti finiscono in ADRESH.I registri ADCON0 e ADCON1 vanno spiegati più a fondo di quanto appena fatto con i primi 2 registri in quanto sono formati da una serie di elementi che gestiscono i vari parametri da settare nel PIC per utilizzare l'A/D. 
 
ADCON0 
A pagina 113 del datasheet dei PIC serie 16F87X, trovate subito una tabella che descrive tutti i singoli bit che compongono il registro ADCON0.Per essere più chiari utilizziamo la notazione binaria, anziché esadecimale e facciamo un esempio di riga di codice in C da inserire nei nostri programmi: 
ADCON0 = 0b11000001; 
Al comando ADCON0 viene dato un parametro numerico che rappresenta diverse cosette, mettiamole in evidenza uno alla volta prendendo come esempio questo appena scritto: 
ADCON0 = 0b11xxxxxx; questo gruppo due numeri sono il bit 6 e il bit 7 del registro DCON0 e sono chiamati anche ADSC1 e ADCS0. La combinazione binaria tra questi due bit setta la frequenza della conversione A/D. (leggetevi il datasheet) Cosa vuol dire???? 
Dobbiamo settare la velocità di campionamento in base alla velocità delle variazioni temporali del segnale da campionare cercando di tenere più bassa possibile la frequenza del campionamento stesso.Questo perché quando si lavora alle frequenze alte, avremo anche misure poco affidabili. (difetti del PIC) Se per ipotesi scriviamo 00 ai bit 6 e 7 e avremo la velocità di conversione pari alla metà della frequenza del quarzo utilizzato (00 = Fosc/2). Il Fosc riportato sul datasheet è la frequenza dell'oscillatore che si sta utilizzando per il PIC (quarzo o rete RC). 
ADCON0 = 0bxx000xxx; i bit 3, 4 e 5 della sequenza numerica (chiamati anche CHS0, CHS1, CHS2) servono a decidere da quale porta analogica leggere il dato. Mettendo 000 a questi bit, decidiamo di leggere il dato analogico dalla RA0 del PIC, se mettiamo 001 la lettura avverrà alla porta RA1, etc. etc. (guardatevi la tabella sul datasheet per le altre porte). 
ADCON0 = 0bxxxxx0xx; questo è il bit che dobbiamo settare a 1 per far partire la conversione.Finita la conversione esso torna a 0 da solo. (ADGO)Nel programma dovremo scrivere ADGO = 1 per far partire la conversione e attendere che esso torni a 0 per andare a leggere il risultato. 
ADCON0 = 0bxxxxxx0x; questo bit non è usato, lasciare sempre a 0. 
ADCON0 = 0bxxxxxxx0; Questo invece serve per attivare l'A/D, a 1 si attiva e a 0 si disattiva.Il registro ADCON0 che abbiamo appena spiegato, va usato in coppia con l'altro registro ADCON1.Prima di andare avanti tenete presente che non siamo tenuti a dare comandi con ADCON0 e ADCON1nella loro interezza. Possiamo anche dare come comando i singoli bit, vedremo come. 
 
ADCON1 
Questo registro và usato in coppia con l'altro e serve per settare le porte del pic come digitali o analogiche e come giustificare il risultato della lettura eseguita e posta in ADRESL e ADRESH. 
ADCON1 = 0b1xxxxxxx; questo bit (il bit 7, chiamato ADFM) decide come giustificare il risultato. 
ADCON1 = 0bx000xxxx; Questi bit non sono utilizzati, lasciare a 0. 
ADCON1 = 0bxxxx0000; Con questi 4 bit (chiamati anche PCFG0, PCFG1, PCFG2, PCFG3 ), decidiamo quali porte devono essere analogiche e quali devono essere digitali. 
Ovviamente stiamo parlando delle porte A del 16F876 e delle porte A + porte E del 16F877. 
Sul datasheet c'è la tabella a pagina 114, dove trovate anche quali pin possono essere usati per le tensioni di riferimento delle acquisizioni. 
Con le porte A/D, esiste anche la possibilità di utilizzare riferimenti di tensione per regolare il range dell'analogico e la precisione delle letture.Farò un approfondimento sui riferimenti appena mi si presenterà l'occasione 
 
Esempio 
Bene… siamo arrivati al punto di dover fare almeno un esempio, altrimenti quello che avete letto finora non vi sarà di aiuto.Dovete soltanto guardarvi le due tabelle del datasheet e seguire l'esempio che andrò a illustrare.Come esempio direi di utilizzare un programma che fa lampeggiare dei LED in sequenza quando si preme un tasto e poi, a questo programma, ci aggiungiamo la regolazione della velocità del lampeggiamento sequenziale tramite un potenziometro.Per fare le prove con il primo esempio, dovremo armarci di demoboard 16f876 (oppure 16F877), il modulo led, il modulo pulsanti e un potenziometro da 1K ohm lineare.Qui sotto vedete le foto relative all'assemblaggio, così vi è più facile seguire l'esempio.
adpot1_600
adpot2_600
 
/*led in sequenza 
* led collegati alla porta C, tasti collegati alla porta B 
* Sergio 29-03-2003 
*/ 
 
#include <pic.h> 
#include "delay.c" 
int step[8] = {1, 2, 4, 8,16,32,64,128};     //sequenza dei led 
 
main() 
    short i,pippo;                //dichiara 2 variabili con segno 
    i=0;                          //pone a 0 il valore di i 
    pippo=50;                     //pone a 50 la variabile pippo 
    PORTB = 0;                    //setta a 0 le porte B 
   TRISB = 1;                    //porte B come entrate 
    PORTC = 0;                    //setta a 0 le porte C 
   TRISC = 0;                    //setta le porte C come uscite 
    for(;;) {                     //inizio ciclo 
                if(PORTB==0b00010)           //verifica la pressione del tasto su RB1 
                        {        DelayMs(3);                //piccola pausa per antirimbalzo 
                        i++;                       //incrementa la variabile di 1 
                        if(i == 8)                 //se la variabile è al massimo 
                        i = 0;                     //lo riporta a 0 
                       PORTC=(step[i]);           //pone a PORTB il valore puntato dalla variabile 
                       DelayMs(pippo);            //pausa per settare la velocità del motore 
                       } 
              if(PORTB==0b00100)           //altro tasto (RB2)per il reverse 
                       { 
                       DelayMs(3); 
                       PORTC=(step[i]);  
                       i--;  
                       if(i == -1) 
                       i = 7; 
                      DelayMs(pippo); 
                      } 
            } 
 
Questo è il programma da qui partiremo, compilatelo e verificatene il funzionamento sulla vostra demoboard.     
Esso esegue un lampeggiamento sequenziale dei 8 led alla pressione di un tasto.Il tasto collegato a RB1 farà lampeggiare in un verso i led, e il tasto RB2 nel verso opposto.La velocità del lampeggiamento è determinata dalla variabile "pippo" che ha il valore di 50. 
Vogliamo rendere la variabile "pippo" proporzionale alla posizione del potenziometro in modo da poter regolare la velocità del lampeggiamento in maniera lineare, manualmente. 
Nel listato successivo mostro lo stesso listato di prima ma con l'aggiunta delle parti che servono a far funzionare il potenziometro. 
Dobbiamo settare le porte A come analogiche, inserire una nuova variabile che contenga il valore letto dal potenziometro ed usarla come parametro da dare al Delay 
 
/* led in sequenza 
* con controllo della velocita' 
* led collegati alla porta C 
* tasti collegati alla porta B, potenziometro collegato a RA0 
* Sergio 29-03-2003 
*/ 
 
#include <pic.h> 
#include "delay.c" 
int step[8] = {1, 2, 4, 8,16,32,64,128}; //sequenza dei led 
 
main(
short i,pippo,valore;          //dichiara 3 variabili *aggiunta una variabile* 
i=0;                           //pone a 0 il valore di i 
PORTB = 0;                     //setta a 0 le porte B 
TRISB = 1;                     //porte B come entrate 
PORTC = 0;                     //setta a 0 le porte C 
TRISC = 0;                     //setta le porte C come uscite 
ADCON0 = 0b11000001;           // ecco il primo elemento per l'A/D 
ADCON1 = 0b10000000;           // ecco il secondo elemento per l'A/D 
for(;;) 
         {                      //inizio ciclo 
         ADGO = 1;                      // fa partire la conversione 
         while(ADGO)                    //aspetta che il bit torni a 0 da solo 
         continue;                      //va avanti con il programma (facoltativo) 
         valore = ADRESL+(ADRESH<<8);   //assegno alla variabile valore il contenuto a 10 
                                                                           // bit che stà nei due registri 
         pippo = (valore/4);            // assegno alla variabile pippo il valore/4 
         if(PORTB==0b00010)            //verifica la pressione del tasto RB1 
                        {  
                        DelayMs(3);                 //piccola pausa per antirimbalzo 
                         i++;                        //incrementa la variabile di 1 
                         if(i == 8)                  //se la variabile è al massimo 
                         i = 0;                      //lo riporta a 0 
                        PORTC=(step[i]);            //pone a PORTC il valore puntato dalla variabile 
                        DelayMs(pippo);             //pausa per settare la velocit… del motore 
                        if(PORTB==0b00100)            //altro tasto per il reverse 
                        {    DelayMs(3); 
                        PORTC=(step[i]); 
                        i--; 
                        if(i == -1)    i = 7; 
                        DelayMs(pippo); 
                        } 
        } 
 
In questo listato ho evidenziato in rosso le parti aggiunte che sono servite a far funzionare il potenziometro. 
Vediamo in dettaglio cosa ho combinato: 
Ho aggiunto una variabile,chiamata "valore" che mi servirà a contenere il risultato a 10 bit letto dai due registri ADRESL e ADRESH. 
 
Ho inserito ADCON0 = 0b11000001; con questi valori che ora andremo a spiegare in dettaglio:  
 
  •    11 questo gruppo di bit settato a 11 decido di usare il clock interno del modulo A/D (è un oscillatore RC  indipendente dal quarzo)(vedere datasheet) 
  •      000 con questi 3 zeri decido che la porta analogica da cui leggere deve essere RA0. Se avessimo messo 001, per esempio, la porta da leggere sarebbe stata RA1. (sarebbe piu corretto dire AN 0 e AN1) 
  •      0  siamo arrivati al bit 2, quello di ADGO. Questo l'ho messo a 0 perché lo attiverò dopo. 
  •      Il penultimo bit, messo a zero, non è utilizzato dal PIC. 
  •      Il bit 0, invece serve per accendere il convertitore A/D (un pò come accendere il motore dell'auto ma restare fermi).  Si chiama ADON 
  •  
     
    Ora che ho spiegato il dettaglio di ADCON0 passiamo a dettagliare anche ADCON1: 
     
    ADCON1 = 0b10000000;    
     
  •      Con 1 indico che il risultato in ADRESH (sono 2 bits) deve essere giustificato a destra e che i rimanenti 6 bits sono letti come 0  
  •      I tre bit adiacenti non sono utilizzati dal pic (bit 4, 5, 6)  
  •      I primi 4 bit della serie di ADCON1 servono per settare i pin del pic come analogiche.   0000 vuol dire che tutti gli ingressi della port A sono analogiche. 
  •  
    Adesso che abbiamo spiegato il settaggio delle porte passiamo ai comandi veri e propri.Qui riporto il pezzo di codice responsabile del campionamento: 
     
    ADGO = 1;                     // fa partire la conversione 
    while(ADGO)                   //aspetta che il bit torni a 0 da solo 
    continue;                     //va avanti con il programma 
     
    Con ADGO=1 setto il bit 2 di ADCON1 facendo partire la conversione. 
    Dal momento che la conversione impiega un pò di tempo, sarà necessario attendere che il bit 2 di ADGO torni a zero prima di continuare e, l'istruzione "continue" permette di uscire dal ciclo di while e continuare. 
    A questo punto abbiamo il valore numerico della conversione, che è da 10 bit, depositato nei due registri ADRESL e ADRESH.    Li dobbiamo far contenere alla variabile "valore" con una somma shiffata di 8. 
    Finita la conversione ci ritroviamo la variabile valore che conterrà un numero che può variare da 0 a 1023.Sappiamo anche che, se al comando Delay gli passiamo un valore superiore a 256, il compilatore ci darà errore.     
    Quindi decido di dividere tale valore per 4 in modo da non superare mai il valore di 256 che sarà contenuto nella variabile pippo. 
    Con la variabile pippo ci regolo il tempo del Delay, regolando così la velocità del lampeggiamento. 
    Tutto chiaro????  
    Spero di si, altrimenti ho fatto una fatica inutile.     Come al solito, per errori, discrepanze, mal di schiena e imprecazioni, scrivete pure….Anche io sono un pivello in programmazione e non è detto che abbia scritto tutto bene 
    Buon lavoro.
     
    barra rossa2 
     
    b_home2_baloon 
     
    © 2002-2006 by Sergio Fiocco -Tutti i diritti riservati- Vietata la riproduzione, anche parziale del presente sito