Implementazione di un driver USB CDC

Sul blog di Elettronica Open Source puoi leggere non solo tutti gli articoli Premium riservati agli abbonati Platinum 2.0 e inseriti nella rivista Firmware 2.0 (insieme ad articoli tecnici, progetti, approfondimenti, news, tutorial a puntate e molto altro) ma anche gli articoli della Rubrica Firmware Reload. In questa Rubrica del blog abbiamo raccolto gli articoli tecnici della vecchia rivista cartacea Firmware, che contengono argomenti e temi evergreen per Professionisti, Makers, Hobbisti e Appassionati di elettronica. Il trasferimento dati può avvenire a velocità superiori rispetto a quelle ottenibili su una comune linea seriale RS232. Il link seriale rappresenta, possiamo dire da sempre, il collegamento maggiormente utilizzato per eseguire il debug e la configurazione dei sistemi embedded. Nonostante l’interfaccia seriale sia praticamente scomparsa dai recenti modelli di PC, è tuttavia possibile utilizzare la classe CDC fornita dal protocollo USB per implementare una UART virtuale. Vediamo come.

Introduzione

Il protocollo USB ha permesso di collegare a un PC, in modo estremamente semplice, diversi tipi di dispositivi elettronici, eliminando una miriade di interfacce di comunicazione differenti e non compatibili tra loro. Purtroppo, a farne le spese è stata anche e soprattutto la gloriosa interfaccia di comunicazione seriale, generalmente implementata attraverso lo standard EIA RS232C. Gli attuali computer, soprattutto i modelli portatili, ne sono infatti sprovvisti, e questo fatto rappresenta un problema per gli sviluppatori che richiedono una semplice e comune connessione seriale per interagire con il dispositivo hardware. Ci viene però incontro lo standard USB, che attraverso la sua classe CDC (Communication Device Class) fornisce una funzionalità equivalente a quella di una normale porta seriale (COMx), mantenendo la semplicità d’uso e le funzionalità tipiche dell’USB (tra le quali ricordiamo l’hot plug). La classe CDC, nata espressamente per dispositivi come modem, fax, telefoni, e interfacce di rete, consente quindi di emulare una porta seriale mettendo a disposizione dell’utente una UART “virtuale”. Come vedremo più in dettaglio nel seguito, se il dispositivo embedded è equipaggiato con un opportuno driver CDC, potrà essere visto e gestito dal PC come un vero e proprio dispositivo seriale. Quando esso viene collegato al bus USB, verrà pertanto riconosciuto automaticamente dal sistema operativo presente sul PC come un dispositivo seriale, e gli verrà assegnato automaticamente un numero di porta COMx. Emulatori di terminale o altre applicazioni seriali potranno così comunicare con il sistema embedded senza richiedere alcuna modifica. L’utilizzo della classe CDC presenta inoltre il notevole vantaggio, per il progettista, di non dovere necessariamente scrivere un driver ad hoc per lo specifico sistema operativo utilizzato, ma sarà sufficiente fornire, in fase di riconoscimento del dispositivo, il file descrittore fornito dal produttore del driver o bridge USB/seriale (il file .INF).

USB VS SERIALE - PRO E CONTRO

Visto che l’argomento trattato riguarda due tipi diversi di comunicazione tra PC e dispositivi embedded, il collegamento tramite USB e quello seriale, vediamo brevemente quali sono i vantaggi e gli svantaggi offerti da ciascuna soluzione.

Seriale

I vantaggi offerti da questo tipo di interfaccia sono fondamentalmente i seguenti:

  • molto semplice da utilizzare dal punto di vista software
  • maggiormente diffusa, rispetto all’USB, nel mondo degli appassionati e degli hobbisti elettronici

I suoi svantaggi sono invece i seguenti:

  • non più supportata dai modelli di computer recenti
  • è necessario ricorrere all’utilizzo di un opportuno adattatore USB-seriale
  • è necessario utilizzare un traslatore di livelli (come il noto integrato MAX232, o similari) per convertire i segnali 0V-5V del microcontrollore in segnali da -12V a +12V

