USB Host con PIC32MX

Con l’avvento di microcontrollori dotati di funzionalità Host, come i PIC32MX460 introdotti tempo fa, è ora possibile usare tutto un insieme di periferiche USB.

USB (Universal Serial Bus) è ormai diventato lo standard di comunicazione per interfacciare un PC con i dispositivi più diversi, dai Flash drive ai mouse, alle tastiere, ai dispositivi di acquisizione audio-video ed innumerevoli altri. Con l’avvento di microcontrollori dotati di funzionalità Host, come i nuovi PIC32MX460, è ora possibile usare tutto un insieme di periferiche USB. Per ridurre il time-to-market di applicazioni USB, Microchip ha sviluppato uno stack denominato Microchip USB Library for PIC32MX che copre sia le funzionalità device che host di questi nuovi microcontrollori a 32 bit.

Le modalità operative degli  apparati USB

Il bus USB è un’interfaccia  seriale asincrona con una topologia a stella connessa, dove il centro di ogni stella è costituito da un hub. In un sistema USB è presente un solo host e fino a 127 device (compresi un massimo di 7 hub). Ad ogni device è assegnato dall’host un indirizzo nel corso del processo di enumerazione, durante il quale il device viene identificato e configurato. Nel seguito faremo riferimento allo standard USB 2.0. Sono state definite le seguenti modalità di funzionamento di un apparato USB:

» USB Device: usata dalle periferiche come Flash drive, Mouse, Tastiere, ecc.

» USB Standard Host: usato normalmente dai PC.

» USB Embedded Host: una versione di host adatta ad essere implementata da un microcontrollore.

» OTG Dual Role: usata da dispositivi che possono cambiare il proprio ruolo da device a host a seconda del cavetto usato per il collegamento.

In particolare, i  dispositivi Embedded Host presentano le caratteristiche seguenti:

» usano una presa tipo ‘A’ per ogni porta; il supporto per gli hub è opzionale;

» ogni porta deve fornire un minimo di 100 mA di alimentazione per un dispositivo non configurato, e fino a 500 mA per un dispositivo configurato;

» devono supportare un elenco specifico di dispositivi, contenuto in una TPL (Targeted Peripheral List);

» devono supportare solo le velocità e le modalità di trasferimento (bulk, interrupt, isochronous) usate dai dispositivi della TPL;

» Non è necessario che i driver dei dispositivi siano aggiornabili.

I microcontrollori  della famiglia PIC32MX4XX comprendono un modulo USB 2.0 in grado di funzionare secondo le modalità Device, Embedded Host od OTG (On-The-Go). Nel caso di funzionamento come device, il modulo USB è Full Speed (12 Mb/s), mentre come Embedded Host od OTG può funzionare sia in Low Speed (1.5 Mb/s) che Full Speed. In figura 1 è riportato lo schema a blocchi del modulo USB dei PIC32MX4XX.

Figura 1: schema a blocchi del modulo USB dei PIC32MX4XX

Figura 1: schema a blocchi del modulo USB dei PIC32MX4XX

Tipologie di trasferimento

In un bus USB, l’unico host è responsabile dell’inizio di ogni trasferimento dati. La comunicazione su un bus USB avviene secondo diverse modalità di trasferimento:

» Control: usato nella fase di enumerazione dei device e per operazioni di controllo nella fase di operatività; il  10% della banda disponibile è sempre riservato a trasferimenti di questo tipo.

» Interrupt: in questa modalità di trasferimento, l’host alloca dei time slot in modo da trasferire  i dati con il dispositivo in maniera periodica. In questa modalità è assicurata anche l’avvenuta ricezione dei dati, tramite un meccanismo di ACK.

» Isochronous: anche in questa modalità di trasferimento, l’host alloca dei time slot in modo da trasferire  i dati con il dispositivo in maniera periodica. Non è però verificata l’avvenuta ricezione dei dati. Questa modalità assicura il throughput più elevato ed è tipicamente usata per lo streaming audio/video.

» Bulk:  questo trasferimento è complementare all’isochronous in quanto i dati sono trasferiti solo quando c’è banda disponibile, mentre è assicurata l’avvenuta ricezione dei dati. Utile per trasferimenti di file di grosse dimensioni quando le tempistiche non siano stringenti. Ad esempio con dispositivi di Mass Storage come i Flash drive USB. In tutte le modalità di trasferimento infine viene controllata l’integrità dei dati tramite CRC. In tabella 1 è riportato un confronto tra le diverse modalità di trasferimento.

Tabella 1: Confronto tra le modalità di trasferimento (USB 2.0 - Full speed).

Tabella 1: Confronto tra le modalità di trasferimento (USB 2.0 - Full speed).

Endpoints  e descrittori

La comunicazione tra dispositivi USB avviene tra due Endpoint. Durante il processo di enumerazione,  il device comunica all’host le sue caratteristiche tramite una serie di descrittori. Lo standard USB prevede 8 tipi di descrittori, i  più rilevanti dei quali sono i seguenti

» Device  descriptor:  produttore (VID), codice del prodotto (PID), classe del dispositivo, numero di configurazioni ed altre informazioni generali.

» Configuration  descriptor:   corrente richiesta, numero di interfacce della configurazione.

» Interface  descriptor: numero di endpoints dell’interfaccia, classe della stessa.

» Endpoint  descriptor:   tipologia di trasferimento (Control/Bulk/…) e direzione (IN/OUT), byte trasmessi per transazione.

» String descriptor: informazioni ‘human readable’ riguardanti alcuni dei descrittori visti, utili soprattutto per facilitare l’identificazione dei dispositivi da parte degli utenti.

I dispositivi  USB sono suddivisi in classi di appartenenza: ad esempio la classe Mass Storage comprende i Flash disk, mentre della classe HID (Human Interface Device) fanno parte periferiche come mouse, tastiere ma anche dispositivi custom. Dal lato host è necessario un client driver per poter operare con un dispositivo di una data classe, mentre sarà necessario sviluppare un driver apposito se il device USB non appartiene ad una classe predefinita.

Struttura dei trasferimenti

Come già accennato, tutto il traffico di un sistema USB è controllato dall’unico host. Un dispositivo può iniziare un trasferimento dati solo in risposta ad una richiesta da parte dell’host, sia che si tratti di una lettura dati (OUT da parte dell’host) che di una scrittura (IN  da parte dell’host). Come visto la direzione del flusso di dati è sempre vista rispetto all’host USB. Un trasferimento di dati ha una dimensione massima compresa tra 64 e 1023 byte a seconda del protocollo di trasferimento usato: ogni trasferimento è a sua volta composto da più transazioni. Esistono tre tipi di transazioni: Setup, Data e Status; in un trasferimento possono esserci più transazioni di tipo Data. Ogni transazione è a sua volta composta da più packet, le unità elementari di trasferimento per il modulo USB. I packet sono a loro volta di 4 tipi diversi: SETUP, IN/OUT,  DATA0/1 (il payload)  e ACK. Per esemplificare, in figura 2 sono raffigurate le transazioni ed i packet che costituiscono un trasferimento secondo la modalità Control di cui abbiamo parlato prima.

Figura 2: struttura di un trasferimento di tipo Control.

Figura 2: struttura di un trasferimento di tipo Control.

In figura 3 è raffigurato invece un trasferimento di tipo Bulk di 128 byte di dati tramite due packet consecutivi di 64 byte.

Figura 3: struttura di un trasferimento di tipo Bulk

Figura 3: struttura di un trasferimento di tipo Bulk

Queste due tipologie di trasferimento ci interessano particolarmente, dato che sono le uniche usate nell’esempio di Host   per USB Flash Drive che vedremo più avanti. Il modulo USB del PIC32MX gestisce in hardware i trasferimenti a livello di packet, tranne i packet di tipo ACK che, se previsti dalla tipologia di trasferimento, sono generati automaticamente. Quindi nell’esempio in figura 3 il firmware dovrà richiamare esplicitamente i  comandi per trasferire i pacchetti di tipo OUT e DATA0/1. Abbiamo visto che in un Embedded Host, la TPL è l’insieme dei dispositivi supportati dall’host. I PIC32MX nella modalità Embedded Host possono supportare dispositivi identificati nella TPL tramite la classe di appartenenza oppure tramite gli identificatori VID/PID:

» VID/PID  (Vendor  ID/Product ID) identificano uno specifico dispositivo di una certa classe, ad esempio un Flash Disk del produttore X del modello Y.

» Classe/sottoclasse/protocollo identificano tutti i dispositivi  di una data classe che supportano un certo protocollo di trasferimento. Ad esempio tutti i  Flash Disk che supportano i comandi SCSI ed un protocollo Bulk.

In tabella 2 è riportato un esempio di TPL basato sulla classe dei dispositivi.

Tabella 2: Esempio di TPL.

Tabella 2: Esempio di TPL.

USB Embedded Host Stack di Microchip

Lo stack Embedded Host di Microchip è compatibile con l’uso di un RTOS (per quanto non necessario) ed utilizza un meccanismo misto di interrupt e di polling. Una state machine è usata per il processing in background, in particolare nel processo di enumerazione dei dispositivi USB connessi. Alla fine del processo di enumerazione, dopo che il dispositivo  è stato configurato entra nello stato di runningQuindi lo stack richiama l’event handler per l’inizializzazione del dispositivo, contenuto nel client driver: se il dispositivo è inizializzato con successo, possono iniziare i trasferimenti  dati normali. In figura 4 sono mostrate le transizioni della state machine.

Figura 4: Transizioni della state machine dello stack USB Embedded Host.

Figura 4: Transizioni della state machine dello stack USB Embedded Host.

I trasferimenti  si avvalgono di un meccanismo di interrupt per soddisfare gli eventi time-critical: in particolare l’interrupt di SOF (Start Of Frame) che si verifica con la cadenza di 1 ms per indicare l’inizio di una sessione di trasferimenti. All’inter no di un frame possono essere trasmesse più transazioni: l’interrupt di Transaction Complete si verifica appunto quando l’ultima transazione è stata completata: lo stack determinerà allora la successiva transazione da eseguire prelevandola da una lista apposita.

Funzioni  di callback (handlers) da definire

Lo stack Embedded Host richiede che siano definiti due handler nel client driver (il driver che gestisce un particolare device):

» Device  Initialization  handler

» Event  handler

Il Device  Initialization  handler, del quale abbiamo già parlato, è richiamato alla fine del processo di enumerazione, dopo che sia stata scelta una particolare configurazione del device (se ne presenta più di una). Nella funzione saranno definite le procedure d’inizializzazione specifiche del device: se l’inizializzazione va a buon fine l’handler dovrà ritor nare il  valore TRUE, in caso contrario dovrà ritornare FALSE.
L’handler dovrà avere un prototipo corrispondente alla typedef seguente (è un puntatore a funzione):
typedef BOOL (*USB_CLIENT_INIT)
(BYTE address, DWORD flags);

L’Event Handler gestisce invece eventi che si verificano durante l’operatività normale del device. Un esempio di evento è quello della disconnessione di un device dal bus, definito come EVENT_DETACH: in questo caso, oltre alle operazioni pertinenti eseguite dallo stack USB, il client driver potrebbe eseguire ulteriori attività specifiche, come ad esempio rimuovere il  device da un elenco di dispositivi collegati. L’handler dovrà avere un prototipo corrispondente alla typedef seguente

typedef BOOL
(*USB_CLIENT_EVENT_HANDLER)
(BYTE address, USB_EVENT event,
void *data, DWORD size);

Altri event handler possono essere necessari a livello di applicazione piuttosto che di client driver. Ad esempio, un evento che si verifica quando un device collegato richiede di essere alimentato con una certa corrente massima, andrà gestito tramite un event handler che eseguirà le azioni opportune al ricevimento dell’evento definito come EVENT_REQUEST_POWER.  Il prototipo di queste funzioni deve essere del tipo seguente

BOOL USB_HOST_APP_EVENT_HANDLER
( BYTE address, USB_EVENT event,
void *data, DWORD size );

I nomi degli event handlers andranno inseriti obbligatoriamente nei file usb_config.c ed usb_config.h, specifici di ogni progetto. In alternativa, è possibile usare la utility di configurazione USB Config Tool, fornita con lo stack USB di Microchip, della quale vedremo l’uso nell’esempio di host per Flash drive USB.

Inizializzazione ed uso dello  stack

L’inizializzazione dello stack USB è eseguita tramite la chiamata della funzione

BYTE USBHostInit(void);

Questa funzione va richiamata una volta sola nell’applicazione. L’USB Config Tool è utilizzabile per creare automaticamente la macro USBInitialize() (presente nel file usb_config.h) che provvederà a richiamare le funzioni di inizializzazione sia dello stack USB che, eventualmente, dei client driver dell’applicazione. Subito dopo l’inizializzazione dello stack, sarà necessario richiamare la funzione

void USBHostTasks(void);

