Lettura
dei dati di una porta |
E
finalmente arriviamo alle operazioni di lettura e scrittura
dalla e verso la porta seriale nPorta indicata dalla funzione
CreateFile. Sebbene questi due processi possano apparire simmetrici
non è soltanto la direzione nella quale si spostano
i bit ad essere differente.
Questo perchè il processo di trasmissione dei dati
ad una periferica è sincrono ossia è un flusso
continuo di dati. Visto che si conosce infatti a priori il
momento esatto in cui il segnale verrà attivato e la
mole di dati inviati, la trasmissione può procedere
senza interruzione.
In particolare quando si decide di inviare dati alla porta
seriale viene richiamata la funzione WriteFile che compie
tale operazione:
Al
contrario il processo di ricezione dei dati da una porta seriale
è sincrono perchè non si conosce il momento
preciso nel quale si attiverà il trasferimento dei
dati. Il flusso Xon/Xoff di cui si parlava nel terzo
articolo ossia l'invio di caratteri che consentono di attivare
o disattivare temporaneamente il flusso rende bene l'idea
di come questa sia un tipo di comunicazione segmentata.
Proprio
per ovviare a questo problema la struttura della comunicazione
seriale prevede due tecniche particolari che vengono adottate
per gestire il ricevimento dei dati.
La prima consiste nel controllare e monitorare periodicamente
la porta per determinare la presenza di eventuali nuovi dati
da ricevere oppure per verificare se c'è stata una
modifica nel segnale. Questa tecnica è molto utilizzata
per la sua semplicità d'uso sebbene dal lato opposto
richieda molta memoria per essere perfezionata (perchè
parte di essa deve essere costantemente utilizzata nel monitoraggio
della porta e quindi non è diponibile per il resto
dell'applicazione) e non permetta di controllare flussi di
dati molto veloci: questo implicherebbe una perdita di dati.
Il
secondo metodo, più complesso, consiste nel richiamare
la funzione di lettura dei dati (la ReadFile) solamente quando
l'hardware della periferica lo segnala generando un'interruzione
hardware.
In che cosa consiste un'interruzione hardware? Non
è niente di più di un segnale inviato verso
il computer ma proveniente dall'esterno proprio come accade
quando l'utente preme un tasto della tastiera o un pulsante
del mouse.
Quando il computer riceve questo segnale interrompe le operazioni
che stava compiendo oppure lo stato di attesa e trasferisce
il controllo ad una routine apposita che gestisce l'evento
segnalato (interrupt service routine):
Il
lato positivo di questa tecnica consiste nella possibilità
di occupare memoria solamente nel momento in cui è
effettivamente indispensabile ma dall'altro lato è
molto più difficile da sviluppare e da gestire soprattutto
in relazione alle operazioni di debug.
Vediamo quindi come gestire il primo metodo. Il progetto di
esempio può essere scaricato in formato compresso al
termine dell'articolo.
Prima di scrivere codice servirà inserire nel progetto
un controllo Timer per permettere il monitoraggio periodico
della porta ed un controllo TextBox per poter leggere i dati
ricevuti.
Al termine della routine Form_Load nella quale è stato
presentato lo stato della porta seriale si dovrà così
inserire la linea di codice che attiva il Timer:
Nella
routine Timer1_Timer() dovrà invece apparire il codice
che gestisce la ricezione dei dati. Innanzitutto ci si deve
occupare delle dimensioni del buffer nel quale archiviare
la serie di dati ricevuta.
Se si impostano tali dimensioni sul valore 10, significa che
si andranno a leggere dieci caratteri alla volta e quindi
ad una velocità pari a dieci volte la massima frequenza
di trasmissione di un singolo carattere:
Const
Lunghezza_Buffer = 10 |
Impostiamo
anche una seconda variabile che indica il totale dei dati
ricevuti:
Dim
Dati_Ricevuti As Long |
ed
un buffer di dieci byte nel quale saranno memorizzati i dati
che indica che andremo a leggere dieci caratteri alla volta
perciò dieci byte alla volta. Questo buffer è
comunemente chiamato FIFO (First In First Out)
termine che indica sostanzialmente una tecnica di memorizzazione
dei dati: semplicemente il carattere presente nel buffer da
più lungo tempo sarà quello prima recuperato.
Un buffer FIFO è dotato inoltre di due puntatori, uno
che indica il carattere da scrivere ed uno che indica la posizione
in cui scrivere il successivo carattere ricevuto. Questi due
puntatori determinano due dei parametri della funzione ReadFile
indicata poco sotto:
Quando
uno dei due puntatori raggiunge la lunghezza massima del buffer
(10 caratteri nel nostro caso), allora torna al primo carattere
(per questo è detto buffer in buffer out, perchè
il primo carattere ad entrare nel buffer è il primo
ad uscirne).
Nel caso in cui i due puntatori coincidano allora il buffer
risulterà completamente vuoto.
Dim
Carattere(1
To Lunghezza_Buffer) As
Byte
|
e
finalmente possiamo richiamare la funzione ReadFile
che come già detto recupera i dati trasmessi dalla
porta seriale. La sua dichiarazione è la seguente:
Declare
Function
ReadFile Lib "kernel32"
(ByVal hFile As
Long, lpBuffer As Any,
_ ByVal nNumberOfBytesToRead
As Long, lpNumberOfBytesRead As
Long, ByVal _ lpOverlapped
As Long) As
Long
|
dove
il primo parametro indica il riferimento al numero della porta
(nPorta, variabile che quindi dovrebbe essere dichiarata nella
sezione generale del codice visto che siamo al di fuori della
routine che l'ha definita), il secondo ossia lpBuffer
punta al buffer che contiene i dati memorizzati ancora da
leggere, nNumberOfBytesToRead indica il numero di byte
da leggere e pNumberOfBytesRead il numero di byte letti
(da impostare su 0).
Assegnando quindi a ciascuno di questi parametri il rispettivo
valore ed assegnando un valore di ritorno alla funzione si
ottiene:
vRitorno
= ReadFile(nPorta, Carattere(1), Lunghezza_Buffer, Dati_Ricevuti,
0) |
Siccome
non si conosce a priori se e quanti dati sono stati trasmessi
dalla periferica, si dovranno gestire due casi. Il primo di
essi si verifica se la variabile Dati_Ricevuti è vuota,
il secondo se è stata riempita coi dati ricevuti e
quindi risulta aver assunto un valore differente da 0:
If
Dati_Ricevuti <> 0 Then
|
Se
ciò si verifica allora si può andare ad inserire
ciascuno dei dieci caratteri all'interno del controllo TextBox
attraverso un semplice ciclo:
For
i = 1 To Dati_Ricevuti
Text1.Text = Text1.Text & Chr(Carattere(i))
Next i
End If
End Sub |
Scarica
qui
l'esempio completo per questo articolo.
|