USB

Il collegamento USB offre diversi vantaggi, tra i quali:

  • è disponibile su tutti i computer recenti, i quali posseggono un numero più o meno elevato di porte USB a seconda dei modelli, ed è pertanto una soluzione conveniente che non richiede hardware addizionale
  • il trasferimento dati può avvenire a velocità superiori rispetto a quelle ottenibili su una comune linea seriale RS232
  • lo standard seriale sta diventando sempre più obsoleto, mentre quello USB continua ad essere mantenuto e sviluppato (siamo arrivati alla versione 3.0).

Non manca, anche nel caso USB, qualche svantaggio, tra cui:

  • complessità maggiore, rispetto alla seriale, dal punto di vista software. Lato microcontrollore (dispositivo embedded) occorre infatti disporre di un’implementazione di stack USB, un lavoro che difficilmente può essere portato a termine da un semplice sviluppatore.

Occorre pertanto accertarsi che il produttore del microcontrollore (come ad esempio avviene per i dispositivi della serie PIC18, PIC24, e PIC32 di Microchip) rilasci anche un’implementazione di stack USB da utilizzare per il progetto software:

  • per alcune versioni di sistema operativo e/o particolare applicazione può essere necessario scrivere anche un device driver lato PC, un’operazione non semplice e che richiede oltretutto un sistema di sviluppo appropriato per i device driver (SDK o similare).

Sicuramente l’interfaccia seriale rappresenta la soluzione più semplice nel caso in cui non siano richieste velocità di trasferimento particolarmente elevate o la comunicazione con altri dispositivi USB (si pensi ad esempio alla funzionalità USB OTG, richiesta oggi in numerose applicazioni portatili). Occorre comunque notare come, nonostante il software abbia una complessità maggiore nel caso USB, utilizzando la classe USB CDC il PC possa dialogare con il dispositivo embedded come se questo fosse collegato tramite una comune porta seriale. Ciò significa che non sarà necessario scrivere un driver specifico o imparare come avviene la comunicazione attraverso l’interfaccia USB; sarà infatti sufficiente leggere e scrivere su una comune porta COM virtuale. In Figura 1 viene mostrato l’approccio classico seguito per la comunicazione tra un PC ed un sistema embedded tramite linea seriale RS232. In Figura 2 sono invece messi a confronto tra loro gli schemi di comunicazione basati su pura linea seriale ed emulazione seriale su USB.

Figura 1: comunicazione PC-dispositvo via seriale.

Figura 1: Comunicazione PC-dispositivo via seriale

 

Figura 2: emulazione seriale su USB.

Figura 2: Emulazione seriale su USB

PORTA COM VIRTUALE

In precedenza si è detto che una porta COM virtuale è un’interfaccia che consente a una applicazione di accedere ad un dispositivo USB come se fosse una vera e propria porta seriale. Occorre comunque osservare che una porta COM virtuale non deve necessariamente avere un’interfaccia seriale. Vi sono ad esempio dispositivi che convertono tra interfaccia parallela e USB. Un metodo comunemente utilizzato per creare una porta COM virtuale è quello di utilizzare un chip dedicato, come ad esempio l’FT232R USB UART di FTDI. Questo integrato gestisce tutta la comunicazione USB a livello hardware, ed è dotato di una porta seriale asincrona in grado di interfacciarsi con un microcontrollore. Il produttore, inoltre, mette a disposizione dello sviluppatore i driver necessari per MS Windows e per altri tipi di sistemi operativi. Del tutto simile al precedente è l’integrato FT245R USB FIFO, dotato di un’interfaccia parallela al posto di quella seriale.

USB CDC

