Ricordiamo, difatti, che il firmware di serie della MuIN USB comprende anche un comodo bootloader, che ci permette di caricare i programmi senza dover acquistare un programmatore, ma unicamente sfruttando la connessione USB e il software gratuito HID Bootloader (che potete scaricare nella sezione downloads).
Le modalità con cui caricare un nuovo firmware tramite booloader sono illustrate in questo articolo
In aggiunta, disponendo la MuIN USB della connessione USB è possibile alimentare la scheda dalla porta USB (leggere il manuale d'uso).Andiamo quindi a descrivere due semplici programmi che fanno uso di tale caratteristica. E' possibile scaricare questi due programmi di esempio, utilizzabili anche come "scheletro" delle nostre applicazioni, in fondo all'articolo. Entrambi i programmi eseguono un compilto basilare: far lampeggiare il led posto di serie sulla scheda e collegato alla porta RA4. Il compito è eseguito dai due programmi di esempio in maniera differente: il primo lo esegue tramite semplici cicli di ritardo e il secondo tramite le interruzioni (o interrupts).
Per poter compilare questi programmi è necessario avere installato MPLAB IDE e MPLAB C18.
Incominciamo con una breve descrizione della struttura di base di questi due programmi di esempio.
Il nostro programma principale deve sempre cominciare con l'inclusione del file header che contiene i nomi mnemonici e le definizioni del pic montato sulla scheda:
Code:
#include <p18f2550.h>
Code:
#define PROGRAMMABLE_WITH_USB_HID_BOOTLOADER
Si ricorda che, quando si fa uso del bootloader, bisogna anche utilizzare un file linker che tiene appunto conto della presenza di un bootloader. Negli esempi è già incluso il linker modificato. Qualora non si voglia usare il bootloader basta non includere nessun linker: il compilatore includerà quello standard per il pic in uso.
Incontriamo quindi le direttive #pragma che ci permettono, tra le altre cose, di specificare i fuses di configurazione. In realtà utilizzando il bootloader non è necessario specificare i fuses di configurazione dal momento che sono già stati preprogrammati sulla scheda e non bisogna riprogrammarli in quanto il bootloader potrebbe non funzionare più. I fuses sono stati comunque inclusi a titolo di esempio e nel caso in cui non si voglia far uso del bootloader (in ogni caso, anche se presenti nel programma, non verranno programmati dal bootloader a meno che non siate voi a specificarlo esplicitamente nel programma HID Bootloader: opzione disattivata di default che si consiglia di non attivare!).
Di particolare interesse sono le prime 4 direttive che permettono di specificare le frequenze di funzionamento:
Code:
#pragma config PLLDIV = 3 // (12 MHz crystal on MuIN USB) #pragma config CPUDIV = OSC1_PLL2 #pragma config USBDIV = 2 // Clock source from 96MHz PLL/2 #pragma config FOSC = HSPLL_HS
Per tale motivo, in ingresso al PLL abbiamo un prescaler che permette di dividere la frequenza in ingresso per portarla a 4MHz: avendo un quarzo da 12MHz e dovendo ottenere 4MHz, il prescaler deve essere impostato per fornire una divisione per 3, questo è il senso della word di configurazione PLLDIV = 3.
Abbiamo detto che in uscita dal PLL abbiamo 96MHz. Questa frequenza sarà ulteriormente divisa per poter alimentare separatamente la CPU e il modulo USB. Il funzionamento del modulo USB richiede una frequenza di 48MHz, questo è il motivo per il quale la word USBDIV è impostata su 2 (96/2=48). La CPU, invece, ha vari valori di divisione tra cui scegliere, il minimo valore è ancora 2 (CPUDIV = OSC1_PLL2) e scegliamo questo. La CPU lavorerà quindi a 48MHz anche se il quarzo è soltanto da 12MHz!
Come detto tali impostazioni vengono programmate unicamente se non si fa uso del bootloader, in ogni caso queste sono anche le impostazioni già preprogrammate sulla scheda. Con un clock (o meglio una FOSC) di 48MHz, avendo i picmicro serie 18 una frequenza di esecuzione delle istruzioni pari a FOSC/4, otteniamo che il tempo necessario per un ciclo completo di istruzioni (Tcy) è pari a 4/FOSC = 0,083uSec.
Le successive istruzioni nel file di esempio permettono di rimappare la memoria programma in base alla scelta di fare uso o meno del bootloader. In particolar modo è importante rimappare i vettori di interruzione: bisognerà lasciare le istruzioni tal quale senza fare modifiche fino ad arrivare alle righe:
Code:
#pragma code #pragma interrupt YourHighPriorityISRCode // Routine Interrupt ad alta priorità o Interrupt standard in modalità PIC16-compatibile void YourHighPriorityISRCode() { } #pragma interruptlow YourLowPriorityISRCode // Routine Interrupt a bassa priorità void YourLowPriorityISRCode() { }
Passiamo a descrivere il funzionamento dei due esempi
Esempio 1 - Lampeggìo led tramite ritardi
Abbiamo detto che il led è collegato su RA4. La prima cosa che facciamo è quindi impostare RA4 come uscita, in realtà nel programma ho impostato tutto il banco A come uscita:
Code:
TRISA=0x00;
Code:
ADCON1 |= 0x0F;
Code:
Delay10KTCYx(100); Delay10KTCYx(100); Delay10KTCYx(100); Delay10KTCYx(100);
Code:
Delay10KTCYx(100);
MPLAB C18 ha anche altre istruzioni per il ritardo, contenute nel file delay.h e ampiamente descritte nel manuale d'uso che si trova nella cartella "docs" del compilatore. In particolare per poter utilizzare queste istruzioni di ritardo è necessario includere in testa al programma la riga #include <delays.h>
Dopo questo ritardo di 332mS incontriamo l'istruzione:Code:
LATAbits.LATA4^=1;
Esempio 2 - Lampeggìo di un led tramite interrupt
In questo esempio, dal momento che andremo a sfruttare l'interrupt sull'overflow del timer0, bisognerà includere anche la liberia timers che ci facilita il compito:
Code:
#include <timers.h>
- timer0 in modalità ad 8 bit (quella classica usata anche sui pic12/16)
- prescaler per timer0 impostato per 1:64
- ciclo di clock per timer0 derivato dal clock interno (ovvero a 48MHz)
- timer0 precaricato con il valore 68
I calcoli per questi valori possono essere fatti mediante appositi programmi tra cui questo (che però è specifico per pic16) e questo (più completo). Un po' di teoria su come eseguire questi calcoli è illustrata in questo articolo.
Il valore con cui caricare il timer0 l'ho definito con una costante in cima al programma:Code:
#define TMR0_preload 68
Code:
OpenTimer0(TIMER_INT_ON & T0_8BIT & T0_SOURCE_INT & T0_PS_1_64);
Code:
TMR0L=TMR0_preload;
Usando il timer0 in modalità ad 8 bit, il valore viene caricato nel registro TMR0L e rimane quindi inutilizzato il registro TMR0H.
e viene abilitato il flag di interrupt generale e quello su overflow del timer0:Code:
INTCONbits.TMR0IE = 1; INTCONbits.GIE = 1;
Code:
#pragma interrupt YourHighPriorityISRCode // Routine Interrupt ad alta priorità o Interrupt standard in modalità PIC16-compatibile void YourHighPriorityISRCode() { // Questo interrupt scatta una volta al millisecondo if (INTCONbits.TMR0IF) { TMR0L=TMR0_preload; led_counter++; if (led_counter==250) { led_counter=0; LATAbits.LATA4^=1; } INTCONbits.TMR0IF=0; } }
Vedete che il main è difatto vuoto: vengono solo eseguite le routine di inizializzazione ma il ciclo infinito non esegue nessuna istruzione in quanto il led viene fatto appunto lampeggiare dalla routine di interrupt.