Microcontrollori come AVR UC3 ed XMega hanno in alcuni modelli un modulo hardware per la comunicazione USB, la scrittura del software per gestire il protocollo non è però banale e molto articolata, la libreria ASF ci viene in aiuto tramite uno stack USB che può risparmiarci molti mesi di lavoro permettendo di focalizzarsi su cosa rende unica la nostra applicazione.
Non analizziamo in questo articolo nel dettaglio tutto lo stack USB che offre molteplici possibilità ma vediamo in modo semplice come sostituire la vecchia comunicazione seriale con la più moderna USB presente su tutti i dispositivi di ultima generazione.
Utilizzeremo la classe USB CDC (Communication Devices Class), non una classe del mondo della programmazione ad oggetti ma un insieme di specifiche a cui attenerci che ci esonera dallo scrivere uno specifico driver per l’Host USB (il sistema operativo lato pc ad esempio) in quanto già presente per generici dispositivi USB CDC ed utilizzabile sotto Windows con un semplice file .inf. La comunicazione verrà semplicemente vista come una porta seriale virtuale utilizzabile con semplicità da un software sul sistema operativo.
Esistono anche altre classi USB ed è possibile crearne personalizzate, sotto una tabella riassuntiva (non completa) per rendere meglio l’idea di cosa possono offrire alcune classi USB ed alcuni vantaggi/svantaggi.
Lo stack USB di Atmel è composto da tre moduli: UDC, UDI ed UDD
UDC (USB Device Controller) fornisce un’astrazione di alto livello del dispositivo USB, presenta metodi come udc_start(), udc_stop() per avviare e fermare lo stack USB, udc_attach(void), udc_detach() per collegare il dispositivo al bus quando la tensione è idonea e scollegarlo dal bus rimuovendo le resistenze interne di pull-up sulle linee D- e D+.
UDI (USB Device Interface) il modulo è distinto per ogni classe di dispositivo che si vuole utilizzare, utilizzeremo UDI for CDC che offre metodi per controllare la linea seriale virtuale e gestire il trasferimento dati
UDD (USB Device Driver) fornisce un’astrazione di basso livello del dispositivo USB, gestisce eventi provenienti dall’hardware come interruzioni chiamando a sua volta funzioni dei moduli UDC e UDI, non utilizzeremo direttamente questo modulo.
Il funzionamento dello stack USB si basa sulle interruzioni, questa soluzione garantisce bassa latenza e non richiede cicli di attesa, bisogna porre però particolare attenzione. Le interruzioni USB hanno bassa priorità e possono essere bloccate da interruzioni con priorità maggiore o sezioni critiche di codice.
Le specifiche USB impongono un tempo massimo di 2ms per assegnare l’indirizzo USB, procedura di basso livello che si svolge durante l’enumerazione, al collegamento del dispositivo.
Gli host USB usano poi un timeout, non previsto dalle specifiche USB, per rilevare e resettare un dispositivo che non risponde ai messaggi di controllo, sotto una tabella di esempio dei tempi di timeout
CBW: Command Block Wrapper, ZLP: Zero Lenght Packet, CSW: Command Status Wrapper
Tralasciando le varie sigle che si riferiscono a meccanismi di basso livello del protocollo USB di cui non parleremo in questo articolo, è possibile notare che per avere compatibilità con i principali sistemi operativi la MCU deve rispondere entro 5 secondi a delle richieste particolari ricevute tramite USB.
L’utilizzo delle funzionalità di Debug (es. Debug con AVR Dragon) può interferire con il funzionamento del modulo hardware USB del microcontrollore, con la modalità USB OCD break mode disabilitata eventuali breakpoint saranno immediatamente soddisfatti ma il comportamento USB sarà imprevedibile mentre con la modalità abilitata le transazioni USB verranno completate prima di fermare l’esecuzione del programma e il micro non accetterà ulteriori transazioni e risponderà con un messaggio NACK ad eventuali richieste dell’host.
Nota: Seppur non specificato nel datasheet o nel manuale del programmatore la modalità USB OCD break mode è automaticamente configurata dal debugger e non richiede intervento manuale. Fonte: supporto tecnico Atmel
Le funzioni più interessanti sono nel modulo UDI con singola interfaccia CDC, sotto riportate per comodità:
Aggiungiamo prima di tutto al nostro progetto HelloASF creato negli articoli precedenti il modulo USB Device tramite il menù ASF / ASF Wizard come visto negli articoli precedenti per gli altri moduli, scegliamo la variante cdc_stdio dal menù a discesa come mostrato in figura e premiamo Apply
cdc_stdio fornisce al nostro progetto un’interfaccia Standard I/O (stdio) che implementa dietro le quinte la classe USB CDC permettendo così di utilizzare metodi come printf(), scanf(), getc(), puts(), etc.. le funzioni del modulo UDI for CDC rimangono però estremamente utili per inviare e ricevere byte o per attendere la ricezione di dati dall’host USB.
Verrà automaticamente aggiunto al nostro progetto tutto quello che serve per utilizzare l’USB con la classe CDC tramite stdio, notiamo dall’ASF Explorer l’aggiunta dei vari moduli dello stack USB.
La prima operazione sarà adesso impostare il clock del modulo USB, nel file conf_clock.h eliminiamo i commenti ai #define relativi al clock usb come nell’immagine seguente
Per il modulo USB verrà quindi utilizzato l’oscillatore RC interno da 32 MHz con la calibrazione di fabbrica a 48 MHz, non sarà però utilizzabile direttamente per la CPU se perlomeno si vuole rimanere entro le specifiche raccomandate (32 MHz) ed evitare l’overclock della CPU.
Una possibilità è utilizzare i prescaler per ridurre la frequenze di clock a 24 MHz, è però anche possibile utilizzare un oscillatore esterno o l’oscillatore RC interno da 2 MHz e tramite il PLL moltiplicare per 16 la frequenza ed ottenere così 32 MHz, seguiremo quest’ultima strada:
Il file conf_usb.h contiene le impostazioni di configurazione per l’USB CDC, è obbligatorio modificarne il Vendor ID con un identificativo rilasciato dietro pagamento dal consorzio USB per applicazioni commerciali.
Per utilizzare stdio_usb è necessario impostare alcuni #define a funzioni del modulo stdio_usb come mostrato di seguito
Da notare l’ #include finale al file <stdio_usb.h> per poter risolvere correttamente le funzioni aggiunte.
Dal file è possibile specificare tramite USB_DEVICE_ATTR se l’alimentazione è prelevata dal bus USB oppure no ed un eventuale risveglio remoto del dispositivo.
Nota per i dispositivi alimentati indipendentemente dal bus USB: E’ necessario un modo per rilevare lo stato della connessione USB, la scheda EWS utilizzata come è possibile vedere dallo schema sul pin PA7 presenta un divisore di tensione 1:3 per questo scopo, 5v saranno rilevati circa come 1,67 e sarà possibile misurarli senza problemi tramite l’ADC per esempio. Sarà poi necessario modificare la funzione stdio_usb_init() nel file stdio_usb.c mostrata di seguito
Sostituendo il valore true del parametro di stdio_usb_vbus_event() con l’effettivo valore del bus USB.
Alimentando continuamente da bus USB il micro questa operazione non sarà però necessaria.
Le impostazioni di comunicazione infine permettono di impostare il data rate e gli altri parametri tipici di una comunicazione seriale che devono coincidere col programma sull’host usb.
Modifichiamo adesso il file main.c col seguente codice
Dopo l’inizializzazione del servizio sysclk notiamo che vengono abilitati gli interrupts, passaggio fondamentale dato che tutto lo stack USB si basa su essi per il funzionamento.
Con l’istruzione stdio_usb_init() inizializziamo il modulo ASF stdio_usb che si occuperò di avviare lo stack USB ed impostare stdin ed stdout all’implementazione USB CDC.
All’interno del ciclo while è stata utilizzata semplicemente l’istruzione printf() per inviare tramite USB una stringa di testo alla porta seriale virtuale.
Il micro è pronto, non rimane adesso che modificare il file .inf che il Wizard ha aggiunto al progetto con le informazioni corrette sul nostro dispositivo (VID, PID) e che dovremo indicare a Windows per l’installazione del driver. Nel progetto allegato, scaricabile alla fine dell’articolo è possibile trovare un esempio di come modificare il file.
Nell’immagine seguente il terminale PuTTY collegato al micro XMega.
Anche per oggi è tutto, la libreria ASF permette di nascondere le complessità dietro al protocollo USB e ci fornisce strumenti semplici da utilizzare per concentrarci sulla nostra applicazione e scambiare senza problemi dati ad esempio tra micro e pc. Spesso il supporto USB è un fattore rilevante e permette di abbattere il BOM (Bill of Materials) evitando l’utilizzo di un IC dedicato.
Nessun commento:
Posta un commento