Consideriamo ora il caso generico in cui non è necessario disporre di un driver specifico fornito da un determinato vendor; in questo caso, il PC utilizzerà il driver USB Communication Device Class (CDC) incluso nel sistema operativo (può trattarsi di MS Windows oppure di un altro sistema operativo). Nel caso del sistema operativo Windows, è necessario fornire/preparare un opportuno file di tipo INF affinchè il driver sia in grado di gestire il dispositivo. Sul mercato sono presenti diversi vendor di microcontrollori che offrono un software completo per la gestione di porte virtuali COM su USB; tra questi ricordiamo Microchip Technology (PIC18F4550), Atmel Corporation (AT89C5131), e NXP Semiconductors (LPX214x). La classe CDC può essere definita come un metodo generale per abilitare tutti i tipi di comunicazione sul bus USB, consentendo la connessione di dispositivi per le telecomunicazioni e la trasmissione dati a distanza come telefoni digitali, modem analogici e digitali, dispositivi di rete come modem e router ADSL.

Numerosi sono, inoltre, i layer fisici di comunicazione e i protocolli supportati dalla classe CDC. Nonostante al suo interno abbia un’implementazione piuttosto complessa, la classe CDC può essere utilizzata come un metodo estremamente semplice per comunicare su USB attraverso una porta COM virtuale. La specifica CDC 1.1 definisce diversi modelli di comunicazione, inclusa l’emulazione su seriale. Il driver USB del sistema operativo MS Windows, ad esempio (usbser.sys), è conforme a questa specifica. La stessa cosa deve avvenire per il sistema embedded. La specifica CDC definisce un modello di controllo astratto per la simulazione di porta seriale su USB, nel quale si prevede l’utilizzo di due tipi di interfacce. La prima viene indicata con il termine Communication Class Interface, e viene utilizzata per la gestione dei dispositivi. Ciò include la gestione dello stato del dispositivo, la gestione delle sue risposte, e la notifica degli eventi. Questa interfaccia può anche essere impiegata, opzionalmente, per instaurare delle chiamate e per impostarne i parametri relativi. La seconda interfaccia è la Data Class Interface, e serve a gestire le trasmissioni generiche di dati, permettendo ad un dispositivo di trasferire dati da e verso un host. Inoltre, permette il multiplexing di dati e comandi su una stessa interfaccia, mediante l’impiego di opportuni wrapper. La Communication Class Interface richiede almeno un endpoint, utilizzato per la gestione del dispositivo. Normalmente per questo scopo viene utilizzato l’endpoint di controllo di default, vale a dire l’endpoint 0. Si può poi usare, opzionalmente, un secondo endpoint per la notifica degli eventi. Per questa attività si usa solitamente l’endpoint Interrupt IN, utilizzato per notificare all’USB host lo stato corrente della connessione RS232. Per quanto riguarda invece la Data Class Interface, occorre che gli endpoint siano presenti in coppie dello stesso tipo. Ciò si rende necessario per consentire una comunicazione sia di tipo IN che di tipo OUT. Solamente gli endpoint di tipo Bulk e Isocrono possono essere impiegati per questo scopo. L’architettura che ne risulta è visualizzata in Figura 3.

Figura 3: le classi USB CDC.

Figura 3: Le classi USB CDC

I DESCRITTORI

La specifica USB definisce il contenuto e il formato dei descrittori USB standard (visibili con sfondo bianco nella Figura 4), mentre la specifica CDC introduce degli ulteriori descrittori specifici di classe (evidenziati in grigio nella stessa figura).

Figura 4: l’architettura USB CDC.

Figura 4: L’architettura USB CDC

Ogni dispositivo USB dispone di un proprio descrittore di device, con una struttura del tipo di quella mostrata nel listato seguente (la struttura è esattamente quella riportata, mentre il contenuto dei campi può variare a seconda della particolare applicazione):