Questa funzione implementa la state machine dello stack utilizzata nella fase di enumerazione: è quindi necessario richiamarla regolarmente all’inter no del loop principale dell’applicazione. Anche in questo caso, è possibile usare la utility USB Config Tool per generare la macro USBTasks(), la quale richiamerà la funzione USBHostTasks() ed eventuali altre funzioni richieste dai task dei client driver. Il codice di questa macro è presente nel file usb_config.h. Per comunicare con il dispositivo,  un dato client driver utilizza alcune funzioni dello stack USB. Un’operazione di lettura dal dispositivo è iniziata tramite la funzione

BYTE USBHostRead(BYTE deviceAddress,
BYTE endpoint, BYTE *data,
DWORD size);

mentre la scrittura verso un device usa la funzione

BYTE USBHostWrite(BYTE deviceAddress,
BYTE endpoint, BYTE *data,
DWORD size);

Durante l’esecuzione delle funzioni, può essere necessario controllare l’avvenuta esecuzione della transazione, tramite la funzione

BOOL
USBHostTransferIsComplete(BYTE
deviceAddress, BYTE endpoint,
BYTE *errorCode, DWORD *byte-
Count);

In questo caso è indispensabile richiamare periodicamente la macro USBTasks() per evitare malfunzionamenti dello stack, come si vede nell’esempio di codice seguente

error = USBHostWrite( device,
EP1, buffer, sizeof(buffer) );
if (error)
{
// errore di scrittura
//…
}
else
{
while (!USBHostTransferIsComplete(
device, EP1, \
&error, &count ))
{
USBHostTasks();
}
if (error)
{
// transazione errata
// …
}
else
{
// transazione eseguita
// …
}
}

Un esempio  di Embedded Host: USB Flash Drive

Vedremo ora un esempio di applicazione per l’uso di un Flash drive USB con i PIC32 MX. Un Flash drive USB è un dispositivo di Mass Storage: è possibile quindi utilizzare  il client driver sviluppato da Microchip (incluso con la distribuzione dello stack USB), per i device appartenenti alla classe di dispositivi Mass Storage. Il client driver fornito nell’esempio è compatibile con i Flash Drive che usano un file system FAT16 ed un’interfaccia comandi di tipo SCSI. Questi comprendono la maggior parte dei Flash Drive di dimensioni inferiori ai 2 GB, mentre quelli di dimensione superiore utilizzano una FAT32.

L’applicazione Data  Logger

L’applicazione d’esempio è un data logger, che memorizza sul Flash Drive due tipologie di dati provenienti da alcuni ingressi dell’Explorer 16:

» Misurazioni  a bassa frequenza: il potenziometro presente sulla scheda è monitorato tramite l’ADC del PIC32, interrogato una volta al secondo. Il valore letto viene quindi memorizzato, insieme all’orario ricavato dal Real T ime Clock (RTCC) del PIC32, su un file del Flash Drive.

» Misurazioni ad alta frequenza:  il sensore di temperatura presente sulla scheda è monitorato tramite l’ADC del PIC32, ogni 10 ms. Il  valore letto viene quindi memorizzato, insieme all’intervallo di tempo trascorso, su un file del Flash Drive. L’applicazione di Embedded Host per Flash Drive USB è stata configurata per il seguente sistema hardware:

» Modulo PIM MA320002, equipaggiato con il PIC32MX460F512L

» Scheda di sviluppo Explorer 16

» Scheda di espansione USB Pictail Plus L’applicazione di data logger si basa sullo stack multi-layer seguente, la cui struttura è visibile in figura 5.

Figura 5: layers dell’applicazione Data Logger

Figura 5: layers dell’applicazione Data Logger

USB Embedded Host Driver

Lo strato inferiore è costituito dallo stack Embedded Host del quale abbiamo discusso nella prima parte dell’articolo. Il Mass Storage client driver si interfaccia direttamente con questo layer.

Mass Storage  Client  Driver

Questo layer è un esempio di client driver di cui abbiamo parlato genericamente in precedenza. Costituisce l’interfaccia di basso livello verso un dispositivo di Mass Storage (del quale il Flash Drive rappresenta un caso particolare).

Gestione  del File System  e supporto per i comandi  SCSI

La gestione del file system del Flash Disk è eseguita da questo layer.  Il file system originario, descritto nell’application note AN1045, utilizza le funzioni riportate nella tabella 3 per interfacciarsi con l’hardware. In questa applicazione, tali funzioni sono state sostituite con dei comandi SCSI, come si vede in tabella, in modo da potersi interfacciare con il client driver di Mass Storage.

Tabella 3: Funzioni d’interfaccia del file system.

Tabella 3: Funzioni d’interfaccia del file system.

Applicazione

Infine, il layer superiore  costituisce l’applicazione vera e propria di data logging.

Funzionalità dell’applicazione

L’applicazione consiste di tre componenti funzionali

» Interfaccia  comandi

» Gestione  del File system

» Data Logging

L’applicazione interagisce con l’utente tramite un collegamento via terminale seriale, ad esempio con Hyperterminal di Windows. Se si dispone della scheda Explorer 16, sarà necessario collegarla al PC tramite il connettore DB9 dell’interfaccia RS232. L’interfaccia andrà settata a 57600 baud, 8 bit di dati, nessuna parità, 1 bit di stop, no flow control. L’applicazione riconosce una serie di comandi per la gestione del file system e del data logging.

Comandi  per la gestione del file  system

Sono riconosciuti i  comandi seguenti, dei quali forniamo una breve descrizione:

» CD: cambia la directory

» COPY:  copia un file

» DEL: cancella un file

» DIR: visualizza  una directory

» MD: crea una nuova directory

» RD: cancella una directory

» TYPE:  visualizza  il contenuto di un file

Comandi  per il Data  Logging

tramite il comando

LOG POT <file>

sarà eseguito il log a bassa frequenza del valore del potenziometro presente sulla scheda, interrogato una volta al secondo. Il valore  letto, insieme all’orario ricavato dal RTCC del PIC32, è quindi memorizzato sul file prescelto del Flash Drive. Il  file generato è formattato in maniera da essere compatibile con tool di analisi come Excel. Il comando

LOG TMP <file>

Esegue la misurazione ad alta frequenza del sensore di temperatura presente sulla scheda Explorer 16. Tramite l’interrupt del timer 3, ogni 10 ms la tensione generata dal sensore viene convertita tramite l’ADC del PIC32: il valore letto viene quindi memorizzato, insieme all’intervallo di tempo trascorso, in un buffer temporaneo. Quando il buffer è pieno, il suo contenuto è copiato sul file prescelto del Flash Drive. In questo modo si supera l’impossibilità di eseguire misure in real-time ad alta velocità con il PC.

La utility  USB Config  Tool

Sebbene l’applicazione di data logger sia già fornita configurata, vediamo come si può utilizzare l’USB Config Tool per generare  i file di configurazione usb_config.c ed usb_config.h (possiamo trovare questi file nella directory del progetto ‘USB Data Logger’). I passi necessari per configurare il progetto sono i seguenti.

1-Nella scheda Main selezioniamo USB Embedded Host come Device Type, quindi Ping-Pong an All Endpoints come PingPong Mode.

2-Nella scheda Host selezioniamo solo Bulk come Transfer Type (i  trasferimenti di tipo Control sono sempre selezionati per default). Immettiamo il nome dell’event handler del livello applicazione (si veda il paragrafo  sulle funzioni di callback) nella casella Name of Application Event Handler : nel nostro caso sarà USB_ApplicationEventHandler. Lasciamo le altre opzioni come da default.

3-Nella scheda TPL, nella casella Description immettiamo un nome per l’elemento della lista, ad esempio Flash Drive. Selezioniamo Mass Storage dalla lista Client Driver, poi Support via Class ID, quindi immettiamo i codici relativi al device di classe Mass Storage (riportati in tabella 2)  nelle caselle Class ID, Subclass ID e Protocol ID. Lasciamo  il resto come da default e clicchiamo su Add to TPL per aggiungere la classe alla lista. Il  risultato dovrà essere come in figura 6.

Figura 6: USB Config Tool - TPL.

Figura 6: USB Config Tool - TPL.

■  4-Nella scheda Mass Storage, selezioniamo Mass Storage Client is used in Host mode, quindi scegliamo SCSI Interface come Media Interface. Notiamo che in questo caso, saranno preselezionati i  nomi degli handler utilizzati dagli handler Initialization ed Event, dei quali abbiamo parlato nel paragrafo sulle funzioni di callback. Si veda la figura 7. Anche il nome del file header va lasciato come da default.

Figura 7: USB Config Tool - Mass Storage client driver.

Figura 7: USB Config Tool - Mass Storage client driver.

 

 

Scrivi un commento

EOS-Academy