I Micro MSP430 nell’uso pratico – Terza Parte

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.

TIMER A ESEMPIO 1.C

Questo programma usa 2 unità di Capture Compare Register (CCRx) e l’overflow del Timer_A per generare quattro onde quadre a frequenze differenti. Per evidenziare i comportamenti dei CCR, è stata scelta una modalità che prevede il toggle del pin di uscita corrispondente a ogni CCR (P1.1 per CCR0 e P1.2 per CCR1). In questo modo i pin pilotati dai CCRX cambiano stato ogni volta che il conteggio del TAR è uguale al valore impostato nel CCRX stesso. Contemporaneamente al toggle viene generato un interrupt nell’handler del quale viene caricato il conteggio da raggiungere per il prossimo toggle. L’handler delI’interrupt di overflow del Timer_A1 è usato per effettuare il toggle della porta P1.0 mediante apposita istruzione. Date le condizioni di clock impostate (ACLK = n/a, MCLK = SMCLK = TACLK = default DCO circa a 1MHz), su P1.1 sarà presente una forma d’onda a circa 2500Hz, su P1.2 una a circa 500Hz e su P1.0 una a circa 8Hz. Nel programma è facile individuare le seguenti operazioni:

  • Stop del watchdog
  • Collegamento delle porte P1.1 e P1.2 alle periferiche interne del micro (i CCR)
  • Set della direzione delle porte da P1.0 a P1.2 in output
  • Set del modo di funzionamento dei due CCR accedendo al rispettivo registro di controllo CCTLx
  • Selezione del modo di funzionamento del Timer_A ed ingresso in LPM0 con risveglio da interrupt

Negli handler degli interrupt troviamo una gestione canonica per l’interrupt del Timer_A0, mentre per il timer A2 non è così. La gestione dell’interrupt del Timer_ A1 evidenzia una interessante caratteristica di questa architettura, ovvero la vettorizzazione della sorgente dell’interrupt: la prima istruzione dell’handler è uno switch sul simbolo TAIV che contiene il vettore specifico associato alla sorgente di quell’interrupt. Così, nello stesso handler si ottengono la gestione (discriminata dal valore di TAIV) dell’interrupt causato dal CCR1 e la gestione dell’interrupt causato dall’overflow del timer (Listato 1).

//*******************************
// MSP430F20xx Demo
//
// Pin di out: P1.0, P1.1, P1.2
// Particolarità; Generazione di
// 3 segnali a freq. Differente
//
// P1.1 = ~2500Hz
// P1.2 = ~500Hz
// P1.0 = ~8Hz
//
//*******************************
#include <msp430x20x3.h> void main(void)
{
WDTCTL = WDTPW + WDTHOLD; P1SEL |= 0x06;
P1DIR |= 0x07;
CCTL0 = OUTMOD_4 + CCIE; CCTL1 = OUTMOD_4 + CCIE;
TACTL = TASSEL_2 + MC_2 + TAIE;
_BIS_SR(LPM0_bits + GIE);
}
// Handler interrupt del Timer A0
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A0 (void)
{
CCR0 += 200;
}
// Handler interrupt del Timer A0
#pragma vector=TIMERA1_VECTOR
__interrupt void Timer_A1(void)
{
switch( TAIV )
{
// Int generato da CCR1 case 2: CCR1 += 1000;
break;
// Int overflow del timer case 10: P1OUT ^= 0x01; break;
}
Listato 1

TIMER_A ESEMPIO 2.C

In questo esempio viene implementato il toggle della porta P1.1 (purtroppo non quella del LED) utilizzando una modalità associata al funzionamento del timer A. Questa modalità prevede che CCR0 definisca il periodo di conteggio del contatore e che quando viene raggiunto il limite impostato, TA0 effettui il toggle del pin ad esso collegato. In questo modo è possibile generare un’onda quadra con duty cycle del 50%, di frequenza selezionabile mediante CCR0 (in questo caso la frequenza è TACLK/1000), senza usare alcuna risorsa software o tempo di elaborazione CPU. Nel sorgente possiamo facilmente individuare lo stop del watchdog, la selezione dell’uso della porta P1.1 che è connessa ad una periferica interna, la programmazione del CCR0, del timer, ed infine la cosa più interessante: se la CPU non serve, possiamo anche spegnerla (ultima riga) con un _BIS_SR(CPUOFF)! (Listato 2).

///******************************
// MSP430F20xx Demo
//
// Pin di uscita: P1.1
// Particolarità; Generatore di
// onda senza l’uso di tempo CPU
//*******************************
#include <msp430x20x3.h>
void main(void)
{
WDTCTL = WDTPW + WDTHOLD;
P1DIR |= 0x02;
P1SEL |= 0x02;
CCTL0 = OUTMOD_4;
CCR0 = 500-1;
TACTL = TASSEL_2 + MC_1;
_BIS_SR(CPUOFF);
}
Listato 2

TIMER_A ESEMPIO 3.C

Un altro esempio di generazione di forme d’onda è costituito dal sorgente sottostante che implementa la generazione di un segnale PWM (dove la variazione del duty cycle è da effettuarsi modificando i parametri di impostazione dei CCRX) sempre senza l’uso di tempo CPU. In questo caso il timer A è programmato per funzionare in up/down mode. Il registro valore scritto nel CCR0 definisce la frequenza del segnale (in particolare il valore del periodo diviso 2) ed il valore scritto in CCR1, il duty cycle. Con i valori forniti nell’esempio, otterremo un segnale con un duty del 75%. Anche in questo caso, dato che la CPU non interviene nella generazione del segnale, l’ultima riga del listato fa entrare in low power (LPM0) il sistema (Listato 3).

//*******************************
// MSP430F20xx Demo
//
// Pin di uscita: P1.2
// Particolarità; PWM senza uso
// di tempo CPU
//*******************************
#include <msp430x20x3.h>
void main(void)
{
WDTCTL = WDTPW + WDTHOLD;
P1DIR |= 0x0C;
P1SEL |= 0x0C;
CCR0 = 128;
CCTL1 = OUTMOD_6;
CCR1 = 32;
TACTL = TASSEL_2 + MC_3;
_BIS_SR(LPM0_bits);
}
Listato 3

SERIALE SPI.C

Questo è l’unico esempio di quelli riportati in questo articolo che non è facilmente verificabile perché implica l’uso di due microprocessori. In ogni caso, è interessante anche la sola analisi dei sorgenti, per vedere come sia semplice l’implementazione di una seriale SPI lato master. La descrizione si limita al lato master perché è quello che si deve implementare per accedere alla maggior parte dei dispositivi seriali quali memorie EEPROM, flash etc. Ovviamente, tra i programmi demo che si possono trovare su internet non mancano quelli per l’implementazione del lato slave e per la realizzazione dell’altro diffusissimo standard seriale che è l’I2C. Il programma seguente prevede di inviare un messaggio che rispecchia lo stato del pin di input P1.4. Questo messaggio è inviato continuamente, mentre lo stato del LED rappresenta il livello logico del pin di input remoto. Nel listato sono individuabili le linee di codice per effettuare:

  • Il blocco del watchdog
  • Il set della direzione delle porte
  • L’abilitazione del pull-up interno sul pin P1.4 per la sua gestione come input (normalmente a 1 logico, se chiuso a massa va a 0).
  • Le operazioni di inizializzazione della seriale fino alla scrittura del dato da inviare (USISRL = P1IN;).
  • La generazione di un breve impulso di reset del dispositivo slave effettuata portando a 1 logico e poi a 0 il pin P1.2. La particolarità di questa operazione è che viene effettuata non con la scrittura di un valore logico su di un pin già configurato come input ma girandolo temporaneamente in output. Questo forza il livello logico del pin mentre quando ritorna in input il livello logico è quello imposto dall’esterno ovvero dal pull-up presente sul segnale.
  • La generazione di un piccolo ritardo SW per attendere il completamento delle operazioni di reset dello slave imposte con la transizione descritta al punto precedente.
  • L’attivazione della trasmissione viene effettuata ponendo a 8 il valore del contatore di bit trasmessi. In questa macchina infatti, il contatore dei bit trasmessi (o da trasmettere), è l’entità che genera l’interrupt di fine “buffer empty” quando arriva a zero.
  • Segue poi l’handler di gestione dell’interrupt della seriale che nelle prime istruzioni si premura di rappresentare sul pin P1.4 lo stato logico rilevato nel buffer di ricezione (ovvero lo stato del pin del dispositivo remoto) e poi di caricare nel buffer di trasmissione il valore del pin P1.4 locale (Listato 4).
//*******************************
// MSP430F20xx Demo
//
// Pin di uscita: P1.5/6/7 linea
// SPI. P1.0 led. P1.4 pin input
// Particolarità; Seriale SPI
// modalità master.
//
//*******************************
#include <msp430x20x3.h>
void main(void)
{
volatile unsigned int i;
WDTCTL = WDTPW + WDTHOLD;
P1REN |= 0x10;
P1OUT = 0x10;
P1DIR = 0x01;
USICTL0 |= USIPE7 + USIPE6 +
USIPE5 + USIMST +
USIOE;
USICTL1 |= USIIE;
USICKCTL = USIDIV_4 +USISSEL_2;
USICTL0 &= ~USISWRST;
USISRL = P1IN;
P1DIR |= 0x04;
P1DIR &= ~0x04;
for (i = 0xFFF; i > 0; i—);
USICNT = 8;
_BIS_SR(LPM0_bits + GIE);
}
// Handler interrupt di seriale
#pragma vector=USI_VECTOR
__interrupt void
USI_hndler(void)
{
if (0x10 & USISRL)
P1OUT |= 0x01;
else
P1OUT &= ~0x01;
USISRL = P1IN;
USICNT = 8;
}
Listato 4

CLOCK E WD.C

In questo semplice esempio è rappresentata una condizione di fail che provoca l’attivazione del watchdog, ma la particolarità sta nel fatto che il codice stesso vuole simulare una disattivazione accidentale del watchdog. Così il sorgente, composto di 3 righe esegue:

- Come visibile il watchdog questa volta non viene fermato ma viene girata in output la porta del LED.
- Quindi viene effettuato il toggle del pin P1.0.
- Poi vengono disabilitati tutti i clock del processore, che in questo modo si pone in una condizione simile all'LPM4.

A questo punto, avendo disabilitato tutti i clock della macchina, il timeout watchdog scatta ugualmente e resetta il microprocessore che ricomincia l’esecuzione del codice. Il LED in questo modo lampeggerà continuamente e l’operazione di disabilitazione di tutti i clock non ha inibito il corretto funzionamento del watchdog (Listato 5).

//*******************************
// MSP430F20xx Demo
//
// Pin di uscita: P1.0
// Particolarità; Toggle del led
// per intervento del watch dog
//
//*******************************
#include <msp430x20x3.h>
void main(void)
{
P1DIR |= 0x01;
P1OUT ^= 0x01;
_BIS_SR(LPM4_bits);
}
Listato 5

LIMITAZIONI DI FETCH.C

Questo esempio evidenzia una delle particolarità di questo microprocessore, ovvero il controllo dell’indirizzo dal quale viene prelevato il codice da eseguire. Dato che l’architettura Von Neumann consente il fetch di codice anche dalla RAM e che le periferiche sono “memory mapped”, per limitare le possibilità di errore è stato inserito un controllo apposito sull’indirizzo del codice in corso di esecuzione. Se il fetch viene effettuato in un'area illegale (ad esempio dagli indirizzi delle periferiche), il microprocessore viene resettato. In questo caso il toggle del LED, effettuato mediante l’applicazione dell’operatore logico XOR sullo stato di P1.0 stesso, rappresenta il reset del microprocessore e quindi l’avvenuto “trap” della condizione illegale generata mediante l’astuta notazione ((void (*)())0x170)(); che corrisponde alla chiamata di una funzione residente all’indirizzo 0x170. Un altro aspetto particolare di questo programma è costituito dal fatto che il registro TAR (il counter del timer A) è caricato con 0x3FFF all’inizio del programma. La falsa chiamata a funzione di cui abbiamo parlato, eseguirebbe proprio il codice presente nel registro del contatore, codice che corrisponde ad un JMP $ ovvero ad un loop infinito. L’intento del programma è anche quello di dimostrare che altri dispositivi della stessa famiglia, ad esempio MSP430F1XX, non posseggono il trap sul fetch da area illegale e con questo stesso programma, invece di resettarsi, eseguono all’infinito (Listato 6).

//*******************************
// MSP430F20xx Demo
//
// Pin di uscita: P1.0
// Particolarità; Reset del
// processore su fetch illegale
//
//*******************************
#include <msp430x20x3.h>
void main(void)
{
WDTCTL = WDTPW + WDTHOLD;
P1DIR |= 0x01;
TAR = 0x3FFF;
for (;;)
{
volatile unsigned int i;
P1OUT ^= 0x01;
i = 50000;
do (i—);
while (i != 0);
((void (*)())0x170)();
}
Listato 6
Scarica subito una copia gratis

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend