Get Adobe Flash player

Guida all'utilizzo dell'USB con Mikrobasic e Visual Basic 2008

User Rating:  / 5
PoorBest 

Link Originale Articolo: http://www.grix.it/viewer.php?page=9263

Attenzione, per visionare l' articolo sulla pagina originale è necessario essere registrati a grix.it

 

Buonasera a tutti, questo che voglio proporvi, non è una delle solite guide che mostra i vari passaggi per poter realizzare un circuito, un firmware o un software. Questa che sto per mostrarvi è un'ampia trattazione della comunicazione USB (o meglio del controller HID) che avviene tra un semplicissimo microcontrollore e un computer. Questa guida vuole essere un punto di riferimento per chi come me si è voluto interessare al mondo della comunicazione USB; ad esempio, se volessi iniziare, dove guardo? cosa devo cercare? Ma funzionerà questo che sto provando? Ma questi esempi non li capisco e poi c'è solo l'hex, dove trovo il progetto completo e commentato? E se volessi cambiare questa cosa funzionerà?...Ecco questa guida vuole essere una risposta COMPLETA a tutte queste domande! ;)

Di cosa abbiamo bisogno
 
 
Innanzitutto, se state leggendo questa guida, molto probabilmente starete usando un computer. Questa è la prima cosa di cui abbiamo bisogno :). Dopo di che servono:
- un PIC18F2550 della microchip acquistabile su ebay o in qualsiasi negozio di elettronica con il quarzo da 20Mhz;
- un programmatore, che può essere seriale, parallelo o usb, non ha importanza;
- il software Mikrobasic Pro for PIC V. 4.60, di cui se ne può scaricare una demo direttamente dal sito di MikroElektronika, ma per quello che dobbiamo fare noi bisogna avere la versione completa;
- il software Visual Basic .NET 2008 Express Edition, liberamente scaricabile dal sito della Microsoft;
- la libreria mcHID.dll che sarà quella che gestirà tutte le comunicazioni tra il pic e il pc;
- pochissimi altri componenti tranquillamente reperibili (resistenze, condensatori, led, pulsanti, cavi, ecc...).
 
 
 
 
Circuito Base


Bene. Cominciamo a parlare del microcontrollore. Quello da me utilizzato, come detto sopra, è un PIC18F2550, ovvero, un pic a 28 pin che ha già incorporato tutto il necessario per essere collegato ad un pc tramite l'USB; questa è una caratteristica molto importante, perchè ormai, come per la porta parallela, anche la seriale sta scomparendo con i nuovi computer e quindi bisogna imparare a sfruttare questa potente tecnologia. Per poter collegare il microcontrollore al pc c'è bisogno di un semplicissimo cavo USB che farà da cavo di comunicazione e da alimentazione, una resistenza da 10kohm che bisognerà collegare al pin MCLR del pic (pin 1) ed a Vcc, un condensatore da 220nF, o anche 2 da 100nF, da collegare al pin VUsb (pin 14) e a massa per poter stabilizzare la tensione del regolatore interno dell'USB ed il solito oscillatore con i 2 condensatori da 22pF che vanno verso massa; l'oscillatore va collegato tra i pin OSC1 (pin 9) e OSC2 (pin 10) del pic, ed infine ovviamente ci va collegata l'alimentazione: Vcc al piedino 20 e GND al piedino 19. Lo schema quì di seguito mostrerà con più chiarezza quanto detto fin'ora:


clicca per ingrandire



clicca per ingrandire

Il connettore formato da 5 contatti è quello che serve per poter programmare il pic. Io come programmatore uso il pickit2, che personalmente trovo stupendo :).

Ora che abbiamo creato la parte hardware, non ci resta che programmarla tramite il connettore ICSP. Bene, per fare ciò abbiamo bisogno del programmatore, del software che comunica con il programmatore, e del compilatore. Come compilatore ho utilizzato Mikrobasic Pro for PIC V.4.60 che include anche un programmino in grado di creare un file contenente le funzioni e le variabili che servono al pic per comunicare attraverso l'USB. Quì di seguito vi mostrerò il firmware, che si trova anche nell'help del compilatore:

program usb_base

 '***************************************************
 ' Questo firmware riceve via usb un pacchetto dati
 ' di 64byte e lo riinvia al PC
 '***************************************************
 
dim cnt as char
dim readbuff as char[64] absolute 0x500           ' I Buffer dovrebbero essere nella RAM, Prego di consultare i datasheet
dim writebuff as char[64] absolute 0x540

sub procedure Interrupt()
   USB_Interrupt_Proc()                           ' Il servizio USB viene eseguito dall'interrupt
end sub

main:
  ADCON1 = ADCON1 or 0x0F                         ' Configura tutte le porte che hanno la funzione analogica come digitali
  CMCON  = CMCON or 7                             ' Disabilita i comparatori

  HID_Enable(@readbuff,@writebuff)                ' Abilita la comunicazione HID

  while TRUE
    while(HID_Read() = 0)                         ' Riceve i dati dal PC
    wend

    for cnt=0 to 63                               ' Assegna ogni byte ricevuto al buffer da inviare
      writebuff[cnt] = readbuff[cnt]
    next cnt

    while(HID_Write(@writebuff,64) = 0)           ' Invia il pacchetto di dati al PC
    wend
  wend
end.

Come si può notare dal programma, tutte le funzioni associate all'usb vengono gestite dall'interrupt. Questo firmware non fa altro che ricevere un pacchetto di dati composto da 64byte, e riinviarlo al pc tale quale come la ricevuto. E' un ottimo esempio per far capire come funziona il protocollo di comunicazione del controller HID. Vorrei fare notare che il ciclo "while((HID_Read)=0)" viene eseguito all'infinito se il pic non riceve nessun dato dal pc; quindi nel momento in qui il pc invia dei dati, l'esecuzione del firmware procede con la funzione successiva. Il pacchetto dati formato da 64byte arriva al firmware direttamnte di tipo char, quindi se volessimo potremmo stamparlo subito su un lcd ad esempio, se dal pc invieremo "ciao", i dati che leggeremo nel buffer saranno readbuff[0]="c", readbuff[2]="i", readbuff[3]="a", readbuff[4]="o"; molto semplice no? :D<br /></span></p> <p style="text-align: left;">Prima ho parlato di un programmino incluso in Mikrobasic Pro, ebbene, questo programmino si chiama HID Descriptor, e genera un file in cui saranno contenute tutte le variabili e le funzioni che servono al pic per far funzionare la comunicazione USB; proprio per questo il file generato da questa applicazione va incluso nel progetto insieme al resto dei file. Con questa semplice applicazione si potranno decidere il VendorID e il ProductID che vorremo dare al nostro apparecchio USB per essere riconosciuto dalla nostra applicazione, la dimensione del buffer in entrata e in uscita espressa in byte, e altri parametri meno importanti. In questo caso ho scelto 64byte come dimensione del buffer sia in entrata che in uscita, e infatti all'inizio del nostro firmware si può notare come sia "readbuff" e "writebuff" siano di 64byte.

Un'ultima cosa da settare sono le impostazioni del firmware, ovvero, il PLL Prescaler, il Postscaler, la sorgente clock per l'USB, l'Oscillatore, e il regolatore di tensione dell'USB; nel mio caso, quarzo di 20Mhz, andranno configurati in questo modo:

PLL Prescaler = Divide by 5 (20Mhz Input)
CPU System Clock Postscaler = [OSC1/OSC2 Src: /1][96Mhz PLL Src/2]
Full-Speed USB Clock Source Selection = Clock Src from 96Mhz PLL/2
Oscillator = HS: USB-HS
USB Voltage Regulator = Enabled


Ora non resta altro che compilare il firmware, trasferire l'hex al pic tramite il software che comunica con il programmatore ed inserire il cavo usb collegato al pic nel computer. Se è andato tutto bene si vedrà subito la notifica di windows che annuncia l'installazione del driver di quella periferica. In teoria dovrebbe installarla come periferica di input USB, ma non si sa mai, con windows :D.

Bene, la parte hardware è completata! Adesso manca la parte Software! Per la parte software ho utilizzato Visual basic .NET 2008 Express edition che per nostra fortuna è gratis! Intanto devo mettere in chiaro una cosa: per permettere la comunicazione USB c'è stata una persona, o un gruppo di persone, che si sono fatti un mazzo così per poter sfornare la libreria mcHID.dll e mi sembra giusto ringraziarle. Dunque, come già detto, il software lato pc utilizza una libreria che gestisce tutta la comunicazione, e grazie ad un modulo saremo in grado di comunicare con l'apparecchio USB con una manciata di funzioni semplicissime. Quello di seguito è un file di testo che spiega il funzionamento completo della libreria mcHID.dll, tramite il modulo mcHIDInterface.vb:

##################################################
Visual Basic 2005 HID Functions
Compilato e modificato da Amr Bekhit
Trattato e tradotto da Angelo Marchì(Acromangelo)
##################################################

-----------------------------
--Come usare questa libreria
-----------------------------

Prima di usare questo codice, bisogna accertarsi che il file mcHID.dll sia presente o nella cartella "System32" del pc
oppure nella stessa cartella dove si trova l'applicazione. Se si pensa di distribuire questa applicazione, il file mcHID.dll
deve essere incluso nell'applicazione per far si che esso funzioni.

E' molto semplice utilizzare questo codice. Prima di tutto vanno settate tutte le seguenti variabili con i loro valori corretti,
che sono determinate dall'hardware connesso via USB:

- VendorID
- ProductID
- BufferInSize
- BufferOutSize

Le variabili possono essere trovate all'inizio del form Main.

Se hai bisogno di un codice che venga eseguito nel momento in cui connetti o disconnetti il tuo apparecchio USB, bisogna semplicemente
posizionare il frammento di codice rispettivamente negli eventi "OnPlugged" e "OnUnplagged" nel form Main.

Quando l'apparecchio USB manda dati al pc, sarà chiamato l'evento "OnRead" e l'array "BufferIn" verrà riempito con i dati ricevuti.
Bisogna prendere nota che i dati ricevuti partono da "BufferIn(1)" e finiscono a "BufferIn(64)". "BufferIn(0)" non è usato.

Se si vuole trasmettere dei dati all'apparecchio USB, bisogna semplicemente riempire l'array "BufferOut con i dati, e infine
eseguire la funzione "hidWriteEx" per trasmettere tutto all'apparecchio USB. Bisogna prendere nota che i dati trasmessi iniziano
da "BufferOut(1)" e finiscono a "BufferOut(64)". "BufferOut(0)" deve essere settato a 0.

Se si vuole integrare questo codice dentro un'applicazione già esistente, bisogna aggiungere il file "mcHIDInterface.vb" al
progetto già esistente e copiare il codice che si trova nel form Main, dentro il form del progetto già esistente, che eseguirà la
comunicazione USB

Per i sistemi operativi a 64bit bisogna fare una piccola modifica nelle opzioni di Visual Basic 2008, perchè la libreria mcHID.dll
è a 32bit e può essere inclusa solo in applicazioni a 32bit. Nel cazo in cui si sta eseguendo il compilatore in un sistema operativo
a 64bit basta seguire le seguenti istruzioni:
    - Strumenti > Opzioni > Progetti e soluzioni > Attiva "Mostra configurazioni della build avanzate"
    - Compila > Gestione configurazione...
    - Alla riga del tuo progetto > Colonna "Configurazione" > Seleziona "Release".
    - Alla riga del tuo progetto > Colonna "Piattaforma" > Seleziona "<new>"(che aprirà una nuova finestra) > Nuova piattaforma > Seleziona "x86" > Premi Ok
    - Clicca chiudi nella Gestione configurazione e ricompila il progetto.

Sempre se verrà usato un sistema operativo a 64bit la libreria mcHID.dll dovrà essere copiata anche nella cartella di sistema "SysWOW64" del pc.

-----------------------------
--Variabili
-----------------------------

Quì di seguito c'è una breve descrizione delle variabili usate nel form Main che permettono la comunicazione USB:

- VendorID
    Variabile Integer che specifica il VendorID dell'apparecchioUSB.

- ProductID
    Variabile Integer che specifica il ProductID dell'apparecchioUSB.
    
- BufferInSize
    Variabile Integer(maggiore di zero) che specifica la dimensione, in byte, del pacchetto di dati che l'apparecchio USB trasmette al PC.

- BufferOutSize
    Variabile Integer(maggiore di zero) che specifica la dimensione, in byte, del pacchetto di dati che il PC trasmette all'apparecchio USB.

- BufferIn()
    Array di byte che contiene il pacchetto di dati ricevuto dall'apparecchio USB. Il primo byte dell'array, "BufferIn(0)",
    non è usato. I dati inizieranno da "BufferIn(1)".

- BufferOut()
    Array di byte che contiene il pacchetto di che verrà trasmesso dall'apparecchio USB. Il primo byte dell'array, "BufferOut(0)",
    deve sempre essere 0. I dati inizieranno da "BufferOut(1)".

-----------------------------
--Funzioni
-----------------------------

Quì di seguito c'è un riassunto delle funzioni più importanti che permettono le comunicazioni USB:

- hidWriteEx(ByVal pVendorID As Integer, ByVal pProductID As Integer, ByRef pData As Byte) As Boolean
    Questa funzione viene usata per inviare i dati all'apparecchio USB. Il suo utilizzo è molto semplice: L'array "BufferOut"(Vedi sopra)
    viene riempito con i dati che devono essere inviati(assicurati di aver settato "BufferOut(0)=0"). Dopo aver fatto ciò
    la funzione viene richiamata in questo modo:

    hidWriteEx(VendorID, ProductID, BufferOut(0))
    
    VendorID, ProductID e BufferOut sono dichiarati tutti all'inizio del form Main.

-----------------------------
--Eventi
-----------------------------

Quì di seguito c'è un riassunto degli eventi più importanti che permettono la comunicazione USB:

- Form1_Load
    Nel momento in cui il form sta caricando, "ConnecttoHID" viene chiamato per inizializzare le funzioni USB.
    Questo comando deve esserci necessariamente, per permettere il giusto funzionamento della comunicazione USB.
    
- Form1_FormClosed
    Nel momento in cui si sta chiudendo il form, qualsiasi connessione USB viene scollegata.
    
- OnPlugged
    Questa funzione viene richiamata quando l'apparecchio viene inserito nella porta USB.
    
- OnUnplugged
    Questa funzione viene richiamata quando l'apparecchio viene disinserito nella porta USB.
    
- OnChanged
    Non si è sicuri di quello che fa, ma non c'è bisogno di perdere tempo con essa per usare l'USB. Basta lasciarla stare che
    funzionerà tutto.
    
- OnRead
    Questa funzione viene chiamata quando l'apparecchio USB invia dei dati al PC. I dati vengono messi dentro l'array "BufferIn"
    dichiarata all'inizio del form.

Ho tradotto questa mini guida e l'ho inclusa in tutti i progetti che ho sviluppato così da facilitare lo sviluppo di qualsiasi tipo di applicazione, avendo sempre a portata di mano la guida. Ho aggiunto io la sezione del del problema del sistema operativo a 64bit che non c'era. Alla fine il vostro progetto dovrà avere il form principale e il modulo che gestisce la libreria. Questo di seguido è il codice del form principale; textbox1 è dove inserisco la frase da inviare, textbox2 è dove scrivo i dati ricevuti e button1 è il pulsante per inviare i dati:

Public Class Main
    ' VendorID e ProductID
    Private Const VendorID As Short = &H1234    ' Sostituisci con il ProductID e
    Private Const ProductID As Short = &H1      ' VendorID del tuo apparecchio USB

    ' Buffer di lettura e di scrittura
    Private Const BufferInSize As Short = 64    ' Dimensione del buffer che arriva al PC
    Private Const BufferOutSize As Short = 64   ' Dimensione del buffer che esce dal PC
    Dim BufferIn(BufferInSize) As Byte          ' I dati ricevuti vengono immagazzinati quì - il primo byte non è usato
    Dim BufferOut(BufferOutSize) As Byte        ' I dati da trasmettere vengono immagazzinati quì - il primo byte deve essere a 0

    ' ****************************************************************
    ' Connetti al controller HID quando il form viene caricato
    ' Viene collegato l'handle della finestra così che il form possa
    ' ricevere gli eventi...
    '*****************************************************************
    Private Sub Main_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' Non rimuovere!
        ConnectToHID(Me)
    End Sub

    '*****************************************************************
    ' Disconnesso dall'apparecchio HID...
    '*****************************************************************
    Private Sub Main_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
        DisconnectFromHID()
    End Sub

    '*****************************************************************
    ' Un apparecchio HID viene collegato...
    '*****************************************************************
    Public Sub OnPlugged(ByVal pHandle As Integer)
        If hidGetVendorID(pHandle) = VendorID And hidGetProductID(pHandle) = ProductID Then
            MsgBox("Collegato")
        End If
    End Sub

    '*****************************************************************
    ' Un apparecchio HID viene scollegato...
    '*****************************************************************
    Public Sub OnUnplugged(ByVal pHandle As Integer)
        If hidGetVendorID(pHandle) = VendorID And hidGetProductID(pHandle) = ProductID Then
            hidSetReadNotify(hidGetHandle(VendorID, ProductID), False)
            MsgBox("Scollegato")
        End If
    End Sub

    '*****************************************************************
    ' Notifica di cambiamento del controller 
    ' Viene chiamata dopo che tutti i dispositivi HID vengono connessi o disconnessi
    '*****************************************************************
    Public Sub OnChanged()
        ' Cattura l'handle dell'apparecchio al quale siamo interessati, dopo di che setta
        ' la sua impostazione di lettura su true - Questo garantisce la lettura di
        ' un messaggio di notifica quanto c'è qualche dato da leggere...
        Dim pHandle As Integer
        pHandle = hidGetHandle(VendorID, ProductID)
        hidSetReadNotify(hidGetHandle(VendorID, ProductID), True)
    End Sub

    '*****************************************************************
    ' Evento di lettura dei dati
    '*****************************************************************
    Public Sub OnRead(ByVal pHandle As Integer)
        ' Legge i dati (non dimenticarsi di analizzare l'intero array)...
        If hidRead(pHandle, BufferIn(0)) Then
            Dim txt As String
            For n = 1 To 64 ' Ricostruisce il dato composto da 64 caratteri
                txt = txt & Chr(BufferIn(n)) ' Trasforma il byte ricevuto in tipo Char e lo aggiunge alla variabile txt
            Next
            TextBox2.Text = txt
            ' ** IL TUO CODICE QUI' **
            ' Il primo byte "BufferIn(0)" non è usato
            ' gli altri byte sono i dati dal microcontroller...
        End If
    End Sub

    '*******************************************************************
    ' Funzione, da me creata, che invia del testo al microcontrollore
    '*******************************************************************
    Private Sub Invia(ByRef testo As String)
        For n = 1 To Strings.Len(testo)                     ' Loop che converte il testo
            BufferOut(n) = Asc(testo.Substring(n - 1, 1))   ' in array di byte con un massimo di
        Next                                                ' 64 byte, quindi 64 char
        BufferOut(0) = 0    ' Il primo byte di "BufferOut" deve sempre essere posto a 0
        hidWriteEx(VendorID, ProductID, BufferOut(0))       ' Invia i dati con la funzione hidWriteEx
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Invia(TextBox1.Text)
    End Sub
End Class


Solo una cosa, ovviamnte sia sul firmware del pic, sia sul software del pc, il vendorid, il productid il bufferinsize e il bufferoutsize dovranno essere uguali.Non penso che ci sia altro da aggiungere visto che è tutto ampiamente commentato. Compilate l'applicazione ed eseguitela.

Il risultato sarà una risposta da parte del controllore tale quale a quello che gli avrete inviato! La comunicazione funziona alla grande! :D

 LINK:

-> Schema circuito elettrico con Eagle <-

-> File di progetto di Mikrobasic ed Hex <-

-> File di progetto di Visual Basic 2008 <-

-> mcHID.dll <-

Primo esempio: Interfaccia I/O


Per agevolare l'apprendimento della comunicazione USB ho realizzato 3 esempi differenti che sfruttano la comunicazione USB. Partono dal più facile al più difficile. Questo che sto per presentarvi è la realizzazione di una scheda capace di gestire delle entrate e delle uscite, e tale controllo e visione viene effettuata tramite un software opportuno, eseguito sul pc, tutto gestito attraverso dei semplici click. Il circuito è quello base con in più 4 uscite rappresentate da 4 led e 4 entrate rappresentate da 4 pulsanti. Ecco il circuito:

clicca per ingrandire
clicca per ingrandire

Come vedete è molto semplice anche questo circuito, e ovviamente verrà sempre collegato al pc tramite la porta USB. Ora passiamo al firmware, questa volta è stato modificato il ciclo aggiungendo la lettura di ogni singolo byte proveniente dal pc per il controllo di ogni singolo led, e successivamente viene fatto il controllo su ogni singola porta dove ci sono i pulsanti per poi costruire il pacchetto di dati da inviare al pc. In pratica il pic e il pc si scambiano un pacchetto di dati di 4byte, tipo 0110, che rappresentano lo stato delle porte per quanto riguarda delle uscite si tratt di RB0:RB3 e per quanto riguarda le uscite RB4:RB7. Ecco il firmware:

 program usb_io

dim readbuff as char[64] absolute 0x500           ' Buffers should be in USB RAM, please consult datasheet
dim writebuff as char[64] absolute 0x540

sub procedure Interrupt()
   USB_Interrupt_Proc()                           ' USB servicing is done inside the interrupt
end sub

main:
     ADCON1 = ADCON1 or 0x0F                         ' Configura tutte le porte analogiche come digitali
     CMCON  = CMCON or 7                             ' Disabilita i comparatori
     PORTB=0
     PORTA=0
     TRISB=%11110000
     TRISA.0=0
     HID_Enable(@readbuff,@writebuff)                ' Abilita la comunicazione HID
     
     while TRUE
           while(HID_Read() = 0)                     ' Leggo i dati provenienti dal pc
           wend

           if readbuff[0]="1" then                   ' Analizzo i primi 4 caratteri dell'intero
                portb.0=1                            ' pacchetto ricevuto e per ogni carattere
            else                                     ' decido l'uscita se deve essere alta o bassa
                portb.0=0
           end if

           if readbuff[1]="1" then
                portb.1=1
           else
                portb.1=0
           end if

           if readbuff[2]="1" then
                portb.2=1
           else
                portb.2=0
           end if

           if readbuff[3]="1" then
                portb.3=1
           else
                portb.3=0
           end if
            
           if Button(PortB, 4, 1, 1) then            ' Invece quì leggo lo stato delle porte
                writebuff="1"                      <span style="color: #339966;">  ' e creo il pacchetto di dati che
           else                                      ' verrà inviato al pc
                writebuff="0"<br />           end if</span></span></span></p> <p>           if Button(PortB, 5, 1, 1) then<br />                writebuff=writebuff+"1"<br />           else<br />                writebuff=writebuff+"0"<br />           end if</p> <p>           if Button(PortB, 6, 1, 1) then<br />                writebuff=writebuff+"1"<br />           else<br />                writebuff=writebuff+"0"<br />           end if</p> <p>           if Button(PortB, 7, 1, 1) then<br />                writebuff=writebuff+"1"<br />           else<br />                writebuff=writebuff+"0"<br />           end if<br />           <br />           while(HID_Write(@writebuff,64) = 0)     <span style="color: #339966;"> ' Invio di dati al PC
           wend
     wend
end.

Anche questo è ampiamente commentato. La funzione Button ha 4 parametri: PORTB è il tipo di porta che dovrà tenere sotto controllo, 4 è il numero della porta, quindi RB4, 50 sono i millisecondi che deve durare l'impulso per essere riconosciuto come una pressione di un pulsante, questo per evitare disturbi e 1 è lo stato della porta nel momento in cui viene premuto il pulsante. La funziona è vera quando viene premuto il pulsante. Tutto il resto è identico al progetto base.

Se questa era la parte Hardware, ora passiamo alla parte software. Si basa sempre sul primo progetto, quello base; quello che cambia sono queste funzioni:

    '*****************************************************************
    ' Evento di lettura dei dati
    '*****************************************************************
    Public Sub OnRead(ByVal pHandle As Integer)
        ' Legge i dati (non dimenticarsi di analizzare l'intero array)...
        If hidRead(pHandle, BufferIn(0)) Then
            Dim txt As String
            For n = 1 To 64 ' Ricostruisce il dato composto da 64 caratteri
                txt = txt & Chr(BufferIn(n)) ' Trasforma il byte ricevuto in tipo Char e lo aggiunge alla variabile txt
            Next
            Riempi(txt.Substring(0, 1), txt.Substring(1, 1), txt.Substring(2, 1), txt.Substring(3, 1)) ' Divide l'intero pacchetto in 4 singoli dati
            ' ** IL TUO CODICE QUI' **                                                                  ' e li passa alla variabile "Riempi"
            ' Il primo byte "BufferIn(0)" non è usato
            ' gli altri byte sono i dati dal microcontroller...
        End If
    End Sub
 

    Private Sub led0_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles led0.Click
        If led0.BackStyle = PowerPacks.BackStyle.Transparent Then
            led0.BackStyle = PowerPacks.BackStyle.Opaque
        Else
            led0.BackStyle = PowerPacks.BackStyle.Transparent
        End If
    End Sub

    Private Sub led1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles led1.Click
        If led1.BackStyle = PowerPacks.BackStyle.Transparent Then
            led1.BackStyle = PowerPacks.BackStyle.Opaque
        Else
            led1.BackStyle = PowerPacks.BackStyle.Transparent
        End If
    End Sub

    Private Sub led2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles led2.Click
        If led2.BackStyle = PowerPacks.BackStyle.Transparent Then
            led2.BackStyle = PowerPacks.BackStyle.Opaque
        Else
            led2.BackStyle = PowerPacks.BackStyle.Transparent
        End If
    End Sub

    Private Sub led3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles led3.Click
        If led3.BackStyle = PowerPacks.BackStyle.Transparent Then
            led3.BackStyle = PowerPacks.BackStyle.Opaque
        Else
            led3.BackStyle = PowerPacks.BackStyle.Transparent
        End If
    End Sub

    Private Sub Riempi(ByVal mod1 As String, ByVal mod2 As String, ByVal mod3 As String, ByVal mod4 As String)    ' Analizza il pacchetto
        If mod1 = "1" Then                                                                                                                                                                      ' ricevuto e aggiorna di
            If led4.BackStyle <> PowerPacks.BackStyle.Opaque Then led4.BackStyle = PowerPacks.BackStyle.Opaque  ' conseguenza l'intrfaccia
        Else
            If led4.BackStyle <> PowerPacks.BackStyle.Transparent Then led4.BackStyle = PowerPacks.BackStyle.Transparent
        End If
        If mod2 = "1" Then
            If led5.BackStyle <> PowerPacks.BackStyle.Opaque Then led5.BackStyle = PowerPacks.BackStyle.Opaque
        Else
            If led5.BackStyle <> PowerPacks.BackStyle.Transparent Then led5.BackStyle = PowerPacks.BackStyle.Transparent
        End If
        If mod3 = "1" Then
            If led6.BackStyle <> PowerPacks.BackStyle.Opaque Then led6.BackStyle = PowerPacks.BackStyle.Opaque
        Else
            If led6.BackStyle <> PowerPacks.BackStyle.Transparent Then led6.BackStyle = PowerPacks.BackStyle.Transparent
        End If
        If mod4 = "1" Then
            If led7.BackStyle <> PowerPacks.BackStyle.Opaque Then led7.BackStyle = PowerPacks.BackStyle.Opaque
        Else
            If led7.BackStyle <> PowerPacks.BackStyle.Transparent Then led7.BackStyle = PowerPacks.BackStyle.Transparent
        End If
    End Sub

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick     ' Ogni 10ms aggiorna l'interfaccia
        Dim dato As String                                                                                                                                                                     ' inviando un pacchetto di dati
        If led0.BackStyle = PowerPacks.BackStyle.Opaque Then                                                                                                 ' con scritto quali led accendere
            dato = "1"<br />        Else<br />            dato = "0"<br />        End If<br />        If led1.BackStyle = PowerPacks.BackStyle.Opaque Then<br />            dato = dato & "1"<br />        Else<br />            dato = dato & "0"<br />        End If<br />        If led2.BackStyle = PowerPacks.BackStyle.Opaque Then<br />            dato = dato & "1"<br />        Else<br />            dato = dato & "0"<br />        End If<br />        If led3.BackStyle = PowerPacks.BackStyle.Opaque Then<br />            dato = dato & "1"<br />        Else<br />            dato = dato & "0"<br />        End If<br />        Invia(dato)<br />    End Sub<br /> </span></span></span></p> <p><span size="4" color="#0000ff" style="color: #0000ff; font-size: medium;">Non credo sia molti complicato questo codice. Vorrei far notare che rappresento le uscite con dei cerchi simili a dei led di colore verde. Nel momento in cui passa allo stato alto l'uscita, il colore di sfondo del cerchio diventa opaco e quindi si vede il verde, mentre allo stato basso lo sfondo del cerchio diventa trasparente e qundi come la finestra, grigio. Ecco la foto dell'applicazione:

Nel riquadro delle uscite, si può cambiare lo stato di ognuna semplicemente cliccandoci sopra, questa diventerà verde o grigia, a seconda dello stato precedente. C'è un timer importante nell'applicazione. Le funzioni all'interno del timer vengono eseguite ogni 10 milli secondi e si tratta di un controllo simile a quello sul microcontrollore, ovvero, controlla lo stato delle porte RB0, RB1, RB2, RB3 e costruisce il pacchetto di dati, in questo caso come nell'immagine il pacchetto che il pc invierebbe al pic sarebbe "0111", ricordandosi sempre che il primo byte "BufferOut(0)" deve sempre essere posto a 0, questo vale per qualsiasi tipo di applicazione; e poi aspetta la risposta dal pic che a sua volta controlla lo stato delle porte RB4, RB5, RB6, RB7 e invia il relativo pacchetto dati al pc, il quale lo rappresenterà attraverso i 4 led degli ingressi.

LINK:

-> Schema circuito elettrico con Eagle <-

-> File di progetto di Mikrobasic ed Hex <-

-> File di progetto di Visual Basic 2008 <-

 

Secondo esempio: Display LCD


Questo esempio da la possibiltà di inviare un messaggio testuale al pic, il quale, lo visualizzerà su un display LCD. Esistono un pò di progetti simili a questo in rete, ma la maggior parte delle volte si trova solo li circuito, l'hex e il software sul pc. Anche in questo caso spiegherò tutto il procedimento, così che possa essere un punto di partenza per altri progetti, e che possa essere modificato, ampliato ed adattato alle proprie esigenze. Ecco il circuito:

clicca per ingrandire
clicca per ingrandire

Il circuito per il display LCD è identico al circuito base, ma con l'aggiunta dell'LCD. Per quanto riguarda i collegamenti, si può fare riferimento allo schema nell'help di mikrobasic, in cui vengono specificati tutti i pin del pic e tutti i pin dell'LCD. In questo caso io ho utilizzato un display con la sigla TBMC16265B, il cui datasheet è facilmente trovabile in rete. Per quanto riguarda il firmware non c'è molto da dire perchè anche quì è tutto ampiamente commentato. Posso aggiungere solo che la tringa da inviare al pic può essere lunga massimo 64 caratteri, perchè quando abbiamo configurato i parametri per la comunicazione HID abbiamo inserito 64byte di buffer sia in entrata che in uscita. Ecco il firmware:

program usb_lcd
' Moduli di connessione LCD
dim LCD_RS as sbit at RB4_bit
    LCD_EN as sbit at RB5_bit
    LCD_D4 as sbit at RB0_bit
    LCD_D5 as sbit at RB1_bit
    LCD_D6 as sbit at RB2_bit
    LCD_D7 as sbit at RB3_bit

    LCD_RS_Direction as sbit at TRISB4_bit
    LCD_EN_Direction as sbit at TRISB5_bit
    LCD_D4_Direction as sbit at TRISB0_bit
    LCD_D5_Direction as sbit at TRISB1_bit
    LCD_D6_Direction as sbit at TRISB2_bit
    LCD_D7_Direction as sbit at TRISB3_bit
' Fine Moduli di connessione LCD