/* Device descriptor */
const USBDeviceDescriptor
deviceDescriptor =
{
// Device descriptor size in
bytes
0x12,
// DEVICE descriptor type
0x01,
// USB version, BCD (2.0)
0x0200,
// Class: CDC
0x02
// Subclass: none
0x00,
// Protocol: none
0x00,
// Max. packet size, Endpoint
0
0x08,
// USB Vendor ID
0x0925,
// USB Product ID
0x9060,
// Device release, BCD (1.0)
0x0100,
// Manufacturer string index
0x00,
// Product string index
0x00,
// Serial number string index
0x01,
// Number of configurations
0x01
};

I campi Vendor ID e Product ID servono ad identificare in modo univoco un particolare device. Il Vendor ID è un numero a 16-bit assegnato dall’organizzazione USB-IF, e perciò ogni produttore deve farne richiesta prima di poter commercializzare i propri dispositivi. Il Product ID è anch’esso un intero a 16-bit, per cui ogni produttore ha a disposizione un massimo di 65.536 product ID. La coppia Product ID-Vendor ID serve a determinare quale driver deve essere utilizzato quando il dispositivo viene enumerato. La stringa serial number è molto utile, in quanto evita la proliferazione di porte COM virtuali. Un dispositivo con serial number mantiene infatti il suo numero di porta COM quando viene spostato da una porta USB ad un’altra su un sistema Windows. Se non avesse il serial number, invece, il dispositivo acquisirebbe un diverso numero di porta una volta spostato su una porta USB differente. Oltre al descrittore di device, un dispositivo CDC presenta un descrittore di configurazione e due descrittori di interfaccia. Il primo specifica i requisiti di alimentazione e il numero di interfacce presenti nella configurazione; i descrittori di interfaccia indicano invece all’host come il dispositivo implementa le funzioni di comunicazione. La prima interfaccia che segue il descrittore di configurazione è il descrittore della Communication Class Interface, che identifica la sottoclasse CDC e il tipo di protocollo, e fornisce un endpoint di interrupt per inviare notifiche all’host USB:

// Communication interface
descriptor
{
// Descriptor size in bytes
0x09,
// INTERFACE descriptor type
0x04,
// Interface number
0x00,
// Alternate setting number
0x00,
// Number of endpoints
0x01,
// Class: CDC communication
0x02,
// Subclass: abstract control
model
0x02,
// Protocol: V.25ter (AT
commands)
0x02,
// Interface string index
0x00,
}

Sebbene la Communication Class Interface utilizzi due endpoint (uno per la gestione del dispositivo e uno per la notifica degli eventi), il descrittore di interfaccia specifica un numero di endpoint pari a 0x01: l’endpoint 0 di default non viene infatti incluso nel conteggio.

La Communication Class Interface è seguita da diversi descrittori funzionali, i quali hanno lo scopo di definire gli attributi del dispositivo:

  • header: specifica la versione di CDC sulla quale è basato il dispositivo
  • call management: specifica come il dispositivo gestisce le chiamate (nel caso di un dispositivo come la porta COM virtuale, il descrittore deve indicare che il dispositivo non gestisce le chiamate)
  • abstract control management: specifica le notifiche e le richieste gestite dal dispositivo
  • union: permette di raggruppare più interfacce in un’unica funzione globale (Communication Interface e Data Interface).

Il Notification Endpoint Descriptor serve a definire il numero, direzione e dimensione dei pacchetti dell’interrupt IN endpoint, responsabile dell’invio delle notifiche all’host. Il descrittore Data Class Interface segue la Communication Class Interface e i relativi descrittori funzionali e di endpoint, e sarà a sua volta seguito da due descrittori endpoint (DATA IN & OUT). Lato host (supponiamo che il sistema operativo adottato sia MS Windows), ogni dispositivo porta COM virtuale deve avere un corrispondente file INF, che contiene i corretti valori di Vendor ID e Product ID, e la definizione della classe USB.

[Manufacturer]
%MFGNAME%=ProduttoreX
[ProduttoreX]
%DESCRIPTION%=DriverInstall,
USB\VID_0123&PID_9876

In questo caso abbiamo VID = 0x0123 e PID = 0x9876.

Scarica subito una copia gratis

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend