Porte seriali (virtuali) e Visual Basic .NET

Per una mia apparecchiatura (oramai funzionante in diverse parti d' europa) mi è stato chiesto di implementare la funzione di lettura diretta da PC tramite USB.

Fino ad ora, infatti, la comunicazione avviene esclusivamente tramite memory card, che, comunque, rimane il metodo più veloce ed efficiente.

L'USB, che, di fatto, ha soppiantato completamente le porte seriali e parallele dei PC (sui portatili non li trovi più da oramai 4/5 anni, sul mio ultimo desktop sono ugualmente scomparse), è un bus Universale, come dice la U del suo nome.

Questo significa che può essere fatto funzionare in diversi modi, detti profili, i più comuni dei quali sono CDC (Traducendo male, Porta seriale virtuale), HID (Human identification device, ovvero tastiera, mouse o quant'altro), MSC (il profilo utilizzato dalle chiavette). Ne esistono, ovviamente, altri, ed ognuno ha i suoi vantaggi ed i suoi svantaggi.

Per la mia apparecchiatura ho implementato il protocollo CDC, quindi, ad installazione del driver completata, questa viene vista, di fatto, come una COMxx. Con il senno di poi mi accorgo che questo non è il profilo ottimale perché leggo e scrivo botte di 1MB, e, pertanto, sarebbe stato meglio utilizzare il profilo Mass Storage Device, ma adesso c'è questo e me lo ricorderò la prossima volta.

Usare una porta seriale in visual basic .net, o, più in generale, in ambiente .NET è decisamente semplice, ma va fatto con le opportune precauzioni e tenendo conto di eventuali "incroccamenti" del sistema che, in particolar modo con USB-CDC sono abbastanza frequenti.

La cosa più importante da tenere a mente è che i dati arrivano pochi per volta, con il loro tempo, pertanto eseguire comunicazioni con la seriale nel thread principale del programma è una cosa assolutamente da evitare.

La seconda è che, tranne casi particolari (vedi dopo), chi comanda la comunicazione è il PC. Ovvero il PC la inizia, la comanda e la chiude. Questo vale tanto più quando tu hai il controllo completo (ovvero sei tu che progetti) dell'apparecchiatura esterna. Il caso particolare (che comunque può essere gestito in maniera più generale) è quello del modem per la gestione delle chiamate entranti, per cui hai sulla seriale un bel "RING".

Nella versione più semplice la seriale può essere gestita con un bel BackgroundWorker, avendo cura di abilitargli la possibilità di aggiornare i progressi e di riportare al processo la cancellazione.

Riporto, nella versione più semplice, una parte del codice di lettura.

Si crea un nuovo progetto e si inserisce in una form un pulsante, un backgroundworker ed una progressbar.

Il codice che ho inserito io è:

Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
       Me.BackgroundWorker1.RunWorkerAsync()
  End Sub

   Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
       Dim seriale As New System.IO.Ports.SerialPort("COM12", 115200, IO.Ports.Parity.None, 8, 1)
       seriale.ReadBufferSize = 2000
       seriale.WriteBufferSize = 2000
       Dim sb As New StringBuilder()
       Dim c As String = ""
       Dim Contapv As Integer = 0
       Dim ContaR As Integer = 0
       Dim Trasmetti As Boolean = False
       Try
           Dim dt As New DateTime(DateTime.Now.Ticks)
           seriale.Open()
           seriale.Write(":S;")
           Conta = 0
           While (Conta < MaxCiclo)
               While seriale.BytesToRead > 0
                   c = Chr(seriale.ReadChar)
                   If c = ";" Then
                       Trasmetti = True
                       Contapv = Contapv + 1
                   Else
                       sb.Append(c)
                   End If
               End While
               Thread.Sleep(2)
               If Trasmetti = True Then
                   If Conta < MaxCiclo Then
                       ContaR = ContaR + 1
                       seriale.Write(":R;")
                       Thread.Sleep(10)
                       BackgroundWorker1.ReportProgress((Conta * 100) / MaxCiclo)
                       Trasmetti = False
                       Conta = Conta + 1
                   End If
               End If
           End While
       Catch ex As Exception
           MsgBox(ex.Message)
           If Not seriale Is Nothing Then
               If seriale.IsOpen Then
                   seriale.Close()
               End If
               Return
           End If
       End Try
       If seriale.IsOpen Then
           seriale.Close()
           BackgroundWorker1.ReportProgress((Conta * 100) / MaxCiclo)
           MsgBox("Finito" & DateTime.Now.Subtract(dt).TotalSeconds)
       End If
   End Sub

   Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
       Me.ProgressBar1.Value = e.ProgressPercentage
   End Sub

Ora, notare bene. Questo codice funziona SOLO per la mia apparecchiatura ma rende bene l'idea.
Manca, in questa versione, un controllo dello stallo della comunicazione, così come il CRC che, data la particolarità dell'applicazione, ho preferito tenere da parte.
Contrariamente a quanto direbbero i GURU ho deciso di non utilizzare gli eventi perchè, alla fine della fiera e dopo numerosi tentativi, ho trovato questo metodo meno efficiente.
Tanto per rendere l'idea, il trasferimento di 1MByte a 115Kbit avviene in 134 secondi, che è un buon risultato considerando l'handshake. 
I vari Thread.Sleep hanno sicuramente influito in questo risultato ma in maniera minimale.
Infine una piccola nota di colore.
La variabile locale sb era, in principio, una variabile di tipo "String" e si chiamava s.
Ebbene.
Usando s ed andando in append ad ogni lettura, il tempo di ciclo lievitava da 134 a... 690 secondi.
Anche i migliori prendono cantonate o fanno cavolate. Figuriamoci io!

posted @ mercoledì 15 giugno 2011 13:58

Print

Comments on this entry:

No comments posted yet.

Your comment:



 (will not be displayed)


 
 
 
Please add 1 and 4 and type the answer here:
 

Live Comment Preview: