T&t - PIC 

 

Una seriale RS232 per tutti i PIC


E' spesso utile poter trasmettere i dati rilevati dal microcontroller verso un personal computer, sul quale potranno essere elaborati, visualizzati e archiviati in modo molto più efficace di quanto offrano le limitate possibilità del microcontroller stesso.

Se il chip ha un modulo UART (USART, EUSART) il problema si risolve con facilità. Però, nei molti modelli che non ne dispongono, come capita per i Baseline e alcuni Midrange, è comunque possibile aggiungere altrettanto facilmente una uscita seriale per collegarsi su una linea RS232.

Questo progetto è stato sviluppato per collegare ad un personal computer dei rilevatori di dati analogici realizzati con 10F2xx, chip a 6 pin con solo 3 I/O utilizzabili come uscite e privi di UART.

Si tratta di un algoritmo in grado di trasmettere un dato su una seriale asincrona usando un pin di I/O digitale e rispettando alcune limitazioni imposte dalle scarse risorse disponibili:

  • deve essere abbastanza compatto (date le limitazioni di memoria)
  • non impegnare livelli di stack (che nei Baseline sono solo 2)
  • non impiegare interrupt (i Baseline ne sono privi)
  • operare con il clock interno, che tipicamente è 4 o 8MHz.

Siccome non si utilizzano altre risorse se non le istruzioni stesse, il sorgente è trasferibile così come sta su tutti i Baseline e i Midrange e gli Enhanced Midrange. Con qualche accortezza è adattabile anche ai PIC18F.


Emulare software una trasmissione RS232.

Sappiamo che un ciclo istruzione, nei PIC, equivale a XTAL_OSC/4 , ovvero alla frequenza dell'oscillatore principale diviso quattro: con il comune clock a 4MHz, disponibile in tutti chip dotati di oscillatore interno e facilmente ottenibile con un comune cristallo in quelli che ne sono privi, il tempo di ciclo è 1us esatto.
Per un clock a 8MHz, il ciclo istruzione diventa 500ns.

Una comunicazione seriale asincrona con baudrate di 9600 impegna un tempo di 104 us circa per ciascun bit, che diventano la metà per un baudrate di 19200.

Sapendo che le istruzioni si eseguono in uno o due cicli, è possibile utilizzare questi tempi per realizzare loop ben definiti in grado di commutare la linea seriale con le giuste temporizzazioni.

Per sua natura, la trasmissione asincrona, che trasporta il clock col dato, richiede una certa precisione nei tempi, altrimenti l'apparato ricevitore non sarà in grado di elaborare correttamente il dato.

Si può pensare che occorra un oscillatore a cristallo, ma questo non è indispensabile.
Infatti, se abbiamo l'accortezza di effettuare la calibrazione dell'oscillatore interno, il clock generato si discosta al massimo dell' 1% dal valore nominale e questo è un errore che è facilmente tollerato dai dispositivi di ricezione integrati nei personal computer, che possono arrivare a sopportare spesso anche il 3% di scostamento della frequenza.

In ogni caso, nel realizzare gli elementi di tempo per la trasmissione seriale asincrona occorre considerare che:

  • quanto minore è lo scostamento del clock dal valore nominale, tanto maggiore sarà la sicurezza della trasmissione su cavi lunghi, dove le capacità parassite deformano i fronti di commutazione dei segnali.
     
  • tanto più alta sarà la frequenza di trasmissione, tanto minore sarà la durata dei singoli impulsi e tanto maggiore diventerà critico il problema della esattezza del baud rate.

Comunque, con un oscillatore calibrato e un buon algoritmo, trasmissioni con baudrate di 9600 e anche 19200 sono possibili senza problemi.
Baud rate minori non presentano alcun problema, mentre maggiori sono anche possibili, ma diventano molto critici con clock del processore così bassi.


Aggiungiamo una trasmissione RS232 a tutti PIC.

La procedura impiega un qualsiasi pin configurabile come uscita per la connessione.
Il mezzo intermedio potrà essere un collegamento diretto al personal computer, attraverso un sola resistenza di protezione, oppure un driver invertente genere MAX232/3232 o semplici transistor, ma si potrà utilizzare anche con una connessione RS485 o un modem.

Il loop implementa una trasmissione di un byte nella forma 9600,8,n,1, ovvero 8 bit a  9600 baud con uno stop bit e senza parità e l'analoga 19200,8,n,1 a velocità doppia.

Va notato che, con baud rate 9600 la durata di un impulso è 104.166us e la metà a 19200, cioè 52.86us.
Si tratta di valori non direttamente ottenibili con istruzioni della durata di 1us; occorrerebbe utilizzare frequenze maggiori oppure quarzi da  4915200MHz o simili, valori multipli di potenze di 2; questo, però, significa aggiungere componenti e perdere pin di I/O, cosa non auspicabile in piccoli microcontroller.
Se usiamo l'oscillatore interno a 4 o 8MHz, potremo ottenere tempi di 104us e 52us, con un errore inferiore allo 0.2%, che, nella pratica, è del tutto trascurabile.

Scritto sotto forma di subroutine, l'applicazione consiste in meno di 30 istruzioni del set Baseline (disponibili quindi su qualsiasi PIC a 8 bit) e impiega 3 locazioni di RAM che vengono modificate durante il funzionamento. 
Non sono usati timer o interrupt, nè richiamate altre subroutines, occupando, quindi, un solo livello di stack, ideale per l'uso con i Baseline che dispongono di risorse limitate.


Il firmware per 9600,8,n,1 .

La routine, denominata Tx232_496, è la versione calcolata per un clock di 4MHz. 

E' molto compatta e potrebbe presentare qualche problema di comprensione per i principianti.
Vediamo di tentare una spiegazione.

Il byte da trasmettere viene inviato attraverso il WREG. Quindi, la chiamata tipica sarà:

       movlw   data            ; carica in W il valore da trasmettere
       call    Tx232_496       ; invia in seriale a 9600,8,n,1 

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 trasmissione seriale 9600,8,B,1 per clock a 4MHz
 #include
C:\PIC\LIBRARY\Baseline\RS232\Tx232_496.asm

oppure con una compilazione rilocabile (forma rilocabile).
Dato che non è possibile modificare i loop di trasmissione, 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.

Inoltre sarà necessario definire il pin utilizzato per la trasmissione. A tale scopo si potrà utilizzare qualsiasi pin disponibile con funzione di uscita digitale. Ad esempio:

#define txpin  GPIO, GP2       ; uscita 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 pin dedicato alla trasmissione viene portato a livello basso per l'emissione del bit di start, quindi il dato presente in W viene salvato in una locazione temporanea e viene inizializzato un contatore con il valore di nove, ovvero 8 bit più quello di stop :

Tx232_496:                ; start data transmission 
       bcf   txpin        ; send start bit
       movwf savew        ; save Tx data
       movlw 9            ; 8 data + 1 stop bit
       movwf bitcntr      ;

Va notato che, a questo punto, il bit di trasmissione è già a 0 per 3 us.

La procedura prosegue con il classico shift a destra del dato, per passare al carry il bit da trasmettere e verificare il suo valore. Dato che lo shift manda al carry il bit 0 e spinge il carry nel bit 7, si porta a 1 il valore per avere quello pronto quello dell'ultimo bit, lo stop, che è a 1.

sendbit setc            ; carry=1 for stop bit  
        movlw   .23     ; (23x4)-1=91us          
        movwf   d1      ; loop 91us  |         
        decfsz  d1,w    ;            |           
         goto   $-2     ;            |          
        goto    $+1     ; +2us 
        rrf     savew,f ; data bit to C  

Il bit da trasmettere è stato passato al carry e, a seconda del suo valore, si adegua il Txpin:

        skpc              ; a '1' bit? y - skip 
         goto  nn0        ; bit=0, jump
; transmit bit=1 (maybe stop bit) 
        goto   $+1       
        bsf    txpin      ; stop
        decfsz bitcntr,f  ; if bit end, skip
         goto  sendbit    ; else send next bit
        movlw  .25        ; (25x4)-1=99
        movwf  d1         ; loop 99us | 
        decfsz d1,w       ;           |
         goto  $-2        ;           |
        retlw  0x0A       ; return w/ <LF> char
; transmit bit=0 (cannot be stop bit) 

nn0     nop               ;
        bcf    txpin      ; 
        decf   bitcntr,f  ; bitcounter-1 
        goto   sendbit    ; and loop 

Se il bit da trasmettere è 0 non può essere il bit di stop, quindi viene comandato il txpin e si ritorna al loop di trasmissione.
Se il bit da trasmettere è 1 e il contatore di bit è azzerato, si tratta del bit di stop, che viene mantenuto per 104us, dopo di che la routine rientra con W che contiene 0Ah, ovvero il carattere ASCII <LF>; questo salva una istruzione nel caso in cui il dato trasmesso sia l'ultimo di una stringa a cui deve seguire un CR/LF.
Se il bit da trasmettere è 1, ma il contatore non è azzerato, si tratta di un bit dato e si opera di conseguenza.

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 Tx232_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.


Trasmettere a 19200.

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

La versione Tx232_419 ricalca la stessa struttura, aggiustando i tempi per un clock di 4MHz
La versione Tx232_819 è calcolata per un clock di 8MHz. 


Trasmissione 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
driver RS232 invertente PC standard (riceiver RS232 invertente)
nessun driver, solo resistenza PC standard (ricevitore RS232 invertente)
driver RS232 invertente microcontroller, driver RS232 invertente
nessun driver microcontroller, nessun receiver

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 microntroller direttamente a livello TTL (cosa possibile senza problemi, ma solo su breve distanza dell'ordine di 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 bsf in bcf e gli skpc in sknc .

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.

 


Ricezione.

Ovviamente è possibile anche la ricezione della trasmissione asincrona in modo analogo a quanto visto ora. 


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 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 utilizzare, 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 $
     
  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, 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 trasmissione


 

 

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