Comunicazione seriale con controllori PIC

  • by

Sebbene parlare oggi di trasmissione seriale possa sembrare anacronistico, vediamo invece l’utilità che questo metodo di comunicazioni continua ad avere. Stiamo parlando di trasferimento dati all’interno di un circuito tra due controllori differenti, o di comunicazione tra due circuiti.

Escludiamo il PC, per cui oggi preferiamo metodi più “nobili” come l’USB o Ethernet, considerando anche che difficilmente si riesce ancora a reperire un computer dotato di porta RS232.

Il trasferimento seriale

La trasmissione seriale avviene inviando in sequenza su un solo filo i singoli bit che compongono i dati da inviare. Solitamente quando due controllori devono comunicare si utilizzano due linee, una in ingresso (RX) e l’altra uscita (TX).

Se vogliamo avvicinarci allo standard RS232, vediamo quali sono le regole che lo governano:

  • A riposo la linea mantiene il valore alto (1)
  • Prima dell’inizio della trasmissione di ciascun byte viene portato a 0 il valore della linea per la durata prevista per un bit (bit di start)
  • Partendo dal meno significativo vengono trasmessi gli 8 bit.
  • In coda viene trasmesso un valore alto per la durata di un bit (bit di stop)

Trasmissione seriale

La durata di ciascun bit è determinata dalla velocità di trasferimento. Per esempio: 9600 baud significa che ogni simbolo ha una durata di 1/9600 secondi = 104 us.

Adesso quasi tutti i controllori PIC contengono il modulo UART (universal asynchronous receiver/transmitter), in questo caso si realizza la comunicazione sfruttando il modulo, utilizzando i registri che lo controllano.

Può comunque capitare di dover implementare la comunicazione lavorando direttamente con piedini I/O generici. Per esempio se utilizziamo i controllori più piccoli, o se dobbiamo avere più di un’interfaccia su linee distinte.

Vediamo quindi ora l’implementazione della trasmissione.

La criticità principale deriva dalla precisione che dobbiamo avere nella gestione dei tempi. Per questo motivo è preferibile lavorare in assembler.

Io utilizzo MPLAB di Microchip e il compilatore C di HITEC (picc). Le parti di codice in assembler le integro nei programmi C, sfruttando il linker di questo compilatore: basta aggiungere il sorgente .as nel progetto, e fa tutto da solo.

La versione freeware di questo compilatore non è affatto ottimizzata, quindi siamo realmente costretti a lavorare in assembler per i controllori midrange, mentre con i baseline ho avuto buoni risultati anche con il c (per esempio il 16F506 e il 12F519).

Partiamo dalla routine di trasmissione scritta in C:

void Putch(unsigned char c) {
unsigned char bitno;

TxData = 0; /* start bit */
bitno = 12; /* Rallenta la trasmissione, ma migliora la qualità*/
do {
delay_2Us (50);
if(c & 1)
TxData = 1;
else
TxData = 0;
c = (c >> 1) | 0x80;
} while(--bitno);

}

Per poi vedere la sua codifica in assembler:

PSECT text,class=CODE,local,delta=2
GLOBAL _Putch
GLOBAL ??_Putch
FNSIZE _Putch, 3, 1
#include "aspic.h"

#define Par ??_Putch
#define Tmp ??_Putch+2
#define Bitn ??_Putch+1
_Putch:
movwf Par
movlw 0x0c
movwf Bitn
bcf TX_port, TX_pin
loop:

; delay 1 bit
movlw bit_delay
movwf Tmp
delay:
nop
decfsz Tmp, f
goto delay
; copia il bit in uscita (ultimo bit dx)
btfss Par, 0 ; skip if set
goto set0
set1:
bsf TX_port, TX_pin
goto cont
set0:
bcf TX_port, TX_pin
cont:
; ruota a dx il byte, mettendo a 1 il più a sx
bsf STATUS, 0
rrf Par, f
decfsz Bitn, f
goto loop

bcf STATUS, 5
bcf STATUS, 6
return

Leggermente più complicata la ricezione. Considerando che la linea è alta a riposo, si reagisce appena si abbassa: presente il valore 0, aspetto metà del tempo di durata di un bit, in modo da posizionarmi nel centro e campiono il valore per otto volte.

Il codice in C


char Getch(void) {
unsigned char c, bitno;
for(;;) {
while(RxData) {
continue; /* attesa bit di start */
}
delay_2Us (25);
if(RxData)
continue; /* filtro i disturbi*/
bitno = 8;
c = 0;
do {
delay_2Us (50);
c = (c >> 1) | (RxData << 7); } while(--bitno); return c; } }

E qui l’assembler


PSECT text,class=CODE,local,delta=2
GLOBAL _Getch
GLOBAL ??_Getch, ?_Getch
FNSIZE _Getch, 3, 1
define Retv ??_Getch
#define Temp ??_Getch+2
#define Bitno ??_Getch+1

_Getch:
clrf Retv
; movlw 0x32
; movwf Temp
Start:
; decf Temp, f
; btfsc STATUS, 2
; goto Uscita
btfsc RX_port, RX_pin
goto Start
; delay mezzo bit
movlw half_delay
movwf Temp
delay1:
nop
decfsz Temp, f
goto delay1
; disturbo sulla linea: accapo!
btfsc RX_port, RX_pin
goto _Getch
movlw 0x08
movwf Bitno

bitRx: ; delay 1 bit intero
movlw bit_delay
movwf Temp
delay2:
nop
decfsz Temp, f
goto delay2

bcf STATUS, 0
btfsc RX_port, RX_pin
bsf STATUS, 0
rrf Retv, f
;finito?
decfsz Bitno, f
goto bitRx
Uscita:
movf Retv, w

bcf STATUS, 5
bcf STATUS, 6
return

Con queste due routines facciamo comunicare i controllori riducendo il numero di connessioni utilizzate. Per comunicare con un PC è necessario un adattatore di livelli. Il PIC infatti lavora con valori TTL (0 = 0V, 1 = +5V), mentre l’interfaccia RS232 utilizza valori EIA (0 = -12V, 1 = +12V). Solitamente si utilizza l’integrato MAX232.

Lavorando con i nuovi controllori PIC con oscillatore interno (senza quarzo) a 8Mhz, i risultati migliori li ho ottenuti a 9600 baud (i valori utilizzati nel codice che ho riportato).

Leave a Reply