Indice:
- Introduzione
- Introduzione alla comunicazione seriale
- Lo standard poco standard
- I segnali usati dalla RS-232
- Handshake, la sincronizzazione delle comunicazioni
- Cablaggi, cavi, connettori e connessioni
- Primi passi con il controllo MSCOMM
- L'evento OnComm() e la proprietà CommEvent
- L'Handshake DTR-DSR con l'MSCOMM
- L'Handshake XON-XOFF e RTS-CTS con l'MSCOMM
- Comunicazioni a pacchetto con l'MSCOMM
- Problemi riscontrabili con l'MSCOMM
- Conclusioni
Introduzione:
Usare la porta seriale del PC è da molti ritenuta cosa ostica, questo perché
spesso non si conosce il reale funzionamento della periferica, o non si è in
possesso delle conoscenze di base per gestire un protocollo di
comunicazione.
L'alone di mistero che circonda questa periferica è però del
tutto ingiustificato, la seriale può essere facilmente gestita con poche righe
di codice, e Visual Basic ci viene incontro fornendo l'oggetto MSCOMM con il
quale è possibile controllare efficacemente la RS-232.
Questo articolo tratta
solo delle connessioni tra dispositivi senza l'utilizzo di modem, ossia con
connessione diretta.
Introduzione alla
comunicazione seriale:
Una comunicazione seriale è uno scambio di dati tra due dispositivi. Queste
informazioni viaggiano sul cavo sotto forma di 1 e di 0, in modo appunto seriale
(sequenziale). Inviando per esempio il carattere esadecimale 26h, sulla seriale
verrà scomposto in bit 00100110, e questi bit verranno inviati, uno alla volta,
sul cavo seriale. Il tempo che intercorre tra l'invio di un bit ed il seguente è
appunto la velocità di trasmissione, ossia il baud rate, e viene espresso
in bit al secondo. La scomposizione dei caratteri in bit è trasparente al
sistema, chi invia i dati si limita a fornire i caratteri alla periferica, chi
li riceve si limiterà ad estrarre i caratteri ricevuti da una coda di
ricezione.
La seriale dei Personal Computers è di tipo asincrona, ossia
permette di trasmettere e ricevere contemporaneamente.
Lo standard poco
standard:
L' RS-232c è uno standard un po' particolare in quanto lascia molta libertà
sulla modalità di trasmissione dei caratteri.
Queste modalità di lavorare
rappresentano la configurabilità dell'hardware della RS-232c.
Vediamo quali
sono queste modalità:
Baud rate = è possibile indicare la velocità di trasmissione dei
bits. Si parte da 300 Bit/Sec fino ad arrivare a 115.200 Bit/Sec, e con i
nuovi chip, sia arriva fino a 921.600.
Numero di BIT = è possibile scegliere se inviare tutti gli 8 bit del
carattere, o escludere l'ultimo inviandone quindi solo 7. Se si usa la
modalità a 7 bit, non potranno essere inviati caratteri con valore superiore a
127 (7Fh). Se si tenta di inviare un carattere con valore maggiore a 127
usando la modalità a 7 BIT, il bit più significativo non verrà inviato (è
possibile avere un numero di bit inferiore a 7, ma di fatto sono modalità che
non vengono mai usate).
Parità = è possibile aggiungere ad ogni invio di carattere un bit
che indica se il numero di bit a 1 del carattere è pari o dispari. Questo può
sembrare assurdo, ma risulta utile per rilevare errori di trasmissione dovuti
a disturbi sulla linea, in quanto il ricevente riesegue il conto e paragona il
risultato al bit di parità ricevuto, se è diverso viene generato un errore
di parità. La parità può anche essere di tipo mark o space, nel primo caso
il bit di parità vale sempre 1, e nel secondo sempre 0.
Bits di stop = I bits di stop sono dei bit a 1 che vengono aggiunti
in coda ad ogni invio di carattere. Questi servono per marcare la fine del
treno di bit del carattere. E' possibile avere 1 o 2 Bits di stop (esiste
anche uno stop di 1,5 bits ma di fatto non viene mai usato)
Ecco i valori impostabili.
|
BaudRate (Bit/sec) |
Nr di BITs |
Parità |
Bits di stop |
|
110 |
4 |
Nessuna (non usato) |
1 |
|
300 |
5 |
Pari |
1,5 |
|
600 |
6 |
Dispari |
2 |
|
1200 |
7 |
Mark |
|
|
2400 |
8 |
Spazio |
|
|
4800 |
|
|
|
|
9600 |
|
|
|
|
19200 |
|
|
|
|
38400 |
|
|
|
|
57600 |
|
|
|
|
115200 |
|
|
|
A questo punto facciamo un esercizio che ci aiuta a capire quanti bits
vengono usati per inviare un carattere e le velocità di trasmissione. Per far
questo occorre ricordare che ogni invio di carattere viene preceduto da un bit
di start (a 0), e seguito da un numero variabile di bits di stop (a 1) in
funzione dell'impostazione dei Bits di stop.
Supponiamo di dover trasmettere un file di 10240 bytes, con la seriale
configurata come 9600,n,8,1 ossia Baudrate=9600, Parità=nessuna, Bits=8,
Stop=1.
Ogni carattere ci porta via 1 bit di start (fisso) + 8 bit di dati +
1 di stop, in tutto 10 bit. Quindi i bits totali da inviare sono 10240*10=102400
ed andando ad una velocità di 9600 bits al secondo ci impiegheremo 102400/9600 =
10,7 secondi.
Supponiamo ora di inviare sempre 10240 bytes, ma con la seriale configurata
come 4800,e,8,2 ossia Baudrate=4800, Parità=Pari, Bits=8, Stop=2.
Ogni
carattere ci porta via 1 bit di start (fisso) + 8 bit di dati + 1 di parità 2
bits di stop, in tutto 12 bits. Quindi i bits totali da inviare sono
10240*12=122880 ed andando ad una velocità di 4800 bits al secondo ci
impiegheremo 122880/4800 = 25,6 secondi.
Qundo si instaura una
comunicazione seriale è quindi indispensabile che la periferica
(l'hardware) sia impostata nel medesimo modo in entrambi i dispositivi. Questa
enorme quantità di combinazioni nella configurazione dell'hardware ha reso lo
standard molto versatile, ma ha avuto anche l'effetto di non essere mai "Plug
and Play". Per esempio, non è possibile collegarsi ad una bilancia senza sapere
come è stata configurata la sua seriale.
I segnali usati dalla
RS-232:
Adesso vediamo quali segnali (elettrici) vengono usati dalla RS-232, ossia i
segnali che troviamo sui connettori.
Iniziamo subito con una bella tabella di
tutti i segnali gestibili.
|
Segnale |
Nome segnale |
Tipologia (IN/OUT) |
|
RXD |
Ricezione dati |
IN |
|
TXD |
Trasmissione dati |
OUT |
|
RTS |
Richiesta di trasmissione (Request To Send) |
OUT |
|
CTS |
Consenso alla trasmissione (Clear To Send) |
IN |
|
DTR |
Il PC è pronto ad instaurare una comunicazione (Data
Terminal Ready) |
OUT |
|
DSR |
Il dispositivo remoto è pronto ad instaurare una
comunicazione (Data Set Ready) |
IN |
|
GND |
Ground (massa) dei segnali |
- |
I segnali RXD e TXD sono rispettivamente la ricezione e la trasmissione dei
dati, RTS, CTS, DTR e DSR sono usati per l'handshake ossia per la
sincronizzazione della comunicazione.
è da notare che i nomi assegnati ai
segnali di handshake sono in funzione al loro utilizzo per la connessione
ad un modem, ma dato che in questa sede analiziamo la connessione diretta senza
modem, il significato dei nomi è irrilevante.
Handshake, la
sincronizzazione delle comunicazioni:
In una comunicazione seriale è spesso necessario utilizzare metodologie di
sincronizzazione.
La sincronizzazione serve per evitare la perdita di dati
dovuta al fatto che la controparte non è pronta a riceverli. Questa situazione
può avvenire o perché il dispositivo che riceve non è operativo, o perché il
buffer di ricezione è pieno, e quindi non è più in grado di accettare altri
caratteri.
Un classico esempio di Buffer Overflow lo danno le stampanti, dove
la velocità di ricezione dei dati è superiore alla loro velocità di stampa, e
quindi, in presenza di grosse moli di stampa, hanno bisogno di segnalare al PC
di interrompere la trasmissione quando il buffer di ricezione è saturo e di
riprenderla quando il buffer si svuota.
Esistono due tipi di
sincronizzazione, DTR-DSR che si basa su segnali hardware e XON-XOFF che si basa
su un protocollo software.
DTR-DSR
Per spiegare come funziona l'handshake DTR-DSR è necessario
sapere che il DTR è un'uscita ed il DSR è un'ingresso, e che il DTR del primo
dispositivo è connesso al DSR del secondo, ed il DTR del secondo dispositivo è
connesso al DSR del primo. Per sincronizzare la comunicazione chi riceve deve
dare il consenso a chi trasmette, ossia alza il segnale di DTR (lo pone a 1)
cosicchè chi deve trasmettere sa che può farlo consultando lo stato del proprio
DSR.
Quando un dispositivo apre la porta di comunicazione, come prima cosa
deve alzare il segnale DTR (segnale in uscita) per informare la controparte che
è pronta a ricevere, e lo abbassa solo se il buffer di ricezione si satura. Di
conseguenza il DSR (segnale in ingresso) è sempre alto, ossia con il consenso a
trasmettere, e risulta basso solo se il ricevente è spento o non connesso, o
perché il buffer di ricezione del ricevente è saturo.
XON-XOFF
La sincronizzazione tramite XON-XOFF è
concettualmente simile alla precedente, però questa volta il consenso a
trasmettere viene dato inviando sulla seriale un carattere XON (11h), mentre per
fare interrompere la trasmissione viene invio un XOFF (13h).
Questo tipo di
sincronizzazione è molto comodo perché permette di usare cavi con soli tre fili
TXD, RXD e GND, (mentre con la sincronizzazione DTR-DSR servono 5 fili), ma ha
due controindicazioni:
- Obbliga l'utilizzo di protocolli di comunicazione
che non usino i caratteri XON (11h) e XOFF (13h), ossia protocolli ASCII,
escludendo quindi tutti i protocolli binari (i protocolli binari usano l'intera
gamma di caratteri disponibile da 0 a 255, mentre i protocolli ASCII usano i
caratteri dal 32 al 127 riservando i caratteri dallo 0 al 31 per la gestione del
protocollo).
- Può essere usato solo per connessioni spot, tipo scarico file,
e non da connessioni vive, in quanto se per qualche motivo, tipo disturbi o
reset di un dispositivo, non viene ricevuto l'XON o l'XOFF, si ha nel primo caso
un blocco delle comunicazioni (non avendo ricevuto l'XON il trasmittente non
trasmette più) e nel secondo caso la perdita di dati (il buffer è pieno ma il
trasmittente continua ad inviare i dati).
RTS-CTS
I segnali RTS-CTS generalmente vengono usati
unicamente col modem per forzare l'abilitazione del segnale di portante sul
doppino telefonico. In pratica il PC deve alzare il segnale RTS ed attendere che
il modem risponda alzano il segnale CTS del PC.
Generalmente nelle
connessioni tra dispositivi questi segnali vengono simulati creando un
ponticello, a livello del connettore del cavo seriale tra i pin dei segnali
RTS-CTS, di modo che quando il dispositivo alza il segnale RTS si ritrova
automaticamente il CTS attivo.
Cablaggi, cavi, connettori e connessioni:
Prima di iniziare l'analisi software, è utile sapere come vanno effettuati i
cablaggi.
Esistono due tipi di dispositivi seriali, il DTE (Data Terminal
Equipment) ed il DCE (Data Communication Equipment) o detto in altre parole, il
DTE è il PC ed il DCE è il modem. Questi due dispositivi hanno un connettore
differente, ossia cambia la posizione dei segnali sui pin del connettore. La
differenziazione tra DTE e DCE è stata fatta per utilizzare cavi seriali
pin-to-pin, infatti, per esempio, il segnale TXD (trasmissione dati) del PC è
sullo stesso pin del RXD (ricezione dati) del modem.
Se però si connettono
due PC tra loro, non è possibile usare un cavo pin-to-pin, ma occorre fare un
cavo particolare (incrociato), infatti se si usasse un cavo pin-to-pin il
segnale TXD del primo si connetterebbe al TXD del secondo, e la cosa è
ovviamente errata.
L'importante è sapere che i computers sono sempre dei DTE
e che le periferiche, tipo modem, stampanti, bilance ecc.., sono sempre dei
DCE.
Detto questo c'è anche da dire che esistono due standard di connettori,
quello a 25pin e quello a 9 pin.
Vediamo in dettaglio tutti i tipi di
connettore:
Connettori a 25 poli
Connettore DTE 25 poli maschio
(PC)
|
PIN |
SEGNALE |
NOME SEGNALE |
TIPO (IN/OUT) |
|
2 |
TXD |
Trasmissione Dati |
OUT |
|
3 |
RXD |
Ricezione Dati |
IN |
|
4 |
RTS |
Request To Send |
OUT |
|
5 |
CTS |
Clear To Send |
IN |
|
6 |
DSR |
Data Set Ready |
IN |
|
7 |
GND |
Ground (Massa segnali) |
- |
|
20 |
DTR |
Data Terminal Ready |
OUT |
Connettore DCE 25 poli femmina
(modem)
|
PIN |
SEGNALE |
NOME SEGNALE |
TIPO (IN/OUT) |
|
2 |
RXD |
Ricezione Dati |
IN |
|
3 |
TXD |
Trasmissione Dati |
OUT |
|
4 |
CTS |
Clear To Send |
IN |
|
5 |
RTS |
Request To Send |
OUT |
|
6 |
DTR |
Data Terminal Ready |
OUT |
|
7 |
GND |
Ground (Massa segnali) |
- |
|
20 |
DSR |
Data Set Ready |
IN |
Connettori a 9 poli
Connettore DTE 9 poli maschio
(PC)
|
PIN |
SEGNALE |
NOME SEGNALE |
TIPO (IN/OUT) |
|
2 |
RXD |
Ricezione Dati |
IN |
|
3 |
TXD |
Trasmissione Dati |
OUT |
|
4 |
DTR |
Data Terminal Ready |
OUT |
|
5 |
GND |
Ground (Massa segnali) |
- |
|
6 |
DSR |
Data Set Ready |
IN |
|
7 |
RTS |
Request To Send |
OUT |
|
8 |
CTS |
Clear To Send |
IN |
Connettore DCE 9 poli femmina
(modem)
|
PIN |
SEGNALE |
NOME SEGNALE |
TIPO (IN/OUT) |
|
2 |
TXD |
Trasmissione Dati |
OUT |
|
3 |
RXD |
Ricezione Dati |
IN |
|
4 |
DSR |
Data Set Ready |
IN |
|
5 |
GND |
Ground (Massa segnali) |
- |
|
6 |
DTR |
Data Terminal Ready |
OUT |
|
7 |
CTS |
Clear To Send |
IN |
|
8 |
RTS |
Request To Send |
OUT |
Primi passi con il controllo MSCOMM:
Con Visual Basic viene fornito un componente per la gestione della seriale,
l' MSCOMM.OCX.
L'MSCOMM dispone di diverse proprietà, di un solo evento (
OnComm() ) e nessun metodo.
Iniziamo col creare un nuovo progetto, quindi rendiamo disponibile il
controllo MSCOMM andando sul menù Progetto->Componenti, e selezionando
"Microsoft comm control x.x".
Aggiungete quindi il controllo, ora presente
nella barra degli strumenti, nel form (Form1) dando vita al controllo
MSComm1.
Per questioni didattiche preferisco configurare il controllo a
run-time, quindi nell'evento Form_Load() inserite quanto segue:
MSComm1.CommPort = 1 ' Selezioniamo la COM1
MSComm1.Settings = "9600,n,8,1" ' Le impostazioni della seriale
MSComm1.PortOpen = True ' Apriamo la porta.
Impostando la proprietà PortOpen a True si richiede al sistema
il permesso d' utilizzo della porta scelta (COM1). Se la porta è disponibile il
sistema operativo la assegnerà a noi, diversamente se la porta è già utilizzata
da un'altro processo verrà generato un errore intercettabile con l'istruzione ON
ERROR.
Per chiudere la porta e rilasciarne il controllo, è sufficiente
impostare PortOpen a False.
A questo punto siamo già in
grado di inviare dati sulla seriale, per esempio un classico "Ciao Mondo".
MSComm1.Output = "Ciao Mondo"
Se avete due PC ed un cavo seriale DTE-DTE (pin 2/3 girati) potete provare a
lanciare sul primo PC l'HyperTerminal e vedere comparire la scritta Ciao Mondo al lancio del programma dimostrativo. (è
possibile anche con un solo PC, basta far usare all'HyperTerminal la COM2, e
connettere il cavo tra COM1 e COM2).
Il codice si può migliorare eseguendo un controllo sugli errori.
MSComm1.CommPort = 1 ' Selezioniamo la COM1
MSComm1.Settings = "9600,n,8,1" ' Le impostazioni della seriale
On Error Resume Next ' Abilito l'intercettazione degli errori
MSComm1.PortOpen = True ' Apriamo la porta.
If Err Then ' se è accaduto un errore lo notifico all'utente
MsgBox "Impossibile aprire la COM" & MSComm1.CommPort & vbCrLf & Error$
End If
On Error GoTo 0
L'evento OnComm() e la
proprietà CommEvent:
Il controllo MSCOMM è asincrono, ossia non occorre che il programma dedichi
tutto il suo tempo a controllare ciò che accade sulla seriale, è il controllo
MSCOMM che si preoccupa di informare il programma di ciò che accade tramite il
richiamo dell'evento OnComm() e l'impostazione della proprietà
CommEvent. Quindi l'intera intelligenza della comunicazione è da
implementare nell'evento OnComm().
Quando viene richiamato l'evento
OnComm(), Nella proprietà CommEvents è riportato l'evento
accaduto. Esso può assumere uno dei seguenti valori (vedi l'help in linea del
VB):
Costanti relative agli errori |
| comEventBreak |
1001 |
Ricezione di un segnale di interruzione. |
| comEventFrame |
1004 |
Errore di frame. L'hardware ha individuato un errore di
frame. |
| comEventOverrun |
1006 |
Overrun della porta. L'hardware non ha letto un carattere
prima dell'arrivo del successivo e il carattere è andato
perduto. |
| comEventRxOver |
1008 |
Overflow del buffer di ricezione. Spazio esaurito nel
buffer di ricezione. |
| comEventRxParity |
1009 |
Errore di parità. È stato rilevato un errore di
parità. |
| comEventTxFull |
1010 |
Buffer di trasmissione pieno. Spazio esaurito nel buffer
di trasmissione durante il tentativo di inserimento di un
carattere. |
| comEventDCB |
1011 |
Errore imprevisto durante il recupero del DCB (Device
Control Block) della porta. |
|
Costanti relative agli eventi di seriale |
| comEvSend |
1 |
Nel buffer di trasmissione è presente un numero di
caratteri inferiore a quello definito dalla proprietà
Sthreshold. |
| comEvReceive |
2 |
Numero di caratteri Rthreshold ricevuti. Questo evento
viene generato fino a quando non si utilizza la proprietà Input per
rimuovere i dati dal buffer di ricezione. |
| comEvCTS |
3 |
Modifica nella linea CTS (Clear To Send). |
| comEvDSR |
4 |
Modifica nella linea DSR (Data Set Ready). Questo evento
viene generato solo quando DSR cambia da 1 a 0. |
| comEvCD |
5 |
Modifica nella linea CD (Carrier Detect). |
| comEvRing |
6 |
Individuato segnale telefonico. È possibile che alcuni
UART (universal asynchronous receiver-transmitters) non supportino questo
evento. |
| comEvEOF |
7 |
Ricevuto carattere indicatore di fine file (carattere
ASCII 26). |
Non è comunque indispensabile la consultazione della proprietà
CommEvent che personalmente uso solo nelle comunicazioni seriali
complesse; vediamo dunque come implementare una semplice ricezione dati dalla
seriale.
Come prima cosa è necessario dire al controllo MSCOMM che desideriamo essere
informati della ricezione dei caratteri, impostando la proprietà
Rthreshold a 1. Quindi l'apertura della porta la modifichiamo come
segue.
Private Sub Form_Load()
MSComm1.CommPort = 1 ' Selezioniamo la COM1
MSComm1.Settings = "9600,n,8,1" ' Le impostazioni della seriale
MSComm1.RThreshold = 1 ' voglio essere informato della ricezione di ogni singolo carattere
MSComm1.PortOpen = True ' Apriamo la porta.
End Sub
Aggiungete al Form un controllo testo Text1 (fatelo bello grande) ed
impostate la proprietà MultiLine a True.
A questo punto
aggiungete il seguente codice:
Private Sub MSComm1_OnComm()
Dim Rx$
Rx$ = MSComm1.Input ' Leggo il contenuto del buffer di ricezione (e svuoto .Input)
If Len(Rx$) Then ' Se ho ricevuto qualcosa lo scrivo nella TextBox
Text1.Text = Text1.Text & Rx$
End If
End Sub
Nella propriatà Input vengono riportati tutti i caratteri ricevuti. è
da notare che la proprietà Input la si può leggere una sola volta
in quanto dopo averla letta (ed in questo caso assegnata alla variabile Rx$)
viene automaticamente azzerata (="").
Per esempio il seguente codice NON
FUNZIONA!
If Len(MSComm1.Input) Then Text1.Text = Text1.Text & MSComm1.Input
perche
l' IF svuota il contenuto di Input ed alla text box viene appesa
una stringa vuota.
Impostando la proprietà Rthreshold a 1 imponiamo al controllo MSCOMM
di richiamare l'evento OnComm() alla ricezione di ogni singolo carattere.
In ogni caso, visto che la segnalazione non viene data in Interrupt-Time, lo
scatenarsi dell'evento OnComm() può avvenire in ritardo se il PC è
impegnato in qualche elaborazione, ed è quindi molto probabile che nella
proprietà Input sia contenuto più di un carattere.
Per poter anche
trasmettere, aggiungete il seguente codice:
Private Sub Text1_KeyPress(KeyAscii As Integer)
MSComm1.Output = Chr$(KeyAscii)
End Sub
Abbiamo così costruito con la TextBox un semplice terminale ASCII.
L'Handshake DTR-DSR
con l'MSCOMM:
L'MSCOMM non supporta l'handshake DTR-DSR, quindi occorre gestirlo a
mano.
Supponiamo di dover inviare un grosso file (50Kbytes) ad una stampante
seriale ad aghi. Se ci limitassimo ad aprire la COMx ed inviare l'intero file,
la stampante sarebbe costretta a porre tutti i dati ricevuti nel buffer di
ricezione, in quanto la sua velocità di stampa è nettamente inferiore alla
velocità di ricezione dei dati. Se quindi la stampante non ha un buffer
sufficientemente grande, perderebbe i dati in eccesso. Contate che una stampante
seriale raramente ha un buffer di ricezione più grande di 16Kbytes.
La
stampante, per risolvere il problema, quando è accesa e pronta a ricevere dati
alza il proprio segnale DTR (DSR del PC), e lo abbassa quando vuole interrompere
il flusso di dati in ingresso.
Quello che noi dobbiamo fare è monitorare il
segnale DSR ed interrompere il flusso di trasmissione quando il segnale si
abbassa, e riprenderlo quando il segnale si alza. Vediamo un esempio:
Const BLOCCO% = 448 ' Qta massima di bytes mantenuta nel buffer di trasmissione
Dim BufFile$ ' Buffer contenente i bytes da inviare alla stampante
Private Sub Form_Load()
MSComm1.Handshaking = comNone ' nessun handshake, xchè lo gestisco a mano
MSComm1.CommPort = 1 ' COM1
MSComm1.OutBufferSize = BLOCCO% + 64 ' 512
MSComm1.RThreshold = 1 ' in verità non serve, non dobbiamo ricevere alcunchè :-))
MSComm1.SThreshold = BLOCCO% - 64 ' Generare un evento di trasmissione ogni volta che sono stati trasmessi 64 bytes
MSComm1.Settings = "9600,n,8,1"
MSComm1.PortOpen = True
BufFile$ = ... .. . ' supponiamo un file da 50Kbytes
MSComm1.Output = Left$(BufFile$, 1) ' Innesco il SEND. Per fermarlo è sufficiente fare Buffer$=""
Buffer$ = Mid$(BufFile$, 2) ' elimino dal buffer il byte ormai inviato sulla seriale
End Sub
La costante BLOCCO% rappresenta la quantità di bytes che viene costantamente
mantenuta nel buffer di trasmissione. Tale costante è stata impostata su 448 il
che suppone che la stampante alzi il segnale di DTR quando il suo buffer non è
ancora completamente pieno, ma è in grado di accettare almeno altri 448 bytes
(il che dovrebbe essere sempre vero, al limite basta abbassare questo
valore).
Notare che l'impostazione dell'handshake è comNone in quanto
l'handshake lo gestiamo noi a mano, che ho limitato la dimensione del buffer di
trasmissione a 512 ossia lo stretto indispensabile, anzi, 64 bytes in più (non
si sa mai :-), e che ho impostato la soglia dell'evento di trasmissione a
BLOCCO% - 64 di modo da generarlo quando sono stati trasmessi 64 bytes (anche se
in verità, causa ritardi di processo, quando verrà generato l'evento di
trasmissione spesso saranno stati trasmessi più di 64 bytes).
Per innescare
la trasmissione viene inviato sulla seriale il primo byte del file da
trasmettere (BufFile$) in quanto questo genererà subito un evento di
soglia di trasmissione raggiunta (CommEvent=comEvSend; la soglia
l'abbiamo impostata a BLOCCO% - 64). E' la funzione posta nell'evento
OnComm() che provvederà al resto.
Vediamo quindi cosa scrivere
nell'evento OnComm().
Private Sub MSComm1_OnComm()
Dim C%
' Controllo se ho dati da inviare
If Len(BufFile$) Then
' controllo se la stampate è pronta
If MSComm1.DSRHolding = True Then
' mi assicuro che il buffer non sia già pieno
If MSComm1.OutBufferCount < BLOCCO% Then
' invio altri bytes fino a riempire il Buffer in TX con BLOCCO% bytes
C% = BLOCCO% - MSComm1.OutBufferCount
If C% > Len(BufFile$) Then C% = Len(BufFile$)
MSComm1.Output = Left$(BufFile$, C%)
BufFile$ = Mid$(BufFile$, C% + 1)
End If
End If
If Len(BufFile$) = 0 Then MsgBox "File trasmesso con successo"
End If
End Sub
Per come abbiamo configurato l'MSCOMM esso genererà l'evento di
OnComm() ogni volta che la quantità di bytes presenti nel buffer di
trasmissione scende sotto BLOCCO%-64, ed ogni volta che la linea DSR passa da
bassa ad alta (quando la linea passa da alta a bassa non viene generato alcun
evento).
In questo modo se la linea DSR è sempre attiva, la trasmissione è
continuamente alimentata dall'evento soglia di trasmissione raggiunto
(CommEvent=comEvSend), e viene interrotta se la linea DSR viene
abbassata. Quando viene rialzata viene generato un evento di DSR
(CommEvent=comEvDSR) e quindi la trasmissione riparte.
Se si
volesse sapere se la stampante è accesa e pronta a ricevere dati, è sufficiente
controllare lo stato della linea DSR subito dopo l'apertura della COMx, in
questo modo:
... . .
MSComm1.Settings = "9600,n,8,1"
MSComm1.PortOpen = True
If MSComm1.DSRHolding = False Then
MsgBox "Stampante non pronta!"
MSComm1.PortOpen = False
Exit Sub
End If
.. . .
Questo era un esempio di come tenere sotto controllo una trasmissione in
funzione dello stato del DSR. Per quanto riguarda il DTR, ossia quando è il
nostro programma che desidera interrompere il flusso di dati in ricezione, è
sufficiente lavorare sulla proprietà DTREnable, ponendola a True
quando siamo pronti a ricevere dati, e su False quando si desidera
interrompere il flusso di dati in ingresso. Se un controllo sul DTR non fosse
necessario, è buona norma tenere sempre il segnale DTR alzato
DTREnable=True.
L'Handshake XON-XOFF e RTS-CTS con l'MSCOMM:
Questi due handshake sono gestibili direttamente dal controllo MSCOMM
semplicemente impostando la proprietà Handshaking rispettivamente su
comXOnXOff per l'XON-XOFF e comRTS per RTS-CTS, si possono
abilitare anche entrambi con comRTSXOnXOff.
Comunicazioni a pacchetto con
l'MSCOMM:
Spesso capita di dover instaurare una comunicazione viva tra PC ed altri
dispositivi. In questi casi vengono usati veri e propri protocolli di
comunicazione più o meno complessi.
Un protocollo di comunicazione standard
non esiste, infatti fino ad oggi non ho mai incontrato due dispositivi diversi
usare lo stesso protocollo, quindi in questa sede mi limiterò a dei protocolli
immaginari. Sta a voi recuperare la documentazione sul protocollo usato dal
dispositivo a cui vi dovete connettere, che se non fornita insieme al
dispositivo, potrà essere reperita con una e-mail/telefonata al produttore.
Cos'è un pacchetto
dati:
Tutte le comunicazioni sono basate sul concetto di
pacchetto (alias record, alias datagram, alias
frame), ossia le informazioni vengono inviate e ricevute in modo da
distinguere l'inizio e la fine delle stesse. Le dimensioni dei pacchetti
dipendono dal protocollo di comunicazione, comunque in genere non superano i
1024 byte.
Possiamo ipotizzare di dover comunicare con uno strumento di
misura, chessò, una bilancia, e di usare un protocollo ASCII che adotta come
carattere di Start del pacchetto un "(" e come carattere di Stop
un ")". E' ovviamente importante che i caratteri di Start e di
Stop non siano usati all'interno dei pacchetti.
Se ricevessimo dalla
seriale la stringa "(12.76)" potrebbe voler dire che la bilancia sta pesando un
oggetto di 12.76 Kg.
Isolare i pacchetti ricevuti:
Quando siamo in
ascolto sulla seriale, riceviamo un carattere alla volta, quindi sta a noi
riuscire ad isolare i vari pacchetti in modo corretto.
Per far questo occorre
far uso di un buffer temporaneo nel quale vengono accodati tutti i dati in
ingresso, e ricercati ed isolati i pacchetti in esso contenuti.
Qui sotto è
riportato un esempio di funzione OnComm() che isola nel modo più corretto
i singoli pacchetti dati. Per capirne il funzionamento leggere attentamente i
commenti al codice.
Dim RxBuffer$
Private Sub MSComm1_OnComm()
Dim Pos As Integer
Dim Rx$
Dim Pacchetto$
' Estraggo i dati arrivati
Rx$ = MSComm1.Input
If Len(Rx$) = 0 Then Exit Sub
' Accodo i dati arrivati al buffer
RxBuffer$ = RxBuffer$ & Rx$
' Mi assicuro di avere il buffer allineato ai pacchetti
If Left$(RxBuffer$, 1) <> "(" Then
' Scarto tutto ciò che sta prima del primo carattere di START
' Questo capita quando si ricevono dei caratteri dovuti a disturbi sulla
' linea seriale, tutt'altro che rari! es: "ÿs2ÿÿ(12.7"
' oppure quando ho aperto la seriale (ed iniziato a bufferizzare i dati)
' a metà di un pacchetto dati in ricezione. es: "2.76)(13.16)"
Pos = InStr(RxBuffer$, "(")
RxBuffer$ = Mid$(RxBuffer$, Pos)
If Len(RxBuffer$) = 0 Then
' se il buffer è vuoto, tanto vale uscire
Exit Sub
End If
End If
' A questo punto controllo se sono arrivati dei pacchetti
' completi, li estraggo (tutti) e li elaboro
Do
Pos = InStr(RxBuffer$, ")")
If Pos = 0 Then Exit Do ' nessun'altro pacchetto completo, esco dal loop
' Estraggo il pacchetto, togliendo i caratteri di START e di STOP
Pacchetto$ = Mid$(RxBuffer$, 2, Pos - 2)
' Elimino il pacchetto dal buffer
RxBuffer$ = Mid$(RxBuffer$, Pos + 1)
' a questo punto se il buffer conteneva "(12.76)(13.16)",
' Pacchetto$="12.76" e RxBuffer$="(13,16)"
' Controllo se ci sono altri caratteri di START nel pacchetto.
' Infatti potrebbe capitare che un disturbo simuli un carattere
' di START ritrovando nel buffer qualcosa del tipo "(ÿ(12.75)"
' oppure un disturbo potrebbe aver eliminato un carattere di STOP,
' e quindi ritrovarci quancosa del tipo "(12.75ÿ(13.16) dove la "ÿ"
' sarebbe dovuta essere un ")" (in questo caso il primo pacchetto è perso)
Pos = InStr(Pacchetto$, "(")
If Pos Then
' esempio pacchetto contiene "12.75ÿ(13.16"
Pacchetto$ = Mid$(Pacchetto$, Pos + 1)
' ora Pacchetto$="13.16"
End If
' Il lavoro dello strato ISO/OSI di linea è compiuto,
' passo il pacchetto ricevuto al prossimo strato, che si preoccuperà di
' interpretare il significato dei dati ricevuti
Call ElaboraPacchetto(Pacchetto$)
Loop
End Sub
Verificare la correttezza dei
dati tramite i caratteri di controllo (CHECKSUM, XOR, CRC):
In
tutte le connessioni (anche quelle Ethernet) esistono disturbi sulla linea che
possono danneggiare i dati in transito.
Per riuscire ad identificare ed
eliminare i pacchetti danneggiati vengono attuate diverse strategie, alcune
delle quali, le più sofisticate, riescono perfino a ricostruire le parti
danneggiate.
Nel nostro caso, ci viene in aiuto il Bit di Parità che
ci segnala se un dato carattere ricevuto è stato danneggiato. Ma la parità
riesce a distinguere un errore solo nel 50% dei casi, senza contare che non è
detto che il dispositivo sia configurato per usare la parità.
Quindi il
metodo utilizzato per scovare i pacchetti danneggiati è quello di inserire nel
pacchetto stesso uno o più caratteri calcolati in funzione dei caratteri dei
dati del pacchetto stesso. In questo modo quando il ricevente riceve il
pacchetto, riesegue il calcolo basandosi sui dati ricevuti, e paragona il
proprio risultato con quello arrivato insieme al pacchetto; se sono uguali il
pacchetto è _quasi_ certamente integro, se sono diversi, il pacchetto è corrotto
e quindi da scartare.
I metodi base per calcolare i caratteri di controllo
sono tre: CHECKSUM, XOR, CRC.
Supponiamo quindi di inserire un carattere di controllo nel pacchetto dati,
in posizione fissa, per esempio in coda ai dati del pacchetto: "(12.75XX)" dove
la "XX" verrà sostituita con i caratteri di controllo.
Ecco un esempio semplice di implementazione della funzione
ElaboraPacchetto().
Function ElaboraPacchetto(Pacchetto$)
Dim Dati$
Dim chk$
' Separo i dati dal carattere di controllo
' (i caratteri di controllo sono gli utimi 2 della stringa)
Dati$ = Left$(Pacchetto$, Len(Pacchetto$) - 2)
chk$ = Mid$(Pacchetto$, Len(Pacchetto$) - 2, 2)
' Controllo se il pacchetto è corrotto
If chk$ <> CalcolaChecksum(Dati$) Then
' Errore, il pacchetto è corrotto
Exit Sub
End If
' Visualizzo il peso
Label1.Caption = Dati$
End Function
Ed ecco degli esempi di calcolo dei caratteri di controllo:
Function CalcolaChecksum(Dati$) As String
Dim ix As Integer
Dim Checksum As Integer
Checksum = 0 ' (sarebbe superfluo)
For ix = 1 To Len(Dati$)
Checksum = (Checksum + Asc(Mid$(Dati$, ix, 1))) Mod 256
Next ix
' Torna il valore esadecimale del carattere di controllo
CalcolaChecksum = Right$("00" & Hex$(Checksum), 2)
End Function
Function CalcolaXor(Dati$) As String
Dim ix As Integer
Dim Checksum As Integer
Checksum = 0 ' (sarebbe superfluo)
For ix = 1 To Len(Dati$)
Checksum = (Checksum Xor Asc(Mid$(Dati$, ix, 1)))
Next ix
' Torna il valore esadecimale del carattere di controllo
CalcolaXor = Right$("00" & Hex$(Checksum), 2)
End Function
Inviare un
pacchetto:
Inviare un pacchetto è molto più semplice rispetto al
riceverlo. Di seguito c'è un esempio di come può essere fatta una funzione di
invio dati.
Sub InviaDati(Dati$)
Dim Pacchetto$
Pacchetto$ = "(" & Dati$ & CalcolaChecksum(Dati$) & ")"
MSComm1.Output = Pacchetto$
End Sub
Problemi
riscontrabili con l'MSCOMM:
Il controllo MSCOMM ha un difetto sull'evento OnComm()
riscontrabile quando si sta facendo del DEBUG del proprio codice. Se ad esempio
si mette in pausa l'esecuzione del programma, e durante l'attesa giungono dei
caratteri alla seriale, al riavvio del programma non verrà generato alcun evento
per i caratteri già ricevuti.
Una scappatoia per ovviare al problema è quella
di aggiungere un TIMER, con una cadenza dell'ordine dei 250-400 mSec, che
si limita a richiamare la funzione MSComm1_OnComm(), compensando così
alla mancata generazione (in debug) dell'evento OnComm().
Esempio:
Private Sub Timer1_Timer()
Call MSComm1_OnComm
End Function
Conclusioni:
Non tutte le proprietà dell'oggetto MSCOMM sono state affrontate, per
il semplice motivo che il VB ha un ottimo help in linea dal quale poter
facilmente reperire e capire tutte le altre caratteristiche di questo
oggetto.
Spero che questo articolo vi sia servito a comprendere l'utilizzo
della seriale, abbattendo i timori nell'affrontare il mondo delle
comunicazioni.
A questo punto la palla è vostra, provate a mettere in pratica
i suggerimenti dati, e vedrete che usare la seriale non è affatto cosa
complessa.