dim readbuff as char[64] absolute 0x500           ' Il buffer dovrebbe essere in RAM, Invito a consultare i datasheet
dim writebuff as char[64] absolute 0x540

dim cnt as byte
dim txt as char[64]                                ' Variabile in cui viene immagazzinato il testo da visualizzare sull'LCD

sub procedure Interrupt()
   USB_Interrupt_Proc()                           ' Il servizio USB viene eseguito nell'interrupt
end sub

main:
     ADCON1 = ADCON1 or 0x0F                   ' Setto tutti i pin analogici come digitali
     CMCON = CMCON or 7                        ' Disabilita i comparatori
     TRISA = 0                                 ' PORTA come output
     TRISB = 0                                 ' PORTB come output
     Lcd_Init()                                ' Inizializzo l'Lcd
     Lcd_Cmd(_LCD_CLEAR)                       ' Pulisco il display
     Lcd_Cmd(_LCD_CURSOR_OFF)                  ' Tolgo la segnalazione del cursore
     HID_Enable(@readbuff,@writebuff)          ' Enable HID communication

     while TRUE
           while(HID_Read() = 0)               ' Ricevo tutti i dati
           wend

           for cnt=0 to 63
               txt[cnt] = readbuff[cnt]        ' Assegno ogni byte ricevuto alla variabile txt
           next cnt

           Lcd_Cmd(_LCD_CLEAR)                 ' Pulisco il display
           Lcd_Out(1,1, txt)                   ' La visualizzo sull'Lcd
    wend

end.

Ovviamente, visto che verrà utilizzato un display, all'inizio del programma bisogna dichiarare tutti i pin che occorreranno per la comunicazione con il display, e successivamente bisogna inizializzare la comunicazione con Lcd_Init(). Per scrivere qualcosa sul display basterà usare il comando Lcd_Out(). Tutto il resto è identico al progetto base.

Se questa era la parte Hardware, ora passiamo alla parte software. Si basa sempre sul primo progetto, quello base; quello che cambia sono queste funzioni:

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Invia(TextBox1.Text)         '  Con questa funzione invio il testo scritto dentro una Textbox al pic

    End Sub

Non c'è niente da aggiungere per l'applicazione, se non la foto:
 

Basta semplicemente eseguire il programma, collegare il circuito al pc tramite l'USB, a questo punto verrà fuori un messaggio con scritto "Collegato". Bene, a questo punto potrete scrivere tutto ciò che vorrete nella textbox e successivamente cliccare "Invia". Magicamente sull'LCD verrà fuori la stessa frase che avrete scritto :). Naturalmente essendo il display di 16 caratteri, se la frase sarò di 17 caratteri o più, verranno visualizzati solo i primi 16. Il testo scritto sul diplay rimmarrà sempre lo stesso finchè il pic non riceverà un nuovo messaggio dal pc.

LINK:

-> Schema circuito elettrico con Eagle <-

-> File di progetto di Mikrobasic ed Hex <-

-> File di progetto di Visual Basic 2008 <-


Terzo esempio: Oscilloscopio


Questo è l'esempio più difficile, secondo il mio parere, che presento. In questo ultimo esempio utilizzo l'entrata analogica del pic per simulare un oscilloscopio. Dato che il controller HID permette la comunicazione tra il pc e il pic ogni millisecondo, il sample di questo oscilloscopio base sarà proprio 1kHz. Nel caso in cui uno volesse aumentare la precisione si potrebbe benissimo creare un buffer per esempio di 10 letture e ogni millisecondo inviare tutto al pc; in questo caso il sample diverrebbe di 10kHz, ma non ci esaltiamo perchè è più facile a dirlo che a farlo ;). Ecco il circuito:

clicca per ingrandire
clicca per ingrandire

A differenza degli altri 2 esempi, questo esempio è esattamente identico al circuito base, l'unica cosa che serve in più, anche se non è necessario al funzionamento del circuito, è un filo dove collegheremo il nostro segnale. Su questo esempio bisogna precisare una cosa: Come si vede dallo schema, il segnale che il pic leggerà è privo di protezioni da sovratensioni e da disturbi; questo può provocare 2 cose: se applicheremo all'entrata analogica una tensione maggiore di Vcc molto probabilmente il vostro pic potrebbe subire dei gravi danni e non funzionare più ed infine, se vorrete analizzare un segnale dovrete accertarvi che non vi siano fonti fonti di disturbo, ad esempio le vostre semplici dita :D. Ecco il firmware:

program usb_oscilloscopio

dim readbuff as char[64] absolute 0x500           ' Buffers should be in USB RAM, please consult datasheet
dim writebuff as char[64] absolute 0x540
dim temp as word                                  ' Variabile dove viene immagazzinato il valore dall'Adc_Read
dim txt as char[4]                                ' Variabile in cui viene convertito il valore dell'Adc_Read

sub procedure Interrupt()
   USB_Interrupt_Proc()                           ' Il servizio USB viene eseguito nell'interrupt
end sub

main:
     TRISA  = 0xFF                             ' PORTA come input
     CMCON  = CMCON or 7                       ' Disabilita i comparatori
     Delay_ms(1000)                            ' Pausa di 1 secondo
     HID_Enable(@readbuff,@writebuff)          ' Enable HID communication
     while TRUE
           temp = Adc_Read(0)                  ' Legge la tensione sulla porta AN0
           wordtostr(temp, txt)                ' Converte il valore da Word a String e lo memorizza in txt
           writebuff=txt                       ' Mette il valore txt dentro il buffer
           while(HID_Write(@writebuff,64) = 0) ' Invia i dati via USB
           wend
     wend
