Progetti - PIC

 

dottor C : Luci e suoni per Natale 2020

Ritorna il nostro dr. C con un simpatico progettino di fine anno.


Il progetto

Il programma e' nato come gioco natalizio prima per le mie figlie ed ora rimodernato per i numerosi nipoti.

Si tratta di un gioco di luci in cui diversi LED si accendono in funzione delle note di una canzoncina riprodotte da una coppia di altoparlanti. 

Il progetto originale era stato sviluppato nel secolo scorso su base Z80 ed ora è stato trasportato su PIC.
In particolare il prototipo è stato realizzato con un PIC18F26K22 e, con poche modifiche si potrà adattarlo a numerosi altri dispositivi, inclusi quelli della serie midrange PIC16xx.

Per quanto riguarda l'hardware c'e' poco da commentare. Oltre all'alimentazione c'è soltanto il PIC, i LED e due altoparlanti o due buzzer (di quelli senza oscillatore interno). Variante per chi volesse maggior potenza consiste nell' aggiunta di un transistor amplificatore o un MOSFET tra le uscite PWM e gli altoparlanti.

Non è stato realizzato un circuito stampato, ma sono state impiegate, per comodità e immediatezza di risultati, schede multiuso, dato che LED e altoparlanti sono collegati esternamente. 

Ovviamente si potrà anche utilizzare una breadboard o una millefori.

In particolare i LED potranno essere fissati su una scatoletta o un alberello di Natale o altro a vostra fantasia.

Veniamo al software.
Lo sviluppo è basato su MPLABX attualmente aggiornato alla versione 5.45 e dal compilatore C XC8 versione 2.30.
Entrambi i software sono liberamente scaricabili ed utilizzabili dal sito Microchip.
Non mi soffermo qui su altri particolari, dando per scontato la conoscenza basilare delle tecniche di programmazione. In caso contrario esistono innumerevoli manuali, applications, corsi, anche su questo sito e su quello Microchip dal quale attingere informazioni.

Se ci si accontenta di far funzionare il progetto così come è basta scaricare il sorgente incluso tutti i file accessori,
ricompilarlo sul proprio PC e trasferirlo nel PIC tramite ICSP con PICKIT3, PICKIT4 o SNAP.

Se, come spero, qualcuno volesse anche capire ciò che sta facendo ecco un piccolo aiuto che potrà semplificare la lettura di quanto offerto.

La prima cosa da fare quando si ha una idea per un progetto è quella di stabilire quali sono le risorse e le competenze necessarie  per portalo a termine. Nel nostro caso vogliamo realizzare un dispositivo in grado di emettere dei suoni a differente frequenza secondo una successione dettata da una partitura e fare in modo che per ogni nota vengano attivati dei LED "proporzionali" alla nota stessa.
A questo punto chiarite le idee su cosa vogliamo ottenere dobbiamo concentrarci su come realizzare quanto abbiamo solo immaginato.

Decidiamo ora la parte più importante: come generare i due canali audio spaziando possibilmente tra una vasta gamma di frequenze, il che si traduce nella possibilità di suonare melodie da molto semplici a molto più complesse.
Per muoversi tra le ottave 3 e 8, si richiedono valori che vanno da 130,8Hz per un DO(3) a 7902Hz per un SI(8).
Si potrebbe dunque pensare ad utilizzare un interrupt a circa 0,12 mS (1/7902) per ottenere la frequenza più elevata. 
Ma come ottenere con sufficiente precisione tutte le frequenze richieste ? Occorrerebbe calcolare un minimo comune multiplo delle frequenze e determinare il periodo come 1/f . Francamente non mi sono cimentato nell'operazione che temo possa portare a periodi inferiori al microsecondo: è vero che ormai i PIC viaggiano a 64 Mhz ma occorre dividere per 4 per avere il tempo di ciclo di una istruzione ed in ogni caso occorre avere il tempo per effettuare delle elaborazioni. Dunque, scartata la generazione diretta in interrupt delle due onde audio, andiamo a produrre una onda quadra con duty cycle del 50% (quella più assimilabile ad un suono reale), attraverso l'uscita di uno dei canali PWM disponibili.

Il problema si pone a monte del PWM, visto che la sua frequenza è determinata dal timer che lo comanda.
In più, in un momento di euforia, si è deciso di realizzare una versione "stereo", dunque con due canali audio
tra loro indipendenti. Questo fatto limita la scelta del processore che dovrà disporre non solo di due canali PWM, cosa questa abbastanza comune nei PIC anche non recentissimi, ma anche della possibilità di comandare ciascun PWM con un timer differente, cosa non sempre possibile con i modelli un po' datati.
Per questa ragione si consiglia di abbandonare i PIC più "antichi" e considerare solo quelli più recenti, che, a parte la prestazioni ampiamente superiori, costano anche meno.

Come già detto la scelta per questo progetto è caduta sul PIC18F26K22, non perchè fosse il migliore in assoluto (ce ne sono di più performanti nella nuova serie Q) ,ma perchè abbastanza comune.

Ovviamente, con un poco di attenzione, sarà possibile passare il sorgente anche ad altri chip.

La parte sulla quale dovremo soffermarci maggiormente è quella relativa ai canali PWM ed al loro pilotaggio da parte dei timer; sintetizziamo quanto è necessario sapere.

Nel PIC usato, un canale PWM, utilizza un timer "tipo 2". Il timer e' preceduto da un pre divisore X 1, X 4 o X 16 Il timer conta su 8 bit il postscaler è ininfluente per la gestione del PWM.
Per stabilire quali valori di pre divisione sono necessari per ottenere le frequenze desiderate ho approntato una tabella Excel in cui per ciascuna frequenza viene determinato il periodo ( 1/f ) e stabilito a priori il valore di pre divisione viene calcolata la corretta costante di divisione del timer per ottenere il giusto periodo, ovvero la giusta frequenza, in uscita dal PWM.
Per questo calcolo viene utilizzata la formula ricavata dal datasheet del PIC.

Da notare che il prescaler è stato impostato "a buon senso", così da ottenere delle costanti nel campo 50 -250 che ben si adattano ad utilizzare al meglio gli 8 bit del timer.
Necessariamente i valori interi PRx da precaricare nel timer risentono di una certa approssimazione che faranno si che la musica riprodotta non risulti certo suonata da un concertista. Diciamo che potremmo dare la colpa ad uno strumento non perfettamente accordato...

Il motivetto natalizio non poteva essere che Jingle Bells.

In base alle note necessarie, è stata elaborata la tabella; nell'ultima colonna del foglio Excel sono riportati per comodità i valori dei bit 0 e 1 dei registri T2CON e T4CON necessari per ottenere la desiderata pre divisione del pre scaler. 

A questo punto, non resta che tradurre in codice software quello che si è già progettato virtualmente.

Il progetto MPLABX allegato contiene tutti gli elementi per essere direttamente compilato, ma facciamo finta di dover iniziare tale progetto da zero.

Apriamo dunque File -> New Project ->Microchip Embedded Standalone Project -> Next
nella successiva finestra selezioniamo il PIC18F26K22 ed il nostro Tool di programmazione -> Next
Selezioniamo ora il compilatore XC8 v2.30 -> Next
Scegliamo quindi un nome per il nostro progetto, io ho scelto Natale , e la posizione in cui memorizzarlo, poi -> Finish.

MPLABX generera' ora l'ambiente applicativo per poter sviluppare il nostro progetto.
Generalmente sono solito inserire i file .h in una directory separata denominata "include" da aggiungere alle cartelle già predisposte.
Per permettere al compilatore di trovare automaticamente i file in essa contenuti occorre modificare le proprietà del progetto. Nella finestra di destra selezionare la cartella principale del progetto, tasto destro -> properties -> XC8 Compiler.
Nella finestra di destra alla voce Include directories aggiungere nella casella di destra ./include e confermare con OK

Ora passiamo ad un altro importante passo, la definizione delle risorse del PIC da utilizzare e la loro inizializzazione.
Fortunatamente ci viene in aiuto un potente plugin: MCC ( MPLAB Code Configurator ). Se non già installato occorrerà farlo ora con: Tools -> Plugins > Available plugins ->> selezionare MCC -> Install.

Si apra ora MCC che si posizionerà sulla scelta dell' oscillatore.
Scegliamo quello interno INTOSC con frequenza 2Mhz. Questo valore è quello che meglio si adatta ad ottenere dai timer le necessarie frequenze.
Passiamo ora a selezionare dalla cartella Available Resource quanto ci serve, aggiungendo la risorsa dall' icona verde +
Iniziamo con TMR0. questo e' il timer che determinerà le battute ed andrà gestito in interrupt. 
La risorsa verrà aggiunta nella finestra di destra Tree View e potrà da li venir richiamata e modificata.
Ora nella finestra principale di TMR0 selezioniamo Enable Timer, Enable prescaler scegliendo 1:256, Timer mode 8-bit, Clock Source FOSC/4, nella casella Requested Period: impostiamo 25ms; in più abilitiamo la gestione interrupt.
Ora per abilitare effettivamente la gestione interrupt scegliamo in alto la cartella Interrupt Module spuntando Single ISR per interrupt ed alla riga TMR0 della tabella sottostante spuntiamo enabled.

Aggiungiamo ora altre risorse: TMR2, TMR4, ECCP1 e ECCP2.
I due timer andranno predisposti nello stesso modo: Enable timer, Postscaler e Prescaler 1:16 e Timer Period 40ms.
ECCP1 ed ECCP2 andranno abilitati come ECCP mode : Enhanced PWM, Timer Select rispettivamente Timer2 e Timer4, PWM Duty Cycle impostato al 50% Enhanced PWM mode in single e PWM pins polarity come da default tutti active high.

Se tutto è stato fatto correttamente dovreste ritrovare calcolata una frequenza PWM frequency di 400,64 Hz.
Passiamo ora ad associare i pin del PIC alle varie funzioni. per far cio' apriamo contemporaneamente la cartella Pin Module e nella riga di stato di MPLAB ( in basso) Pin Manager: Grid View. Nella tabella che si aprirà selezioniamo le porta A e B come output e P1A su PC2 e P2A su PC1.
Nella finestra Pin Module togliere tutte le spunte Analog se presenti e selezionare tutti i pin in output.
Ciò fatto ritornare alla finestra di sinistra Tree View e cliccare in alto su Generate e ..opla' .. vengono automaticamente generati numerosi file che ci permetteranno di semplificare il lavoro di generazione del nostro programma.

Cliccando nuovamente sull'icona MCC si ritorna a questo punto al progetto principale nel quale si potranno notare importanti cambiamenti:

Primo, la comparsa si due cartelle denominate MCC Generated Files, una sotto Source Files e l'altra sotto Header Files, secondo la generazione di un programma main.c che include le strutture indispensabili alla compilazione del progetto.
Importante notare che sono ora disponibili tutte le funzioni generate automaticamente da MCC e depositate nel vari files sorgente in MCC Generated Files

Ora dopo aver compreso come si genera l'ambiente principale su cui sviluppare il progetto, abbandoniamo quanto fatto sino ad ora e concentriamoci sul progetto definitivo scompattandolo in una cartella a piacere dove siete soliti tenere i progetti PIC.

Apriamo ora dall'interno di MPLABX il progetto scompattato di cui analizzeremo il funzionamento.
Prima di passare al main.c converra' dare una occhiata al file natale.h che include importanti strutture indispensabili al funzionamento del nostro progetto.
Nella prima parte del file sono definite delle variabili enumerate. Cosa vuol dire ? Significa che queste variabili possono assumere solo i valori associati all'enunciato "enum" relativo. Così, dando ad esempio alla variabile durata_1 il valore SEMIBREVE questa assumerà il valore 32.
Comportamento simile si verifica nelle variabili nota_1 e nota_2 con la differenza che i valori assunti partono da 0 per DO_3 e si incrementano di 1 per ogni enumerazione.
Un'altra struttura importante e' costituita dall'array tabella_note[] in cui a coppie sono memorizzati i valori del timer e del predivisore per generare una specifica nota.

Da ultimo sono riportate le melodie. Queste sono costituite da array con coppie di valori indicanti la nota da suonare e la sua durata.
Come vedremo nel main e' possibile scegliere per i due canali audio melodie differenti, che abbiano però la stessa durata complessiva, oppure associare la stessa melodia ad entrambi i canali.

Chiunque con un minimo di cultura musicale potrà sbizzarrirsi a generare nuove melodie, ricordando di terminarle sempre con PAUSA, FINE_BRANO 

Torniamo ora al nostro main premettendo che ho modificato leggermente il file tmr0 generato automaticamente, in modo da semplificare la gestione dell'interrupt da TMR0 facendolo puntare alla funzione My_TMR0()
Pertanto ogni 25ms il timer 0 genera un interrupt che fa decontare i contatori cnt_durata1 e cnt_durata2 . Al loro valore 0 vengono ricaricati con la durata della nota successiva e contemporaneamente vendono caricati nuovi valori nei timer T2 e T4 a nei relativi prescaler per generare la nuova nota. Inoltre come effetto luminoso il valore binario del timer viene anche copiato sull'uscita dei LED.

Passiamo ora alla funzione main() che dopo l'inizializzazione del sistema si deve occupare di preparare le note da far suonare allo scadere della battuta.
Per consentire l'esecuzione di varie armonie ho dovuto far ricorso all' impiego dei puntatori che permettono di accedere indirettamente alle tabelle delle melodie
precisandone solo inizialmente l'indirizzo iniziale. Così, avendo indicato per melodia_1 e 2 l'indirizzo della melodia_C attraverso l'assegnazione melodia_1 = &melodia_C ; l'accesso a valori puntati da melodia_1 equivarrà ad accesso alla tabella melodia_C[].

A questo punto il gioco è fatto: in base all'indice che viene incrementato ad ogni battuta ed azzerato a fine brano vengono ricavati i valori di durata , frequenza e predivisione ( da caricare in TxCON ). Questi valori oltre che in fase iniziale vengono ricalcolati in un loop infinito in tutta calma in attesa di un nuovo interrupt da TMR0.

Finito?
...certamente NO!

A differenza di quanto avveniva nei vecchi PIC, in quello usato, modificando la frequenza di comando del PWM varia anche il Duty Cycle, ragion per cui è necessario ricalcolare per ogni nota anche il nuovo Duty Cycle secondo la formula indicata nel datasheet e caricarne il valore in CCPRx
Attenzione: la formula varia in base ai vari PIC, documentatevi in caso di cambiamenti .

Ora e' davvero tutto!
Grazie e a presto
PS. se qualcuno sperimentasse qualche altra musica e volesse inviarcela la divulgheremo volentieri.

Buone Feste.
Adriano

 


 

 

 

Copyright © afg. Tutti i diritti riservati.
Aggiornato il 11/12/20.