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 sulle tecnologie emergenti, 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. Nonostante l’MSP430 di Texas Instruments sia un microcontrollore piuttosto datato, ancora oggi è molto utilizzato e tiene testa ai più moderni chip presenti sul mercato. In questo articolo ne verranno illustrate le caratteristiche ed alcuni esempi applicativi.
Per avere una indicazione dell'efficienza dell’assembler di questa famiglia di microprocessori, quello che segue è lo stesso programma appena illustrato, scritto però in linguaggio macchina (Listato 1).
;****************************** ; ; Toggle di P1.0 con una ; quadra di 1/100 di duty cycle ; Particolarità: funzionamento ; a bassissima freq. ~ 1.5Khz ; ;****************************** #include "msp430x20x3 . h" ;———————————————— ORG 0F800h ;———————————————— RESET mov.w #0280h,SP mov.w #WDTPW+WDTHOLD,&WDTCTL bis.b #LFXT1S_2,&BCSCTL3 bic.b #OFIFG,&IFG1 bis.w #SCG1+SCG0,SR bis.b #SELM_3+DIVM_3,&BCSCTL2 mov.b #0FFh,&P1DIR clr.b &P1OUT mov.b #0FFh,&P2DIR clr.b &P2OUT loop bis.b #001h,&P1OUT mov.w #020,R15 DLY1 dec.w R15 jnz DLY1 bic.b #001h,&P1OUT mov.w #02000,R15 DLY2 dec.w R15 jnz DLY2 jmp loop ;———————————————— ; Interrupt Vectors ;———————————————— ORG 0FFFEh DW RESET END
Listato 1
Come si può notare, la dimensione dei listati è simile ed anche le istruzioni. L’occupazione di memoria infatti è di 91 byte per la versione in C e di 74 byte per la versione assembler.
SCRITTURA IN FLASH.C
Questo esempio è l’illustrazione di una delle principali caratteristiche della famiglia dei microprocessori MSP430 FXXXX: la possibilità di accedere in scrittura alla propria memoria codice. In questo particolare esempio la scrittura viene effettuata in due segmenti di memoria chiamati “information memory” e quindi deputati alla memorizzazione di parametri e dati variabili da ritenere anche in assenza di alimentazione. La potenzialità è comunque quella di poter accedere a qualunque area e di gestire le protezioni dei segmenti. Ovviamente, non si tratta di memoria RAM, quindi gli algoritmi di accesso sono stati ridotti al minimo pur conservando un livello di sicurezza tale da proteggere il sistema da accessi non intenzionali. Come indicato nel sorgente, l’esecuzione del programma prevede un ciclo continuo di ri-scritture della flash; dato che ciò avviene in tempi molto contenuti, in fase di debug occorre mettere un breakpoint sull’istruzione NOP indicata (quello all’interno del while) al fine di non far riscrivere troppe volte la flash che sopporta un numero non infinito di scritture (“solo” 100.000 cicli). Nel main possiamo osservare l’inizializzazione del clock per il processore e per la macchina di gestione di accesso alla flash. Viene poi chiamata all’infinito la routine di scrittura in flash e la copia del segmento C nel segmento D. La prima delle funzioni chiamate, la Scrittura_Seg_C, implementa la procedura di accesso alla flash che è ampiamente descritta nella documentazione della famiglia del microprocessore, ma che possiamo riassumere nelle seguenti fasi:
- Set del bit di erase nel registro di controllo accesso alla flash per richiedere una operazione di cancellazione. Questo set è fatto con la contemporanea scrittura di una chiave di accesso di sicurezza detta FWKEY che permette di evitare che il comando sia scritto per sbaglio.
- Cancellazione del bit di protezione del settore riconfermando la FWKEY.
- Una scrittura fittizia per attivare la macchina di scrittura.
- In questo modo il settore indirizzato è stato cancellato e si può direttamente passare alla scrittura dei dati attivando il bit di write (sempre confermato dalla FWKEY) che predispone la macchina di gestione flash all’accesso.
- La scrittura vera e propria dei dati che nel nostro caso sono i 64 byte del settore posti al valore di “value”.
- Cancellazione del bit di write per chiudere correttamente la sequenza di scrittura.
- Conferma del bit di lock della flash comprensiva della FWKEY.
La funzione Copia_Seg_C_in_D implementa lo stesso algoritmo, ma invece di scrivere il valore “value”, nel settore D copia il contenuto del settore C dimostrando come sia possibile accedere a memoria codice e memoria dati nello stesso ciclo “for”, sia in lettura che in scrittura. Il programma è chiaramente dimostrativo e va usato con il breakpoint del quale si è già parlato, in modo da poter verificare l’avanzamento delle varie fasi (Listato 2).
///****************************** // MSP430F20xx Demo // // Pin di uscita: nessuno // Particolarità; Scrittura in // flash (information memory) // //******************************* #include <msp430x20x3.h> char data; // *** Prototipi *** void Scrittura_Seg_C (char data); void Copia_Seg_C_in_D (void); void main(void) { WDTCTL = WDTPW + WDTHOLD; BCSCTL1 = CALBC1_1MHZ; DCOCTL = CALDCO_1MHZ; FCTL2 = FWKEY + FSSEL0 + FN1; data = 0; while(1) { Scrittura_Seg_C(data++); Copia_Seg_C_in_D(); _NOP(); } } void Scrittura_Seg_C (char data) { char *Flash_ptr; unsigned int i; Flash_ptr = (char *) 0x1040; FCTL1 = FWKEY + ERASE; FCTL3 = FWKEY; *Flash_ptr = 0; FCTL1 = FWKEY + WRT; for (i=0; i<64; i++) { *Flash_ptr++ = data; } FCTL1 = FWKEY; FCTL3 = FWKEY + LOCK; } void Copia_Seg_C_in_D (void) { char *Flash_ptrC; char *Flash_ptrD; unsigned int i; Flash_ptrC = (char *) 0x1040; Flash_ptrD = (char *) 0x1000; FCTL1 = FWKEY + ERASE; FCTL3 = FWKEY; *Flash_ptrD = 0; FCTL1 = FWKEY + WRT; for (i=0; i<64; i++) { *Flash_ptrD++ = *Flash_ptrC++; } FCTL1 = FWKEY; FCTL3 = FWKEY + LOCK; }
Listato 2
CONVERTITORE SIGMADELTA.C
In questo programma si comincia a fare un uso contemporaneo di più risorse del microprocessore. Il convertitore analogico/digitale sigma-delta “legge” un canale analogico connesso al sensore di temperatura interno al microprocessore, campionandolo ogni 240ms. Il valore rilevato viene comparato con la precedente lettura e se la differenza è maggiore di 0,5 gradi centigradi (circa) il LED connesso alla porta P1 viene acceso, altrimenti viene spento. Per generare la tempistica del campionamento si utilizza il watchdog e la ISR viene usata per dare lo start al convertitore per la lettura successiva. Questo banale rilevatore di variazioni di temperatura suggerisce interessanti progetti quali il controllo di caricabatterie, termostati e sicurezze varie su circuiti di potenza. Vediamo più nel dettaglio il sorgente ultra-compatto. La define ADC_Delta rappresenta il valore decimale corrispondente a mezzo grado Celsius nella scala di lettura del convertitore, date le impostazioni prescelte. Segue la definizione della variabile usata per la memorizzazione del valore precedente. Vi sono poi sette righe di impostazioni nelle quali viene attivato il watchdog ed abilitato l’interrupt ad esso associato, attivata la porta di output e programmato il modo di funzionamento del convertitore analogico/digitale. Il processore viene quindi mandato in modalità low power LPM0 in attesa di interrupt. Con la direttiva #pragma vector=SD16_VECTOR si informa il compilatore che la funzione che segue è l’handler di gestione dell’interrupt, che ha come vettore SD16_VECTOR. In questo modo il compilatore metterà automaticamente, all’indirizzo del vettore specificato, un salto alla funzione indicata. Il tipo __interrupt per la funzione void SD16ISR(void) specifica che si tratta di un handler di interrupt e quindi l’istruzione finale dell’assembler corrispondente non deve essere una RET (ritorno da call) ma una RETI (ritorno da handler di interrupt. Nel corpo della funzione che sarà attivata dall’interrupt di fine conversione del convertitore, c’è l’analisi della lettura effettuata e la conseguente azione sul LED. A questo punto il convertitore attiverà un altro ciclo di conversione solo quando l’handler dell’interrupt del watchdog provvederà a dare un nuovo segnale di start. Il debug di questo programma è molto semplice e “curioso”: dopo aver dato il run al programma, sarà sufficiente alitare sulla vostra scheda target per vedere accendere il LED (a meno che non stiate eseguendo il debug a 37°C ...) (Listato 3).
///****************************** // MSP430F20xx Demo // // Pin di uscita: P1.0 // Particolarità; Misuratore di // variazione di temperatura // //******************************* #include <msp430x20x3.h> #define ADC_Delta 31 // ~0.5 ° C // Var. per Ultimo Valore Letto static unsigned int U_V_L; void main(void) { BCSCTL2 |= DIVS_3; WDTCTL = WDT_MDLY_32; IE1 |= WDTIE; P1DIR |= 0x01; SD16CTL = SD16REFON+SD16SSEL_1; SD16INCTL0 = SD16INCH_6; SD16CCTL0 = SD16SNGL + SD16IE ; _BIS_SR(LPM0_bits + GIE); } // Handler interrupt dell’ADC #pragma vector=SD16_VECTOR __interrupt void ADC_handl(void) { if(SD16MEM0 <= UVL + ADC_Delta) P1OUT &= ~0x01; else P1OUT |= 0x01; Ultimo_Valore_Letto = SD16MEM0; } // Handler interrupt del watchdog #pragma vector=WDT_VECTOR __interrupt void wtchdg_tim(void) { SD16CCTL0 |= SD16SC; }
Listato 3