Oscilloscopio USB: Progetto PitaScope

 


Premessa

Lo scopo di questo progetto è di costruire un oscilloscopio USB sfruttando un microcontrollore PIC18F4550.

Per iniziare voglio parlare un pò dello standard USB, ma per chi volesse andare direttamente alla pagina con i file del progetto, basta andare alla pagina 6 del progetto.

Vantaggi principali della trasmissione USB

• alimentazione tramite bus
• alta velocità
• correzione d'errore
• standard diffuso
• facile espandibilità
Le caratteristiche principali del microcontrollore usato sono riportate qui sotto:
• supporto usb 2.0 con velocità 1.5 Mbyte/secondo (low-speed) o 12 MByte/secondo (full-speed), con modulo ricetrasmettitore USB integrato
• 32 kbytes di memoria flash e 2 kbytes di memoria RAM(di cui 1 kbytes disponibili per il buffer USB)
• 35 pin di ingresso-uscita
• frequenza di lavoro massima 48 MHz, ottenuta tramite un moltiplicatore di frequenza a PLL interno
• convertitore analogico-digitale con risoluzione di 10 bit

Cominciamo con il clock di sistema . . .

In questo pic sono indipendenti il clock principale del microcontrollore e il clock del modulo USB.
Il modulo PLL genera, a partire da un ingresso a 4 MHz, un segnale di clock di 96 MHz la cui frequenza è poi divisa per due, ottenendo 48 MHz.
Questa frequenza è necessaria per far funzionare correttamente il modulo USB in modalità full-speed.
Come clock principale di sistema si può scegliere se usare lo stesso segnale a 48 MHz (diviso eventualmente da un postscaler per un fattore 2,3,4 o 6) oppure la sorgente di clock interna.
In questa applicazione viene usato un quarzo da 8 MHz, collegato ai piedini OSC1 e OSC2 (13 e 14): gli 8 MHz passano attraverso un prescaler che divide per due la frequenza (ottenendo i 4 MHz richiesti) e poi entrano nel PLL; inoltre vengono usati i 48 MHz sia come clock di sistema che per il modulo USB.
Per cominciare bisogna quindi configurare tutti i bit relativi alle sorgenti di clock; per far ciò sono presenti:
  • due word di configurazione, CONFIG1L e CONFIG1H.
  • il registro OSCCON: seleziona la sorgente del clock di sistema tra oscillatore interno, oscillatore esterno e oscillatore primario. Quello che interessa è il primario, ovvero quello selezionato tramite i bit di configurazione FOSC3:FOSC0 (word CONFIG1H) e costituito dal quarzo esterno. Inoltre il datasheet mostra che dopo un evento di POR (Power On Reset e quindi anche all'accensione) tale registro contiene già il valore binario 0100xx00, ovvero è già selezionato l'oscillatore primario come clock di sistema.
  • il registro OSCTUNE: serve a calibrare la frequenza generata dall'oscillatore interno di tipo RC, non usato in questo progetto.
Di seguito trovate una "piantina", tratta dal datasheet, che mostra più chiaramente i percorsi da attivare per ottenere il clock di sistema e del modulo USB:

 

I bit di configurazione

I bit di configurazione abilitano varie caratteristiche del micro; si impostano al momento della programmazione.
Ogni compilatore prevede una diversa sintassi per la specificazione dei bit di configurazione direttamente dal codice; per la scrittura del codice di questo progetto uso il compilatore HI-TECH per PIC18, il quale sfrutta la direttiva __CONFIG(_numero_del_configuration_word_ , _attributi_).
Di seguito trovate il nome dei vari bit di configurazione seguito dal valore richiesto dal compilatore in questione:

 

Configuration Word Configuration Bits Valore per questo progetto Descrizione
CONFIG1L USBDIV=USBPLL
CPUDIV1:CPUDIV0=PLLPOSTDIV2
PLLDIV2:PLLDIV0=PLLDIV2
0xE1 imposta i vari pre/post-scaler
CONFIG1H IESO=IESOEN
FCMEN=FCMEN
FOSC3:FOSC0=HSPLL
0XFE seleziona la configurazione HS+PLL
CONFIG2L VREGEN=VREGEN
BORV1:BORV0=ignorato
BOREN1:BOREN0=BORDIS
PWRTEN=PWRTEN
0XF8 abilita il regolatore di tensione a 3.3V necessario alla comunicazione USB, disabilita il Brown-out reset e abilita il Power-up timer
CONFIG2H WDTPS3:WDTPS0=ignorato
WDTEN=WDTDIS
0XFE disabilita il Watchdog timer
CONFIG3H MCLRE=MCLREN
LPT1OSC=LPT1DIS
PBADEN=PBDIGITAL
CCP2MX=CCP2RC1
0XF1 abilita il Master Clear e abilita gli ingressi digitali su PORTB
CONFIG4L DEBUG=DEBUGDIS
XINST=XINSTDIS
ICPRT=ICPORTDIS
LVP=LVPDIS
STVREN=STVRDIS
0X9A disabilita il set di istruzioni esteso e la modalità debug
CONFIG5L UNPROTECT 0XFF disabilita protezione in lettura sul codice
CONFIG5H UNPROTECT 0xFF disabilita protezione in lettura su EEPROM e sul Boot Block
CONFIG6L UNPROTECT 0XFF disabilita protezione in scrittura sui blocchi di memoria
CONFIG6H UNPROTECT 0xFF disabilita protezione in scrittura su EEPROM, Boot Block e sui registri di configurazione
CONFIG7L UNPROTECT 0XFF disabilita protezione in lettura sulle tabelle
CONFIG7H UNPROTECT 0xFF disabilita protezione in lettura sul Boot Block

 

La memoria

I 32 Kbytes di memoria programma flash sono gestiti con un "Program Counter" da 21 bit, diviso su tre registri: TBLPTRU, PBLPTRH e TBLPTRL.
I 2 kbytes di memoria RAM (memoria dati) sono divisi in 8 banchi (bank0÷bank7), ciascuno da 256 bytes; la selezione di un banco avviene tramite un registro di controllo, il Bank Select Register.
Gli indirizzi di memoria RAM vanno perciò da 0x000 a 0x7FF, mentre i registri di configurazione (Special Functions Registers) sono mappati negli indirizzi da 0xF60 a 0xFFF.
Una limitata parte della RAM (da 0x000 a 0x05F), identificata come Access Bank, ha tempi di accesso più ridotti, e si può sfruttare per memorizzare variabili di uso frequente mediante un flag presente nella codifica di certe istruzioni.
Un'altra parte della RAM, cioè i banchi di memoria dal 4 al 7, vengono invece messi in comune con il modulo USB: nel caso in cui quest'ultimo sia attivo è sconsigliabile il loro uso per allocare dati (a meno che i Buffer USB non sfruttino tali spazi di memoria, o li utilizzino solo in parte).
Una ulteriore caratterisitca di questo microcontrollore è la sua capacità di autoprogrammarsi, o di salvare autonomamente dati nella memoria flash di programma.

 

Gli interrupt

Il PIC18F4550 dispone di varie sorgenti di interrupt, che come in tutti i PIC sono abilitabili tramite i bit presenti nei registri di configurazione specifici (INTCON, INTCON2, INTCON3, PIE1 e PIE2).
Ha inoltre un sistema di priorità che consente di dare a ciascuna sorgente di interrupt una priorità alta o bassa (registri IPR1, IPR2 e RCON in cui si può disabilitare il sistema di priorità); infine ci sono i registri con i flag degli avvenuti interrupt (PIR1 e PIR2).
Comunque per questo progetto, almeno in questa prima fase, serviranno solamente gli interrupt relativi alla comunicazione USB, e quindi saranno i soli ad essere attivati; inoltre la priorità degli interrupt è disabilitata e perciò, come scrive il datasheet, il micro lavorerà in modalità "compatibile" con i micro delle serie precedenti. I registri da modificare nella fase di inizializzazione sono quindi:

 

Nome registro Valore assegnato Descrizione
INTCON 0xC0 abilitazione generale degli interrupt di sistema e delle periferiche
INTCON2 0x75 abilita le resistenze di pull-up su PORTB
PIE2 0x20 abilita interrupt dovuti al modulo USB

 

All'arrivo di un interrupt il micro "salta" all'esecuzione della parte di programma presente all'indirizzo 0x000008, posizione della routine di interrupt, e nel caso fossero attive più sorgenti di interrupt bisognerebbe procedere mediante "polling" (interrogazione) di tutti i flag di avvenuti interrupt.
Nella specifica, il flag relativo all'interrupt generale del modulo USB è il bit USBIF del registro PIR2.

...e finalmente il modulo USB

Prima di accendere il modulo USB bisogna configurarlo: non si possono cambiare le sue impostazioni a tempo di esecuzione.
In sequenza bisogna seguire i seguenti passi:
UCFG=0x16 abilita le resistenze di pull-up del modulo USB (necessarie per l'host a riconoscere la velocità di trasmissione del dispositivo), imposta il micro per una trasmissione full-speed, attiva il ricetrasmettitore interno (default) e disattiva il ping-pong buffering
• bisogna attivare il regolatore di tensione a 3.3V mediante il Configuration Bit VREGEN (già impostato nella sezione "I bit di configurazione")
• ora si può accendere l'USB settando il bit USBEN del registro UCON: UCON=0x0C (abilita anche il segnale di RESUME, vedi più avanti)

Vi sono poi altri registri relativi al modulo USB:
USTAT contiene la direzione e il numero dell'endpoint usato nell'ultima transazione USB, ovvero dopo l'arrivo di un interrupt sul bit TRNIF del registro UIR. Come mosta il datasheet, questo registro è una finestra di lettura sul primo elemento di una coda FIFO che tiene traccia delle ultime 4 transazioni in attesa per essere processate: questo per esempio permette al micro di processare i dati ricevuti da un endpoint mentre un altro ne invia. Tale registro deve essere quindi analizzato per servire il corretto endpoint.
Quando si completa la richiesta relativa all'endpoint presente bisogna azzerare il flag di interrupt TRNIF: così facendo la FIFO avanza e viene immediatamente lanciato un nuovo interrupt solo nel caso in cui la FIFO abbia elementi validi, ovvero nel caso siano rimaste altre richieste in attesa. Se viene ricevuta una richiesta mentre la FIFO è piena il micro invia automaticamente un segnale di NAK all'host.
UADDR contiene l'indirizzo a 7 bit del dispositivo USB: tale indirizzo è assegnato al momento dell'enumerazione del dispositivo da parte dell'host ed è univoco tra tutti i dispositivi connessi all'host in quel momento. Prima della connessione e dopo il segnale di RESET-USB questo registro deve contenere 0x00
UFRMH e UFRML (registri read-only) contengono rispettivamente la parte alta e bassa del numero di frame ricevuti/trasmessi dopo il segnale di Start Of Frame (SOF)
• 16 registri che controllano i 16 endpoint disponibili: UEPn, dove n è il numero dell'endpoint. In essi si descrive se un endpoint supporta il trasferimento di controllo, l'handshake, l'input e/o l'output e se è in stato di stallo

Per definire le funzionalità degli endpoint usati bisogna scrivere nella Buffer Descriptor Table (BDT) presente nel banco 4 della memoria RAM (che come già detto è un banco da tenere riservato alla funzionalità USB, se questa è abilitata).
La BDT è una "tabella" composta da campi, ognuno dei quali descrive le funzionalità di uno specifico endpoint. Tali campi sono i Buffer Descriptor (BD) e sono disposti in successione per numero di endpoint crescente, in modo che al BD di un qualsiasi endpoint, diciamo l'endpoint n-esimo, si possa accedere con un offset di (4n-1) dalla locazione di memoria RAM 0x400.
Ogni BD è composto a sua volta di 4 registri:
• BDnSTAT
• BDnCNT
• BDnADRL
• BDnADRH

Il contenuto di questi 4 registri varia a seconda che l'endpoint sia in uso dal modulo USB (UOWN=1, SIE-mode) o no (UOWN=0, CPU-mode); in questi due casi quindi i registri presentano contenuti diversi, tranne che per i seguenti 3 bit del registro BDnSTAT che sono comuni nei due casi:

• il bit UOWN che, ripeto, se settato assegna la gestione del BD n-esimo al modulo USB (se azzerato la gestione è affidata al microcontrollore, anche se l'azzeramento viene effettuato esclusivamente dal modulo USB al termine di una transazione)
• i bit BC9 e BC8 che sono i bit più significativi del "byte-count"; i bit meno significativi sono presenti nel registro BDnCNT

Infatti nel datasheet si distinguono due modalità di funzionamento: se il modulo USB sta gestendo una comunicazione attraverso un endpoint (UOWN settato), il core del micro non dovrebbe nè leggere nè scrivere in questi registri perchè la loro modifica produrrebbe effetti imprevisti e la loro lettura non restituirebbe valori certi. Viceversa, se il core del micro sta agendo su un BD allora il modulo USB non tocca questi registri finchè non gli si dà il "via libera" settando UOWN.
Nella realtà questi ognuno di questi registri è implementato in coppia, e dei due ne viene reso visibile solo uno in accordo col bit di selezione UOWN.
Quando ci si prepara per "armare" un endpoint, scrivendo nel suo BD, bisogna aggiornare per ultimo il registro BDnSTAT.
I due registri BDnADRL e BDnADRH contengono l'indirizzo di memoria di partenza per il buffer dell'endpoint n-esimo.
Come già detto, il modulo USB è capace di generare una varietà di interrupt; questi possono essere selezionati tramite il registro UIE e monitorati (e anche azzerati prima di uscire dalla routine di interrupt) tramite il registro UIR.

Riferimenti: Datasheet PIC18F4550 -- Compilatore C Hi-tech

 

Seconda parte -->

 

Ultimo aggiornamento: 4 agosto 2011