Tutorials - PIC - Corso A&C

 

Esercitazioni PIC Baseline


21A - Applicazioni delle uscite digitali: Trasmissione RS232.


Lo scopo dell' esercitazione è verificare la possibilità degli I/O digitali del microcontroller, usati per generare segnali in una trasmissione su linea RS232.

Abbiamo visto nella parte del corso dedicato ai Baseline essenzialmente applicazioni in cui sono messe in finzione uscite digitali, accendendo LED.
Ovviamente le possibilità non si esauriscono a questo, ma, attraverso la manipolazione di queste uscite digitali, possiamo sviluppare moltissime altre applicazioni, ad esempio una comunicazione su seriale RS232.


Trasmettere dati su RS-232

Trasmettere su  una line RS232 permette ad dispositivo governato da un microcontroller di comunicare all' esterno, ad esempio verso un Personal Computer, i risultati di una acquisizione dati o lo stato del processo controllato.
Si tratta di una applicazione che pare complessa e che, usualmente, viene demandata ad una periferica specializzata (UART, USART, EUSART). 

In effetti, si può realizzare una funzione simile con una semplice movimentazione di un pin configurato come output digitale, con le temporizzazioni opportune. Questa soluzione diventa necessaria per i PIC delle famiglia Baseline, che sono privi di modulo di comunicazione seriale, ma lo stesso principio è applicabile a qualsiasi altro microcontroller delle famiglie superiori.

Vogliamo, dunque, trasmettere dati su RS-232, a partire dal PIC e verso un personal computer.
Non implementiamo particolari protocolli, eseguendo una semplice trasmissione bit bang in una modalità standard 9600,n,8,1 , ovvero 9600 baud, senza parità, 8 bit più uno stop.

Non è compito di questo corso una descrizione dettagliata della comunicazione seriale nello standard RS-232C. Se avete necessità di informazioni sull' argomento, potete consultare queste pagine o le molteplici altre presenti in rete.  Qui, dobbiamo specificare quanto ci riguarda per poter far si che il microcontroller possa inviare dati sulla linea seriale RS-232.
In breve, si tratta di due punti essenziali: come avviene la trasmissione e cosa dobbiamo utilizzare come interfaccia.


Come trasmettere il dato.

Come si trasmette un dato sulla linea seriale RS232? 
Innanzitutto va detto che:

  • i dati sono trasmessi solitalmente come byte, ovvero blocchi di 8 bit, che è poi la larghezza del bus dati dei microcontroller di cui ci stiamo occupando. Se devo trasmettere più bit, li suddividerò in più bytes.
  • il byte viene trasmesso ponendo sulla linea un bit dopo l' altro, iniziando dal meno significativo. Ovvero, viene trasmesso per primo il bit 0 e per ultimo il bit 7
  • il bit resta in linea per un tempo prestabilito, dato che la trasmissione è del tipo asincrono e non prevede un clock separato dal segnale. Questo, se fa risparmiare un conduttore, richiede una temporizzazione precisa durante la trasmissione dei bit.

Questa temporizzazione è standardizzata e suddivisa in valori multipli di 75, denominati baudrate, e che esprimono la frequenza con cui i bit sono inviati in linea. Abbiamo così valori di baudrate che vanno da 75 a 19200Hz (75/150/300/1200/2400/4800/9600/19200). Inizialmente, questi limiti erano dovuti alla bassa velocità delle periferiche e delle linee di trasmissione ed alla ridotta capacità di elaborazione dei circuiti elettronici (lo standard ha oltre 40 anni di età e nasce in un periodo in cui molte periferiche erano totalmente elettromeccaniche, come le telescriventi). Più recentemente sono diventati comuni baudrate maggiori (standard RS-562 a 64kHz), mentre nei personal computer sono possibili velocità superiori a 115200Hz e più, data la più elevata capacità di elaborazione disponibile. 
Usualmente la trasmissione è gestita da una periferica dedicata, denominata UART, USART, EUSART, che scarica il processore (e il programma) dalla gestione della comunicazione, la quale, dove siano presenti segnali di controllo e flusso bidirezionale (full duplex) può essere molto complessa. 
Però, dato che la trasmissione di un bit richiede un tempo molto maggiore di quanto un attuale microcontroller impieghi per eseguire un ciclo di istruzione, ne deriva che, nonostante esistano queste periferiche specifiche, dove non siano richieste prestazioni elevate, la trasmissione e la ricezione sono possibili anche utilizzando i semplici I/O digitali maneggiati dal programma. Possiamo così far comunicare in seriale anche piccoli PIC come i Baseline, che non dispongono di modulo UART integrato

Poichè operiamo attraverso una emulazione software dell' attività di un UART, ovvero determinando interamente dal programma la gestione della comunicazione, dobbiamo limitare la frequenza di trasmissione alle possibilità di elaborazione del microcontroller, dove l' unico modo per valutare il tempo è dipendente dal suo clock.
Con un clock di 4MHz è possibile arrivare a 19200 baud e più; utilizzando un quarzo esterno si otterrà una ottima precisione e stabilità dei tempi, ma questo richiede componenti addizionali e determina una perdita di pin cosa poco gradita su microcontroller con un pinout limitato.
Se, invece, utilizziamo l'oscillatore interno, recuperiamo pin e non abbiamo bisogno di altri componenti. Per contro, riduciamo precisione e stabilità. All'atto pratico, però, si può verificare che l'1% di errore eventualmente introdotto dall' oscillatore interno non influisce su una trasmissione con frequenze fino a 9600 baud. Ovviamente sarà indispensabile la calibrazione dell' oscillatore.

9600 baud indica che abbiamo un periodo di:

periodo = 1/f = 1/9600 = 104.166us

Con un clock di 4MHz, abbiamo un ciclo istruzione di 1us e dovremo arrotondare il periodo calcolato a 104us.
Per ottenere il preciso valore dobbiamo usare un clock multiplo del valore voluto, ad esempio 1.8432MHz o 2.4576MHz e simili, il che necessita di un quarzo esterno, con i relativi componenti.
Però, se valutiamo l' errore di tempo che si ottiene utilizzano 104us al posto di 104,166us, considerando anche il possibile errore dell' oscillatore interno (1%) esso risulta inferiore all'2% e questo valore è tollerato dalla maggior parte dei sistemi di ricetrasmissione. Queste considerazioni sono valide per frequenze di comunicazione non eccessivamente elevate (dove la breve durata dell' impulso relativo ad ogni bit richiede una altrettanto elevata precisione del tempo di emissione).

Un altro punto da considerare riguarda il formato del dato da trasmettere:

  • gli 8 bit del dato vero e proprio richiedono un bit addizionale di start ed uno di stop. Il loro scopo è quello di informare l' apparato ricevente dell' inizio e della fine della trasmissione, permettendo il sincronismo tra trasmettitore e ricevitore.

Solitamente si utilizza il tempo di un impulso per lo start ed altrettanto per lo stop, ottenendo così la trasmissione di 10 bit per ogni 8 di dati effettivi (start + 8bit dati + stop). Ad essi può essere aggiunto un bit di parità che ha lo scopo di aumentare la sicurezza della trasmissione, bit che nel nostro esempio non aggiungiamo.

In sostanza, dovendo trasmettere il byte di valore  '10111110'  dovremo variare il livello del pin usato per la trasmissione come nel diagramma a lato.
Ogni bit viene sostenuto sulla linea per il tempo stabilito dal baudrate. La trasmissione del dato inizia con il bit di start e termina con quello di stop.

Ne risulta che la trasmissione, al minimo, è composta da 10 impulsi pari al valore del bit trasmesso, della durata ognuno di 104us, ovvero complessivamente un byte viene inviato in 10x104=1.040ms. Questo è un risultato facilmente ottenibile, ad esempio nel modo simile a quanto abbiamo visto nelle esercitazioni precedenti. Si tratta solo di comandare opportunamente un pin di I/O configurato come uscita digitale.

Osserviamo che viene inviato per primo il bit 0, il meno significativo, e per ultimo il bit 7 del dato (il byte è trasmesso "al contrario", cioè va letto da destra verso sinistra).

Una ultima considerazione: quanto detto, se è sufficiente dal punto di vista del programma, non lo è da quello dell' hardware, nel senso che il microcontroller e la linea RS-232 hanno livelli di tensione molto differenti: il microcontroller è alimentato da una unica tensione positiva rispetto alla massa, tipicamente tra 3 e 5V. La linea RS232 impegna una tensione bipolare che varia tra +V e -V.

La linea si trova normalmente nello stato di riposo (MARK o idle - nessun dato in transito), alla massima tensione negativa; la transizione di livello passa da segnale negativo a positivo (SPACE - massima tensione positiva) e indica l’inizio della trasmissione, ovvero il bit di start). 
La tensione positiva può spaziare tra 3 e 25V ed altrettanto quella negativa.  In pratica, nei PC si utilizzano il +12V e il -12V forniti dall' alimentatore AT/ATX.

Per comandare la linea RS-232, a tensione bipolare e con un livello maggiore, occorre una interfaccia tra microcontroller e linea seriale. Questa è facilmente realizzabile, dato che sono stati costruiti numerosi circuiti integrati che effettuano questa conversione di livello, cosa peraltro possibile anche con semplici transistor.
Da notare che, per la logica dello standard, si ha l' equivalenza riportata nella tabella precedente, che comporta una "inversione" e i circuiti di interfaccia sono effettivamente invertenti; si ha, quindi, questa equivalenza:

Bit Microcontroller RS-232
0 Vss (0V) +12V
1 Vdd (5V) -12V

Ne deriva che, sul lato microcontroller, il pin di uscita dovrà essere comandato in questo modo:

 ovvero, a riposo, l'uscita si trova a livello alto. 

Vediamo, dunque, che la trasmissione consiste in una semplice manipolazione dell'I/O tra i livelli 1 e 0 secondo temporizzazioni precise, cosa che possiamo fare con le istruzioni bcf e bsf e valutando i tempi attraverso la durata delle istruzioni stesse. 

Ma per prima cosa come aggiungere al nostro circuito l' interfaccia richiesta.


Hardware RS-232

Abbiamo detto che la trasmissione del nostro microcontroller sarà diretta al personal computer. Però va fatta una osservazione: dopo l' avvento dell' USB (che è pure un protocollo seriale), la connessione RS-232, che fino a poco tempo fa era presente in tutti i personal, attualmente è diventata piuttosto rara, nonostante sia ancora la principale interfaccia usata dai sistemi industriali, principalmente per la sua semplicità di implementazione e le sue caratteristiche che ben si prestano alla trasmissione di dati tra apparati digitali nell' ambito della strumentazione, del controllo di processo, delle periferiche dedicate, ecc.

E' evidente che volete ricevere una comunicazione RS-232 sul vostro PC, è necessario che questo disponga della relativa interfaccia.

Anche se su una buona parte dei personal desktop una linea seriale è presente, questo non è vero per i portatili, dove parallela e seriale sono sparite, sostituite da USB.

Se sul vostro personal computer non è presente una connessione RS-232, essa può essere realizzata molto facilmente con l' aggiunta di una scheda sul bus PCI o PCIe per i desktop oppure, più semplicemente, con un dongle che trasforma un port USB in un port RS-232, soluzione adatta per i notebook. Questi oggetti sono facilmente reperibili ed hanno un costo limitato.

Un secondo problema riguarda la necessità di applicare al microcontroller una interfaccia che adegui l' uscita digitale al livello di tensione della linea seriale. 
La nostra LPCuB non ha a bordo una simile interfaccia, in quanto abbiamo detto che la RS-232 è solamente una delle tante possibili modalità di comunicazione seriale.
 
Però esiste una predisposizione per il collegamento di moduli di interfaccia esterni.

In questo modo sarà possibile sperimentare non solo RS-232, ma anche RS-422, RS485, Current Loop, USB, SATA, Ethernet, LIN, CAN, I2C, comunicazioni sincrone, bus per domotica, ecc., tutte gestibili dal microcontroller, attraverso interfacce specifiche, tra di loro assai diverse.

Per dare il più ampio spettro di possibilità, non è stata integrata nella scheda alcuna interfaccia specifica, rimandando all' uso di elementi esterni, peraltro spesso assai semplici e poco costosi, quasi tutti realizzabili in casa da un hobbista attrezzato. 

Anche perchè, pur restando semplicemente nel campo della RS-232, l' interfaccia con la linea può essere realizzata in molti modi e non solo con chip specifici, come MAX232 o MAX3232 di Maxim, Texas, ecc..

Una interfaccia con la linea RS-232 di facile realizzazione è descritta qui.

Se si preferisce il già pronto, esistono moltissimi prodotti commerciali, ad esempio i moduli di Mikroelektronika, per i quali occorre realizzare solamente il cavetto di connessione.

In ogni caso il WEB offre una miriade di adattatori analoghi e c'è solo l'imbarazzo della scelta.

 


Schema elettrico.

C'è poco da dire sullo schema elettrico. Un solo pin è utilizzato per la connessione all'interfaccia RS232.

Viene usato il pin GP2, che è collegato ad una interfaccia per la linea RS232 attraverso il JSC e da questa al PC con un cavetto DB9 M/F. Utilizzando un driver MAX232/MAX3232 la lunghezza della connessione non dovrà superare i 25m.
Il chip usa il suo clock interno a 4MHz.

Sulla LPCuB l'unico collegamento è quello tra il pin di trasmissione e il connettore di uscita, dove si trova collegata l'interfaccia seriale.

Come al solito, osservare la posizione del chip nello zoccolo e quella dei due jumper "blu" di selezione.


Il programma

Come primo esempio, trasmettiamo un breve messaggio, cadenzato una volta ogni secondo.
Per rilevare la trasmissione dovremo utilizzare sul PC un qualunque emulatore di terminale, dal Hyperterminal di Windows ad uno dei tanti free o share disponibili sul WEB (Realterm, Terminal.exe, ecc.).

Dal lato del microcontroller la soluzione più semplice consiste nel creare una subroutine che effettui le operazioni di movimentazione dei bit necessari all'invio di un byte di dati. In questo modo abbiamo un blocco software che potremo usare in ogni altra circostanza; La routine consente essenzialmente di aggiungere una porta di uscita seriale a qualsiasi PIC. 

;********************************************************************
;--------------------------------------------------------------------
; XMIT-232.asm
;
; Titolo   :  Trasmissione di 8 bit + 1 bit di start e 1 bit di stop
;             no parità a 9600 (9600,8,N,1).
;             Il dato da trasmettere è passato attraverso W.
;             Uscita su un qualsiasi I/O digitale (Txpin) per comando
;             di driver invertente.
;             Se usato con collegamento diretto bcf txpin va cambiato
;             in bsf txpin e viceversa.
;             9600 ->104.166us approssimato a 104us
; PIC      :  Baseline
; Supporto : MPASM
; Versione : 1.0
; Risorse  : RAM necessaria 3 bytes: bitcntr, d1, savew
; Data     : 01-05-2013
; Ref. hdw :
; Autore   : afg
;
;********************************************************************

;####################################################################

Xmit232 movf   savew    ; salva dato da trasmettere
        movlw  8        ; 8 bit
        movwf  bitcntr
; start bit
        bcf    txpin    ; start bit ---|
        movlw  0x22     ; 97 cicli     |
        movwf  d1       ;              |
xmt0    decfsz d1, f    ;              |
         goto  xmt0     ;            97us
        goto   $+1      ;             2us
; data byte                            |
xmtlp   rrf    savew,f  ; lsb prima   1us--1us
        skpnc           ;             1us  2us
         goto  xmt1     ;             2us   |
        skpc            ;              |   1us
         bcf   txpin    ;              |   1us¬104us    (5us)
        goto   xmt2     ;              |                 2us
xmt1    bsf    txpin    ;             1us¬104us----(5us)  |
        goto   $+1      ;                           2us   |
xmt2    movlw  0x1F     ; 94 cicli                   |    |
        movwf  d1       ;                            |    |
