Sei sulla pagina 1di 45

Relazione di Sistemi

Trasmissione
Seriale
Standard RS232/C
Protocollo Master/Slave con Eco
Sviluppato in Linguaggio C++

Andrea Asta
Autore del fascicolo
Nome Andrea Asta
Sito Web www.andrea-asta.com
Email asta.andrea@tin.it
Classe 5A INFO

Sommario
Autore del fascicolo............................................................................................................................2
Sommario............................................................................................................................................2
Introduzione .......................................................................................................................................3
1.1 Titolo del progetto................................................................................................................3
1.2 Descrizione del progetto ......................................................................................................3
2. Progettazione generale del sistema...............................................................................................3
2.1 Strati di progettazione ..........................................................................................................3
2.2 Strato fisico ..........................................................................................................................4
2.3 Strato logico .........................................................................................................................4
2.4 Strato di programmazione....................................................................................................5
2.5 Riepilogo dei vari strati........................................................................................................6
3. Analisi approfondita dello strato fisico ........................................................................................7
3.1 La trasmissione in generale..................................................................................................7
3.2 La trasmissione seriale asincrona.........................................................................................8
3.3 Standard RS232/C..............................................................................................................13
3.5 Trasmissione RS232/C Null – Modem ..............................................................................15
3.6 Programmazione RS232/C.................................................................................................16
4. Analisi approfondita dello strato logico .....................................................................................19
4.1 Definizione del protocollo .................................................................................................19
4.2 Studio del protocollo..........................................................................................................20
4.3 Limitazioni note .................................................................................................................24
4.4 Algoritmo "Master Trasmette"...........................................................................................25
4.5 Algoritmo "Master Riceve" ...............................................................................................27
4.6 Unione degli algoritmi .......................................................................................................28
5. Programmazione ..........................................................................................................................29
5.1 Scelta del linguaggio di programmazione..........................................................................29
5.2 Routine di base del programma .........................................................................................29
5.3 Traduzione degli algoritmi.................................................................................................32
5.5 Software di supporto ..........................................................................................................33
5.6 Codice completo del programma .......................................................................................33
6. Conclusioni ...................................................................................................................................42
6.1 Conclusioni ........................................................................................................................42
6.2 Fonti ...................................................................................................................................42
7. Allegati ..........................................................................................................................................43
7.1 Documentazione _bios_serialcom()...................................................................................43
7.2 Documentazione clock() ....................................................................................................45

2
Introduzione
1.1 Titolo del progetto
Realizzazione di un sistema di comunicazione seriale RS232/C, trami-
te la definizione di un protocollo Master/Slave con eco.

1.2 Descrizione del progetto


L'intento prefissato è l'ideazione, progettazione e realizzazione di un
sistema di comunicazione tra sistemi a microprocessore, attraverso la
porta seriale RS232.

2. Progettazione generale del sistema


2.1 Strati di progettazione
Il progetto sarà analizzato e realizzato a diversi strati, che permette-
ranno un incremento graduale delle prestazioni del sistema. In altre pa-
role, un primo livello di base permetterà la comunicazione fisica tra i
due microprocessori; un secondo strato prevederà la definizione del si-
stema logico di comunicazione (in che modo, in che ordine i due ela-
boratori comunicheranno); infine, lo strato più esterno sarà costituito
dal vero e proprio codice.
1. Strato fisico
2. Strato logico
3. Strato di programmazione

Figura 1: Strati di progettazione

Ogni strato, come si può osservare dall'immagine, è strettamente


dipendente dallo strato di livello inferiore: in particolare, possiamo di-
re che uno strato esterno eredita, amplia e dipende dalle caratteristiche
dello strato immediatamente più interno. Eliminando uno strato inter-
no, anche tutti quelli immediatamente esterni perdono significatività.

3
Da questo si deduce come, prima di poter passare alla creazione di
uno strato superiore, sia vitale testare il perfetto funzionamento di tutti
gli strati più interni, in ogni sua parte. Infatti, come spiegato, ogni stra-
to esterno è strettamente dipendente dagli strati interni, non può esiste-
re senza di essi; ne consegue che un errore in uno strato interno com-
promette il funzionamento anche dello strato esterno.

2.2 Strato fisico


Lo strato fisico riguarda l'analisi e la gestione della comunicazione e-
lementare tra due microprocessori. Per quanto stabilito, la trasmissione
dovrà essere di tipo seriale e si atterrà allo standard di comunicazione
RS232/C.
Lo strato fisico comprende quindi la gestione a livello hardware
della comunicazione tra due microprocessori, il modo utilizzato per la
sincronizzazione e i controlli basilari degli errori fisici di trasmissione.
Lo scopo finale dello strato fisico è quello di fornire algoritmi
chiari all'utente per la trasmissione e la ricezione di un carattere attra-
verso la porta seriale RS232/C del microprocessore.

2.3 Strato logico


Completato lo strato basilare del progetto, ossia la trasmissione e la ri-
cezione elementare attraverso lo standard RS232/C, è necessario am-
pliare lo studio del progetto e aggiungere una serie di controlli aggiun-
tivi che definiscano un protocollo chiaro di comunicazione.
In altre parole, una volta che i due microprocessori hanno a dispo-
sizione gli strumenti necessari per comunicare (strato fisico) è necessa-
rio che si prendano degli accordi sul modo in cui essi dovranno scam-
biarsi i dati. Ad esempio, all'inizio dell'esecuzione, chi dovrà inviare
un dato e chi dovrà riceverlo? Come potranno i due microprocessori
sincronizzarsi? In che modo potremo verificare l'esattezza del dato ri-
cevuto? Tutte queste domande troveranno risposta nel nostro protocol-
lo.
In generale, un protocollo di comunicazione è un insieme di rego-
le rigorose, chiare, il più possibili elementari e comunemente accettate
che vengono definite per instaurare una comunicazione corretta.
Con il termine rigorose si intende che esse devono essere definite e
non ambigue: in ogni istante della comunicazione deve essere chiaro in
modo univoco che cosa si vuole fare.
Con il termine chiare si intende invece il fatto che i metodi utiliz-
zati da un protocollo per instaurare la comunicazione devono essere di
comprensione immediata, non ci devono essere incomprensioni tra i
sistemi che tentano di comunicare.
Inoltre, siccome in genere il protocollo utilizza lo strato fisico per
trasmettere parole speciali utilizzate come comandi, è necessario che
esse siano il più possibile semplici ed elementari: bisogna escludere
ogni comando superfluo, in quanto ogni trasmissione e ricezione in ec-
cesso rallenta, anche se in minima parte, il tempo di esecuzione dell'in-
tera trasmissione. Se, ad esempio, per trasmettere un singolo carattere,
fosse prima necessario inviare una decina di parole chiave, più una de-
cina di parole alla fine, probabilmente il protocollo definito non sareb-
4
be elementare e, in generale, il tempo speso per la trasmissione dei
comandi sarebbe molto superiore rispetto a quello della trasmissione
del dato.
Infine, il protocollo definito deve essere comunemente noto e ac-
cettato, nel senso che esso deve essere rispettato da tutti i sistemi inte-
ressati nella comunicazione.
Va aggiunto che, generalmente, lo strato fisico fornisce le routine
per la gestione di un'unità elementare di dato: ad esempio, normalmen-
te, con le routine RS232 non si potrà inviare più di un byte. Scopo del
protocollo che sarà definito è anche quello di permettere l'invio di una
quantità superiore di dati (generalmente una sequenza di dati elemen-
tari) e controllare, mediante appositi strumenti, la correttezza dell'inte-
ra sequenza inviata o ricevuta.

2.4 Strato di programmazione


In realtà lo strato di programmazione non è un vero e proprio strato di
progettazione, nel senso che esso dovrà, banalmente, tradurre lo strato
fisico in codice e lo strato logico in codice (che utilizzi quello dello
strato fisico): tuttavia, è bene ricordare che ogni linguaggio di pro-
grammazione, per sua natura, ha pregi e difetti, quindi è possibile che,
a seconda del linguaggio scelto, sia necessario applicare qualche pic-
cola modifica, qualche piccolo cambiamento al progetto iniziale.
La gestione di input esterni, inoltre, porterà a controlli aggiuntivi,
pertanto possiamo dire che il codice è, seppur in forma ridotta, un
nuovo strato di progettazione, che deve essere curato come gli altri
due.
Lo strato di programmazione sarà quindi quello che, oltre a tradur-
re gli algoritmi degli strati fisico e logico, aggiungerà l'interattività fi-
nale con l'utente esterno: ad esempio, si occuperà di ricevere in input i
dati da trasmettere, o di fornire in output i dati ricevuti.
Sarà quindi suo compito principale quello di rendere la trasmis-
sione trasparente all'utilizzatore, ricevendo in input solamente le in-
formazioni strettamente necessarie e fornendo in output le informazio-
ni più importanti. In altre parole, lo strado di programmazione dovrà
fornire un'interfaccia elementare e trasparente all'utente finale.
Il prodotto finale dello strato di programmazione risulterà quindi
un software di semplice utilizzo, che permetta a chiunque di scambiare
dati mediante la porta seriale.
L'utente finale non dovrà conoscere i dettagli del protocollo di co-
municazione e, tanto meno, dovrà essere a conoscenza dello strato fisi-
co del progetto, ossia dei metodi hardware utilizzati per la trasmissio-
ne. Tutto ciò che gli sarà richiesto sarà di connettere i due sistemi me-
diante un cavo Null – Modem apposito e di eseguire il software da noi
creato.

5
2.5 Riepilogo dei vari strati
La tabella seguente riassume in forma sintetica le caratteristiche ed i
compiti dei vari strati utilizzati per questo progetto.

Strato Compiti Caratteristiche


ƒ Gestione hardware della comunica- ƒ Efficiente
Fisico
zione
ƒ Algoritmi efficienti per la comunica-
zione elementare
ƒ Definizione di un protocollo di co- ƒ Rigoroso
Logico
municazione ƒ Chiaro
ƒ Gestione degli errori di comunica- ƒ Elementare
zione ƒ Comunemente accettato
ƒ Creazione di un'interfaccia per l'uten- ƒ Semplice
te finale ƒ Intuitiva
Programmazione
ƒ Definizione di un sistema di intera- ƒ Trasparente
zione con l'utente ƒ Efficiente
ƒ Traduzione degli algoritmi fisici e
logici in programma

6
3. Analisi approfondita dello strato fisico
3.1 La trasmissione in generale
Esistono diverse modalità di comunicazione tra due o più sistemi.
Quella più elementare è semplicemente quella in cui i vari sistemi so-
no fisicamente collegati per mezzo di una serie di connessioni elettri-
che collegate a delle porte di input e output del sistema.

Sistema Sistema
A B

Port Port
Connessione fisica
X1 X2

Figura 2: Collegamento fisico tra due sistemi

Esistono altri tipi di trasmissione: ad esempio esiste la trasmissione


via linee telefoniche, la trasmissione via fibre ottiche, via raggi infra-
rossi, via bluetooth, via radio eccetera.
Nel nostro progetto ci occuperemo della trasmissione tramite col-
legamento fisico.
A questo proposito, esistono ancora molti tipi di trasmissione, rag-
gruppabili in genere in due grandi famiglie:
ƒ Trasmissione seriale
ƒ Trasmissione parallela
In generale, la differenza tra questi due tipi di comunicazione sta
nel modo utilizzato per trasmettere (e, di conseguenza, ricevere) un da-
to: nel primo caso il dato è mandato bit per bit, quindi è necessaria una
sola linea dati per la trasmissione. Nel secondo caso, invece, tutto il
dato è trasmesso contemporaneamente, quindi saranno necessarie tante
linee quanti sono i bit da trasmettere.

Sistema Sistema
A B

Parallel Parallel
#01 n #02

Serial Serial
#01 #02

Figura 3: Comunicazione seriale e parallela

Ogni porta di input o output, in genere, dispone anche di ulteriori


segnali di controllo, chiamati segnali di hand – shaking. Questi se-

7
gnali sono ovviamente diversi per la trasmissione seriale e per quella
parallela, in quando in ognuna delle due trasmissioni ci sono dei con-
trolli differenti da effettuare.
Dalla descrizione, seppur sommaria, dei due tipi di trasmissione, si
possono dedurre i principali pregi e difetti di questi due tipi di comuni-
cazione.

Trasmissione Pregi Difetti


Seriale ƒ E' sufficiente una sola linea ƒ Trasmissione più lenta
dati per la trasmissione dei
dati
ƒ Minor rischio di interferenze
ƒ Costi più ridotti
ƒ Fili più lunghi
Parallela ƒ Trasmissione più veloce ƒ Necessaria una linea dati per
ogni bit, quindi utilizzo di
cavi grossi ed ingombranti
ƒ Maggior rischio di interferen-
ze
ƒ Costi più elevati

Va sottolineato inoltre che, negli ultimi anni, con l'introduzione


delle trasmissioni seriali USB e FireWire, la velocità di trasmissione è
cresciuta esponenzialmente, tanto da rendere queste due porte più ve-
loci anche della trasmissione parallela.

Velocità di comunicazione
Parallela 150KB/s - 2.4MB/s
RS232 10,5 Kb/sec - 115,2 Kb/sec
USB 480 Mb/sec (USB 2.0)
FireWire 400 Mb/sec

3.2 La trasmissione seriale asincrona


La trasmissione seriale nasce essenzialmente per collegare il mi-
croprocessore ad un modem (modulatore demodulatore), in grado di
modulare i dati in frequenza, fase ed ampiezza e quindi trasferirli, per
mezzo della linea telefonica, ad un altro modem. Nella trasmissione
seriale si è soliti indicare il sistema microprocessore con la sigla DTE
(Data Terminal Equipment) e il modem con DCE (Data Communica-
tion Equipment).

Linea
telefonica
Seriale Seriale

Figura 4: Comunicazione con modem

8
Il primo problema che è necessario risolvere in una trasmissione
seriale è il seguente: il microprocessore, per sua natura, lavora sempre
e solo con dati paralleli: i suoi bus, infatti, non sono, per motivi ovvi
legati alla velocità, di tipo seriale: ogni dato inviato in uscita sulle li-
nee del Data Bus, quindi, sarà di tipo parallelo. L'unità elementare di
trasmissione è, generalmente, il byte.
C'è quindi la necessità di convertire il dato parallelo in seriale: in
termini tecnici questo problema è noto come serializzazione del dato.
Per questo scopo esiste una particolare porta, chiamata UART (Unità
Asincrona Ricezione Trasmissione) o USART (Unità Sincrona e Asin-
crona Ricezione Trasmissione), in grado di serializzare il dato e gestire
tutta la trasmissione seriale in generale.

8
UART
uP
8250

Figura 5: Interfaccia seriale

Le UART e le USART sono dette interfacce seriali e sono presen-


ti sul mercato sotto forma di circuiti integrati interfacciabili con i di-
versi microprocessori: ad esempio, per la famiglia Intel 80XX sono di-
sponibili gli integrati 8250 (UART) e 8251 (USART).
Recentemente il nome UART è caduto in disuso, in favore del più
recente ACE (Elementi di comunicazione asincrona).
Da quanto appena detto si deduce che esistono almeno due tipi di
comunicazione seriale:
ƒ Comunicazione sincrona
ƒ Comunicazione asincrona
Nella trasmissione asincrona il significato di ogni bit ricevuto di-
pende dalla sua collocazione nell'ambito temporale, mentre nella tra-
smissione sincrona è aggiunta una linea di clock che permette la sin-
cronizzazione tra i due dispositivi.
La trasmissione sincrona è, nella pratica, poco utilizzata, in quanto
perdere il sincronismo tra due clock è decisamente semplice e, perso il
sincronismo di clock, la trasmissione è destinata ad avere esito negati-
vo. Inoltre, tra due sistemi a microprocessore, l'aggiunta di una linea di
clock è del tutto superflua.
Nella trasmissione asincrona, in genere, i due sistemi si sincroniz-
zano prima dell'invio di ogni singolo pacchetto di dati, in questo modo
la trasmissione risulta ancora più rallentata, ma decisamente più affi-
dabile.
La trasmissione asincrona è caratterizzata dall'invio di un frame di
dati: con questo termine si intende il dato vero e proprio (serializzato),
più una serie di bit aggiuntivi utilizzati per il controllo del flusso di
trasmissione: in altre parole, sulla linea dati non viaggiano solamente i
veri e propri dati, ma anche alcuni segnali utilizzati per l'hand - sha-
king.

9
Per quanto appena detto, un esempio di frame potrebbe essere:
1. Segnale di inizio trasmissione
2. Dato serializzato
3. Segnale di fine trasmissione
La trasmissione asincrona, quindi, non necessita di una linea ag-
giuntiva per il clock: è intuibile tuttavia come sia necessaria una forma
di sincronismo tra i due dispositivi in comunicazione.
Generalmente il sincronismo è definito grazie ad un parametro del-
la comunicazione, la velocità di trasmissione.
I due dispositivi, quindi, lavoreranno alla stessa velocità di tra-
smissione: una volta che è stato ricevuto il segnale di inizio trasmis-
sione, il ricevitore controllerà la linea dati alla velocità impostata, se-
gnalando il valore come bit del dato. Dopo 8 letture (se il dato è a 8
bit), si riceverà il bit di fine trasmissione e la comunicazione sarà ter-
minata.
La velocità di trasmissione è misurabile mediante due unità di mi-
sura differenti: i bps (bit per second) o baud.
I bps indicano, come indica il nome, il numero di bit trasmessi nel-
l'unità di tempo.
Il baud, invece, indica il numero di transizioni che avvengono sulla
linea nell'unità di tempo.
Se, ad esempio, la trasmissione avviene attraverso valori di tensio-
ne variabili tra 0V e 7V e ogni singolo Volt corrisponde ad una parola
diversa a 3 bit (0V = 000, 7V = 111), è logico pensare che, con una so-
la transizione, sarò in grado di trasmettere 3 bit: in altre parole, in un
caso del genere la velocità espressa in baudrate è un terzo di quella e-
spressa in bps.
Se la trasmissione si serve di due soli valori logici '0' e '1', come
nella maggior parte dei casi, è logico che ogni transizione permetterà
di trasmettere un solo bit, quindi baudrate e bps coincideranno.
Riassumendo, se con una transizione si trasmettono 2n bit, si può
scrivere la seguente relazione:

1
1 baud = bps
n

Volendo essere più generali, se con una transizione sono trasmessi


k bit, la relazione tra baud e bps diventa la seguente:

1
1 baud = bps
log 2 k

In genere, la linea dato è a riposo sul valore logico '1'. Nel momen-
to in cui il suo valore è spostato sul valore '0', la trasmissione ha inizio
e i due dispositivi inizieranno a trasferire i bit del dato.
In particolare, il trasmettitore avrà cura di cambiare la linea del bit
opportunamente, mentre il ricevitore si limiterà a leggere la linea dati
in relazione alla velocità di trasmissione impostata.
Terminato l'invio, la linea tornerà a riposo e la trasmissione del
frame sarà ultimata.

10
Dato

'1'
d0 d1 d2 d3 d4 d5 d6 d7 S
t
Riposo Dato Stop + Riposo

Start

Figura 6: Esempio di invio di un frame

Il primo problema che sorge quando si parla di sincronismo è l'i-


stante in cui il dato deve essere campionato: se, ad esempio, la velocità
di trasmissione è impostata a 300b/s, il bit dovrà essere campionato
ogni
ds ds 1b 1
v= ⇒ dt = = = s
dt v b 300
300
s

Tuttavia, se si legge esattamente ogni trecentesimo di secondo, an-


che il più piccolo ritardo nel cambiamento della linea da parte del tra-
smettitore causerebbe un errore nella comunicazione (ad esempio, se il
ricevitore legge poco prima che il trasmettitore cambi la linea, si a-
vrebbe una lettura errata). Generalmente le UART risolvono il pro-
blema campionando il bit a metà del periodo, in questo modo la pro-
babilità che un piccolo ritardo generi errori di trasmissione è molto
bassa. In questo modo, quando la linea viene posta a '0', il ricevitore
lascerà passare la metà di un trecentesimo di secondo (trovandosi
quindi a metà del periodo di permanenza dello '0'), quindi inizierà a
campionare regolarmente ogni trecentesimo di secondo.

Dato
T/2 T
Campionamenti

'1'
d0 d1 d2 d3 d4 d5 d6 d7 S1 S2
t

Figura 7: Campionamento dei bit

Va notato che la fine della trasmissione è trattata esattamente come


un ulteriore bit: in altre parole, la linea portata allo stato di riposo è
nuovamente campionata. Il bit così rilevato è chiamato bit di stop. In
alcuni casi, per maggiore sicurezza durante la trasmissione, la linea è

11
mantenuta alta per un secondo ciclo, in questo caso la comunicazione
avrà due bit di stop.
Al frame di comunicazione, infine, è spesso aggiunto il bit di pari-
tà, che può essere pari o dispari1. In questo caso, il frame necessita di
un'ulteriore aggiunta, generalmente posta prima dei bit di stop.
Dato

'1'
d0 d1 d2 d3 d4 d5 d6 d7 P S1 S2
t
Riposo Dato

Start Parità

Bit di Stop + Riposo

Figura 8: Frame completo

Va notato che il controllo sul bit di parità è totalmente affidato al-


l'hardware, non al software. In altre parole, sarà la UART del ricevito-
re a valutare la correttezza del bit di parità e fornire segnali di hand –
shaking appropriati.
Dallo studio del frame si evince come, in effetti, la trasmissione se-
riale asincrona risulti da 10 (dato, start, stop) a 13 (dato, start, doppio
stop, parità) volte più lento di una trasmissione parallela. Come detto,
tuttavia, si hanno vantaggi legati all'affidabilità e alla lunghezza mas-
sima dei cavi di connessione.
Le trasmissioni seriali possono essere di tre tipi:
ƒ Simplex
ƒ Half – Duplex
ƒ Full – Duplex
Questa divisione indica in quale direzione è possibile comunicare:
nella comunicazione simplex, essa avviene "a senso unico", ossia in
una sola direzione. Nella trasmissione duplex, invece, è possibile avere
comunicazione bidirezionale, con una piccola differenza: nel caso del-
la half – duplex, la trasmissione bidirezionale è possibile, ma non può
essere contemporanea, caratteristica permessa invece nelle comunica-
zioni full – duplex.
In conclusione, affinché una trasmissione seriale asincrona avven-
ga in modo corretto, i due dispositivi devono avere accordi precisi e
comuni riguardo a:
ƒ Numero di bit del dato
ƒ Numero di bit di stop
ƒ Numero di bit di parità e sua tipologia
ƒ Velocità di trasmissione

1
Il bit di parità pari è il numero di '1' da aggiungere al dato affinché il numero di '1' al suo interno sia pari. Ad esempio,
se il dato è 10101100, il bit di parità pari è '0', visto che il numero di '1' nel dato è 4, ossia pari. La parità dispari funzio-
na allo stesso modo, ma indica il numero di '1' da aggiungere affinché il numero di '1' al suo interno sia dispari.
12
3.3 Standard RS232/C
Lo standard RS232 nacque nell'anno 1962 per opera della Electronic
Industries Association ed era orientato alla comunicazione tra i main-
frame e i terminali attraverso la linea telefonica e un modem. Esso in-
cludeva le caratteristiche elettriche dei segnali, la struttura e temporiz-
zazioni dei dati seriali, la definizione dei segnali e dei protocolli per il
controllo del flusso di dati seriali su un canale telefonico, il connettore
e la disposizione dei suoi pin ed infine il tipo e la lunghezza massima
dei possibili cavi di collegamento.
Nel corso degli anni lo standard fu più volte corretto e sistemato,
per poter essere accettato su scala sempre più vasta: nell'anno 1962 fu
definita la versione più famosa e ancora oggi più diffusa, la versione
RS232/C. Tuttavia, la sua diffusione si ebbe solo nel 1982, quando per
la prima volta l'IBM incluse in un suo computer una porta COM, la
prima porta seriale diffusa su scala mondiale.
Da allora seguirono altre versioni fino alla RS232/F, ma la mag-
gior parte dei sistemi a microprocessore integra oggi solamente la ver-
sione C. C'è da aggiungere, inoltre, che la comunicazione seriale qui
descritta sta lentamente scomparendo dai moderni calcolatori, per dare
spazio alle più veloci tecnologie USB, FireWire, Ethernet e in fibra ot-
tica.
Lo standard RS232/C è caratterizzato dalle seguenti proprietà:
ƒ La trasmissione è di tipo seriale asincrona
ƒ La trasmissione è di tipo full – duplex
ƒ E' possibile inserire 1 o 2 bit di stop (in alcuni casi anche 1 e ½)
ƒ Ogni transizione permette la trasmissione di un bit, quindi il baud
rate e i bps coincidono
ƒ Connettori a 9 pin (originariamente 25 di cui 10 significativi)
Lo standard, inoltre, si serve di particolari valori elettrici per defi-
nire i valori logici '0' e '1', diversi da quelli utilizzati, ad esempio, dalle
tecnologie TTL o CMOS.

Valore Logico Tensione TTL Tensione RS232/C Denominazione


'0' 0V Da +3V a +25V Space
'1' 5V Da -3V a -25V Mark

La velocità di trasmissione è legata alla lunghezza massima dei cavi:

Baud Rate (baud) Distanza (m)


50 4800
110 2182
300 800
600 400
1200 200
2400 100
3600 67
4800 50
7200 33
9600 25
19200 14

13
La tabella seguente illustra i nomi dei 9 segnali significativi per lo
standard.

Segnale Significato Direzione


TD Transmitted Data DTE Æ DCE
RD Received Data DTE Å DCE
RTS Request To Send DTE Æ DCE
CTS Clear To Send DTE Å DCE
DCD Data Carrier Detect DTE Å DCE
DTR Data Terminal Ready DTE Æ DCE
DSR Data Set Ready DTE Å DCE
SGND Signal Ground ---
RI Ring Indicator DTE Å DCE

Quando il sistema entra in funzione, il DTE attiva la linea Data


Terminal Ready. Il DCE, a sua volta, quando pronto attiverà la linea
Data Set Ready. Infine, il DCE controllerà se è disponibile un dispo-
sitivo esterno in ascolto (in altre parole, se esiste un altro modem pron-
to a ricevere, attraverso la linea telefonica, ciò che stiamo trasmetten-
do) e fornirà l'esito mediante il segnale Data Carrier Detect.
A questo punto, i due dispositivi sono pronti per la comunicazione
seriale.
Quando il DTE vuole trasmettere un dato serializzato, attiva la li-
nea Request To Send e attende il "via libera" dal DCE mediante la li-
nea Clear To Send: in generale, prima di attivarla, il DCE controllerà
la portante. A questo punto il DTE è pronto ad inviare un dato sulla li-
nea Transmitted Data. Se, invece, è il DCE a dover trasmettere, la li-
nea utilizzata sarà la Received Data.
Il modo più semplice e diffuso per connettere i due terminali è
quello di connettere semplicemente ogni segnale con l'omonimo nel
dispositivo di destinazione.

DTE DCE
TD TD
RD RD
RTS RTS
CTS CTS
DCD DCD
DTR DTR
DSR DSR
SGND SGND
RI RI

Figura 9: Connessione RS232/C tra DTE e DCE

Tuttavia, esistono altri diversi modi di connessione.

14
In alcuni casi, i cavi seriali incrociano alcuni segnali, in modo da
avere più segnali di hand – shaking anche quando è il DCE a dover
trasmettere un dato.

DTE DCE
TD TD
RD RD
RTS RTS
CTS CTS
DCD DCD
DTR DTR
DSR DSR
SGND SGND
RI RI

Figura 10: Collegamento con incroci

3.5 Trasmissione RS232/C Null – Modem


Spesso si ha la necessità, come nel nostro progetto, di connettere due
computer, ma non si ha invece bisogno di passare attraverso un mo-
dem: ad esempio, se i due terminali risiedono ad una breve distanza tra
loro, sarebbe superfluo passare per il modem.
Lo standard RS232/C può essere adattato anche per creare una
connessione, detta Null – Modem, che permette di collegare due ter-
minali senza necessità di un modem.
Anche in questo caso, esistono differenti versioni di collegamento:
di seguito è riportata la più diffusa, nonché quella da noi utilizzata.

DTE1 DTE2
TD TD
RD RD
RTS RTS
CTS CTS
DCD DCD
DTR DTR
DSR DSR
SGND SGND
RI RI

Figura 11: Connessione Null – Modem

Come si nota, alcuni segnali sono corto – circuitati, infatti l'assenza


di modem comporta il fatto che non ci sia bisogno di rilevare portanti
o verificare l'accensione di questo dispositivo. Allo stesso modo,
15
quando il primo terminale è pronto a trasmettere, può farlo senza a-
spettare conferme, visto che il modem non è presente.

3.6 Programmazione RS232/C


La porta RS232 è programmabile in tre modi diversi:
ƒ Accedendo direttamente alla porta
ƒ Attraverso gli interrupt BIOS
ƒ Attraverso gli interrupt DOS
Nel nostro progetto, sviluppato sotto ambiente Windows XP, ci
serviremo degli interrupt BIOS, che rappresentano un buon compro-
messo tra semplicità d'uso ed affidabilità (che sarebbe maggiore nel
caso di accesso diretto alla porta).
L'interrupt software BIOS 14h fornisce una serie di routine per la
gestione delle porte COM1, COM2, COM3, COM4.
Dei vari servizi a disposizione, ce ne sono quattro particolarmente
importanti:

Servizio 0: Inizializzazione porta seriale


Servizio 1: Invio di un carattere
Servizio 2: Ricezione di un carattere
Servizio 3: Stato della porta

Ognuno di questi servizi prevede dei parametri in ingresso, tra cui,


sempre, il numero di porta COM a cui ci si riferisce.
In generale, ogni porta utilizzata deve essere inizializzata, quindi è
possibile richiedere operazioni di invio e ricezione, controllando even-
tualmente lo stato della porta in caso di errori.

Inizializzazione della porta


Questa operazione è necessaria per programmare la porta seriale, im-
postando quindi i corretti valori per:
ƒ Velocità di trasferimento
ƒ Bit di parità
ƒ Lunghezza della parola
ƒ Numero di bit di stop
E' logico che la programmazione della porta seriale dovrà essere
identica su tutti e due i dispositivi interessati alla trasmissione.
La parola di programmazione è generalmente costituita da un byte,
che racchiude al suo interno le informazioni sopraccitate. In particolare
esso è così costituito:

b7 b0

V2 V1 V0 P1 P0 S L1 L0

Figura 12: Programmazione della porta seriale

I tre bit più significativi servono ad impostare la velocità di trasfe-


rimento, secondo questi valori:

16
b7…b5 Baud Rate (baud)
000 110
001 150
010 300
011 600
100 1200
101 2400
110 4800
111 9600

I successivi due bit impostano la parità:

b4-b3 Parità
00 Nessun bit di parità
01 Parità dispari
10 Nessun bit di parità
11 Parità pari

Il bit successivo indica quanti bit di stop devono essere inclusi nel
frame:

b2 Stop
0 1 bit di stop
1 2 bit di stop

Gli ultimi due bit, infine, impostano la lunghezza delle parole da


trasferire:

b1-b0 Parità
00 Configurazione non ammessa
01 Configurazione non ammessa
10 Parole a 7 bit
11 Parole a 8 bit

Il risultato della programmazione è esattamente uguale al risultato


dell'invocazione dello stato della porta seriale e sarà quindi descritto in
seguito.

Utilizzo della porta


Una volta programmata, la porta è pronta ad essere utilizzata. In gene-
rale, le routine di invio e ricezione utilizzeranno una variabile a 7 o 8
bit (a seconda di come è stata programmata la porta) per l'invio o la ri-
cezione di un carattere. Esisterà poi un flag che indicherà la corretta
esecuzione dell'operazione. In caso di errore, sarà possibile richiamare
la routine di status per maggiori informazioni.

Stato della porta


Il servizio di stato della porta seriale restituisce due byte, in cui il pri-
mo indica lo stato della linea, il secondo lo stato del modem.
Se, come nel nostro caso, si utilizza una connessione Null - Mo-
dem del tipo precedentemente descritto, è chiaro che lo stato del mo-
17
dem sarà irrilevante, visto che non vi è interconnessione tra i segnali di
hand – shaking di un dispositivo con quelli dell'altro.

Stato della linea


b7 b6 b5 b4 b3 b2 b1 b0 Significato
1 Dato pronto da ricevere
1 Errore di sovrapposizione (Overrun Error)
1 Errore di parità (Parity Error)
1 Errore di composizione (Framing Error)
1 Segnale di Break (Break Interrupt)
1 Registro di trasmissione THR vuoto
1 Registro di trasmissione TSR vuoto
1 Errore di timeout (Timeout Error)

Stato del modem


b7 b6 b5 b4 b3 b2 b1 b0 Significato
1 La linea CTS ha cambiato valore dopo l'ultima lettura
1 La linea DSR ha cambiato valore dopo l'ultima lettura
1 La linea RI ha cambiato valore dopo l'ultima lettura
1 La linea DCD ha cambiato valore dopo l'ultima lettura
X Valore del segnale CTS
X Valore del segnale DSR
X Valore del segnale RI
X Valore del segnale DCD

In generale, prima di una trasmissione bisognerà attendere che i


registri di trasmissione siano vuoti (tutto ciò che prima era presente è
stato trasmesso, i due registri vengono svuotati solo alla fine della tra-
smissione), mentre prima di una ricezione bisognerà verificare che sia
settato il bit di Data is Ready, che indica se è presente un nuovo dato
da leggere.
Nel nostro programma implementeremo una funzione con un time-
out, in modo che il programma non rimanga all'infinito a provare la
trasmissione o la ricezione, ma solo per un tempo prefissato.

18
4. Analisi approfondita dello strato logico
4.1 Definizione del protocollo
Il protocollo che andremo a definire sarà di tipo Master – Slave con
eco.
Un protocollo è di tipo Master – Slave quando una delle due parti
in gioco ha il potere di decidere chi compierà una determinata opera-
zione. Nel nostro caso, il Master deciderà, ad ogni iterazione, quale tra
i due dispositivi sarà in trasmissione e quale in ricezione. Lo Slave do-
vrà quindi attenersi alle disposizioni del Master.
Un protocollo è di tipo Con Eco quando ad ogni parola comando
inviata corrisponde un eco di conferma. Se, ad esempio, uno dei due
dispositivi manda il comando "Inizio Trasmissione", l'altro rimanderà
lo stesso comando "Inizio Trasmissione" per confermare di aver ben
inteso l'operazione che si dovrà eseguire. Questo tipo di controllo per-
mette maggiore sicurezza nel flusso del programma.
Il nostro protocollo prevederà, inoltre, una seconda forma di con-
trollo sulla completezza del dato inviato: si aggiungerà infatti un'ulte-
riore parola, la checksum del dato inviato.
Il trasmettitore manderà un segnale di inizio trasmissione, quindi
un pacchetto di dati di lunghezza variabile, ed un segnale di fine tra-
smissione, seguito da una parola di controllo degli errori.
Il ricevitore manderà l'eco per ogni comando e fornirà l'esito della
checksum II e, quindi, della comunicazione in generale.
Per la comunicazione, quindi, si seguirà una procedura di questo
tipo:
ƒ Il Master decide chi trasmetterà
ƒ Inizio della trasmissione
ƒ Invio del dato
ƒ Fine della trasmissione
ƒ Invio della checksum
ƒ Esito della trasmissione
Dovendoci basare unicamente sullo strato fisico, è chiaro che le
parole chiave saranno dei normali dati inviati.
Sarà inoltre definita una parola errore che, quando ricevuta, indi-
cherà di riposizionarsi all'inizio dell'esecuzione e ricominciare la tra-
smissione dall'inizio.
Definiamo quindi i seguenti valori costanti come keywords:

Valore Descrizione
0Bh Il Master decide di trasmettere
0Ch Il Master decide che trasmetterà lo Slave
01h Inizio della trasmissione
04h Fine della trasmissione
00h Errore
59h Checksum corretta, trasmissione riuscita
4Eh Checksum errata, trasmissione fallita

II
La checksum è il bit di parità pari eseguito sull'intera serie dei dati. In altre parole, il bit bi della checksum altro non è
che il bit di parità dell'intera serie dei bit bi del pacchetto dati in questione.
19
4.2 Studio del protocollo
Una volta definito il protocollo, si esegue una serie di test per verifi-
carne la validità.
I primi test sono quelli eseguiti in assenza di errori.

Test di correttezza in assenza di errori


Master in trasmissione
Master Slave
Decisione
01
0B
Eco
02
0B
Inizio trasmissione
03
01
Eco
04
01
Dato
05
XX
Fine trasmissione
06
04
Eco
07
04
Checksum
08
YY
Esito
09
59 or 4E

Test di correttezza in assenza di errori


Master in ricezione
Master Slave
Decisione
01
0C
Eco
02
0C
Inizio trasmissione
03
01
Eco
04
01
Dato
05
XX
Fine trasmissione
06
04
Eco
07
04
Checksum
08
YY
Esito
09
59 or 4E

A questo punto, l'analisi si sposta nello studio di ogni tipo di errore


che può avvenire nella trasmissione. Dobbiamo innanzitutto distingue-
re tra errori fisici (errori causati dalla trasmissione e ricezione RS232)
ed errori logici (eco sbagliato, checksum errata). A questo riguardo,
20
stabiliamo che, in ogni caso, sarà inviato il codice di errore e la tra-
smissione ricomincerà dall'inizio. E' logico pensare che il dato utilizza-
to per l'errore non potrà essere presente all'interno di alcun dato, visto
che, ad ogni istante, la ricezione di un codice di errore comporterà l'az-
zeramento della trasmissione.
Importante è notare la seguente questione: se è stato riportato un
errore fisico in trasmissione, chi ci assicura che il codice di errore in-
viato da una parte all'altra arrivi correttamente? In effetti nessuno. Tut-
tavia, la probabilità che fallisca anche questa seconda trasmissione è
piuttosto bassa e, comunque, non abbiamo altra scelta in merito.
Analizziamo ora il caso in cui avvenga un errore durante la tra-
smissione del Master.

Errore nel primo eco


Master in trasmissione
Master Slave
Decisione
01
0B
Eco
02
0D
Errore
03
00

Esiste tuttavia un caso particolare che va ben tenuto presente: se lo


Slave capisce che sarà lui a trasmettere, non si mette in ascolto di un
comando, ma tenterà di inviare il codice di inizio trasmissione. In que-
sto caso il Master dovrà attendere una lettura e quindi mandare il codi-
ce di errore come eco dell'inizio trasmissione.

Errore nel primo eco – Llo slave pensa di trasmettere


Master in trasmissione
Master Slave
Decisione
01
0B
Eco
02
0C
Inizio trasmissione
03
01
Errore
04
00

Negli altri casi, invece, ci si limita ad inviare un codice di errore e


si ricomincia la trasmissione.

Errore nell'eco di inizio trasmissione


Master in trasmissione
Master Slave
Decisione
01
0B
Eco
02
0B
03 Inizio trasmissione

21
01
Eco
04
02
Dato (Errore)
05
00

Errore nell'eco di fine trasmissione


Master in trasmissione
Master Slave
Decisione
01
0B
Eco
02
0B
Inizio trasmissione
03
01
Eco
04
01
Dato
05
XX
Fine trasmissione
06
04
Eco
07
02
Checksum (Errore)
08
00
Esito
09
59 or 4E

Come si nota, anche questo caso appare particolare: infatti, se in-


viamo il codice di errore dopo l'eco di fine trasmissione, lo Slave lo in-
terpreterà come una normale checksum. Tuttavia, come è facile imma-
ginare, la checksum può valere esattamente zero. Tuttavia, il pro-
blema non si pone, per il seguente motivo: se lo Slave interpreta la fine
della trasmissione come un comando diverso, esso lo considererà an-
cora come dato, quindi sarà sufficiente inviare un codice di errore.
Nel caso in cui sia lo Slave a trasmettere, si ricorrerà, generalmen-
te, agli stessi metodi. Ancora una volta, vanno tenuti presente i casi
particolari.

Errore nel primo eco


Master in ricezione
Master Slave
Decisione
01
0C
Eco
02
0E
Inizio trasmissione (Errore)
03
00

Da qui si evince che, se lo Slave non pensa di dover trasmettere, si


posizionerà comunque in condizione di ricezione, attendendo il codice
di errore.

22
Errore nel primo eco – Lo Slave pensa di ricevere
Master in ricezione
Master Slave
Decisione
01
0C
Eco
02
0B
Inizio trasmissione (Errore)
03
00

Errore nell'eco di inizio trasmissione


Master in ricezione
Master Slave
Decisione
01
0C
Eco
02
0C
Inizio trasmissione
03
01
Eco
04
02
Dato (Errore)
05
00

Anche lo Slave, quindi, ha la possibilità di inviare un codice di er-


rore, ma solamente nel caso dell'invio del dato.

Errore nell'eco di fine trasmissione


Master in trasmissione
Master Slave
Decisione
01
0C
Eco
02
0C
Inizio trasmissione
03
01
Eco
04
01
Dato
05
XX
Fine trasmissione
06
04
Eco
07
05
Checksum (Errore)
08
0

Nel caso in cui si verifichino errori fisici nella trasmissione, si pro-


verà comunque ad inviare il carattere di errore e ci si posizionerà all'i-
nizio della nuova trasmissione.
L'ultimo studio importante riguarda i timeout: come già spiegato,
saranno implementati dei timeout nelle routine di trasmissione e rice-
zione, in modo che non si aspetti all'infinito. Tuttavia, esiste un caso in

23
cui la strategia adottata non è sufficiente: quando il Master invia il co-
mando allo Slave che indica che sarà quest'ultimo a trasmettere, non è
possibile impostare un timeout nell'attesa del comando di inizio tra-
smissione. Infatti, solo dopo il comando del Master lo Slave potrà ri-
cevere in input dall'esterno un dato da mandare, quindi l'utente potreb-
be impiegare parecchio tempo per l'input. Non sarebbe formalmente
corretto imporre un timeout in questo caso, quindi sarà importante ri-
muoverlo.
L'ultima modifica apportata al protocollo è stata la seguente: do-
vendo essere il Master ad avere potere decisionale su ogni situazione,
abbiamo stabilito che esso confermerà anche il codice di errore ricevu-
to dallo Slave, prima di riposizionarsi all'inizio dell'esecuzione. Questa
ulteriore forma di controllo, seppur non strettamente necessaria, ci aiu-
ta a mantenere coerenza con il fatto che è sempre il Master a prendere
le decisioni. Partendo da questa base, una possibile evoluzione futura
sarebbe che, ad esempio, quando il Master riceve un codice di errore,
ha la possibilità di eseguire altre operazioni, quindi mandare l'eco del-
l'errore allo Slave e poter quindi autorizzare il reset della comunica-
zione.

4.3 Limitazioni note


Esistono alcune limitazioni al nostro protocollo, riassunte di seguito.

1. Il dato non potrà contenere byte uguali al codice di errore


2. Il dato non potrà contenere byte uguali al codice di fine tra-
smissione
3. Si suppone che, in seguito ad un errore fisico o logico, l'invio
del codice di errore possa avvenire
4. Si suppone l'utilizzo di uno strato fisico RS232 con collega-
mento mediante cavo Null – Modem senza interconnessione tra
i segnali di handshaking dei due dispositivi (l'unica connessio-
ne è tra i segnali di ricezione e trasmissione, incrociati)

Ognuna di queste limitazioni è già stata motivata in precedenza: in


particolare, le limitazioni sulla tipologia del dato sono causate dal fatto
che le informazioni vietate (errore, fine trasmissione) sarebbero inter-
pretate dal nostro algoritmo come comandi, quindi tutte le informazio-
ni inviate di seguito risulterebbero inattese e genererebbero un errore.
La limitazione sull'invio del codice di errore, invece, è causata dal
fatto che è comunque necessario rendere noto ad entrambi i dispositivi
la presenza di un errore, quindi l'unico modo disponibile è appunto
quello di tentare una nuova trasmissione. Se anche questa trasmissione
dovesse fallire, è chiaro che il problema sarebbe di dimensioni troppo
vaste per poter essere gestito efficacemente e, presumibilmente, il si-
stema genererà spontaneamente un errore e terminerà l'esecuzione del
programma. Un'ulteriore ipotesi è che il programma risulti "bloccato"
e che l'utente termini forzatamente dall'esterno la sua esecuzione.

24
4.4 Algoritmo "Master Trasmette"
Il nostro progetto si interesserà della creazione del programma del
Master. Il primo punto è quindi quello di creare un algoritmo efficiente
nel caso in cui sia il Master a Trasmettere.

Algoritmo Master-Trasmette
1. Send 0Bh
2. X Å Ricevi()
3. error Å 0
4. Se X = 0Bh
1. Send 01h
2. Y Å Ricevi()
3. Se Y = 01h
1. Send dati
2. Send 04h
3. Z Å Ricevi()
4. Se Z = 04h
1. Send Checksum
2. CS Å Ricevi()
3. Se CS = 59h
1. Trasmissione corretta
4. Altrimenti
1. Trasmissione errata
5. Altrimenti
1. error Å 1
4. Altrimenti
1. error Å 1
5. Altrimenti
1. Se X = 0Ch
1. Ricevi()
2. error Å 1
6. Se error = 1
1. Send 00h
Questa versione dell'algoritmo si cura solamente degli errori logici:
il controllo sugli errori fisici verrà, per comodità, aggiunto solamente
in fase di stesura del codice; del resto, sarà sufficiente impostare a 1 la
variabile di errore se si è riscontrato un errore fisico.
Di seguito è riportato il diagramma a blocchi dell'algoritmo: alcune
parti sono state leggermente modificate rispetto all'algoritmo scritto
(che, per ragioni legate alla programmazione e allo stile, impone di
non usare determinate tecniche di salto), in modo da rendere più chiaro
il ragionamento.

25
Inizio

Invia
0Bh

Eco Eco
Ricezione
= =
a vuoto
0Bh 0Ch
Yes

Yes

Invia
01h

Eco
=
01h

Yes

Invia
Dati

Invia
04h

Eco
=
04h

Yes

Invia
Checksum
Invia
00h

Yes Esito
=
59h

Trasmissione Trasmissione
corretta errata

Fine

26
4.5 Algoritmo "Master Riceve"
Il secondo punto da creare è quello riguardante il Master in ricezione.

Algoritmo Master-Riceve
1. Send 0Ch
2. X Å Ricevi()
3. error Å 0
4. Stringa s
5. i Å 0
6. Se X = 0Ch
1. Y Å Ricevi()
2. Se Y = 01h
1. Send 01h
2. Ripeti
1. C Å Ricevi()
2. Se ( C ≠ 00h && C ≠ 04h )
1. s[i] Å C
2. i Å i + 1
3. finché ( C ≠ 00h && C ≠ 04h )
4. Se C ≠ 00h
1. Z Å Ricevi()
2. Se Z = 04h
1. Send 04h
2. CS Å Ricevi()
3. CS2 Å CalcolaChecksum (str)
4. Se CS = CS2
1. Send 59h
2. Trasmissione corretta
5. Altrimenti
1. Send 4Eh
2. Trasmissione errata
3. Altrimenti
1. error Å 1
5. Altrimenti
1. error Å 1
3. Altrimenti
1. error Å 1
7. Altrimenti
1. error Å 1
8. Se error = 1
1. Send 00h

Questo algoritmo è simile a quello precedente: nella maggior parte


dei casi, la scelta adottata è quella di rendere possibile solo al Master
l'invio di un valore di errore: l'unico caso in cui lo Slave ha questa pos-
sibilità è durante il dato e, in ogni caso, dovrà attendere il codice di er-
rore del master prima di riposizionarsi all'inizio.
Anche in questo caso, gli errori fisici porteranno sempre al codice
di errore.

27
4.6 Unione degli algoritmi
La parte finale è quella di unire gli algoritmi in precedenza progettati.

Inizio

Leggi
X

Yes No
X = 'T'

Algoritmo Algoritmo
"MASTER TRASMETTE" "MASTER RICEVE"

Yes
Ancora?

No

Fine

28
5. Programmazione
5.1 Scelta del linguaggio di programmazione
La scelta del linguaggio di programmazione è critica, nel senso che da
essa dipendono alcune caratteristiche aggiuntive al progetto.
In generale, i linguaggi su cui scegliere sono stati il C++ e l'As-
sembly 8086. E' stato preferito il C++ per i seguenti motivi:
ƒ Dispone di ottime librerie per la gestione delle funzioni fisiche
RS232
ƒ E' un linguaggio strutturato, quindi il programma risulterà di
comprensione più immediata
ƒ Fornisce interessanti routine predisposte per la gestione dei time-
out
ƒ Contiene routine di utilizzo immediato per l'input e l'output con
l'utente, sia per l'utilizzo della console video testuale che per la
gestione dei file

5.2 Routine di base del programma


Per prima cosa, utilizzando il C++, si definiscono alcune strutture dati
considerate essenziali per la semplificazione del programma.

Tipi di dati di base


// Definizione del dato booleano
enum bool { false = 0 , true };

// Definizione della word


union integer {
unsigned int word;
struct {
unsigned char low;
unsigned char high;
} byte;
};

// Definizione del dato byte


typedef unsigned char byte;

Nel caso del tipo di dato bool, è possibile che alcuni compilatori lo
implementino di default: se così non fosse, sarà necessario (e comodo)
definirlo.
Il tipo byte, invece, è stato creato per rendere più immediata la
comprensione del codice: del resto, questa nomenclatura è decisamen-
te più efficace rispetto a "unsigned char".
Per quanto riguarda il tipo integer, esso permette di riferirsi ad una
locazione da 2 byte indifferentemente in modo totale oppure riferendo-
si ai singoli byte che lo compongono.
Il file di header bios.h include una funzione che permette la ge-
stione delle porte seriali, che si serve degli interrupt hardware.

unsigned _bios_serialcom(unsigned cmd, unsingned serialport, unsigned data);

29
Utilizzando la documentazione della funzione, è possibile scrivere
le routine di base di inizializzazione, invio e ricezione.
Tuttavia, per prima cosa, è necessario definire una routine per la
gestione dello stato della porta seriale, che renda possibile anche la
gestione di un timeout. In particolare, ci interesserà controllare lo stato
finché non si ottiene uno stato ben definito (ad esempio, prima di rice-
vere, aspetteremo che ci sia un nuovo dato), ma ovviamente fino ad un
tempo massimo prefissato. Ci serviremo delle funzioni di clock della
libreria time.h per gestire il clock.

Funzione di stato
bool rs232_status (unsigned int mask, byte port, int secs)
// 1 = Ok, 0 = Errore
{
// secs = 0 -> Ci provo una sola volta
// secs < 0 -> Ci provo all'infinito (Attenzione, la routine ha il controllo
del programma)
clock_t start, end;
start = end = clock();
integer result;
bool esci = false;
while ( ((end-start)/CLK_TCK <= secs || secs < 0) && !esci)
{
result.word = _bios_serialcom (_COM_STATUS , port, 0);
// In ESCI ho il valore, devo fare la maschera
result.word &= mask;
if (result.word == mask)
esci = true;
end = clock();
}
return esci;
}

A questo punto si possono definire anche le funzioni di inizializza-


zione, invio e ricezione.

Inizializzazione porta seriale


void rs232_initialize (byte protocol, int port)
{
_bios_serialcom (_COM_INIT , port, protocol);
}

La funzione di inizializzazione può fornire solamente errori nella


linea del modem: utilizzando noi una connessione Null – Modem, è
logico pensare che non vi saranno errori legati a questa periferica. Il
valore di ritorno della funzione, pertanto, può essere ignorato.

Invio di un byte
bool rs232_send (byte x, byte port, unsigned int secs)
// 1 = Errori, 0 = Ok
{

// Aspetto che eventuali trasferimenti in corso siano terminati


bool r = rs232_status (0x6000, port, secs);

bool exit_value;
// Se c'è stato un errore di timeout esco

30
if (r)
{
// Invio il carattere
integer rs_res;
rs_res.word = _bios_serialcom (_COM_SEND , port, x);

// Maschero per controllare eventuali timeout fisici


int mask = rs_res.byte.high & 0x80;

if (mask)
exit_value = true;
else
exit_value = false;
}
else
exit_value = true; // C'è stato un errore

return exit_value;

Questa funzione attende che eventuali invii precedenti siano termi-


nati, quindi tenta l'invio e restituisce l'esito dell'operazione.

Ricezione di un carattere
bool rs232_receive (byte* dato, byte port, int secs)
// 1 = Errori, 0 = Ok
{

integer result;

// Aspetto che ci sia un dato da leggere


bool s = rs232_status (0x0100,port,secs);

bool exit_value;
if (s)
{
// Non ci sono stati errori
result.word = _bios_serialcom (_COM_RECEIVE , port, 0);

// Maschero il risultato per vedere eventuali errori


int mask = result.byte.high & 0x8E;

if (mask)
exit_value = true;
else
exit_value = false;
}
else
exit_value = true;

// Imposto il dato ricevuto nel parametro di output


*dato = result.byte.low;
return exit_value;

In questa routine, si attende (nei limiti del timeout) che ci sia un


nuovo dato da leggere, quindi si procede con l'operazione e si restitui-
sce il dato letto, più il controllo degli errori.

31
I controlli effettuati in queste routine sono legati al modo di opera-
re della funzione di gestione della porta seriale (legata, a sua volta, agli
interrupt BIOS), quindi si rimanda alla documentazione della stessa
(allegata a questo documento) per maggiori dettagli.
Per semplificare il codice, infine, sono state definite delle costanti
che riassumono i valori necessari allo strato fisico e allo strato logico.

Costanti di sistema
// Secondi prima del timeout
#define RS232_TIMEOUT 5

// Definizione del protocollo di comunicazione utilizzato


#define RS232_PROTOCOL ( _COM_CHR8 | _COM_STOP1 | _COM_EVENPARITY | _COM_300 )

// Parole di comando
#define RS232_MASTER_TR 0x0B
#define RS232_SLAVE_TR 0x0C
#define RS232_START_TR 0x01
#define RS232_END_TR 0x04
#define RS232_CHECKSUM_OK 0x59
#define RS232_CHECKSUM_ERR 0x4E
#define RS232_ERROR 0x00

// Definizione della porta seriale


#define COM1 0
#define COM2 1
#define COM3 2
#define COM4 3

E' stata inoltre scritta una funzione di errore generico, che si oc-
cuperà di inviare allo Slave il codice di errore e di stampare a schermo
un messaggio di errore.

Routine di errore generico


void generic_error(char* msg, byte port)
{
// Manda uno 0 allo slave
byte command = 0;
rs232_send (command,port,0);
cout << "\a" << msg << "\a" << endl;
return;
}

Per quanto precedentemente assunto, si suppone che questa fun-


zione possa sempre essere eseguita, quindi si tralascia il ritorno di una
variabile di controllo errori.

5.3 Traduzione degli algoritmi


Una volta predisposte tutte le routine di gestione della porta seriale, è
sufficiente tradurre in codice gli algoritmi precedentemente progettati.
Una serie di condizioni annidate permetterà di ottenere un unico punto
in cui gestire gli errori.
Dovendo gestire le stringhe come strutture statiche (l'utilizzo di
strutture dinamiche complicherebbe il codice più di quanto in realtà sia
necessario), stabiliamo che la lunghezza massima sia di 255 caratteri.

32
In questo modo, anche un eventuale programma che si serva di inter-
rupt DOS per l'interazione esterna con l'utente potrà essere compatibile
con la struttura del programma.
Infine, è importante decidere come avverrà l'interazione con l'uten-
te: per comodità stabiliamo che l'utente potrà interagire con il pro-
gramma mediante la console. Riceverà messaggi a schermo e potrà in-
serire input da tastiera. Per la corretta gestione si utilizzeranno le fun-
zioni di gestione della console previste in C++ dalla classe iostream.

5.5 Software di supporto


Per testare il funzionamento del programma ci siamo serviti anzitutto
del debugger integrato nel software Turbo C++, che ci ha permesso di
eseguire ogni routine passo per passo e verificare quindi l'esito di ogni
frammento di codice.
Ci siamo serviti inoltre di PROCOM, un software testato e fun-
zionante per il trasferimento di dati attraverso la porta seriale. Una vol-
ta programmata la porta, il programma stamperà a schermo tutto ciò
che la porta riceve e invierà in output tutto ciò che viene digitato da ta-
stiera. Il software gestisce la porta in modo diretto, andando a lavorare
con i registri interessati, quindi il suo funzionamento è decisamente si-
curo e affidabile.

5.6 Codice completo del programma


Di seguito è riportato il codice intero del programma sviluppato.

File rs_master.cpp
#include <iostream.h>
#include <conio.h>
#include <dos.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <bios.h>
#include <stdio.h>

// Secondi prima del timeout


#define RS232_TIMEOUT 5

// Definizione del protocollo di comunicazione utilizzato


#define RS232_PROTOCOL ( _COM_CHR8 | _COM_STOP1 | _COM_EVENPARITY | _COM_300 )

// Parole di comando
#define RS232_MASTER_TR 0x0B
#define RS232_SLAVE_TR 0x0C
#define RS232_START_TR 0x01
#define RS232_END_TR 0x04
#define RS232_CHECKSUM_OK 0x59
#define RS232_CHECKSUM_ERR 0x4E
#define RS232_ERROR 0x00

// Definizione della porta seriale


#define COM1 0
#define COM2 1
#define COM3 2
#define COM4 3

33
// Definizione del dato booleano
enum bool { false = 0 , true };

// Definizione della word


union integer {
unsigned int word;
struct {
unsigned char low;
unsigned char high;
} byte;
};

// Definizione del dato byte


typedef unsigned char byte;

// FUNZIONI DI SISTEMA RS232

// Stato della porta


bool rs232_status (unsigned int mask, byte port, int secs = 0);

// Inizializzazione della porta


void rs232_initialize (byte protocol, int port);

// Invio di un byte
bool rs232_send (byte x, byte port, unsigned int secs);

// Ricezione di un byte
bool rs232_receive (byte* dato, byte port, int secs);

// Routine generica di invio errore allo slave


void generic_error(char* msg, byte port);

// Programma principale

int main ()
{

// Pulizia dello schermo


clrscr();

// Porta seriale utilizzata


int port = COM1;

// Protocollo utilizzato
byte protocol = RS232_PROTOCOL;

// Inizializzazione porta seriale


rs232_initialize(protocol,port);

// Carattere di comando e variabile di controllo


byte command;

// Carattere di eco e variabile di controllo


byte echo;

// Checksum e variabile di controllo


byte checksum;

// Esito della checksum e variabile di controllo


byte cs_return;

// Controllo del loop

34
int esci;

bool errors;

// Loop infinito
do {
// Trasmetto o ricevo?
char tr;
do {
cout << "[T]rasmettere o [R]icevere?";
cin >> tr;
tr = toupper (tr);
} while (tr != 'T' && tr != 'R');

if (tr == 'T')
{
char str[256]; // Massimo 255 caratteri
cout << "Dato da mandare: ";
gets(str);
int lung = strlen (str);

// Calcolo della checksum


checksum = 0;
for (register int i = 0; i < lung; ++i)
checksum ^= str[i];

// Voglio trasmettere
command = RS232_MASTER_TR;

if (!rs232_send(command,port,RS232_TIMEOUT))
{

// Attendo l'eco
if (!rs232_receive(&echo,port,RS232_TIMEOUT))
{

// Controllo l'eco
if (echo == RS232_MASTER_TR)
{
// Eco corretto
// Inizio trasmissione
command = RS232_START_TR;

if (!rs232_send(command,port,RS232_TIMEOUT))
{

// Attendo l'eco
if (!rs232_receive(&echo,port,RS232_TIMEOUT))
{
if (echo == RS232_START_TR)
{
// Eco corretto, mando il dato
for (register int j = 0; j < strlen (str) && error == false;
++j)
{
command = str[j];
if (rs232_send(command,port,RS232_TIMEOUT))
error = true;
}

if (error == false)
{
// Fine trasmissione

35
command = RS232_END_TR;

if (!rs232_send(command,port,RS232_TIMEOUT))
{
// Attendo l'eco
if (!rs232_receive(&echo,port,RS232_TIMEOUT))
{
if (echo == RS232_END_TR)
{
// Eco corretto
// Mando la checksum

if (!rs232_send(checksum,port,RS232_TIMEOUT))
{
if
(!rs232_receive(&cs_return,port,RS232_TIMEOUT))
{
if (cs_return == RS232_CHECKSUM_OK)
{
// Trasmissione corretta
cout << "Trasmissione corretta" << endl;
}

else
{
// Trasmissione errata
errors = true;
}
}
else
errors = true;
}
else
errors = true;
}
else
{
// Eco errato
errors = true;
}
}
else
errors = true;
}
else
errors = true;

}
else
{
// Eco errato
errors = true;
}
}
else
errors = true;

}
else
errors = true;
}

36
else
{
// Eco errato
if (echo == RS232_SLAVE_TR)
{
// Lo slave pensa di poter trasmettere!
// ...ma noi siamo più furbi!

// Ora facciamo un'imboscata allo slave...


// ... facciamo finta di accettare la sua trasmissione...
if (rs232_receive(&echo,port,-1))
{
// E ora possiamo impostare l'errore
errors = true;
}
}
else
errors = true;

}
else
errors = true;
}
else
errors = true;
}
else
{
// Master in ricezione
char str[256]; // Massimo 80 caratteri
int lung = 0; // Lunghezza della stringa ricevuta

// Calcolo della checksum


checksum = 0;

// Voglio ricevere
command = RS232_SLAVE_TR;

if (!rs232_send(command,port,RS232_TIMEOUT))
{
if (!rs232_receive(&echo,port,RS232_TIMEOUT))
{

// Controllo l'eco
if (echo == RS232_SLAVE_TR)
{
// Eco corretto, aspetto l'inizio della trasmissione
if (rs232_receive(&command,port,-1))
{

if (command == RS232_START_TR)
{
// Inizio trasmissione ricevuto, mando l'eco
echo = RS232_START_TR;
if (!rs232_send(echo,port,RS232_TIMEOUT))
{

// Echo mandato
// Ricevo il dato
byte data_rx;
do {
if (!rs232_receive(&data_rx,port,RS232_TIMEOUT))
{

37
// Vediamo se è fine trasmissione
if (data_rx != RS232_END_TR && data_rx != RS232_ERROR)
{
// E' un carattere
str[lung++] = data_rx;
checksum ^= data_rx;
}

else
{
if (data_rx == RS232_END_TR)
{
str[lung] = '\0';
}
}
}
else
errors = true;
} while (data_rx != RS232_END_TR && data_rx != RS232_ERROR
&& errors == false);

if (errors == false)
{
if (data_rx == RS232_END_TR)
{
// Trasmissione terminata, mando l'eco
echo = RS232_END_TR;
if (!rs232_send(echo,port,RS232_TIMEOUT))
{
// Ora ricevo la checksum
byte cs_received;
if (!rs232_receive(&cs_received,port,RS232_TIMEOUT))
{

if (cs_received == checksum)
cs_return = RS232_CHECKSUM_OK;
else
cs_return = RS232_CHECKSUM_ERR;

// Mando l'esito
if (!rs232_send(cs_return,port,RS232_TIMEOUT))
{

if (cs_return == RS232_CHECKSUM_OK)
{
// Trasmissione corretta
cout << "RICEVUTO: " << str << endl;
}
else
{
// Trasmissione errata
cout << "Trasmissione errata" << endl;
}

}
else
errors = true;

}
else
errors = true;
}
else

38
errors = true;
}
else
{
// C'Š stato un errore, bisogna tornare all'inizio
errors = true;
}

}
else
errors = true;
}
else
errors = true;

}
else
errors = true;
}
else
errors = true;

}
else
errors = true;
}
else
errors = true;

// Ci sono stati errori?


if (errors == true)
generic_error ("Errore nella comunicazione" , port);

cout << "Ancora? (S/N)";


char risp = getche();
if (risp == 's' || risp == 'S')
esci = 0;
else
esci = 1;

} while (!esci);

// Attesa del carattere


getch();
return 0;

bool rs232_status (unsigned int mask, byte port, int secs)


// 1 = Ok, 0 = Errore
{
// secs = 0 -> Ci provo una sola volta
// secs < 0 -> Ci provo all'infinito (Attenzione, la routine ah il controllo
del programma)
clock_t start, end;
start = end = clock();
integer result;
bool esci = false;
while ( ((end-start)/CLK_TCK <= secs || secs < 0) && !esci)

39
{
result.word = _bios_serialcom (_COM_STATUS , port, 0);
// In ESCI ho il valore, devo fare la maschera
result.word &= mask;
if (result.word == mask)
esci = true;
end = clock();
}
return esci;
}

void rs232_initialize (byte protocol, int port)


{
_bios_serialcom (_COM_INIT , port, protocol);
}

bool rs232_send (byte x, byte port, unsigned int secs)


// 1 = Errori, 0 = Ok
{

// Aspetto che eventuali trasferimenti in corso siano terminati


bool r = rs232_status (0x6000, port, secs);

bool exit_value;
// Se c'Š stato un errore di timeout esco
if (r)
{
// Invio il carattere
integer rs_res;
rs_res.word = _bios_serialcom (_COM_SEND , port, x);

// Maschero per controllare eventuali timeout fisici


int mask = rs_res.byte.high & 0x80;

if (mask)
exit_value = true;
else
exit_value = false;
}
else
exit_value = true; // C'Š stato un errore

return exit_value;

bool rs232_receive (byte* dato, byte port, int secs)


// 1 = Errori, 0 = Ok
{

integer result;

// Aspetto che ci sia un dato da leggere


bool s = rs232_status (0x0100,port,secs);

bool exit_value;
if (s)
{
// Non ci sono stati errori
result.word = _bios_serialcom (_COM_RECEIVE , port, 0);

// Maschero il risultato per vedere eventuali errori

40
int mask = result.byte.high & 0x8E;

if (mask)
exit_value = true;
else
exit_value = false;
}
else
exit_value = true;

// Imposto il dato ricevuto nel parametro di output


*dato = result.byte.low;
return exit_value;

void generic_error(char* msg, byte port)


{
// Manda uno 0 allo slave
byte command = 0;
rs232_send (command,port,0);
cout << "\a" << msg << "\a" << endl;
return;
}

41
6. Conclusioni
6.1 Conclusioni
La realizzazione di questo progetto si è rilevata ben più ostica di quan-
to ci si aspettasse: la porta seriale, gestita mediante gli interrupt BIOS
(come fanno le routine C++), è sicuramente di comprensione semplice,
ma il suo funzionamento non è costante e, addirittura, un programma
che funziona un giorno potrebbe non funzionare il giorno seguente.
Tuttavia, alla fine siamo riusciti a far funzionare adeguatamente il
programma in ogni situazione, cercando di eliminare le limitazioni su-
perflue e tentando di prevedere il maggior numero possibile di errori.
Il software finale risulta nel complesso funzionante e testato.
Grazie a questa prova abbiamo appreso l'importanza del controllo
di un processo complesso quale la comunicazione tra due sistemi: ogni
operazione deve essere rigorosamente controllata e ogni comando, per
maggiore sicurezza, deve essere seguito da un eco. Trasmettendo un
pacchetto di dati, inoltre, è necessario aggiungere un controllo sull'in-
tero pacchetto, controllo che noi abbiamo aggiunto sotto forma di che-
cksum.
Inoltre abbiamo completato il lavoro relativo alla trasmissione dati
iniziata in precedenza con la trasmissione parallela: è da segnalare che
la trasmissione seriale è decisamente più complessa da realizzare, in
quanto i controlli da eseguire sono superiori rispetto a quelli per la tra-
smissione parallela.
In conclusione, il progetto risulta completo, testato e funzionante.

6.2 Fonti
Informazioni sulla trasmissione seriale
ƒ www.wikipedia.org
ƒ Corso di Sistemi Volume 3
ƒ Presentazione Power Point (professoressa Amaroli)
ƒ www.giobe2000.it

Informazioni e guida di riferimento funzioni C++


ƒ www.cplusplus.com
ƒ http://www.cplusplus.com/ref/ctime/clock.html (funzione clock)
ƒ http://www.delorie.com/djgpp/doc/libc/libc_68.html (funzione _bios_serialcom)

42
7. Allegati
7.1 Documentazione _bios_serialcom()
_bios_serialcom
Syntax

#include <bios.h>

unsigned _bios_serialcom(unsigned cmd, unsingned serialport,


unsigned data);

Description

The _bios_serialcom routine uses INT 0x14 to provide serial communications services. The serial-
port argument is set to 0 for COM1, to 1 for COM2, and so on. The cmd argument can be set to one
of the following manifest constants:

_COM_INIT
Initialize com port (data is the settings)
_COM_RECEIVE
Read a byte from port
_COM_SEND
Write a byte to port
_COM_STATUS
Get the port status

The data argument is ignored if cmd is set to _COM_RECEIVE or _COM_STATUS. The data ar-
gument for _COM_INIT is created by combining one or more of the following constants (with the
OR operator):

_COM_CHR7 7 bits/character
_COM_CHR8 8 bits/character
_COM_STOP1 1 stop bit
_COM_STOP2 2 stop bits
_COM_NOPARITY no parity
_COM_EVENPARITY even parity
_COM_ODDPARITY odd parity
_COM_110 110 baud
_COM_150 150 baud
_COM_300 300 baud
_COM_600 600 baud
_COM_1200 1200 baud
_COM_2400 2400 baud
_COM_4800 4800 baud
_COM_9600 9600 baud

The default value of data is 1 stop bit, no parity, and 110 baud.

43
Return Value

The function returns a 16-bit integer whose high-order byte contains status bits. The meaning of the
low-order byte varies, depending on the cmd value. The high-order bits are as follows:

Bit Meaning if Set

15 Timed out
14 Transmission-shift register empty
13 Transmission-hold register empty
12 Break detected
11 Framing error
10 Parity error
9 Overrun error
8 Data ready

When service is _COM_SEND, bit 15 is set if data cannot be sent.

When service is _COM_RECEIVE, the byte read is returned in the low-order bits if the call is suc-
cessful. If an error occurs, any of the bits 9, 10, 11, or 15 is set.

When service is _COM_INIT or _COM_STATUS, the low-order bits are defined as follows:

Bit Meaning if Set

7 Receive-line signal detected


6 Ring indicator
5 Data-set-ready
4 Clear-to-send
3 Change in receive-line signal detected
2 Trailing-edge ring indicator
1 Change in data-set-ready status
0 Change in clear-to-send status

Portability

ANSI/ISO C No
POSIX No

Example

/* 9600 baud, no parity, one stop, 8 bits */


_bios_serialcom(_COM_INIT, 0,
_COM_9600|_COM_NOPARITY|_COM_STOP1|_COM_CHR8);
for(i=0; buf[i]; i++)
_bios_serialcom(_COM_SEND, 0, buf[i]);

44
7.2 Documentazione clock()
clock_t clock ( void );
Return number of clock ticks since process start.
Returns the number of clock ticks elapsed.
A macro constant called CLK_TCK defines the relation betwen clock tick and second (clock ticks
per second).

Parameters.

(none)

Return Value.
The number of clock ticks elapsed since start.
clock_t type is defined by default as long int by most compilers.

Portability.
Defined in ANSI-C.

45

Potrebbero piacerti anche