Un generatore di onda quadra da 1Hz a 16MHz con un microcontrollore PIC

In un articolo precedente ho illustrato l’utilizzo della scheda PIC16F375 Curiosity Nano insieme all’MPLAB Code Configurator; continuando sulla falsariga di quell’articolo, presento qui un progetto che ho fatto più per divertimento. Volevo in realtà spiegare l’utilizzo di un modulo che serve a generare onde quadre (non PWM) e diciamo che mi sono fatto prendere la mano!

Il modulo NCO

Il modulo NCO (Numerically Controlled Oscillator) è una periferica core-independent, questo vuol dire che il suo utilizzo non occupa cicli macchina ed ha anche la possibilità di rimanere attivo quando il sistema va in sleep. Il suo utilizzo principale è quello di generare onde quadre. Sappiamo che anche il modulo PWM genera onde quadre, ma tra i due moduli ci sono delle sostanziali differenze.

Il modulo NCO ha innanzitutto la possibilità di variare la frequenza in maniera continua in un range molto ampio, mentre il PWM ha delle frequenze “fisse” e non è possibile passare con soluzione di continuità da una frequenza all’altra. Secondariamente, il PWM necessita un timer per poter funzionare, mentre il modulo NCO è indipendente dai timers. Il modulo NCO, però, non ha la possibilità di variare il duty cycle, o quanto meno non può farlo in maniera continua come con il modulo PWM.

L’NCO ha due modalità di funzionamento: FDC (Fixed Duty Cycle) e PF (Pulse Frequency). La prima modalità di funzionamento consente di ottenere un range di frequenze continue da 0Hz fino a 16Mhz ma con duty cycle fisso al 50%. La seconda modalità somiglia ad un PWM: consente di avere un range di frequenze ancora più esteso, fino a 32MHz con dei valori di duty cycle “a scatti”. Ma analizziamo nel dettaglio il funzionamento di queste due modalità capendo dapprima come funziona questo modulo.

Il modulo NCO possiede un registro di accumulo, NCOxACC, a 20 bit (diviso in 3 registri: NCOxACCU, NCOxACCH e NCOxACCL dove le lettere finali U,H e L stanno per Upper, High e Low). In tale registro viene caricato un numero preso da un altro registro, accessibile all’utente, NCOxINC (diviso anch’esso in 3 parti allo stesso modo del registro di accumulo) sommato con una unità in concomitanza del colpo di clock.

Nel momento in cui si verifica l’overflow del registro di accumulo si ha la possibilità di eseguire il toggle di un pin o utilizzare questo segnale per inviarlo ad altre periferiche. In modalità FDC si capisce che, dato che ogni overflow causa un toggle, la frequenza in uscita sarà pari alla metà della frequenza con cui si verifica l’overflow e il segnale ha un duty cycle del 50% perchè i due eventi di toggle successivi hanno la stessa durata essendo stati entrambi causati da un overflow.

In modalità PF, invece, nel momento in cui si verifica l’overflow del registro di accumulo, l’uscita si porta a livello alto per un certo tempo dopodichè si riporta a livello basso in attesa di un colpo di clock successivo. Il tempo per il quale il segnale rimane a livello alto è calcolato in base ad un certo numero di colpi di clock, selezionabili nel registro NCOxCLK, che non prevedono una continuità numerica ma variano a potenze di 2. Si capisce quindi che questa modalità consente di avere una frequenza pari alla stessa dell’overflow (quindi si arriva fino a circa 32MHz sul microcontrollore PIC in oggetto) ma con un duty cycle variabile a scatti, in aggiunta se il tempo di livello alto è troppo grande (ovvero si verifica un overflow prima che il tempo di livello alto finisca), si ha che il segnale in uscita rimane sempre a livello alto.

Le frequenze massime/minime ottenibili dal modulo NCO dipendono dalla velocità di incremento del buffer di accumulo che è determinata dalla sorgente di clock selezionata per far funzionare il modulo: la scelta è molto ampia e viene selezionata nel registro NCOxCLK. Nell’esempio che presento in questo articolo possono essere selezionate due frequenze di base premendo un pulsante: l’oscillatore interno a bassa frequenza, che procuce un clock di 31250Hz e l’oscillatore interno ad alta frequenza utilizzato per la CPU, che in questo esempio è settato a 32MHz. Le frequenze massime ottenibili in modalità FDC saranno quindi pari a circa la metà della frequenza dell’oscillatore. La frequenza con cui si verifica l’overflow è data dalla formula:

Per la modalità FDC tale valore va diviso per 2.

Il generatore di onda quadra

Per il progetto viene utilizzato un display LCD 16×2 sul quale viene visualizzata la frequenza impostata, un encoder rotativo per la selezione della frequenza e i pulsanti on-board (SWo) e dell’encoder. Lo schema è davvero molto semplice, io l’ho provato su una breadboard:

Dal momento che ho avuto problemi a far funzionare il tutto mediante la tensione prelevata dalla porta USB (disponibile sul pin VBUS della Curiosity Nano, non riportato nello schema), ho provveduto ad alimentare il circuito dall’esterno: per fare questo, però, è necessario portare il pin identificato come VOFF a GND. In questo modo viene disabilitato il regolatore di tensione on board ed è possibile applicare una tensione esterna al pin VTG. Se applicate una tensione esterna a VTG senza aver messo VOFF a GND, la scheda si guasterà sicuramente!

Premendo l’alberino dell’encoder viene cambiata la sorgente di clock associata al modulo NCO e questo viene indicato sul display in basso a destra con “MF” nel caso di 31250Hz o “FO” per 32MHz: ho incluso questa possibilità perchè non riuscivo ad ottenere valori precisi alle basse frequenze utilizzando FOSC come sorgente.

Premendo il pulsante SW0 a bordo della scheda è possibile incrementare gli step di frequenza aggiunti o sottratti tramite rotazione dell’encoder. Utilizzando l’oscillatore a bassa frequenza si può incrementare fino a 1KHz ad ogni tick dell’encoder, con FOSC fino a 1MHz. 

L’uscita dell’oscillatore è disponibile sul pin RD2. Utilizzando un oscilloscopio personalmente noto che l’onda quadra in uscita incomincia a deformarsi superati i 9/10MHz, ma ho fatto le prove su una basetta millefori e direi che tutto l’accrocco montato così non è decisamente adatto a gestire le alte frequenze.

Ad ogni modo si tratta di un progetto di esempio, sicuramente con molti difetti e mancanze e quindi migliorabile. Avendo utilizzato il code configurator, la cosa bella è che potete realizzare lo stesso circuito anche con un microcontrollore PIC differente (purchè dotato degli stessi moduli) semplicemente cambiando il microcontrollore nelle proprietà del progetto: aprendo il Configurator vi verrà detto che il micro è cambiato e che proverà a fare le modifiche (ancora in fase sperimentale), ma io ci ho provato e non ha sbagliato nulla. Addirittura in un video Microchip si vede che il codice può anche essere portato da un AVR ad un PIC e viceversa, ovviamente entro certi limiti dovuti sicuramente alle differenze nelle periferiche. Se non avete la Curiosity Nano, quindi, potete anche utilizzare il PIC nudo e crudo (avendo a disposizione il programmatore) o anche un altro PIC.

Il sorgente si trova sul mio Github a questo indirizzo. E’ possibile aprire il progetto in MPLABX e fare modifiche, per settare tutto ho utilizzato il Code Configurator per cui premendo il pulsante MCC potete vedere tutti i settaggi fatti. Tra l’altro questo esempio è anche buono per imparare come vengono gestiti gli interrupt mediante il codice generato dal configurator.