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:
- 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
$.
- 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.
- 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.
- 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.
- 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
|