T&t - PIC 

 

Una seriale RS232 per tutti i PIC


Se è semplice trasmettere emulando software il segnale di uscita da un UART, è altrettanto semplice ricevere.

La possibilità di ricevere dati dall' esterno, ad esempio da un PC supervisore, consente di dotare il microcontroller di rispondere ad un set di comandi che possono servire per attivare e disattivare funzioni, variare impostazioni, ecc.


Ricevere dalla linea RS232.

Il dato seriale proveniente dalla linea RS232 è caratterizzato, come quello trasmesso, da una cadenza definita dal baud rate, che stabilisce la durata del singolo bit.
La sequenza inizia con un segnale di start, ampio il tempo di un bit (bit di start). Questo bit serve ad avvisare l'apparato ricevente che è in arrivo un dato ed a sincronizzare la ricezione dei bit successivi.

Esistono vari modi di effettuare questo, il più semplice dei quali prevede questa sequenza:

  1. il ricevitore attende la variazione del livello sul pin di ricezione, dovuta all' arrivo del bit di start
  2. una volta rilevato lo start, il ricevitore attende un tempo pari alla metà della durata di un bit, in modo da posizionarsi a metà (circa) del bit di start
  3. a questo punto, una verifica ulteriore del livello della linea di ricezione conferma che si tratta del bit di start  non di un disturbo casuale.
  4. Se così è, il ricevitore attende un tempo pari alla durata di un bit e campiona nuovamente la linea. Il valore rilevato è quello del bit che è stato trasmesso
  5. si ripete i punti 4 e 5 per il totale di bit in arrivo.
  6. una ulteriore attesa serve a confermare che è presente il bit di stop e la trasmissione è conclusa

Vediamo in un diagramma la ricezione del byte 9Ah (10011010 binario):

L'algoritmo che, come per la trasmissione, basa il conteggio dei tempi esclusivamente sulla durata delle istruzioni contiene possibilità di errore. 
Essenzialmente, occorre che l'identificazione dell'inizio del bit di start sia quanto più possibile precisa, dato che i campionamenti successivi saranno effettuati a partire da questo momento. Se si inizia la serie dei campionamenti in ritardo sull'avvio della trasmissione, ci si può trovare non al centro del tempo di ogni bit, ma sui bordi, dove, a causa del mezzo di trasmissione, i fronti di commutazione possono essere imprecisi. Questo diventa tanto più critico quanto più si aumenta il baudrate e quindi si riduce l'ampiezza degli impulsi.

Dato che abbiamo a che fare con clock di sistema non elevati e non utilizziamo altre funzioni del chip al di là delle istruzioni, si è posto rimedio al problema verificando in polling, il più stretto possibile, lo stato del pin di ingresso.
Dal momento del rilevamento dello start, viene realizzato un loop che, con frequenza pari alla durata del bit, scandisce il livello delle linea. Questo garantisce che il punto di campionamento sia molto vicino al centro di ogni bit.
Il sistema fornisce ottimi risultati, ma rende l'operazione di ricezione assolutamente non interrompibile, nel senso che il processore è impegnabile solamente per questa operazione.

Abbiamo considerato anche che, sulla linea di comunicazione, sono possibili disturbi che potrebbero alterare la trasmissione. Una correzione adeguata del problema si effettua utilizzando un bit di parità, che, in questo caso in cui è cercata la semplicità, non è stato implementato. Però sono stati aggiunti due punti di controllo con la generazione di un errore corrispondente.
Il primo è in relazione al bit di start: se, dopo il tempo pari a 1/2 bit il livello di start è assente, si trattava di un disturbo; la routine si interrompe e ritorna con un codice di errore in W.
Il secondo punto di controllo è sul bit di stop: se, a metà del bit di stop questo non è presente, la routine rientra con un codice di errore in W.
E' lasciato all'utilizzatore implementare come meglio crede la gestione di queste situazioni di errore.
In ogni caso, con poche modifiche, è possibile aggiungere anche il bit di parità, che richiede ovviamente la relativa gestione.


Ricevere a 9600.

La versione Rx232_496 è calcolata per un clock di 4MHz. 

E' scritta come una subroutine e rende il dato ricevuto nella locazione di RAM savew

       call    Rx232_496       ; riceve un byte dalla seriale 9600,8,n,1
       xorlw   0               ; verifica stato di errore  
      
skpnz                   ; n - recupera dato
        goto   rxerror         ; s - gestisci l'errore  
      
movf    savew,w         ; recupera il dato in W

Abbiamo anche l'analisi di una eventuale situazione di errore che rimanda ad una gestione adeguata, a cura dell'utilizzatore e secondo l'esigenza dell'applicazione. Se la ricezione è completata senza errore, il dato ricevuto viene elaborato.

La routine potrà essere inclusa nel sorgente (forma assoluta) con un #include, curando eventuali limitazioni (ad esempio la prima metà della pagina per i Baseline). Ad esempio:

; include gestione ricezione seriale 9600,8,B,1 per clock a 4MHz
 #include
C:\PIC\LIBRARY\Baseline\RS232\Rx232_496.asm

oppure con una compilazione rilocabile (forma rilocabile).
Dato che non è possibile modificare i loop di ricezione, occorre che eventuali selezioni di banchi e pagine siano effettuati al di fuori della routine. Quindi è necessario utilizzare RAM o nello stesso banco dell'I/O o in area condivisa.

Sarà necessario aver definito il pin utilizzato per la ricezione. A tale scopo si potrà usare qualsiasi pin disponibile con funzione di ingresso digitale. Ad esempio:

#define rxpin  GPIO, GP3       ; ingresso seriale

Per una compilazione assoluta occorrerà anche definire la RAM necessaria. Ad esempio:

      CBLOCK  0x10
      
savew            ; temporaneo per W
       d1               ; temporaneo per delay
       bitcntr          ; counter bit da trasmettere 
      ENDC             

All'entrata della subroutine, il processore è in attesa, su un loop stretto, della variazione di stato del pin di ricezione, che può essere un qualsiasi I/O. Ad esempio, può essere un buon impiego del pin MCLR che può essere configurato come I/O digitale, ma solo in ingresso.
Non appena rilevato l'arrivo del bit di start, viene attivato un ritardo pari a 1/2 bit. Al termine, viene verificato lo stato della linea e se il bit di start è ancora presente, la ricezione prosegue. Se è cessato prima del tempo, si trattava di un disturbo: la routine termina con un codice di errore in W.

       btfsc  Rxpin       ; Rxpin=0? s - skip
        goto  $-1         ; n - loop attesa
       movlw  .12         ; (12*4)-1=47us 
       movwf  d1          ; 47us | 
       decfsz d1,w        ;      |
        goto  $-2         ;      |
       btfsc  Rxpin       ; verifica start bit
        retlw rxstarterr  ; start bit error
       goto   $+1         ; +2us
       nop                ; +1us

I codici degli errori sono definiti in precedenza e sono facilmente modificabili.

La procedura prosegue cadenzando campionamenti del pin di ingresso a distanza di 1 bit. Il classico shift a destra passa il bit di dato al carry e da qui alla locazioni temporanea di salvataggio.
Per l'ultimo bit, quello di stop, si verifica se a metà tempo esso è presente e carica W con un indicatore di errore in caso contrario. La routine termina con il contenuto di W=0 se non ci sono stati errori.

rxloop goto   $+1         ; +2us
       nop                ; +1us
       movlw  .23         ; (23*4)-1=91us
       movwf  d1          ; 91us |
       decfsz d1,w        ;      |
        goto  $-2         ;      |
       decfsz bitcntr,f   ; fine bit? s - skip
        goto  rxbit       ; n - prossimo
       goto   $+1         ; +2us 
       nop                ; +1us 
       btfss  Rxpin       ; stop bit? s - skip
        retlw rxstoperr   ; n - stop bit error
       retlw  0           ; dato ricevuto ok

rxbit  clrc               ; carry=0
       btfsc  Rxpin       ; rxpin=0? s - skip
        setc              ; n - carry=1
       rrf    savew,f     ; salva
       goto   rxloop      ; prossimo bit 

Per ottenere le giuste durate dei bit sono aggiunti loop di tempo e istruzioni goto $+1, che impegna 2us o nop, che impegna 1us.

La versione Rx232_896 è calcolata per un clock di 8MHz, dove il tempo di ciclo di una istruzione è 500ns. Confrontandola con la precedente si può notare che, sulla medesima struttura di base, sono state applicati gli aggiustamenti nei valori dei loop per ottenere tempi quanto più possibile precisi.

 


Ricevere a 19200.

Un baudrate di 19200 significa che la durata dell' impulso relativo ad un singolo bit è di soli 52us. Con un ciclo istruzione di 1us c'è ancora spazio per ottenere un risultato ragionevole, anche considerando le tolleranze del clock principale. Con un clock maggiore diminuisce la criticità.

La versione Rx232_419 è calcolata per un clock di 4MHz.
La versione Rx232_819 è calcolata per un clock di 8MHz. 


Ricezione diretta o invertita?

Ricordiamo che il collegamento tra microcontroller e linea richiede di considerare che il livello di tensione sulla seriale RS232 va da -V a +V, dove V tipicamente per un PC è di 12V.
Invece, il segnale del microcontroller è variabile tra 0 e la Vdd.


Occorre, quindi, una interfaccia. Abbiamo proposto qui alcune possibili soluzioni.

Normalmente possiamo trovarci con 4 possibili combinazioni:

Microcontroller Ricevitore
receiver RS232 invertente PC standard (driver RS232 invertente)
nessun receiver, solo resistenza PC standard (driver RS232 invertente)
receiver RS232 invertente microcontroller, driver RS232 invertente
nessun receiver microcontroller, nessun driver

Nel primo caso, il segnale viene invertito agli estremi della linea.
Nel secondo caso, il segnale è invertito solo ad una estremità della linea.
Nel terzo caso, la connessione tra due microcontroller è effettuata sulla linea RS232 interfacciandola con driver e receiver invertenti.
Abbiamo anche una quarta possibilità, ovvero collegare due microcontroller direttamente a livello TTL (cosa possibile senza problemi, ma solo su breve distanza dell'ordine delle decine di centimetri).

Se il segnale non è invertito in linea o è invertito ad entrambe le estremità, le routine proposte attribuiscono all'I/O i giusti livelli.
Se una delle estremità non inverte, come nel caso in cui si utilizzi una semplice resistenza, occorre che sia la routine ad invertire il livello del bit trasmesso: si rende necessario cambiare i btfss in btfsc e gli setc in clrc .

Da considerare che l'uso dei driver opportuni (MAX232, MAX3232, ecc) garantisce la massima sicurezza e la massima distanza di trasmissione, mentre l'impiego di interfacce semplificate limita sicuramente la distanza raggiungibile col collegamento e non fornisce protezione ESD.

 


Note:

  1. Le procedure che abbiamo visto sono previste principalmente per l'uso con Baseline, che hanno risorse molto limitate. In chip differenti è possibile implementare altre soluzioni, ma questo non impedisce di usare queste routines con qualsiasi altro PIC, con i dovuti aggiustamenti.
    In particolare:
    - nei Baseline le subroutines devono essere posizionate nelle prime 256 locazioni della pagina
    - con i chip dotati di più banchi e pagine, volendo mantenere inalterata la struttura degli algoritmi, sarà necessario posizionare la RAM usata nello stesso banco del port di I/O oppure utilizzare memoria condivisa ed effettuare ogni switch di pagina al di fuori delle routines. Questo è necessario in quanto l'azione sugli switch di banco e pagina è costituita da istruzioni che impegnano tempo che andrebbe ad alterare le temporizzazioni (che sono determinate solo dalla durata e quantità dei cicli di istruzione).
    Nulla vieta di usare questi algoritmi anche con chip dotati di UART, quando non lo si desidera attivare, e perfino con PIC18F, anche se qui occorre un lavoro di revisione, perchè ci sono alcune differenze essenziali rispetto ai PIC minori, come ad esempio l'ampiezza diversa delle istruzioni che richiede attenzione nell'uso del simbolo $ (raddoppio). 
     
  2. Queste routines, se usate con chip dotati di interrupt una volta iniziate, non devono essere interrotte in quanto la durata degli impulsi dipende strettamente dal ciclo delle istruzioni.
    Se c'è un interrupt attivo, questo dovrà essere disabilitato durante l'esecuzione della trasmissione o ricezione.
     
  3. Ovviamente, sono possibili solo comunicazioni half duplex, in quanto il processore può svolgere solamente l'operazione di ricezione o di trasmissione e non entrambe contemporaneamente.
     
  4. Questo genere di applicazioni è possibile solo se l'oscillatore interno è calibrato, aggiungendo le due istruzioni necessarie al  vettore del reset. 
    Se  l'oscillatore non è calibrato, la frequenza generata al default del POR può essere sensibilmente diversa da quella nominale e determinare quindi un tempo di ciclo istruzione del tutto inadeguato.
    Va poi aggiunto che differenze di temperatura e di tensione di alimentazione variano la frequenza dell'oscillatore interno (ma anche di uno esterno...) per cui baud rate superiori a 19200 diventano problematici con il metodo che stiamo descrivendo, mentre comunicazioni a 9600 sono risultate sicure per una ampia gamma di condizioni ambientali e di alimentazione.
     
  5. 9600 e 19200 sono stati scelti in quanto sono i massimi possibili con una certa sicurezza per i clock dei processori utilizzati e perchè, nella trasmissione di dati, una maggiore velocità è sempre ben accetta. Baudrate 4800 o minori sono facilmente ottenibili adattando i loop di tempo. In particolare, la criticità della ricezione e della trasmissione "software" diminuiscono al diminuire del baudrate.

 


Le quattro routine di ricezione

 


 

 

Copyright © afg. Tutti i diritti riservati.
Aggiornato il 14/10/15.