Seriale - comunicazione porta seriale




Usare l' RS-232 da Visual Basic
v 1.11 21/02/2001
Ettore Maronese
http://www.it-lang-vb.net/Documentazione/Articoli/MSCOMM.htm


Indice:
Introduzione
Introduzione alla comunicazione seriale
Lo standard poco standard
I segnali usati dalla RS-232
Handshake, la sincronizzazione delle comunicazioni
DTR-DSR
XON-XOFF
RTS-CTS
Cablaggi, cavi, connettori e connessioni
Esempi di cablaggi seriali
Primi passi con il controllo MSCOMM
L'evento OnComm() e la proprieta' CommEvent
L'Handshake DTR-DSR con l'MSCOMM
L'Handshake XON-XOFF e RTS-CTS con l'MSCOMM
Comunicazioni a pacchetto con l'MSCOMM
Cos'e' un pacchetto dati
Isolare i pacchetti ricevuti
Verificare la correttezza dei dati tramite i caratteri di controllo (CHECKSUM, XOR, CRC)
Inviare un pacchetto
Problemi riscontrabili con l'MSCOMM
Conclusioni


Introduzione:

Usare la porta seriale del PC e' da molti ritenuta cosa ostica, questo perche' spesso non si conosce il reale funzionamento della periferica, o non si e' in possesso delle conoscenze di base per gestire un protocollo di comunicazione.
L'alone di mistero che circonda questa periferica e' pero' del tutto ingiustificato, la seriale puo' essere facilmente gestita con poche righe di codice, e Visual Basic ci viene incontro fornendo l'oggetto MSCOMM con il quale e' 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 e' 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 verra' 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 e' appunto la velocita' di trasmissione, ossia il baud rate, e viene espresso in bit al secondo. La scomposizione dei caratteri in bit e' trasparente al sistema, chi invia i dati si limita a fornire i caratteri alla periferica, chi li riceve si limitera' ad estrarre i caratteri ricevuti da una coda di ricezione.
La seriale dei Personal Computers e' di tipo asincrona, ossia permette di trasmettere e ricevere contemporaneamente.




Lo standard poco standard:

L' RS-232c e' uno standard un po' particolare in quanto lascia molta liberta' sulla modalita' di trasmissione dei caratteri.
Queste modalita' di lavorare rappresentano la configurabilita' dell'hardware della RS-232c.
Vediamo quali sono queste modalita':

Baud rate = e' possibile indicare la velocita' 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 = e' possibile scegliere se inviare tutti gli 8 bit del carattere, o escludere l'ultimo inviandone quindi solo 7. Se si usa la modalita' 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 modalita' a 7 BIT, il bit piu' significativo non verra' inviato (e' possibile avere un numero di bit inferiore a 7, ma di fatto sono modalita' che non vengono mai usate).

Parita' = e' possibile aggiungere ad ogni invio di carattere un bit che indica se il numero di bit a 1 del carattere e' pari o dispari. Questo puo' 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 parita' ricevuto, se e' diverso viene generato un errore di parita'. La parita' puo' anche essere di tipo mark o space, nel primo caso il bit di parita' 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
Parita'
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 velocita' 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, Parita'=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 velocita' 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, Parita'=Pari, Bits=8, Stop=2.
Ogni carattere ci porta via 1 bit di start (fisso) + 8 bit di dati + 1 di parita' 2 bits di stop, in tutto 12 bits. Quindi i bits totali da inviare sono 10240*12=122880 ed andando ad una velocita' di 4800 bits al secondo ci impiegheremo 122880/4800 = 25,6 secondi.

Qundo si instaura una comunicazione seriale e' quindi indispensabile che la periferica (l'hardware) sia impostata nel medesimo modo in entrambi i dispositivi. Questa enorme quantita' 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 e' possibile collegarsi ad una bilancia senza sapere come e' 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 e' pronto ad instaurare una comunicazione
(Data Terminal Ready)
OUT

DSR
Il dispositivo remoto e' 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.
e' 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 e' irrilevante.




Handshake, la sincronizzazione delle comunicazioni:

