Tutorials - PIC18 by Data Sheet

 
 

MSSP - I2C

 

Gestione della comunicazione I2C.

Se riassumiamo quanto detto finora, osserviamo che la comunicazione sul bus consiste in una serie di azioni a cui devono corrispondere delle reazioni. Ne risultano sequenze ben definite. Ecco alcune di quelle descritte nelle pagine precedenti: 

Il Master scrive un byte verso uno SLave (7 bit)

  • il Master verifica il bus libero e invia uno Start

  • il Master invia un indirizzo in cui R/W=0 (scrittura)

  • lo Slave risponde con un ACK

  • il Master riconosce l' ACK e invia il byte di dati

  • lo Slave risponde con un ACK

  • il Master chiude la comunicazione con uno Stop

il Master legge un byte dallo Slave (7 bit)

  • il Master verifica il bus libero e invia uno Start

  • il Master invia un indirizzo in cui R/W=1 (scrittura)

  • lo Slave risponde con un ACK

  • il Master si predispone in ricezione

  • lo Slave invia un byte di dati

  • il Master risponde con un NACK fermando la trasmissione

  • il Master chiude la comunicazione con uno Stop

il Master legge un byte da uno Slave (10 bit)

  • il Master verifica il bus libero e invia uno Start

  • il Master invia il primo byte di indirizzo in cui R/W=0 (scrittura)

  • lo Slave risponde con un ACK

  • il Master riconosce l' ACK e invia il secondo byte di indirizzo

  • lo Slave risponde con un ACK

  • il Master invia un Restart

  • il Master invia nuovamente il primo byte di indirizzo con R/W=1 (lettura)

  • lo Slave invia un byte

  • il Master risponde con un NACK fermando la trasmissione

  • il Master chiude la comunicazione con uno Stop

Ne risulta che la struttura del software sarà analoga ad una macchina a stati in cui si passa alla stato successivo solo quando il precedente è concluso. Questo è un obbligo in quanto il modulo MSSP non può accodare comandi.
La scelta del progettista non è limitante, ma logica, in quanto le risposte possibili al comando possono essere diverse: ad esempio, la periferica potrà rispondere NACK o ACK e le due situazioni non possono avere la stessa soluzione automatica. Così pure sarà necessaria una consistente complicazione del software se si prevede un sistema multi-master, con la relativa gestione dei conflitti oppure si debba tenere conto di possibili guasti nelle periferiche, se non di sistemi dinamici e auto-configuranti, situazioni che richiedono un programma di controllo molto articolato.

Negli esempi che facciamo qui, prendiamo in considerazione situazioni ideali, in sistemi a Master singolo, senza prevedere guasti alla periferica, che richiederebbero strutture di timeout per risolvere il problema e neppure situazioni di conflitto sul bus. Dovendo implementare bus con caratteristiche superiori, sarà necessario studiare con cura tutte le possibili configurazioni che si dovranno affrontare.

In ogni caso. la chiave della gestione del protocollo è l' impiego corretto delle segnalazioni che il modulo MSSP fornisce attraverso i vari flag.

Ci sono due possibili alternative:

  • una gestione in polling

  • una gestione in interrupt

Quale è la differenza di una gestione polling e di una in interrupt?

Semplicemente si tratta di valutare quale è il carico del processore, dato che la necessità assoluta di rispettare le sequenze previste dal protocollo del bus impegna in modo sensibile il tempo della CPU nei test sui flag.
Trattandosi di una comunicazione sincrona con il clock separato dai dati, durante la trasmissione il clock può anche variare, tanto che è definito, come abbiamo visto, un clock stretching per rallentare la velocità di trasmissione. E' chiaro, però, che un forte rallentamento dovuto alla necessità del firmware di espletare le varie fasi della comunicazione, finirebbe per essere penalizzante.
Inoltre, se il processore deve svolgere anche altre operazioni i cui tempi siano critici, una gestione in polling è improponibile.

Come sistemi in cui è possibile un polling, si possono esemplificare bus costituiti dal PIC come Master, collegato a sensori (temperatura, pressione, ecc), RTC, EEPROM, ecc.
Il compito svolto è quello di recuperare i dati dai sensori e collezionarli in EEPROM e anche inviarli a un display o a una linea di comunicazione esterna (RS-232 e simili).
In configurazioni simili il processore sarà impegnato a comunicare con una periferica per volta, con il suo firmware che stabilisce le sequenze. Non essendo critici i tempi, ogni operazione può essere svolta consecutivamente con le prestazioni che potrà offrire la CPU e senza rischio di conflitti sul bus, dato che sarà il PIC a decidere ogni manovra.

Per contro, se il PIC ha funzione di Slave e deve fornire ad un Master dati provenienti da vari sensori con tempi critici, come ad esempio segnali tachimetrici o di posizione, encoder e simili, o effettuare la funzione di scambiatore di protocolli (ad esempio tra RS-232 e I2C o I2C e USB), la gestione dei tempi costringe obbligatoriamente ad abbandonare il polling e la soluzione, allora, sarà una gestione in interrupt, dove ogni task chiama il processore al momento opportuno, lasciandolo libero per le altre quando non è necessario il controllo della task stessa.

La struttura tipica di una gestione in polling è:

  • lanciare il comando

  • attendere l' esecuzione del comando

Una altra alternativa è questa:

  • verificare se ci sono comandi in corso; se sì, ritentare più tardi

  • se il modulo MSSP è libero, lanciare il comando

Ovviamente, la gestione ideale è quella che sfrutta gli interrupt e lascia libero il processore di effettuare altre attività:

  • interrupt da MSSP: verificare la causa

  • eseguire i passi necessari

  • cacellare il flag di interrupt e riprendere l' attività corrente

In tutti i casi si utilizza per i test il flag di interrupt del modulo MSSP (SSPIF) e quelli di condizione di SSPSTAT.

Nel primo caso di polling, sappiamo che l'evento viene rilevato alla fine del comando in corso. La struttura tipica è un polling stretto del flag e la routine è bloccata fino a che il comando è eseguito.
Nel secondo caso di polling, per prima cosa viene verificato se è possibile lanciare il comando, poi questo viene eseguito e si abbandona la procedura per passare ad altro. Prima del prossimo comando occorrerà verificare che il modulo sia libero.

E' indispensabile in ogni caso essere consapevoli delle funzioni dei vari flag e della loro gestione nelle varie fasi, esendoci flag che si cancella automaticamente a seguito di operazioni di scrittura/lettura e altri che vanno cancellati in software. Ad esempio, il bit UA viene cancellato automaticamente una volta riscritto SSPADD, ma il flag SSPIF va cancellato da programma.
Anche perchè, come abbiamo visto, le situazioni non risolte portano ad un fallimento della comunicazione, con la genrazione di NACK al posto di ACK, se non con il blocco del bus (SCL=0 ) fino alla rimozione della causa.

Di conseguenza è necessario determinare tutti i casi possibili, ad esempio:

  • Trasmissione in corso (indicato dal bit R/W Registro SSPSTAT)
  • Condizione di Start in corso (indicato dal bit SEN in SSPCON2)
  • Restart in corso (indicato dal bit di RSEN di SSPCON2)
  • Condizione di Stop in corso (indicato dal bit PEN di SSPCON2)
  • Ricezione in corso (indicato da CEN di SSPCON2)
  • ACK in corso (indicato dal bit  ACKEN di SSPCON2)
  • Necessità di aggiornamento di SSPADD (indicato da UA)
  • Dato ricevuto o in trasmissione (Buffer Full BF )
  • Errori di Overflow (SSPOV) o collisioni (WCOL, BCLIF)
  • Generico completamento di una azione (SSPIF)

Come consigli generici per la gestione del modulo MSSP in I2C, si può sintetizzare quanto segue.

Per la modalità master o multi-master:

  • Se il tempo non è un fattore critico, è preferibile inviare i comandi ed attenderne la conclusione.
  • Se il tempo è stringente, meglio effettuare la verifica prima di lanciare il comando (è possibile che il comando precedente sia già concluso)
  • Se occorre effettuare altre attività assieme alla comunicazione, non c'è altra via che implementare una gestione in interrupt, che risolve ogni problema di tempo.

Per il modo Slave è preferibile in tutti i casi la gestione in interrupt, in quanto la chiamata da parte del Master può avvenire in qualsiasi istante in modo del tutto asincrono dalle attività svolte dallo Slave.
inoltre, come abbiamo visto, il clock di bus è pure fornito dal Master, il che condiziona i tempi di risposta della Slave.
In ogni caso sarà assolutamente necessario prevedere ogni possibile condizione anomala (flag SSPOV, BF, UA) per evitare di bloccare il bus con una mancata risposta agli eventi critici.


Un po' di codice per l' MSSP in I2C.

Vediamo qualche esempio minimale di azioni su un bus ideale. Gestione in polling e con routine bloccanti (non rilasciano fino a che la condizione richiesta è completata). 

Va osservato che gli esempi qui sopra sono strutture del tutto elementari, che partono dalla condizione che le operazioni vadano tutte a buon fine e non ci siano problemi sul bus, di cui manca la gestione.

Un breve esempio di setup dell' MSSP per I2C con le seguenti condizioni:

  • modo Master
  • Fosc 40 MHz
  • baud rate 400 kHz
  • in forma di subroutine

; configurazione MSSP per I2C
   movlw  0x18        ; valore per 400 kHz
   movwf  SSPADD      ; nel registro
   clrf   SSPCON1     ; azzera flag in SSPCON1
   clrf   SSPCON2     ; e in SSPCON2
   bcf    SSPSTAT,SMP ; disabilita Slew Rate
  
bcf    SSPSTAT,CKE ; disabilita SMBus
  
bsf    TRISC,SCL   ; imposta SDA
   bsf    TRISC,SDA   ; e SCL come input
  
movlw  b'00001000' ; I2C modo Master
   iorwf  SSPCON1 
   bcf    PIR2, BCLIF ; cancella flag interrupt
   bcf    PIR1, SSPIF ; cancella flag interrupt 
  
MSSP_on            ; accende MSSP (SSPEN=1)
   
return     

Se il modulo MSSP non è utilizzato, può essere spento per risparmio di energia. Il modulo va spento anche quando si deve configurare in un modo differente.

; spegni MSSP 
MSSP_off MACRO
  
bcf    PSSPCON1, SSPEN ; spegne MSSP
 
        ENDM

; accendi MSSP 
MSSP_on MACRO
  
bsf    PSSPCON1, SSPEN ; accende MSSP
 
        ENDM

Ecco, ad esempio, una delle possibili subroutine che attende che l'operazione precedente sia completata:

; verifica per il bus idle
i2c_idle  btfsc  SSPSTAT, R_W   ; trasmissione in corso?
          
bra   i2c_idle       ; si, attesa
12c_id1
   movf   SSPCON2,W      ; no, ACKEN,RCEN,PEN,RSEN, SEN 
   
       andlw  0x1F           ; sono a 1 ?
         
bnz    i2c_id1        ; si, attesa
   
       return                ; no, fine test

Il Master genera una condizione di Start.

; Genera uno Start iniziale
i2c_s  bsf    SSPCON2, SEN     ; abilita condizione di Start
i2c_s1 btfsc  PIR1, SSPIF      ; check per controllo bus acquisito
        bra   i2c_s1           ; No, attesa
       bcf    PIR1, SSPIF      ; Sì, cancella SSPIF
       return                  ; fine comando

Il Master invia un byte in WREG sul bus.

; il Master invia un byte
i2c_wr  movwf  SSPBUF         ; scrive in buffer
i2c_wr1 btfsc  PIR1, SSPIF    ; è stato ricevuto un ACK ?
         bra   i2c_wr1        ; no, attesa
        bcf    PIR1, SSPIF    ; sì, cancella SSPIF
        return                ; fine comando                 

oppure, analogo, con il test sul bit di avviamento del comando:

; Genera uno Start iniziale
i2c_s  bsf    SSPCON2, SEN     ; abilita condizione di Start
i2c_s1 btfsc  SSPCON2, SEN     ; check per controllo di fine comando
        bra   i2c_s1           ; No, attesa
       bcf    PIR1, SSPIF      ; Sì, cancella SSPIF
       return                  ; fine comando

O anche una sequenza dove il comando è lanciato previa verifica del bus:

; Genera uno Start iniziale
i2c_s  rcall  Si2c_idle        ; verifica bus libero
i2c_s1 bsf    PSSPCON2, SEN    ; avvia comando
       return                  ; e ritorna

Il Master genera una condizione di Stop.

; Genera uno Stop
i2c_p  bsf    SSPCON2, PEN     ; abilita condizione di Stop
i2c_p1 btfsc  PIR1, SSPIF      ; check per controllo di fine comando
        bra   i2c_s1           ; no, attesa
       bcf    PIR1, SSPIF      ; sì, cancella SSPIF
       return                  ; fine comando

Il Master genera una condizione di Stop.

; Genera uno Stop
i2c_p  bsf    SSPCON2, PEN     ; abilita condizione di Stop
i2c_p1 btfsc  PIR1, SSPIF      ; check per controllo di fine comando
        bra   i2c_s1           ; no, attesa
       bcf    PIR1, SSPIF      ; sì, cancella SSPIF
       return           

Il Master genera un ACK.

; Genera ACK
i2c_ak rcall   i2c_idle          ; verifica per il bus idle
       bcf
     SSPCON2, ACKDT    ; livello aknowledge=0 - ACK
       bsf     SSPCON2, ACKEN    ; e avvia
       return                    ; esci

Il Master genera un NACK.

; Genera NACK
i2c_nak rcall   i2c_idle          ; verifica per il bus idle
        bsf
     SSPCON2, ACKDT    ; livello aknowledge=1 - NACK
        bsf     SSPCON2, ACKEN    ; e avvia
        return                    ; esci

Se il test è al rilascio del comando:

; Genera NACK
i2c_nak bcf     SSPCON2, ACKDT    ; lvello aknowledge=0 - ACK
        bsf     SSPCON2, ACKEN    ; e avvia
i2c_na1 btfsc   SSPCON2, ACKEN    ; completato ?
         bra    i2c_na1           ; no, attesa
       return                     ; si, fine comando

Il Master trasmette un byte allo Slave

; trasmettere un byte
i2c_wr rcall  i2c_idle         ; verifica per il bus idle
       movlw  dato             ; copia dato in W
       movwf  SSPBUF           ; copia W in buffer trasmisione
       return                  ; fine comando

o, in alternativa, con il dato in WREG: 

; trasmettere un byte
i2c_wr   movwf  SSPBUF         ; copia W in buffer trasmisione
i2c_wr1  btfsc  SSPSTAT, R_W   ; trasmissione in corso?
       
   bra    i2c_wr1       ; si, attesa

         return                ; no, fine comando

Il Master riceve un byte dallo Slave e ritorna con il dato in WREG.

; Ricevere un byte
i2c_rd bsf    SSPCON2, RCEN    ; abilita la ricezione
i2c_r1 btfsc  PIR1, SSPIF      ; dati pronti ?
        bra   i2c_r1           ; No, attesa
       bcf    PIR1, SSPIF      ; Sì, cancella SSPIF
      
movf   SSPBUF, W        ; copia buffer in WREG 
       return

Il Master invia l'indirizzo allo Slave e riceve un byte di dati; un concatenamento semplificato delle routines precedenti sotto forma di una unica subroutine.

; Il master prende il controllo del bus
       
bcf    PIR1, SSPIF      ; cancella SSPIF 
i2c_s   bsf    SSPCON2, SEN     ; abilita condizione di Start
        rcall  i2c_wait         ; attesa completamento comando

; invia l' indirizzo da WREG
        movlw  address          ; carica indirizzo con R/W=1      
i2c_wr  movwf  SSPBUF           ; scrive in buffer
        rcall  i2c_wait         ; attesa completamento comando

; riceve un byte
i2c_rd bsf    SSPCON2, RCEN    ; abilita la ricezione
       rcall  i2c_wait         ; attesa completamento comando

; genera uno Stop per chiudere la connessione
i2c_p  bsf    SSPCON2, PEN     ; abilita condizione di Stop

; attesa per l' esecuzione del comando
i2c_wait 
      btfsc   PIR1, SSPIF      ; comando eseguito ?
        bra   i2c_wait         ; No, attesa
       bcf    PIR1, SSPIF      ; Sì, cancella SSPIF
     
return

 

L' operazione di formazione dell' indirizzo dello Slave partendo da un valore assoluto può essere svolta dal calcolatore dell' Assembler.
Ad esempio, se l' indirizzo è address = 19h (b'001 1001'), si dovrà trasmettere b'0011 0011'. 
*2 crea uno shift a sinistra di una posizione e | 1 setta il bit 0.

; invia l' indirizzo da WREG
        movlw  (address * 2) | 1  ; carica indirizzo con R/W=1 

Dove si desideri o sia necessario utilizzare l' interrupt per gestire questi eventi, occorre impostare attiva la chiamata di MSSP col bit SSPIE in PIE1.
 
Questo fa si che ogni volta SSPIF sia posto a 1, si attivi la richiesta di interrupt.
Da osservare che, se non è abilitata la modalità Slave con interrupt allo Start e allo Stop, SSPIF viene settato alla fine di una sequenza che deve essere poi determinata testando i vari flag.
 
Questo non costituisce un grosso problema se abbiamo impostato la gestione della comunicazione con la macchina a stati prima consigliata, in quanto ad uno stato potranno far seguito solo due situazione: il comando andato a buon fine oppure no.
Una jump table con un contatore di stati può essere una soluzione rapida per far passare da uno stato al successivo.


Per una gestione completa e ben organizzata, sono fondamentali le Application Notes di Microchip:

per la gestione in interrupt con il supporto completo di MSSP.
Per l implementazione del Master in software:

E per il bit-banging in PIC privi del modulo MSSP:


 

 

Copyright © afg. Tutti i diritti riservati.
Aggiornato il 15/01/12.