Corso programmazione PICMicro in C – Lezione 10 – L’USART e la comunicazione seriale con il protocollo RS232. Realizziamo un semplice sistema di automazione
In questa puntata analizzeremo come è possibile far comunicare il nostro picmicro attraverso il protocollo seriale RS232 e realizzeremo una semplice applicazione che sfrutta tale sistema di comunicazione interfacciandoci col pc: faremo accendere/spegnere dei led impartendo dei comandi da porta seriale e verificheremo da terminale quali pulsanti l’operatore ha premuto sulla scheda. Impareremo quindi anche ad utilizzare il classico programma che Windows mette a disposizione per interfacciarsi con la porta seriale: Hyperterminal. Mettetevi comodi perchè oggi c’è parecchio da leggere e capire: sarebbe semplice mettere un codice già pronto e non imparereste a risolvere eventuali problemi qualora si dovessero presentare, quindi meglio capire per bene tutto quello che c’è dietro prima di cominciare, anche perchè sicuramente procura molta più soddisfazione.
Indice dei contenuti
- 1 Panoramica sui sistemi di comunicazione seriale
- 2 Il protocollo RS232
- 3 I livelli logici di trasmissione e Il MAX232
- 4 La porta seriale sui pc
- 5 La comunicazione RS232 con un picmicro
- 6 Registro TXSTA
- 7 Registro RCSTA
- 8 Selezionare il Baud Rate
- 9 Applicazione pratica
- 10 Cenni preliminari sul programma: le funzioni GETCH, PUTCH e PRINTF
- 11 Hyperterminal
- 12 Descrizione del programma
- 13 Downloads
Panoramica sui sistemi di comunicazione seriale
Prendiamo in considerazione la trasmissione di un byte: 8 bit. In una comunicazione di tipo parallelo, per poter inviare un byte abbiamo bisogno di 8 linee dati (non è sempre così, ma la maggior parte delle volte lo è): ogni linea invierà un bit, per cui gli 8 bit saranno trasmessi contemporaneamente. In una comunicazione di tipo seriale, invece, si utilizzerà un’ unica linea sulla quale saranno inviati in sequenza gli 8 bit. Le modalità con cui questi bit vengono inviati variano da protocollo a protocollo: ogni protocollo di comunicazione seriale ha le sue regole ed i suoi limiti e quindi le sue applicazioni.
Come si capisce, a parità di velocità del sistema di trasmissione, una comunicazione di tipo parallelo è sicuramente più veloce: gli 8 bit vengono inviati in contemporanea, ovviamente questo ha anche i suoi svantaggi: c’è bisogno di molte linee dati e le cose si complicano ulteriormente quando su un unico bus (su un’unica linea di trasmissione dati) bisogna collegarvi più dispositivi: una marea di cavi. Per tale motivo le comunicazioni di tipo parallelo stanno pian piano scomparendo: anche nei pc, giusto per fare un esempio, si sta abbandonando lo standard EIDE a favore del SATA: i cavi occupano meno spazio e c’è la possibilità di staccare i dispositivi anche mentre sono in funzione senza influenzare il resto del sistema. Riguardo alla velocità… ormai le velocità raggiunte hanno sorpassato i vecchi sistemi di comunicazione parallela (questo perchè si è deciso di non investire più sulla parallela per i motivi appena detti).
Le modalità di comunicazione (sia di tipo seriale che parallelo) possono essere Full Duplex oppure Half Duplex: si parla di comunicazione full duplex quando lo scambio di dati avviene in entrambi i sensi e in contemporanea. Nel caso di una trasmissione seriale full duplex possiamo immaginare che vi siano due fili che collegano due dispositivi A e B, il dispositivo A comunicherà verso il B attraverso un filo e il B verso l’A attraverso l’altro filo e questo può avvenire in contemporanea essendo le due linee separate.
In ogni tipo di comunicazione abbiamo ovviamente bisogno anche della linea di massa comune.
In una trasmissione Half Duplex abbiamo invece a disposizione un’unica linea di comunicazione: potrà trasmettere un unico dispositivo per volta, in questo tipo di comunicazione generalmente si individua un dispositivo “master” che comanda la comunicazione e uno o più dispositivi “slave” che ricevono gli ordini impartiti dal master.
Vi sono anche le comunicazioni di tipo simplex, seppur più rare, in cui c’è un solo “filo” (una sola linea di comunicazione) e comunica un unico dispositivo: l’altro riceve soltanto e non comunica.
Oltre alla modalità con cui si determina la direzione dei dati, una trasmissione seriale viene ancora suddivisa in sincrona o asincrona. Si parla di comunicazioni sincrone quando abbiamo un’ulteriore linea destinata al “clock”: un segnale ad onda quadra che scandisce il tempo: ad ogni colpo di clock viene inviato/ricevuto un bit, questo tipo di trasmissione permette di raggiungere elevate velocità.
Abbiamo una comunicazione di tipo asincrona quando non abbiamo un clock che scandisce i tempi e i due dispositivi sono preimpostati per lavorare alla stessa velocità.
Il protocollo RS232
Un protocollo, quindi, definisce il modo e le regole con cui i dati vengono scambiati. Il protocollo RS232 prevede innanzitutto una comunicazione seriale, asincrona, full duplex: abbiamo due linee di trasmissione dati (una di trasmissione e una di ricezione, la linea di trasmissione del terminale A diventa ovviamente la linea di ricezione del terminale B e viceversa), i dati vengono trasmessi in maniera seriale (un bit dopo l’altro) e non abbiamo un clock che scandisce i tempi di trasmissione, per cui i due dispositivi che comunicano devono essere preimpostati alla stessa velocità di trasmissione altrimenti non potranno comunicare. Il protocollo RS232 in realtà è molto più esteso: vi sono infatti altre linee di comunicazione oltre alle due riservate a trasmissione e ricezione, ma non ce ne occuperemo.
Dal momento che la trattazione su tale protocollo di comunicazione è davvero molto lunga (e quindi richiederebbe pagine e pagine di spiegazioni) e dal momento che ritengo ci siano già dei validi tutorial in giro sull’argomento, vi consiglio fortemente di leggere il tutorial sul protocollo RS232 scritto da Mauro Laurenti, che è possibile trovare in questa pagina: http://www.laurtec.com/Italiano/Tutorial/Il%20protocollo%20RS232/Tutorial.html
E’ possibile leggere una trattazione, ancora più estesa, sul protocollo RS232 nelle pagine di Vincenzo Villa a partire da qui: http://www.vincenzov.net/tutorial/rs232/rs232.htm
Una lettura a tali tutorial va fatta sicuramente per apprendere appieno tutto ciò che sta dietro a questo tipo di comunicazione.
I livelli logici di trasmissione e Il MAX232
Un’altra caratteristica, importantissima, del protocollo RS232 sono i livelli di tensione che determinano gli stati logici. Nei picmicro, che sono dispositivi a logica TTL, un livello di tensione di +5 volts corrisponde allo stato logico 1, un livello di tensione pari a 0 Volt corrisponde allo stato logico 0. Nel protocollo RS232 la specifica per i livelli logici è completamente differente: abbiamo un livello logico 1 quando la tensione vale circa -12Volts e un livello logico 0 quando la tensione è circa +12Volts (in realtà il range di tensione è più esteso, questi che ho indicato sono i livelli di tensione medi). Come vedete, quindi non è possibile interfacciare direttamente un dispositivo a logica TTL con una comunicazione RS232: un segnale di +5Volt che dal lato TTL è un livello logico alto, dal lato RS232 può essere interpretato come un livello logico basso, invece un livello TTL basso può non essere interpretato affatto.
Per tale motivo, al fine di poter interfacciare un dispositivo a logica TTL con una comunicazione RS232 esistono quelli che alcuni chiamano traslatori di livello oppure più semplicemente RS232 Drivers. Il driver maggiormente utilizzato in questo campo è il circuito integrato MAX232 prodotto dalla Maxim (datasheet scaricabile in fondo all’articolo) e i suoi cloni, che riesce ad ottenere i giusti livelli di tensione sfruttando un circuito a pompa di carica. Si tratta quindi di un semplice convertitore di livello logico, ne vediamo nel datasheet l’applicazione:
Abbiamo l’integrato suddiviso in due “lati”: un lato TTL/CMOS e un lato RS232. Applicando un segnale logico TTL agli ingressi 11 (T1IN) e 10 (T2IN), avremo in uscita (ai pin 14 e 7 rispettivamente: T1OUT e T2OUT) un segnale logico RS232. Al contrario applicando un segnale logico RS232 agli ingressi 13 (R1IN) e 8 (R2IN), avremo in uscita (ai pin 12 e 9 rispettivamente: R1OUT e R2OUT) un segnale logico TTL. Il circuito è molto semplice, basta soltanto piazzare i condensatori come indicato dallo schema, senza stupirsi del fatto che due condensatori vengono montati al contrario (vedete che C4 ha il positivo verso massa e C3 ha il negativo verso l’alimentazione). In realtà non vengono montati al contrario se si capisce il principio che sta alla base del funzionamento di una pompa di carica.
Guardando la tabella notiamo che se scegliamo un MAX232A (da notare la A finale) possono essere utilizzati condensatori da 0,1μF (100nF), con un MAX232 sono necessari condensatori da almeno 1μF (in realtà io avendo una scorta di condensatori da 10μF uso questi, quindi non vi preoccupate se non li avete: potete mettere anche una capacità più alta, senza esagerare con le dimensioni).
Inoltre, come si vede dallo schema, il MAX232 supporta 4 linee di comunicazione (vi sono pure integrati della stessa famiglia che ne supportano di più), ma a noi per realizzare una comunicazione semplice ne bastano soltanto 2.
In questo nostro articolo è presentata una valida interfaccia TTL/RS232
La porta seriale sui pc
I normali pc fissi di oggi hanno sul retro almeno una porta seriale di tipo RS232 (anzi: dovrebbero avere), identificata esternamente da un connettore maschio a 9 poli a forma di “D” rovesciata (questo tipo di connettore viene generalmente chiamato DSUB9, SUBD9 o DB9):
Fate caso che anche quando acquistiamo uno di questi connettori nuovi da cablare, sia maschio che femmina, è sempre riportato il numero affianco ad ogni pin. Il pinout è possibile vederlo nel documento Piedinatura seriale presente nella sezione risorse ([download#33]). Come potete vedere dal pinout il connettore maschio (sul pc) e il connettore femmina (che in genere si trova sul dispositivo da accoppiare al pc) hanno i pin di ricezione (RX) e trasmissione (TX) nella stessa posizione quando vengono accoppiati, per cui innestandoli uno nell’altro non si ha comunicazione: a tale scopo vengono difatti utilizzati dei cavi chiamati null-modem (o semplicemente cavo seriale incrociato) che mettono in comunicazione il pin 3 (trasmissione) con il pin 2 (ricezione) dell’altro.
Difatti per realizzare una semplice comunicazione RS232 tra due dispositivi, basta unicamente un cavo con 3 fili : uno che collega insieme i due pin di massa (il numero 5), uno che collega il pin 2 di uno col pin 3 dell’altro e uno che collega il pin 3 col pin 2 dell’altro. Il cavo avrà il connettore femmina da un lato (da innestare sul pc) e il connettore maschio dall’altro (da innestare sul dispositivo). Se non abbiamo il materiale per realizzarlo, tale cavetto può comunque essere acquistato nei negozi di informatica intorno ai 5 euro se non di meno.
Sui portatili generalmente la porta seriale non è presente. Per sopperire a tale mancanza la soluzione migliore, anche se abbastanza costosa, è quella di montare una scheda seriale PCMCIA. Altrimenti un’altra soluzione, più pratica ed economica è quella di usare gli adattatori USB/RS232 anche se in effetti in questo modo non si ha una seriale vera e propria ma viene emulata via software: proprio a causa di questo spesso alcuni di questi adattatori (spesso quelli più economici) non funzionano con tutti i dispositivi seriali: io ho una completa sfiducia in questo tipo di adattatori e non li consiglio quasi mai.
Personalmente però, ho avuto modo di provarne uno che con questa applicazione mi ha funzionato, lasciandomi stupito, anche se in realtà proprio economico non era (pagato 20 euro, ma ce ne sono alcuni che costano molto di più, la differenza suppongo stia nei chip utilizzati).
In giro si trovano moltissimi progetti per costruirsi da soli un convertitore USB/RS232, la maggior parte sono basati sul noto FDTI232BM, che è uno dei migliori chip in commercio per fare questa operazione. Trattandosi comunque di un componente SMD non tutti hanno l’attrezzatura adatta per potersi realizzare il circuito.
In una comunicazione RS232, i due dispositivi che comunicano tra loro devono avere impostati alcuni parametri: la velocità di trasmissione, i bit di dati, i bit di stop e la parità.
La velocità di trasmissione viene anche chiamata Baud Rate, e in una comunicazione RS232 rappresenta il numero di bit trasmessi in un secondo. I suoi valori più comuni sono 1200, 2400, 4800, 9600, 19200, 38400, 57600. Non tutti i chip destinati ad effettuare la comunicazione seriale supportano tutte le velocità e tutte le modalità di trasmissione dati.
I bit di dati rappresentano la quantità di bit inviati per ogni carattere: in una normale comunicazione standard, viene generalmente inviato un byte per ogni dato. Dal momento che ogni byte identifica un carattere secondo il codice ascii ([download#32]), osservando una comunicazione seriale vedremo che lungo le linee di trasmissione vengono trasmessi dei caratteri: è il modo per rappresentare un byte. In una trasmissione RS232 il bit meno significativo viene trasmesso per primo.
Il bit di stop serve a creare una pausa tra l’invio di un carattere e il successivo.
Il bit di parità serve ad aggiungere eventualmente un controllo di errore sui dati inviati (si veda il tutorial di Mauro per maggiori informazioni). Ho scritto eventualmente perchè tale controllo può anche essere omesso.
La comunicazione RS232 con un picmicro
La comunicazione RS232, anche se tende ad essere soppiantata dall’USB (che sfrutta sempre una comunicazione di tipo seriale ma senza un protocollo ben definito: sull’USB la comunicazione viene fatta via software, quindi ogni dispositivo deve avere necessariamente un suo driver di comunicazione) è quasi sempre presente sui pc fissi, e si trova soprattutto sulle strumentazioni industriali. Difficilmente verrà soppiantata dal momento che comunicare con l’RS232 è molto semplice, difatti su alcuni portatili hanno cominciato a rimetterla.
Il protocollo di comunicazione RS232 sui picmicro si implementa normalmentefacendo uso della periferica chiamata USART (Universal Synchronous Asynchronous Receiver Transmitter), opportunamente coadiuvata dal MAX232 per traslare i livelli di tensione. La periferica USART può essere impostata per lavorare in modalità asincrona full duplex (ed è questa la modalità che ovviamente sfrutteremo per la RS232) oppure per lavorare in modalità sincrona half duplex (come master o come slave), questa ultima modalità non la prenderemo in considerazione.
Esistono comunque in giro dei codici scritti in C che permettono di implementare la comunicazione RS232 anche sui picmicro che non hanno a bordo la periferica USART. Conoscendo perfettamente il protocollo di comunicazione è possibile difatti emulare la comunicazione via software.
Ci sono vari registri del picmicro associati all’USART. Analizziamoli per capire come avviene la comunicazione.
Registro TXSTA
Tale registro si occupa di una parte dei settaggi dell’USART e dello stato della trasmissione. Vediamo che il bit 4 di tale registro, denominato SYNC, se posto a zero ci permette di realizzare la modalità asincrona, realizzando la modalità asincrona avremo che l’I/O RC6 (situato sul pin n°25 del 16F877) sarà il pin utilizzato dal picmicro per trasmettere i dati e RC7 (pin 26) sarà il pin destinato a riceverli (in modalità sincrona, invece, il primo avrebbe avuto la funzione di clock e il secondo di linea dati).
Il bit TX9 ci permette di selezionare la modalità di trasmissione ad 8 o a 9 bit. La modalità ad 8 bit è generalmente quella più diffusa.
Il bit TXEN abilita la trasmissione.
BRGH abilita (1) l’alta velocità, questo bit deve essere tenuto in considerazione quando successivamente andiamo a scegliere la velocità di trasmissione.
Il bit TRMT serve ad indicarci lo stato dello shift register di trasmissione: quando vogliamo trasmettere un byte, il byte deve essere caricato nel registro chiamato TXREG, il dato quindi passa in automatico da TXREG allo shift register (TSR) e quindi da qui viene trasmesso, quando il dato viene trasmesso, lo shift register si svuota e viene quindi settato ad 1 il bit TRMT. Tale bit comunque non viene quasi mai utilizzato nei programmi.
Abbiamo infine il bit TX9D nel quale dovremo impostare il nono bit di dati qualora avessimo selezionato la trasmissione a 9 bit. Nel caso stiamo realizzando una trasmissione ad 8 bit, tale bit può essere utilizzato per inviare il bit di parità, ma non ce ne occuperemo.
Registro RCSTA
Tale registro si occupa di un’altra parte del settaggio dell’ USART e dello stato relativo alla ricezione. Per non dilungarmi troppo, dirò che di tale registro ci interessano principalmente i bit:
SPEN, che abilita (1) appunto l’utilizzo del modulo USART, configurando i pin RC6 e RC7 come destinati all’utilizzo dell’USART (dovremo comunque impostare queste due porte anche come ingressi agendo sul registro tristato).
RX9, che abilita la ricezione a 9 bit.
CREN, che abilita la ricezione continua dei dati.
RX9D, nono bit di dati ricevuto eventualmente lo volessimo utilizzare.
Selezionare il Baud Rate
Il baud rate, la velocità di trasmissione, è in funzione del quarzo utilizzato per il clock di sistema, del bit BRGH e del valore caricato nel registro SPBRG. Il suo valore può essere calcolato con le formule fornite sul datasheet:
Ovviamente sul picmicro non otterremo mai dei valori di baud rate precisi come quelli impostabili sul computer, ci sarà sempre una certa percentuale di errore, sul datasheet si consiglia comunque di selezionare sempre BRGH=1 anche con baudrate bassi in maniera da ottenere un errore inferiore. Alla pagina successiva del datasheet è riportata un’utile tabella con i valori da impostare nel registro SPBRG (valore x della formula vista poco fa) in funzione del bit BRGH e del quarzo utilizzato. Diamo un occhio alla tabella relativa a BRGH=1 :
Tale tabella ci fornisce subito i valori di errore rispetto ai valori standard di baud rate. Vediamo subito che utilizzando un quarzo a 20MHz e BRGH=1, i minori valori di errore di ottengono per un baudrate di 9600 e 19200, difatti in genere è sempre a questi due baudrate che si opera con i picmicro, nella colonna affianco possiamo quindi vedere il valore da assegnare al registro SPBRG.
Una volta settati propriamente i registri per lavorare con la seriale, dobbiamo soltanto sapere che per trasmettere un byte basta semplicemente caricarlo nel registro TXREG (basta fare TXREG=valore del byte), finito di trasmettere il byte viene settato il flag di interrupt TXIF, tale interrupt può essere intercettato nella nostra routine ISR abilitando innanzitutto gli interrupt di periferica (oltre all’interrupt generale) e quindi anche l’interrupt di trasmissione seriale (bit TXIE, che non si trova sul registro INTCON ma sul registro PIE1, che è appunto un registro dedicato all’abilitazione degli interrupt delle periferiche). Generalmente non si abilita mai il flag di interrupt su trasmissione seriale ma si controlla semplicemente il bit TXIF (che viene settato comunque) per verificare che la trasmissione precedente sia finita prima di procedere alla trasmissione di un nuovo dato. Il bit TXIF viene azzerato in automatico appena si caricano nuovi dati nel registro di tramissione.
Eventualmente si voglia effettuare una trasmissione a 9 bit, il nono bit va caricato nel bit TX9D prima di caricare TXREG
Quando invece vorremo ricevere un bit, la cosa migliore da fare è sicuramente intercettare l’interrupt di ricezione seriale, che si abilita portando ad 1 il bit RCIE: nell’ISR intercetteremo quindi il flag di avvenuta ricezione seriale, ovvero il bit RCIF. Il dato ricevuto da seriale lo avremo disponibile nel registro RCREG. Il flag di ricezione su seriale, RCIF, viene resettato in automatico appena si effettua la lettura del registro RCREG, per cui non dovremo preoccuparci di resettarlo (anche perchè non si può dal momento che è a sola lettura).
I flag di abilitazione di ricezione e di trasmissione su seriale, quindi, si trovano nel registro PIE1.
Come vedete, il buffer di ricezione seriale è limitato ad un byte, il che significa che ogni volta che riceviamo un byte, scatta l’interrupt. Se abbiamo bisogno di elaborare più di un byte è necessario dichiarare un array nel quale memorizzare i byte ricevuti incrementando un contatore. Generalmente per semplici applicazioni un byte basta. Ricordo in questa fase che ogni byte è rappresentato da una lettera secondo il codice ascii.
Applicazione pratica
Il circuito utilizzato è il seguente, tenete conto che non è illustrata la parte relativa al circuito di programmazione e reset, se utilizzate la Freedom 2 non dovrete fare nulla:
come vedete questa volta abbiamo adottato una soluzione circuitale leggermente differente dalle precedenti lezioni: i 4 pulsanti non hanno le resistenze di pullup (verrà sfruttata la funzione delle resistenze di pullup interne) e presentano invece una (piccola) resistenza verso massa: serve ad evitare che possa succedere qualcosa se si premono i pulsanti durante la programmazione, dal momento che BT4 e BT3 si trovano sulle linee di programmazione.
Se utilizzate una breadboard per i vostri esperimenti, o volete avere una interfaccia RS232 a portata di mano potete anche utilizzare l’interfaccia RS232/TTL presentata in questo nostro articolo.
Cenni preliminari sul programma: le funzioni GETCH, PUTCH e PRINTF
Il programma che andremo a caricare sul picmicro svolge le seguenti funzioni: invia un messaggio sulla seriale quando si premono i pulsanti, resta in ascolto sulla seriale per controllare se vengono impartiti i comandi per l’accensione dei led, suona in un modo quando riceve un comando, suona in un altro modo quando riceve un comando che non riconosce.
Per il settaggio dell’usart utilizzeremo una piccola libreria che si trova nella cartella “samples” dell’hitec-c, costituita da due soli files: usart.c e usart.h. Dovremo editare il contenuto di usart.h per impostare la velocità di trasmissione, la frequenza del quarzo espressa in Hertz (seguita dal prefisso L che sta ad indicare che il numero è un intero lungo) e se vogliamo utilizzare o meno il nono bit di dati:
#define BAUD 9600 #define FOSC 20000000L #define NINE 0 |
Ho impostato la velocità a 9600 bit al secondo, il quarzo a 20MHz (attenzione agli zeri!) e la comunicazione ad 8 bit. Fatto questo non ci resta che includere “usart.c” nel main.c e quindi richiamare la funzione “init_comms();” nel main.
Fatto questo, all’avvio del programma sul picmicro, l’usart risulta impostato. Nel settings abbiamo abilitato l’interrupt di ricezione su seriale e le resistenze di pullup sulla porta B:
RBPU=0; // attivo le resistenze di pullup su porta B GIE=1; // gestione globale interrupt attiva PEIE=1; // interrupt di periferica attivati RCIE=1; // interrupt di ricezione su seriale attivato |
Adesso, ogni volta che riceveremo un byte sulla seriale, scatterà l’interrupt che intercettiamo nell’ISR testando il bit di avvenuta ricezione su seriale (RCIF):
void interrupt isr(void) { if (RCIF) { |
Ricordiamoci che tale bit non va azzerato, verrà fatto in automatico appena viene effettuata la lettura del buffer di ricezione. Ok, ma come si fa la lettura? Abbiamo detto che il byte ricevuto si trova nel registro RCREG. Nel file “usart.c” ci sono 3 comode funzioni utili al nostro scopo:
void putch(unsigned char byte) { /* output one byte */ while(!TXIF) /* set when register is empty */ continue; TXREG = byte; } unsigned char getch() { /* retrieve one byte */ while(!RCIF) /* set when register is not empty */ continue; return RCREG; } unsigned char getche(void) { unsigned char c; putch(c = getch()); return c; } |
La funzione getch ci permette di recuperare il byte ricevuto: vediamo che resta in attesa, tramite il while, fino a che il bit RCIF non è settato, restituisce proprio RCREG. Quindi nel momento in cui avremo bisogno di recuperare il byte ricevuto su seriale ci basterà impostare una variabile di tipo char e fare:
variabile=getch(); |
La funzione putch ci permette invece di trasmettere un byte su seriale, vediamo che resta in attesa fino a quando la trasmissione precedente non è terminata, dopodichè imposta il registro di trasmissione (TXREG) sul valore passato alla funzione.
La funzione getche non fa altro che effettuare l’eco del byte passato restituendolo come valore di ritorno della funzione: in pratica riceve un byte e lo ritrasmette.
La maniera più comoda di inviare un byte, potendolo rappresentare come un carattere ascii, è proprio quella di scrivere un carattere tra singoli apici:
putch('A'); // invio il carattere A putch(65); // invio il numero 65, è la stessa cosa che inviare il carattere A, dato che il codice ascii di A è 65 putch(0x41); // lo posso pure scrivere in esadecimale putch(0b01000001); // o in binario |
Come vedete è molto semplice, ma se vogliamo inviare un’intera stringa? Una frase per esempio, da poter leggere sulla seriale? Semplice, si ricorre alla funzione “printf”, che fa parte della libreria standard stdio.h inclusa da usart.c. La funzione printf in c è forse una delle più famose: dirige l’output sulla periferica standard di I/O. In questo caso la periferica standard è la porta seriale: printf infatti utilizza la funzione putch per inviare i caratteri. Ulteriori informazioni sulla funzione printf le potete trovare anche su wikipedia.
Quando avremo bisogno di scrivere qualcosa sulla seriale faremo una cosa del tipo:
printf("Ciao mondo!\n\r"); |
I caratteri \n e \r sono due caratteri particolari che fanno parte di quei gruppi particolari di caratteri chiamati sequenze di escape. In pratica il carattere \n è il carattere (in genere viene identificato più precisamente come carattere di controllo. Anche se non visualizza niente anche lui ha un codice ascii e quindi occupa un byte di dati) nuova linea (Line Feed o LF) e il carattere \r è il ritorno carrello (Carriage Return o CR). In pratica inserendo \n, il cursore andrà sulla linea successiva, ma rimarrà sulla stessa colonna in cui si trovava prima di cambiare linea, il carattere \r, invece, fa ritornare il cursore alla prima colonna, facendolo rimanere sulla stessa linea, il ritorno a capo completo, quindi, è l’effetto di una nuova linea seguito da un ritorno carrello (o anche prima il ritorno carrello e poi la nuova linea).
In realtà questo è vero soltanto per i sistemi windows ed è una cosa ereditata dalle vecchie macchine da scrivere, che avevano due comandi separati per cambiare linea e per andare all’inizio della linea. Nei sistemi unix il ritorno a capo “completo” (nuova riga+inizio riga) viene eseguito unicamente con con il line feed \n. Nei sistemi MacOs, invece, il ritorno a capo viene eseguito unicamente con il carriage return \r. Per tale motivo avrete notato che aprendo un file di testo creato su Mac o Linux in Windows, non vedrete le linee andare a capo ma tutto si troverà su un’unica riga, mentre il contrario non da problemi.
Il carattere Line Feed rappresenta il codice ASCII n°10 (0x0A). Il carattere Carriage Return è il codice ascii n°13 (0x0D)
Ovviamente a questo punto sarete impazienti di capire come fare a inviare e ricevere comandi da seriale lato pc.
Hyperterminal
Windows ha un programma chiamato Hyperterminal che ci permette appunto, tra le altre cose, di comunicare con la porta seriale. Su windows XP si trova in Start -> Programmi -> Accessori -> Comunicazioni -> Hyperterminal (cliccate sull’icona del programma e non sulla cartella dal nome hyperterminal!).
Su alcune versioni di Windows tale programma non è presente, oppure lo è nella cartella Programmi\Windows NT\hypertrm.exe ma non è presente il link nel menù Start.
Chi non ha Hyperterminal, oppure utilizza Windows Vista (che non ce l’ha proprio in nessuna versione), può fare riferimento a questo articolo dove vengono illustrate soluzioni alternative ad Hyperterminal, fate riferimento ai siti ufficiali per conoscere il funzionamento di questi altri programmi.
Una volta avviato Hyperterminal si presenta la seguente finestra:
Digitiamo un nome da dare alla connessione che stiamo per realizzare e premiamo OK. Si presenta un’altra finestra in cui nel campo “connetti” dobbiamo selezionare la porta seriale (la porta COM) che intendiamo utilizzare per gli esperimenti, con un PC fisso con un’unica seriale o con un portatile con scheda PCMCIA, dovrebbe apparire unicamente COM1, quando invece si utilizza un adattatore USB/RS232 il numero della porta COM varia a seconda della porta USB alla quale è stato collegato (ma comunque non può mai essere COM1 o COM2).
Non spaventatevi se c’è scritto “immettere dettagli per il numero telefonico da comporre”, Hyperterminal difatti serve anche a connettersi in remoto con altri sistemi.
Selezioniamo la porta seriale giusta e premiamo ok, si presenta la seguente finestra:
Per il nostro esempio imposteremo i parametri come illustrato nell’immagine e premiamo OK.
Ci troveremo di fronte una finestra bianca vuota: tenendo la finestra in primo piano e premendo i tasti sulla tastiera, verranno inviati i byte relativi ai tasti premuti sul pin TX della porta seriale. Premendo ad esempio la lettera A maiuscola verrà inviato un byte il cui valore sarà 65 e così via secondo la tabella ascii. Ovviamente premendo i tasti in questa fase, non vedremo niente a video: nella finestra del terminale, infatti, devono apparire unicamente i byte ricevuti sul pin RX della seriale, che in questo momento non è collegato a niente.
Potete fare una prova collegando insieme con uno spezzone di filo i pin 2 e 3 della porta seriale: premendo i tasti, vedremo apparire le lettere corrispondenti nel monitor: questo perchè inviamo il byte sul pin TX e viene ricevuto sull’RX della stessa porta, è una prova stupida sicuramente ma spesso serve a diagnosticare eventuali problemi sulla porta seriale (Vi ho svelato un altro dei miei trucchi!).
Nella finestra di hyperterminal abbiamo due tasti in cima (li ho evidenziati in rosso) col disegno di un telefono:
Il tasto attivo nell’immagine, serve per “riagganciare” : hyperterminal smetterà di monitorare la seriale e si attiverà l’altro tasto con la forma di un telefono a riposo. Premendo questo tasto hyperterminal ricomincerà a monitorare la seriale. Chiudendo Hyperterminal apparirà una finestra che ci permetterà di salvare la sessione: in pratica verranno salvate tutte le impostazioni come le abbiamo definite nella fase iniziale (porta com, baud rate ecc). La volta successiva, anzichè aprire hyperterminal programma, possiamo dare un’occhio alla cartella Hyperterminal (quella che prima ho detto di non cliccare nel menù start): qui ci saranno le sessioni salvate.
Descrizione del programma
Capito come si fa a dialogare con la seriale lato picmicro e lato pc, possiamo passare a descrivere il funzionamento del programma. All’avvio viene inviata una stringa verso il pc per segnalare che tutto funziona correttamente.
Il programma cicla di continuo per verificare la pressione dei pulsanti: la pressione di un pulsante stampa a video una stringa che indica quale pulsante è stato premuto. Fin qui il funzionamento dovrebbe essere abbastanza chiaro leggendo il codice.
Faccio quindi uso di 3 flag per eseguire routine più “faticose” che non conviene far eseguire nell’ISR, che come detto sempre deve rimanere il più snello possibile:
bit ledmode; // flag per capire se siamo in modalità di accensione dei led bit statusmode; // flag per capire se dobbiamo stampare a video lo stato dei led bit errormode; // flag per indicare che abbiamo inviato un comando non riconosciuto bit okmode; // flag per indicare che abbiamo inviato un comando riconosciuto |
Questi flag vengono settati nell’ISR e verificati nel main (tranne il flag ledmode) per attivare le corrispondenti funzioni.
Il flag errormode viene settato per far suonare il cicalino in maniera più “sottile” a segnalare un comando non riconosciuto, il flag okmode viene invece settato per far suonare il cicalino con una tonalità più marcata ad indicare che il comando è stato riconosciuto.
Il flag statusmode viene settato quando da terminale si preme la lettera S maiuscola o minuscola (eh si, se non voglio creare confusione devo controllare sia la maiuscola che la minuscola dal momento che hanno codici ascii differenti):
// premuto S maiuscola o minuscola case 'S': case 's': statusmode=1; okmode=1; break; |
Questa condizione è verificata in uno switch nell’ISR determinato dal byte ricevuto: se viene ricevuto un byte corrispondente alla lettera S o alla lettera s, allora setto il flag statusmode che mi attiva nel main una routine che stampa a video lo stato dei led:
Il flag ledmode viene attivato alla pressione del tasto L o l (elle minuscola), questo cambia il comportamento dell’ISR: non verrà più verificata la pressione di S o L ma si attiva un’altra routine che si aspetta la pressione di altri tasti. Viene inoltre stampata a video una stringa che ci ricorda cosa si aspetta adesso il programma:
Bisogna quindi premere un numero da 0 a 4: la pressione dello zero effettua lo spegnimento dei 4 led, la pressione di un numero da 1 a 4 inverte lo stato del led corrispondente. Se si preme un tasto diverso da quelli elencati nella condizione switch, si esce da questa routine e il cicalino suona l’errore. Altrimenti viene stampato “ok” a video e lo stato del led viene invertito.
Tutto questo avviene perchè l’ISR è strutturato in questo modo:
void interrupt isr(void) { if (RCIF) // abbiamo ricevuto un byte sulla seriale? { input = getch(); // recupero carattere ricevuto if (ledmode) { switch(input) { //... VARIE CONDIZIONI default: // NESSUNA DELLE CONDIZIONI PRECEDENTI errormode=1; // segnalo che non è stato premuto un numero di quelli che mi servivano break; } ledmode=0; // esco dalla modalità ledmode } else // altrimenti non mi trovo in modalità ledmode { switch(input) { //... VARIE CONDIZIONI // premuto un altro carattere che non è né L né S default: errormode=1; // segnalo che il comando inviato non è riconosciuto break; } } } } |
Leggete il programma per capirne meglio il funzionamento. Come potrete vedere, e come ho spiegato anche in altre occasioni, nell’ISR cerco di eseguire meno compiti possibile: se devo eseguire qualcosa di laborioso mi setto un flag che verrà poi controllato nel main, ovviamente tale flag dovrà essere poi resettato nella stessa routine che lo verifica. L’unico flag che non verifico nel main è appunto ledmode, primo perchè mi deve cambiare il comportamento dell’ISR e quindi necessariamente lo devo verificare qui, e poi perchè non deve eseguire un compito particolarmente gravoso dovendo invertire unicamente un’uscita.
Ovviamente pensate se al posto dei led mettete dei relè e se al posto di hyperterminal vi scrivete un vostro software personalizzato per dialogare con la scheda. Come vedete i meccanismi che stanno alla base di alcuni prodotti commerciali e industriali non sono poi così impossibili da realizzare. Comunicare con la RS232 è una cosa semplicissima come avete visto, e vi permette di creare delle belle cose permettendo al computer di interagire con la vostra elettronica.
Downloads
Nota: i programmi di esempio sono stati sviluppati con una versione precedente dell’Hitec-C Compiler, per cui compilati con la nuova versione, restituiscono errori. Fate riferimento a questo articolo per maggiori informazioni su come adattare i vecchi programmi. Consiglio spassionato se volete davvero imparare a programmare: non utilizzate l’include legacy headers, ma imparate a cambiare i nomi mnemonici.
[download#94]
[download#95]