xmt3    decfsz d1, f    ;                            |    |
         goto  xmt3     ;                          94us 94us
        decfsz bitcntr  ;                           1us  2us
         goto  xmtlp    ;                     104us¬2us   |
; stop bit                                                |
        bsf    txpin    ; stop bit                 104us¬1us
        movlw  0x21     ; 100 cicli ----|
        movwf  d1       ;               |
xmt4    decfsz d1, f    ;               |
         goto  xmt4     ;            100us
        goto   $+1      ;              2us
        retlw  0        ;              2us¬104us

;********************************************************************
;####################################################################

Il dato da trasmettere viene passato attraverso W e viene salvato in una locazione temporanea (savew). Viene trasmesso il bit di start da 104us, quindi gli 8 bit del dato. E' usata l' istruzione di rotazione verso destra, che manda al Carry il bit meno significativo. Il suo valore viene riflesso sul pin di uscita. 
Un contatore caricato con 8 (bitcntr) viene decrementato ad ogni emissione, che dura 104us, per determinare gli 8 bit da trasmettere. 

Da osservare che le temporizzazioni, basate sulla durata del ciclo istruzione, sono calcolate per un clock di 4MHz (Tcyc = 1us).  Con clock di frequenza diversa occorrerà ricalcolare i tempi; così, per un clock di 8MHz (tcyc = 500ns), la trasmissione avverrà a velocità doppia (19200 baud).

Accanto alle linee di istruzione è aggiunto come commento il conteggio dei cicli. Possiamo vederlo nel dettaglio.
Per il bit di start la situazione è semplice:

; start bit
        bcf    txpin    ; start bit ---|
        movlw  0x22     ; 97 cicli     |
        movwf  d1       ;              |
xmt0    decfsz d1, f    ;              |
         goto  xmt0     ;            97us
        goto   $+1      ;             2us
; data byte                            |
xmtlp   rrf    savew,f  ; lsb prima   1us--1us
        skpnc           ;             1us  2us
         goto  xmt1     ;             2us   |
        skpc            ;              |   1us
         bcf   txpin    ;              |   1us¬104us    (5us)
        goto   xmt2     ;              |                 2us
xmt1    bsf    txpin    ;             1us¬104us----(5us)  |
        goto   $+1      ;                           2us   |

Dal momento in cui viene clearato il txpin (1us) al test del bit da trasmettere viene inserito un ritardo di 97us, per un totale di 99us e si innesta nel test del bit dati da trasmettere. La verifica del valore del bit viene effettuata sul Carry, in cui la rotazione rrf savew,f sposta il bit meno significativo; questa sequenza impiega 5us, per il totale voluto di 104us tra il momento di inizio del bit di start e quello del primo bit dati (i branch skpnc/skpc  impiegano 1us se non effettuano il salto e 2us se lo effettuano). 2us sono addizionati dal goto $+1 per pareggiare i due rami della selezione. 

Una volta che il primo bit di dati è posto in uscita, viene inserita una attesa di 94us. Sono quindi necessari 3us per verificare la condizione del contatore dei bit da trasmettere, per un totale di 99us.
Ora il l riparte dal test del bit successivo, che andrà "in linea" dopo 5us. Il bit precedente è rimasto "in linea" per i 104us richiesti (99+5). 

; data byte                            |
xmtlp   rrf    savew,f  ; lsb prima   1us--1us
        skpnc           ;             1us  2us
         goto  xmt1     ;             2us   |
        skpc            ;              |   1us
         bcf   txpin    ;              |   1us¬104us    (5us)
        goto   xmt2     ;              |                 2us
xmt1    bsf    txpin    ;             1us¬104us----(5us)  |
        goto   $+1      ;                           2us   |
xmt2    movlw  0x1F     ; 94 cicli                   |    |
        movwf  d1       ;                            |    |
xmt3    decfsz d1, f    ;                            |    |
         goto  xmt3     ;                          94us 94us
        decfsz bitcntr  ;                           1us  2us
         goto  xmtlp    ;                     104us¬2us   |

Esauriti i bit da trasmettere, occorre aggiungere alcuni microsecondi per compensare il loop non più eseguito, aggiungendo 5us:

        decfsz bitcntr  ;                           1us  2us
         goto  xmtlp    ;                     104us¬2us   |
; stop bit                                                |
        bsf    txpin    ; stop bit                 104us¬1us
        movlw  0x21     ; 100 cicli ----|
        movwf  d1       ;               |
xmt4    decfsz d1, f    ;               |
         goto  xmt4     ;            100us
        goto   $+1      ;              2us
        retlw  0        ;              2us¬104us

A questi si somma il ciclo iniziale dello stop bit, per il totale voluto di 104us.
Alla fine, il bit di stop dura anch'esso, per pignoleria, 104us; all' atto pratico, si potrebbe risparmiare l' ultimo goto $+1 dato che, a questo punto, la routine è rilasciata e le istruzioni del mainloop  aggiungeranno anche più dei 2us mancanti. In pratica, lo stop bit è una situazione di linea "vuota" (idle o MARK) che è prevista per permettere alla periferica ricevente di trattare il dato e predisporsi per quello successivo ed ha una lunghezza minima, ma non un massimo, dato che dopo di esso la trasmissione potrebbe anche essere sospesa per qualsiasi durata di tempo.

Da notare che clrc  e sknc  sono pseudo opcode di MPASM; l' editor a colori evidenzia la differenza.

Una volta scritta questa subroutine, il main risulta del tutto banale:

; invia il messaggio sulla linea RS232

txloop   movlw 0         ; azzera puntatore
         movwf index
txlp1    call  TxTable   ; prendi carattere dalla tabella
         movwf savew     ; salva provvisoriamente
         xorlw 0         ; ultimo carattere ?
         skpz            ; si, salta
          goto nextchr   ; no, riprendi
; fine caratteri - trasmette CR & LF

         movlw 0x0D      ; CR
         call  Xmit232
         movlw 0x0A      ; LF
         call  Xmit232
         call  Delay1s   ; attesa 1s
         goto  txloop    ; loop continuo

nextchr  movf  savew,w   ; recupera carattere
         call  Xmit232   ; invia il carattere in linea
         incf  index,f   ; aggiorna puntatore
         movf  index,w
         goto  txlp1     ; loop del messaggio

Vengono trasmessi in successione i caratteri del messaggio, dopo di che viene emessa la coppia carriage return (CR) e line feed (LF), seguita da una attesa di 1 secondo. Poi il ciclo ricomincia. 

Il messaggio è costituito da una semplice lookup table costituita da una tabella retlw che è chiusa da uno 0.

; Tabella messaggio
; Il messaggio termina con 0

TxTable   addwf PCL,f
  dt "Hello !",0

La tabella e le subroutines per la trasmissione e per il ritardo di 1 secondo sono incluse all' inizio del sorgente, date le pesanti limitazioni che impongono i Baseline.

; include subroutines

; trasmissione 9600n81 @ 4MHz

 #include C:\PIC\LIBRARY\Baseline\Xmit232.asm
; ritardo di 1s @ 4MHz
 #include C:\PIC\LIBRARY\Baseline\Delays\Delay1s.asm

Ovviamente, se nel vostro hard disk le subroutines sono conservate diversamente, occorrerà adeguare il relativo path di inclusione.

Ecco come si presenta all' oscilloscopio la prima parte della trasmissione.

Si notano bene le diverse durate degli impulsi a seconda che si stia trasmettendo un 1 o uno 0.

  

Ed ecco il messaggio ricevuto nella finestra di RealTerm, che consente di visualizzare anche i caratteri ASCII di controllo CR e LF.

Il sorgente è fornito all'interno del completo progetto MPLAB. Questo è stato verificato su un PIC10F206. Se usate un diverso componente, modificate la selezione del chip dal menù Configuration.

Il sorgente si compila per 10F200/202/204/206, ma è facilmente portabile su qualsiasi altro PIC di qualsiasi famiglia, semplicemente cambiando la definizione del processore, il relativo config e l'adattamento dell' I/O digitale utilizzato.

 


 

 

Copyright © afg. Tutti i diritti riservati.
Aggiornato il 07/09/15.