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:
- il ricevitore attende la variazione del livello sul pin di ricezione,
dovuta all' arrivo del bit di start
- 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
- 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.
- 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
- si ripete i punti 4 e 5 per il totale di bit in arrivo.
- 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:
- 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).
- 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 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
|