In una comunicazione seriale e' spesso necessario utilizzare metodologie di sincronizzazione.
La sincronizzazione serve per evitare la perdita di dati dovuta al fatto che la controparte non e' pronta a riceverli. Questa situazione puo' avvenire o perche' il dispositivo che riceve non e' operativo, o perche' il buffer di ricezione e' pieno, e quindi non e' piu' in grado di accettare altri caratteri.
Un classico esempio di Buffer Overflow lo danno le stampanti, dove la velocita' di ricezione dei dati e' superiore alla loro velocita' 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 e' 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 e' necessario sapere che il DTR e' un'uscita ed il DSR e' un'ingresso, e che il DTR del primo dispositivo e' connesso al DSR del secondo, ed il DTR del secondo dispositivo e' 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) cosicche' chi deve trasmettere sa che puo' 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 e' pronta a ricevere, e lo abbassa solo se il buffer di ricezione si satura. Di conseguenza il DSR (segnale in ingresso) e' sempre alto, ossia con il consenso a trasmettere, e risulta basso solo se il ricevente e' spento o non connesso, o perche' il buffer di ricezione del ricevente e' saturo.

XON-XOFF
La sincronizzazione tramite XON-XOFF e' concettualmente simile alla precedente, pero' 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 e' molto comodo perche' 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).
- Puo' 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 piu') e nel secondo caso la perdita di dati (il buffer e' 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, e' 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 e' il PC ed il DCE e' 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 e' stata fatta per utilizzare cavi seriali pin-to-pin, infatti, per esempio, il segnale TXD (trasmissione dati) del PC e' sullo stesso pin del RXD (ricezione dati) del modem.
Se pero' si connettono due PC tra loro, non e' 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 e' ovviamente errata.
L'importante e' sapere che i computers sono sempre dei DTE e che le periferiche, tipo modem, stampanti, bilance ecc.., sono sempre dei DCE.
Detto questo c'e' 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



Se si desidera avere una panoramica di come fare i cavi di connessione, fate un salto nella sezione Cablaggi Seriali.




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 proprieta', di un solo evento ( OnComm() ) e nessun metodo.

Iniziamo col creare un nuovo progetto, quindi rendiamo disponibile il controllo MSCOMM andando sul menu' 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 proprieta' PortOpen a True si richiede al sistema il permesso d' utilizzo della porta scelta (COM1). Se la porta e' disponibile il sistema operativo la assegnera' a noi, diversamente se la porta e' gia' utilizzata da un'altro processo verra' generato un errore intercettabile con l'istruzione ON ERROR.
Per chiudere la porta e rilasciarne il controllo, e' sufficiente impostare PortOpen a False.

A questo punto siamo gia' 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. (e' possibile anche con un solo PC, basta far usare all'HyperTerminal la COM2, e connettere il cavo tra COM1 e COM2).

Il codice si puo' 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 e' 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 proprieta' CommEvent:

Il controllo MSCOMM e' asincrono, ossia non occorre che il programma dedichi tutto il suo tempo a controllare cio' che accade sulla seriale, e' il controllo MSCOMM che si preoccupa di informare il programma di cio' che accade tramite il richiamo dell'evento OnComm() e l'impostazione della proprieta' CommEvent. Quindi l'intera intelligenza della comunicazione e' da implementare nell'evento OnComm().
Quando viene richiamato l'evento OnComm(), Nella proprieta' CommEvents e' riportato l'evento accaduto. Esso puo' 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 e' andato perduto.
comEventRxOver 1008 Overflow del buffer di ricezione. Spazio esaurito nel buffer di ricezione.
comEventRxParity 1009 Errore di parita'. È stato rilevato un errore di parita'.
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 e' presente un numero di caratteri inferiore a quello definito dalla proprieta' Sthreshold.
comEvReceive 2 Numero di caratteri Rthreshold ricevuti. Questo evento viene generato fino a quando non si utilizza la proprieta' 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 e' comunque indispensabile la consultazione della proprieta' CommEvent che personalmente uso solo nelle comunicazioni seriali complesse; vediamo dunque come implementare una semplice ricezione dati dalla seriale.

Come prima cosa e' necessario dire al controllo MSCOMM che desideriamo essere informati della ricezione dei caratteri, impostando la proprieta' 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 proprieta' 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 propriata' Input vengono riportati tutti i caratteri ricevuti. e' da notare che la proprieta' Input la si puo' 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 proprieta' 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() puo' avvenire in ritardo se il PC e' impegnato in qualche elaborazione, ed e' quindi molto probabile che nella proprieta' Input sia contenuto piu' 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 cosi' 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 velocita' di stampa e' nettamente inferiore alla velocita' 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 piu' grande di 16Kbytes.
La stampante, per risolvere il problema, quando e' 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 e' 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, xche' lo gestisco a mano
MSComm1.CommPort = 1 ' COM1
MSComm1.OutBufferSize = BLOCCO% + 64 ' 512
MSComm1.RThreshold = 1 ' in verita' non serve, non dobbiamo ricevere alcunche' :-))
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 e' sufficiente fare Buffer$=""
Buffer$ = Mid$(BufFile$, 2) ' elimino dal buffer il byte ormai inviato sulla seriale
End Sub
La costante BLOCCO% rappresenta la quantita' di bytes che viene costantamente mantenuta nel buffer di trasmissione. Tale costante e' stata impostata su 448 il che suppone che la stampante alzi il segnale di DTR quando il suo buffer non e' ancora completamente pieno, ma e' 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 e' 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 piu' (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 verita', causa ritardi di processo, quando verra' generato l'evento di trasmissione spesso saranno stati trasmessi piu' di 64 bytes).
Per innescare la trasmissione viene inviato sulla seriale il primo byte del file da trasmettere (BufFile$) in quanto questo generera' 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 provvedera' 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 e' pronta

If MSComm1.DSRHolding = True Then
' mi assicuro che il buffer non sia gia' 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 generera' l'evento di OnComm() ogni volta che la quantita' 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 e' sempre attiva, la trasmissione e' 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 e' accesa e pronta a ricevere dati, e' 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 e' il nostro programma che desidera interrompere il flusso di dati in ricezione, e' sufficiente lavorare sulla proprieta' 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, e' 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 proprieta' 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 piu' 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 limitero' 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, potra' essere reperita con una e-mail/telefonata al produttore.

Cos'e' 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, chesso', 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 e' riportato un esempio di funzione OnComm() che isola nel modo piu' 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 cio' 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 meta' 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 e' 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 e' 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 e' compiuto,

' passo il pacchetto ricevuto al prossimo strato, che si preoccupera' 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 piu' sofisticate, riescono perfino a ricostruire le parti danneggiate.
Nel nostro caso, ci viene in aiuto il Bit di Parita' che ci segnala se un dato carattere ricevuto e' stato danneggiato. Ma la parita' riesce a distinguere un errore solo nel 50% dei casi, senza contare che non e' detto che il dispositivo sia configurato per usare la parita'.
Quindi il metodo utilizzato per scovare i pacchetti danneggiati e' quello di inserire nel pacchetto stesso uno o piu' 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 e' _quasi_ certamente integro, se sono diversi, il pacchetto e' 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" verra' 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 e' corrotto

If chk$ <> CalcolaChecksum(Dati$) Then
' Errore, il pacchetto e' 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
Per quanto riguarda il CRC, rimando all'esempio presente nella sezione Sorgenti del Sito Comune del NG it.comp.lang.visual-basic http://www.murialdo.it/it_lang_vb.


Inviare un pacchetto:
Inviare un pacchetto e' molto piu' semplice rispetto al riceverlo. Di seguito c'e' un esempio di come puo' 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 verra' generato alcun evento per i caratteri gia' ricevuti.
Una scappatoia per ovviare al problema e' quella di aggiungere un TIMER, con una cadenza dell'ordine dei 250-400 mSec, che si limita a richiamare la funzione MSComm1_OnComm(), compensando cosi' alla mancata generazione (in debug) dell'evento OnComm().
Esempio:

Private Sub Timer1_Timer()
Call MSComm1_OnComm
End Function



Conclusioni:

Non tutte le proprieta' 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 e' vostra, provate a mettere in pratica i suggerimenti dati, e vedrete che usare la seriale non e' affatto cosa complessa.


Ettore Maronese By -MES-
wm-mes@maronese.com
http://www.maronese.com/mes













( serialecomunicazioneportaseriale.html )- by Paolo Puglisi - Modifica del 17/12/2023