Creative Commons BY-NC-ND 2.5Questo sito e tutto il suo contenuto sono distribuiti sotto la licenza Creative Commons Attribuzione - Non Commerciale - Non opere derivate 2.5 Italia e con le condizioni d'uso definite nel disclaimer: siete pregati di leggere entrambi questi documenti prima di usufruire dei contenuti di questo sito. Per alcuni contenuti è necessaria una registrazione gratuita: non è necessario pagare e non è necessario accumulare punteggi per accedere agli articoli e scaricare i sorgenti. Basta solo essere onesti. Se volete che questo sito continui a rimanere attivo, a contribuire ogni giorno alla diffusione della cultura libera, non copiate il materiale per ripubblicarlo in altri luoghi : chi fa questo è solo un miserabile e un perdente. Se volete partecipare su settorezero e rendere le vostre idee, i vostri progetti, fruibili da tutti senza limitazioni, come dovrebbe essere in un paese civile e acculturato, potete farlo tranquillamente.

Funzioni avanzate di IO digitale su PIC24/dsPIC. Il registro LAT: cosa fa di realmente diverso dal registro PORT?

Autore: Giovanni Bernardo | Data pubblicazione: 10 giugno 2011
Categorie: PICmicro 10/12/16 PICmicro 18 dsPIC / PIC24

I pin di I/O sono considerati le più semplici delle periferiche. Abbiamo già imparato che sui PIC16 della serie Midrange (sono quindi esclusi quelli appartenenti alle serie Enhanced) abbiamo essenzialmente due soli registri associati alle funzioni di I/O: TRISx e PORTx (dove la x va sostituita con la lettera che identifica il banco di porte).

Su molti PIC10/12, invece, essendoci un unico banco di porte, il registro PORT diventa GPIO (General Purpose I/O) e il registro TRIS diventa TRISIO: nomi leggermenti differenti ma stesso succo.

Alcuni di noi sanno anche che su tutti PIC18 (ma anche sugli ultimi nati delle famiglie PIC10/12/16 Enhanced) oltre a questi due registri è stato aggiunto un registro LATx che per molti aspetti sembra svolgere le stesse funzioni del registro PORTx ma eliminando alcuni inconvenienti che analizzeremo dopo in dettaglio (per cui questo articolo non è destinato solo agli utilizzatori dei pic a 16bit ma un po’ a tutti).

Sui pic a 16 bit (PIC24 e dsPIC) oltre questi 3 registri ce ne sono altri che permettono di settare altre funzionalità specifiche dei “semplici” I/O digitali. Alcuni di questi registri li abbiamo già incontrati nell’articolo relativo alla caratteristica del Peripheral Pin Select.

Approfitto quindi di questo articolo per riassumere brevemente il funzionamento di questi vecchi (non nel senso di obsoleti) registri affrontando i problemi del registro PORT per capire del perchè del registro LAT e introdurre quindi i nuovi registri presenti sui pic a 16bit.

Registri TRIS

I registri TRISx (TRISA, TRISB, TRISC ecc), o il registro TRISIO su molti PIC10/12, determinano il funzionamento di un pin di I/O come ingresso o come uscita. Se il bit di una porta nel registro TRISx viene posto ad 1, il pin viene impostato come Ingresso (ovvero: il pin va in alta impedenza), se invece il bit viene posto a 0, il pin relativo funziona come uscita.

Ricordo ancora una volta che il funzionamento di un I/O come digitale è sempre e comunque influenzato dalla eventuale presenza di altre periferiche multiplexate sul pin: il pin funziona come I/O digitale se le periferiche aggiuntive (come ad esempio un convertitore A/D, un comparatore ecc) sono disattivate.

Un sistema semplice per ricordarsi di questo comportamento è associare il numero 1 alla lettera I (Input) e il numero 0 alla lettera O (Output).

Registri PORT

I registri PORT servono ad accedere al dato presente su un pin di I/O. Quando un pin è configurato come ingresso, una lettura del bit ci indica il livello presente sul pin: se viene restituito un 1 vuol dire che il pin sta leggendo un livello logico alto, uno zero indica un livello logico basso. Se un pin è configurato come uscita e sul bit relativo nel registro PORT scriviamo un 1, portiamo il pin a livello logico alto, se scriviamo uno zero portiamo il pin a livello logico basso.

Quando un pin viene utilizzato come uscita e quindi intendiamo impostare il suo livello logico di uscita, il dato che noi andiamo a scrivere viene prima posizionato in un latch, ovvero una cella elementare di memoria e quindi successivamente trasferito al pin (alla “cella” del pin), mentre la lettura del registro PORT viene eseguita direttamente sulla cella di uscita del pin.

Il valore in tensione dei livelli logici è in funzione della tensione di alimentazione del dispositivo. Generalmente il livello logico basso è identificato dal riferimento di 0V indicato anche come GND o Vss, mentre il livello logico alto è identificato dalla tensione di alimentazione Vdd del picmicro (in genere 5V per i pic10/12/16/18/dsPIC30 e 3.3V per i PIC24/dsPIC33).

C’è ovviamente un valore di soglia per il quale anche un livello logico più basso di Vdd viene identificato come alto, un valore di soglia per il quale anche un livello logico più alto di Vss viene identificato come basso e quindi un range “indeterminato” nel quale un valore di tensione può essere identificato come alto o basso e quindi nel quale non bisogna mai lavorare.

Per maggiori informazioni potete guardarvi il grafico presente in questa pagina

Un pin può essere configurato dinamicamente (a run time) sia come ingresso che come uscita e può anche passare da uno stato all’altro di continuo. Ovviamente quando si esegue un’operazione del genere (passaggio da uscita a ingresso e viceversa) bisogna prendere delle precauzioni sia lato hardware per evitare cortocircuiti sia per evitare comportamenti indesiderati lato software. Un esempio: supponiamo che un pin configurato come ingresso viene impostato come uscita; ad un tratto potrebbe verificarsi la presenza di un valore logico non voluto sul pin di uscita per cui generalmente si scrive prima il valore a cui impostare il pin nel registro PORT e solo successivamente si cambia il funzionamento del pin nel registro TRIS.

Comportamenti “strani” del registro PORT

C’è da aggiungere una questione molto importante e spesso sottovalutata riguardante il funzionamento del registro PORT.  Quando un pin funziona come uscita, noi da software settiamo il livello di uscita (lo impostiamo a 1 o a zero).

Questa operazione viene eseguita in assembler mediante le istruzioni BSET (Bit Set => Setta un bit, ovvero lo  imposta a 1) e BCLR (Bit Clear => Resetta un bit, ovvero lo imposta a zero).

Le istruzioni BSET e BCLR sono in realtà istruzioni di lettura-modifica-scrittura ovvero quando vengono eseguite viene dapprima letto lo stato di tutto il banco (viene letto lo stato di PORT e salvato in memoria ram), il valore del bit viene quindi modificato nella ram e solo alla fine il dato viene trasferito (scritto) nel  latch di uscita. Impostare un bit ad un nuovo valore, quindi, comporta a livello hardware una prima operazione di lettura di tutto il banco. Una cosa che generalmente viene ignorata.

Quando eseguiamo operazioni di modifica del registro PORT mentre il pin è configurato come uscita, si potrebbero avere comportamenti indesiderati se c’è un elevato carico capacitivo sul pin o se il dispositivo funziona a frequenze di clock molto elevate e queste operazioni vengono eseguite di continuo, questo proprio a causa di questa operazione “nascosta” di lettura a cui non si pensa.

Spiego questa cosa con un esempio. Mettiamo di avere un pic24 o comunque un pic funzionante ad una frequenza di clock molto elevata (es.: 32MHz) e di avere i pin 0 e  1 del banco A impostati come uscita.

Immaginiamo ora di impostare in sequenza il pin 0 a livello logico alto e quindi il pin 1 pure a livello logico alto.  Vediamo in questo schema ciò che accade a livello hardware:

La prima istruzione eseguita è quella che prevede di portare a livello alto il pin 0 (la linea inferiore nello schema). Vediamo che dall’inizio fino al punto 1 l’istruzione viene caricata ed eseguita (la cpu legge lo stato di PORTA, ne carica il valore in memoria, lo modifica e quindi trasferisce il nuovo valore al latch di uscita del banco PORTA). L’istruzione quindi termina in 1 e di conseguenza la tensione sul pin 0 comincia a salire per portarsi al livello che gli abbiamo specificato. Questo accadrà in un certo tempo, molto molto breve.

Nel punto 2 comincia l’esecuzione dell’istruzione successiva. Abbiamo detto che prima di eseguire il Bit Set, il registro PORTA viene letto. Quindi nel punto 2 il processore legge tutto il banco A e leggerà il pin 0 come a livello logico zero in quanto la tensione non ha ancora raggiunto il valore di soglia. Ecco perchè specificavo che questo problema si ha soprattutto quando le frequenze di funzionamento sono molto elevate: ha fatto prima il processore a leggere lo stato della porta che non il pin a portarsi a livello logico alto! Questo può accadere anche nei casi in cui sui pin in uscita ci sono capacità molto elevate e che quindi rallentano l’operazione di passaggio di stato.

Nel punto 3 il pin 0 ha quindi raggiunto il valore di soglia e si trova pertanto a livello alto. Nel frattempo la seconda istruzione, però, non è ancora terminata!

Nel punto 4 finalmente anche la seconda istruzione termina il che vuol dire che il processore, dopo aver modificato il valore di PORTA che ha salvato in memoria, lo dovrà scrivere sul latch di uscita. Il valore di Pin 1 lo scrive correttamente perchè è l’ultimo che abbiamo impostato, il valore di pin 0 invece verrà scritto come zero in quanto è stato l’ultimo valore che ha letto prima della successiva modifica. Il risultato è che soltanto il pin 1 si troverà a livello alto mentre il pin 0 si troverà ancora a livello basso nonostante noi abbiamo eseguito l’operazione.

Capite quindi che bel pasticcio quando andiamo a lavorare con dispositivi ad alta frequenza di clock. Ma mamma Microchip ha pensato a tutto e ha quindi introdotto il registro LAT.

Il registro LAT

Il registro LAT, sempre presente su tutti i pic dalla serie 18 in su e sui nuovi PIC10/12/16 serie enhanced, elimina i problemi relativi a queste operazioni di lettura-modifica-scrittura. Come?

In maniera piuttosto semplice: l’operazione di lettura del registro LATx esegue la lettura del Latch di uscita anzichè del pin. Il vantaggio è palese: se il pin non ha ancora cambiato lo stato logico (perchè il processore sta andando troppo veloce e ha letto prima che cambiasse o perchè c’è un elevato carico capacitivo che rallenta l’operazione “elettrica” di cambio stato) siamo sicuri che invece nel latch di uscita c’è sicuramente il valore che noi abbiamo impostato e che successivamente sarà trasferito al pin di uscita! La scrittura in LATx, invece, è identica alla scrittura su PORTx, senza alcuna differenza.

Quindi, ricapitolando:

  • Una scrittura in PORTx scrive nel latch di uscita
  • Una scrittura in LATx pure scrive nel latch di uscita, quindi per le operazioni di scrittura, usare LAT o PORT è indifferente
  • Una lettura di PORTx esegue la lettura dello stato in cui si trovano i pin
  • Una lettura di LATx esegue la lettura del latch di uscita, il cui valore potrebbe essere differente da quello di PORTx o meglio: non subito uguale.

Per cui: per operazioni di scrittura potete usare indifferentemente uno dei due registri, per operazioni di lettura è più sicuro leggere il registro LAT (ma poi dipende dalla particolare applicazione).

Registri ODC

Questi sono presenti unicamente sui pic a 16 bit (per ora!) e hanno l’importante funzione di permettere ad un pin configurato come uscita di essere un’uscita Open Drain.

Mettendo ad 1 il bit di una porta nel relativo registro ODCx permette a tale pin di agire come uscita Open Drain. Mettendo il bit a zero (condizione di default) il pin agisce come normale uscita. Un’uscita open drain è capace di fornire unicamente il livello logico basso ma non quello alto. Il vantaggio di avere un’uscita open drain è quella di poter pilotare dei carichi funzionanti a tensioni più elevate (ma anche più basse) di Vdd: si mette una resistenza di pullup per fornire il livello logico alto.

Ovviamente abbiamo dei limiti, il valore massimo di tensione applicabile è definito dal parametro VIH, mentre il minimo vale Vss (GND).

I registri ODCx permettono ad un pin di uscita di essere open drain anche nel caso in cui vi è una periferica attiva che controlla il pin. Questo ovviamente non vale per l’I2C in quanto i pin relativi all’I2C sono sempre open-drain per cui anche mettendo il bit a zero rimangono open drain.

Pin con notifica di cambiamento, registri CN (Change Notification) e Resistenze di PullUp / PullDown integrate

Sappiamo che sui pic16 abbiamo la funzione di interrupt sul cambio di stato del pin RB0/INT o sulle porte RB4-7. Sui pic a 16 bit questa cosa è stata eliminata a favore di un meccanismo più efficente. Dando uno sguardo al datasheet di un PIC24, ad esempio, vediamo che alcuni pin sono contrassegnati, tra le altre cose, con la dicitura CNx: questo significa che su quel pin è possibile la generazione di un interrupt sul cambio di stato (Change Notification).

I registri associati con questa funzionalità sono principalmente 2:

  • CNENx : Abilità la funzionalità di interrupt per i pin. In pratica in questo registro ci sono i vari bit CNxIE che permettono di abilitare (bit posto a 1)/disabilitare (bit posto a zero) la generazione dell’interrupt sul cambio di stato per i pin aventi il contrassegno CNx. I bit vengono abilitati singolarmente ma per poter essere rilevati da una funzione di interrupt è necessario settare il flag di abilitazione globale per il change notification: CNIE situato nel registro IEC1. Il vettore di interrupt per il cambio di stato è difatti uno solo (vettore n°19, indirizzo 0×00003A, indirizzo alternativo 0×00013A) e all’interno di questo discerniamo su quale pin si è verificato il cambio di stato controllando il flag di interrupt CNxIF. Esempio (non testato):
    int main(void)
       {
       ConfigIntCN(INT_ENABLE & INT_PRI_4); // abilito l'interrupt su CN e gli imposto priorità 4
       } // main
     
    void _ISR _CNInterrupt(void)
       {
       _CNIF=0; // resetto il flag di interrupt globale
       if (_CN1IF) // cambio di stato su CN1
          {      // ...operazioni
          _CN1IF=0; // azzero il flag
          }
       if (_CN2IF) // cambio di stato su CN2
          {      // ...operazioni
          _CN2IF=0; // azzero il flag
          }
       } // _CNInterrupt

    Per maggiori informazioni sulla gestione degli interrupt sui pic a 16 bit fate riferimento a questo mio articolo.

  • CNPUx : Questo registro serve per abilitare le resistenze di pullup integrate sui pin CNx, una funzione comodissima soprattutto se su tali pin abbiamo collegato dei pulsanti. In questo registro sono contenuti i bit CNxPUE, che servono ad abilitare (1) o disabilitare (0) la resistenza di pullup sul pin CNx. Ovviamente questa funzione non è strettamente collegata alla questione dell’interrupt: potete anche lasciar perdere l’interrupt ma sfruttare la resistenza di pullup.

    La resistenza di pullup si può abilitare/disabilitare anche utilizzando le macro disponibili mediante l’inclusione di <port.h>:

    EnablePullUpCN0; // abilita la resistenza di pullup sul pin CN0
    DisablePullUpCN1; // disabilita la resistenza di pullup sul pin CN1

Alcuni pic a 16bit, oltre alla possibilità di avere la resistenza di pullup, possono anche avere sul pin una resistenza di pulldown. Questi pic hanno quindi un registro CNPDx nel quale sono contenuti i bit CNxPDE che abilitano/disabilitano la resistenza di pulldown sul pin CNx.

La funzione di Peripheral Pin Select

Questa caratteristica anche fa parte delle funzioni avanzate di I/O e abbiamo già visto in un articolo precedente che il PPS ci permette di rimappare una periferica su un pin a piacere tra quelli aventi il contrassegno RPx. E questo è anche uno dei tanti motivi per i quali sui datasheet non appaiono i pin destinati all’UART, all’SPI o ad altre periferiche digitali ad eccezione dell’I2C (che ha solo due assegnazioni fisse da scegliere tramite word di configurazione).

Di questa caratteristica ne ho già discusso in questo articolo.

Articoli che potrebbero interessarti

L'articolo ti è piaciuto o ti è stato utile per risolvere un problema? Supporta e mantieni in vita questo sito, ci basta soltanto un caffè o una birra.
Se desiderate che settorezero continui a rimanere gratuito e fruibile da tutti, non copiate il nostro materiale e segnalateci se qualcuno lo fa

Puoi lasciare un commento, o un trackback dal tuo sito.

  1. #1 da voska il 24 aprile 2012

    Grazie mille per la spiegazione, da qualche giorno ho a che fare con un PIC18 e fino ad oggi non mi era ancora chiara la differenza tra port e latch, mi sento sollevata ora che ho capito un problema di base!

    Devo lavorare con un sensore a gas, così mi è stato presentato il PIC18F2520 all’interno di una scheda MuIn 990.005 .

    Tramite i diversi esempi “Hello Word”, sono facilmente riuscita a generare i due segnali da mandare al sensore ed al riscaldatore, ma sto avendo difficoltà (nelle varie librerie di MPLAB C18 e del manuale C18 ci sono pochi esempi concreti) per prelevare il segnale dal sensore, quindi ho problemi con la conversione A/D.

    Non mi è chiaro come prelevare il segnale: devo impostare il canale con SetChanADC, un timer, le rispettive funzioni di OpenADC o si può tutto ovviare con readADC? Provandole e compilarle così come le ho trovate scritte giustamente non ho avuto buoni risultati.

    Non capisco fisicamente cosa sono gli ADC_xANA, gli ADC_0_TAD, gli ADCONx, etc… e non mi è chiaro come capirli dato che trovo esempi nelle librerie con questi dati per scontato.

    Copio qui di seguito il codice con cui ho generato i due segnali da mandare al sensore ed al riscaldatore, potreste aiutarmi su come prelevare la risposta del sensore al segnale inviatogli, per favore? (Le istruzioni commentate sono quelle di prove A/D prese dai manuali ma ovviamente non vanno bene) .

    Grazie Mille.

    #include
    #include
    #include

    #pragma config OSC = HSPLL // fosc = 40MHz
    #pragma config WDT = OFF
    #pragma config LVP = OFF
    #pragma config PBADEN = ON

    #define VD LATAbits.LATA0 //COLLEGO VD a RA0
    #define VH LATAbits.LATA2 //COLLEGO VH a RA2

    // int result;

    void main()
    {
    TRISA=0×00; // tutto in output
    LATA=0×00; //tutto a zero, quindi il led è acceso
    SetChanADC( ADC_CH0 );

    while(1)

    {

    VD=1; //accendo VD

    Delay10KTCYx(5); //lo tengo acceso per 5ms
    VD=0; //spengo VD
    VH=1; //accendo VH
    Delay10KTCYx(14); //lo tengo acceso per 14ms

    VH=0; //spengo VH
    Delay10KTCYx(181); //faccio passare un intervallo di tempo pari a 995-14=981 prima di riaccender VH
    Delay10KTCYx(200);
    Delay10KTCYx(200);
    Delay10KTCYx(200);
    Delay10KTCYx(200);

    /* OpenADC( ADC_FOSC_RC & ADC_RIGHT_JUST & ADC_12_TAD, ADC_CH0 & ADC_INT_OFF, 15 );
    Delay10TCYx( 5 ); // Delay for 50TCY
    ConvertADC(); // Start conversion
    while( BusyADC() ); // Wait for completion
    result = ReadADC(); // Read result
    CloseADC();

    result=readADC();*/

    }

    }

  2. #2 da voska il 24 aprile 2012

    Gli include non sono stati incollati, preciso che sono riferiti a: p18f2520.h ; delays.h; adc.h

  3. #4 da voska il 27 aprile 2012

    Wow Giovanni, grazie per l’immedita risposta!!!
    Ti scrivo per informarti che oggi ho fatto la conversione dei dati acquisiti (mi sono sbizzarrita: ho provato sia con i registri che con le funzioni!) e la compilazione non ha dato errori :)) ; non mi resta che trasferire i dati convertiti all’interfaccia labview e vedere se tutto va bene!!!
    Non so ancora che tipo di connessione mi assegneranno ma ho visto che quella seriale RS232 è presente nel manuale da te indicatomi; se invece dovessi avere a che fare con una usb, cosa mi consiglieresti?
    Grazie ancora!

    • #5 da Giovanni Bernardo il 27 aprile 2012

      L’USB è questione ben piu complicata. La via più semplice di utilizzare l’USB è quello di farlo in modalità CDC (emulazione seriale). In pratica il programma che carichi sul pic fa in modo da far riconoscere la porta USB del PIC come una porta seriale. Tu lato firmware la tratti più o meno come una seriale. Scriverti da te un framework per usare l’USB è impresa assurda. Generalmente si modificano a proprio piacimento i progetti già pronti della MAL (Microchip Application Libraries), disponibile gratuitamente sul sito della Microchip. Questi esempi però sono progettati per schede di sviluppo microchip, per cui adattarli alle proprie esigenze può essere molto difficile, specie per chi è alle prime armi e non conosce bene l’architettura del processore utilizzato. Altrimenti in giro, se non erro su Dangerous Prototypes, c’era un framework USB open source per i PIC18F. Devi cercare un po’ con Google.

Devi essere collegato per lasciare un commento.

  1. Ancora nessun trackback
settorezero.com e il logo Zroid™ ©2007÷2012 Giovanni Bernardo - E' vietata la copia e la distribuzione anche parziale dei contenuti di questo sito web senza l'esplicito consenso dell'autore.
I contenuti di settorezero.com sono distribuiti sotto una licenza Creative Commons Attribuzione-Non Commerciale-Non Opere derivate 2.5 Italia a cui vanno aggiunte le condizioni d'uso definite nel disclaimer.
settorezero.com e tutti i suoi contenuti sono tutelati dalla legge sul diritto d'autore per cui i trasgressori sono perseguibili a norma di legge.
Creative Commons BY-NC-ND 2.5
Il tema di questo sito è basato sul tema Fusion per wordpress, realizzato originariamente da digitalnature e fa uso del plugin Wassup per il computo delle statistiche. Per contattare l'autore siete pregati di utilizzare la sezione contatti.
Per essere aggiornato con tutte le novità di settorezero.com seguici anche anche su Facebook Twitter Tumblr Blogspot Youtube.