end.

Anche se può sembrare difficile, realizzare un oscilloscopio base non è così laborioso. E' sempre tutto commentato e fa semplicemente ciò di cui ho parlato sopra: dopo aver settato i vari pin, abilita la comunicazione USB ed esegue un ciclo infinito, in cui, legge la tensione sulla porta AN0, la converte in stringa e la invia al pc. Per le varie funzioni del'ADC si può studiare la sezione nell'help di mikrobasic.

Se questa era la parte Hardware, ora passiamo alla parte software. Si basa sempre sul primo progetto, quello base; quello che cambia sono queste funzioni:

    ' Queste dichiarazioni vanno inserite all'inizio 
    Dim myPen As New System.Drawing.Pen(System.Drawing.Color.Red) ' Variabili necessarie
    Dim formGraphics As System.Drawing.Graphics                   ' per il disegno
    Dim oldy As Integer                         ' Vecchio valore di y
    Dim assex As Integer                        ' Valore di X
    Dim nuovay As Integer                       ' Nuovo valore di Y
    Dim cont As Integer                         ' Conteggio valori letti ogni secondo
 

    Private Sub Main_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' Non rimuovere!
        ConnectToHID(Me)
        ' Funzione per il disegno
        formGraphics = Me.CreateGraphics()
    End Sub

      '*****************************************************************
    ' Evento di lettura dei dati
    '*****************************************************************
    Public Sub OnRead(ByVal pHandle As Integer)
        ' Legge i dati (non dimenticarsi di analizzare l'intero array)...
        If hidRead(pHandle, BufferIn(0)) Then
            Dim txt As String
            For n = 1 To 5 ' Ricostruisce il dato composto da 5 caratteri
                txt = txt & Chr(BufferIn(n)) ' Trasforma il byte ricevuto in tipo Char e lo aggiunge alla variabile txt
            Next
            cont = cont + 1 ' Aumenta la variabile Cont per il conteggio dei valori letti ogni secondo
            If assex >= Me.Width Then ' Quando arriva alla fine del form torna da capo per disegnare di nuovo il segnale
                assex = 1
                formGraphics.Clear(Color.Black)
            Else
                assex = assex + 1
            End If
            nuovay = 600 - (Convert.ToInt32(txt) / 2) ' divide il valore Y per 2 perchè 1023 come valore massimo sarebbe
            ' troppo alto e uscirebbe dal form
            formGraphics.DrawLine(myPen, assex, oldy, assex + 1, nuovay) ' Disegna il pezzettino di linea
            oldy = nuovay ' tiene in memoria il vecchio valore di Y
            ' ** IL TUO CODICE QUI' **
            ' Il primo byte "BufferIn(0)" non è usato
            ' gli altri byte sono i dati dal microcontroller...
        End If
    End Sub

    '*****************************************************
    ' Timer per il conteggio degli impulsi in un secondo
    '*****************************************************
    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        Me.Text = cont
        Cont = 0
    End Sub
 

Per visualizzare il segnale analizzato dal pic, sul pc, ho sfruttato la funzione di disegno del framework.NET ed ho utilizzato l'intero form come sfondo. Questa applicazione, oltre a scrivere sul suo titolo il numero dei dati ricevuti in un secondo, crea delle microlinee che ricostruiscono il segnale da analizzare. In pratica l'applicazione riceve dal pic un valore da 0 a 1023 ( siccome il pic ha una risoluzione di 10 bit = 2^10 = 1024), quello è il valore della y del punto di arrivo che formerà la nuova retta, il valore della x sarà semplicemente il valore di prima con l'aggiunta di 1(questo per muovere il segnale sempre più verso destra), mentre i valori di x e y del punto di partenza della nuova retta sono uguali ai valori del punto di arrivo della retta precedentemente tracciata. Nel codice ho diviso il valore ricevuto dal pic per 2 perchè altrimenti il segnale sarebbe uscito dalla finestra dell'applicazione e in alcuni casi non ci starebbe neanche nel monitor. Detto questo ecco la foto:
 

clicca per ingrandire

Non bisogna fare nient'altro che collegare il pic al pc tramite l'USB, avviare l'applicazione, e visualizzeremo istantaneamente il segnale in ingresso sulla porta AN0. In questo caso, il segnale in ingresso era un segnale sinusoidale a 100Hz con valore picco picco di 2V (valore minimo 1V, valore massimo 3V).

LINK:

-> Schema circuito elettrico con Eagle <-

-> File di progetto di Mikrobasic ed Hex <-

-> File di progetto di Visual Basic 2008 <-

Conclusioni


Vorrei concludere, anticipando le domande di qualcuno, che nel caso in cui, abbiate a disposizione solo il pic18f4550, potete tranquillamente sperimentare tutta la guida, scaricando i vari file; dovete solo ricordarvi di ricompilare il file HEX con mikrobasic scegliendo come pic il PIC18F4550. Per tutto il resto, ovvero il collegamento dell'LCD, il collegamento dell'alimentazione, del cristallo, ecc. fate riferimento all'help del compilatore oppure al datasheet dei vari componenti ;)

Spero, con questa guida, di avervi illuminato e di avervi aperto ad una via che prima di aver letto la suddetta, vi sembrava complicata ed impossibile da raggiungere! Vi invito dunque, ad ampliare i vostri progetti ed ad espanderli con una comunicazione flessibile, veloce e universale come l'USB.

Ciao a tutti e alla prossima!

Angelo Marchì

Login Form

Elettronica Open Source