Sei sulla pagina 1di 126

ESERCITAZIONI

CON I MICROCONTROLLORI
TI-MSP430

INDICE

CAP.1 INTRODUZIONE AI MICROCONTROLLORI.....5


Struttura di un Microcontrollore. Scelta del modello. Architettura dei C. Core. Periferiche. Programmare i
microcontrollori. Linguaggio C. Istruzioni e modi di indirizzamento.

CAP. 2 MICROCONTROLLORI TI-MSP430.....11


Famiglie MSP430. Architettura e periferiche ultra low power. Architettura della CPU. Organizzazione della
Memoria. MSP430: Circuiteria di Reset. Moduli e periferiche. Distribuzione del clock. Basic clock module.
Oscillatori del clock. Oscillatori al quarzo. Unified clock system. Modalit low-power. Risveglio del
microcontrollore. Le interruzioni. Interrupt handling procedure.

CAP. 3 HARDWARE E SOFTWARE DI SVILUPPO27


Software di sviluppo. Hardware di sviluppo. LaunchPad Kit. Code Composer Studio (CCS). Software per
iniziare. Ambiente di Sviluppo Eclipse. Installare CCS. Avvio di CCS. Creare un Progetto con CCS.
Compilare un Programma con CCS. Aprire un Progetto con CCS. Creare un File .HEX con CCS.

CAP. 4 ELEMENTI DI PROGRAMMAZIONE.33


Utilizzo ed impostazione delle porte I/O. Registri associati alle Porte di I/O. Controllo dei Bit associati alle
Porte. Esempio: Accendere i LED. Analisi del Programma. Esempio: Uso dei Pulsanti. Filtro Anti-Spikes.
Controllo di attuatori tramite le porte GPIO. Impostazione del clock. DCO. Timer. Timer_A della value line.
Modalit di funzionamento del timer. Modalit Compare. Modalit Capture. Timer Output. Clock accurati.
Oscillatori al quarzo. Auto-calibrazione del DCO. Impostazione delle modalit low power. Impostazione
dei registri per modalit LPM. Esempio di utilizzo del clock e low power mode. Le interruzioni. Codifica
degli interrupt. Applicazioni. Esempio 1: GPIO interrupt. Esempio 2: Timer_A. Esempio 3: Pulse Width
Modulation. Esempio 4: Risveglio da pressione del tasto. Misura della Corrente. Esempio 5: Lampeggio di
un LED per mezzo del Timer_A ed interruzioni. Esempio 6: Lampeggio di un LED per mezzo delle uscite del
modulo Capture e Compare. Esempio 7: Lampeggio di un LED e controllo dellilluminazione con PWM.
Esempio 8: Misura della Frequenza di un segnale digitale.

CAP. 5 PERIFERICHE ANALOGICHE E DIGITALI...65


Comparatori. Configurazione dei registri. Esempio 1: Confronto con tensione di riferimento. Esempio 2:
Capacitance meter. Display LCD. Visualizzare un valore intero. Creazione di una libreria. Convertitori
analogico digitali. Configurazione dei Registri. Memoria Flash. Tecnologie flash. Information memory.
Programmare la flash memory. Flash memory controller

CAP. 6 COMUNICAZIONE SERIALE75


Introduzione. Comunicazione seriale asincrona. UART Protocollo di comunicazione. Accuratezza del
Clock. UART Transmitter. UART Receiver. UART Transceiver. Modalit USCI/UART. Sistemi di
comunicazione sincroni. Serial peripheral interface (SPI). Architettura della SPI. Modalit USI/SPI.
Configurare i registri USI. SPI EEPROM 25XX080. Comandi dellMSP430. Inter-integrated Circuit Bus (I2C).
Introduzione. IC in azione. Modalit USCI/IC. Modalit USI/I2C. EEPROM Microchip 24xx08.

APP. 1 RICHIAMI SUI SISTEMI DIGITALI99


Codifica binaria. Altre codifiche. Rappresentazione numerica in virgola fissa. Rappresentazione numerica in
virgola mobile. Logica combinatoria. Operazioni logiche. Algebra booleana. Elementi di memoria. Latch.
Flip-Flop. Registri. Shift register. Memorie. Memorie elettroniche. Memorie di massa embedded

APP. 2 LINGUAGGIO C..109


Variabili, identificatori, tipi. Tipi di dati. Printf e scanf. La struttura di un programma C. Operatori.
Operatori bitwise. Il controllo di flusso. Cicli. Array e puntatori. Le funzioni. Istruzione sizeof. Stringhe. I/O
da files. Allocazione dinamica. Le strutture. La compilazione. Libreria matematica.

Introduzione ai Microcontrollori 1

CAP. 1

INTRODUZIONE AI MICROCONTROLLORI

I microcontrollori (C) si differenziano dai microprocessori perch hanno molte pi funzioni integrate sullo
stesso chip, qualit che ne consente un uso esteso a molte applicazioni, sebbene una potenza di calcolo
inferiore. In questo capitolo vengono forniti alcuni elementi sulla loro struttura e programmazione in
linguaggio C, la cui conoscenza risulta pertanto un indispensabile presupposto.
Dopo aver presentato gli aspetti generali dei C, si descriver una piattaforma di sviluppo particolare,
lMSP430 Launchpad della Texas Instrument, adatta a chi si accosta per la prima volta al mondo dei C.

STRUTTURA DI UN MICROCONTROLLORE
SCELTA DEL MODELLO
Il modello di C deve soddisfare le specifiche in termini di potenza computazionale. Deve essere in grado di
gestire i segnali di input/output, processare i dati nel tempo stabilito, ed avere abbastanza memoria per
contenere il programma e i dati. Il data processing pu essere realizzato via hw o sw, secondo un opportuno
compromesso. Se si usano periferiche semplici, necessaria maggiore potenza computazionale dal core del
C, mentre con periferiche pi sofisticate si possono eseguire operazioni che richiedono molto tempo,
consentendo di usare un core meno potente. In genere si consiglia di scegliere un C di potenza superiore a
quella strettamente necessaria, per consentire un aggiornamento dellapplicazione senza dover cambiare il
dispositivo.
I tool di sviluppo del software includono tutto quanto necessario per scrivere il programma, in linguaggio
assembly o di alto livello (es. Basic, C, ecc), tradurlo in linguaggio macchina, e caricarlo sulla program
memory dellapplicazione. Questi tool sono in grado di testare sia lhw che il sw attraverso un computer
connesso ad uno strumento esterno, come un emulatore, analizzatore, programmatore PROM ecc., a seconda
della fase di sviluppo.

ARCHITETTURA DEI C
Esiste una vasta gamma di C sul mercato, a seconda della potenza di calcolo, organizzazione interna,
numero di input e output, tipo di periferiche. Comunque un C sempre un sistema completo su un unico
chip, che include un core connesso a della memoria, e circondato da periferiche, secondo lo schema della
figura.

Architettura Von Neumann

Architettura Harvard

Introduzione ai Microcontrollori 1

I vecchi C hanno unarchitettura di tipo Von Neumann, in cui un unico spazio di memoria viene utilizzato
sia per la memoria programma che per la memoria dati (Figura sinistra). Questa architettura si contrappone
allarchitettura di molti altri C, quali i PIC della Microchip, progettati con architettura Harvard, ovvero in
cui memoria Programma e dati risiedono in due diverse sezioni accessibili rispettivamente da un bus
dedicato (Figura destra). Per le istruzioni del programma esiste un bus specifico (dati e indirizzi) che
connette core, memoria di programma, ROM o EEPROM, e un altro bus per i dati, che connette la memoria e
le periferiche.
Nei C presente sia la program memory, che pu essere scritta solo dal programmatore, che la memoria
dati, che volatile e riscrivibile. Ci sono inoltre registri ausiliari come il PC, gli stack register ecc. Nei C, a
differenza dei Processori, sono presenti dispositivi interni come memorie, timer, ADC, porte di I/O ecc., che
non si trovano nei P, sebbene questi ultimi abbiano maggiori capacit di calcolo.
Le periferiche qui mostrate sono solo le pi comuni che si possono trovare in un C, ma altre possono essere
incluse per servizi particolari o protocolli di comunicazione, come, ad esempio, interfacce seriali, decoder RF,
interfacce LCD.

C package
Il package di un C pu essere di tipo DIL (dual in-line), ovvero pin allineati su due linee parallele, che
devono essere infilati nei buchi di una scheda e saldati dalla parte opposta, oppure di tipo SMD (surfacemounted device), ovvero pin di tipo piatto saldati su piazzole sulla stessa faccia della scheda. Per C con pi
di 44 pin ci sono anche le tecniche di packaging PLCC (plastic lead chip-carrier) e QFP (quad flat-pack), un
quadrato con pin sui lati.
Per diminuire il numero di pin, molti pin possono essere configurati sia come input che output.
Central Processing Unit (CPU)
La CPU composta dal core, pi blocchi ausiliari come il generatore di clock, i circuiti di reset, ecc. Deve
essere circondata da dispositivi come la memoria e le interfacce di input/output, integrate in un unico chip. I
dati da processare, sebbene siano sotto forma di bit, possono rappresentare le pi svariate informazioni,
come lo stato di uno switch, la tensione ad un terminale, una stringa di testo, ecc. Questi bit sono
raggruppati in pacchetti, che vengono processati in parallelo. Oggi un C elabora almeno 8 bit in parallelo,
ma ce ne sono da 16, 32 e 64 bit. La CPU controlla quasi tutti i componenti del sistema, eccetto gli interrupt o
il direct memory access (DMA) dove intervengono alcune periferiche.
Memoria Memorizza sia il programma, che dice alla CPU cosa fare, sia i dati, che sono memorizzati
temporaneamente come risultati intermedi del calcolo, sia lo stato globale del sistema. Questo tipo di
memoria volatile (RAM), e deve essere affiancata da una memoria permanente (EEPROM, FLASH),
6

Introduzione ai Microcontrollori 1

quando il dispositivo spento. Lindirizzamento delle due memorie pu essere gestito dallo stesso bus dati,
o da un bus dedicato.
Input/Output Le periferiche di I/O sono il punto di contatto del processore con il mondo esterno, che
generalmente analogico, per cui i dati devono essere convertiti in forma digitale, e poi di nuovo in forma
analogica dopo lelaborazione. Pertanto il throughput del sistema non dipende solo dalla potenza della CPU,
ma anche dallefficienza delle periferiche.
Interrupt Controller Gli interrupt sono il mezzo pi comune per alterare il normale flusso di programma,
quando accade un evento inaspettato. Le richieste di interrupt possono essere gestite secondo una coda,
eventualmente secondo delle priorit, o anche la modifica delle priorit.
Bus Il bus e la serie di connessioni che connettono tutti i componenti del sistema e permettono ai dati di
muoversi, nonch la distribuzione degli indirizzi e dei segnali di controllo.
Clock generator Fornisce un flusso di segnali di clock calibrati, ad una frequenza precisa, che gestisce tutti
i movimenti di dati nel bus, e i calcoli nella CPU. ricavato dalla divisione della frequenza di oscillazione
(fornita da un oscillatore locale interno, o esterno al quarzo), che programmabile, per ottenere il
compromesso migliore tra velocit e consumo di potenza, usando i bit contenuti nel Miscellaneous Register
(MR).
Reset generator Questo circuito si accorge quando il sistema viene acceso, e lo resetta in uno stato predefinito, da cui parte lesecuzione del programma.

CORE
Le principali componenti del core sono:
Unit Logico Aritmetica (ALU) Le principali operazioni dellALU sono Addizione e Sottrazione con
riporto, Incremento e Decremento, Scorrimento (shift) a sinistra e destra a livello di bit, anche circolare,
Operazioni logiche OR, AND, EX-OR, Complemento logico. Alcune eseguono ulteriori operazioni come
Moltiplicazione, Divisione, ecc.
Program counter (PC) un registro, cui connessa lALU, che contiene lindirizzo della successiva
istruzione da eseguire. Viene inizializzato dal Reset generator, in modo da contenere lindirizzo della prima
istruzione da eseguire.
Instruction decoder Questo circuito prende listruzione dalla Program memory e la traduce nel suo
significato, determinando le azioni del core. Le istruzioni sono delle seguenti categorie:
istruzioni di data processing, che forniscono il tipo di operazione e lindirizzo degli operandi;
istruzioni che modificano il flusso di programma, come jump e call.
Stack pointer (SP) un registro che indirizza uno stack, ovvero un area di memoria in cui i dati inseriti in
un certo ordine, possono essere recuperati in ordine opposto (memoria LIFO, Last In First Out). Serve a
gestire interruzioni temporanee del flusso di programma, o attraverso due istruzioni, CALL, che memorizza
lindirizzo della successiva istruzione, prima di saltare in un altro punto, e RETurn, che recupera questo
indirizzo dallo stack, e salta allistruzione corrispondente, o attraverso un interrupt, in cui eventi (dati)
esterni possono interrompere il flusso del programma, che poi verr recuperato.

PERIFERICHE
Le periferiche sono i punti dove il core in contatto con il mondo reale, rappresentato da segnali elettrici. Le
pi comuni periferiche sono:

Introduzione ai Microcontrollori 1

Porte Input/Output parallele Servono per portare dati binary dentro e fuori il core. Una volta
inizializzate, esse appaiono al core come una locazione di memoria, che pu essere letta o scritta. Essi
consistono di porte o latch che consentono la comunicazione nei tempi che decide il programma. Alcuni
costruttori forniscono pin di I/O configurabili. Questi pin possono essere configurati come Input o Output di
altre periferiche, come Timers, interfacce da Seriale a Parallela, o input di un circuito di interrupt o di un
ADC. Talvolta per necessario leggere lI/O bit per bit, piuttosto che interi byte. Inoltre alcuni segnali
esterni di altre periferiche (timer, UART ecc.) usano alcuni bit delle porte parallele. In genere ogni bit pu
essere configurato come input (con o senza resistenza di pull-up), o un output (open-drain o push-pull).
Sono anche disponibili alcuni pin con driver per alte correnti. possibile selezionare lopzione attraverso
registri associati a ciascuna porta. Sono memory-mapped e si chiamano Option Register (OR) e Data
Direction Register (DDR). Alcuni pin configurati come input possono anche essere connessi ai circuiti
esterni di interrupt, se il corrispondente bit dellOR settato. Ci permette che una richiesta di interrupt sia
triggerata quando lo stato del pin diventa basso, si alza o si abbassa, a seconda di come configurato nel
MR.
Timer Programmabile un blocco complesso, basato su un contatore, che pu essere usato per contare
impulsi, misurarne la durata o la frequenza, produrre impulsi di uscita precisamente sincronizzati, e molte
altre applicazioni grazie alla sua flessibilit e precisione. Per esempio quando si misura un parametro fisico
(temperatura, livello, pressione), invece di utilizzare un sensore che fornisce una tensione analogica, pi
facile e accurato usarne uno che produce un segnale squadrato, con una frequenza che dipende dal valore
del parametro, che pu essere calcolato con il timer. Questi segnali sono pi insensibili alle interferenze ed al
rumore.
Serial Peripheral Interface (SPI) basata su un registro a scorrimento (shift register), che pu operare una
conversione da seriale a parallela e viceversa. Pu essere usata per interfacciare memorie esterne ad accesso
seriale, che forniscono una memorizzazione non volatile a basso costo.
Watchdog Timer un timer supplementare che pu essere usato per proteggere il sistema contro guasti
dovuti al programma stesso (per esempio quando uneventualit non era stata prevista), o un guasto
momentaneo dellalimentazione, o uninterferenza elettromagnetica. In entrambi i casi il programma va in
crash ed il sistema non pi stabile. Questo pu avere gravi conseguenze in applicazioni come i sistemi
automotive o quelli di sicurezza. Questo timer settato per una certa durata allaccensione. Il programma lo
deve resettare periodicamente al suo valore iniziale, altrimenti il timer va in overflow, e questo evento
procura un reset dellhardware che rimette il sistema nello stato iniziale di accensione.
Non una protezione totale, poich alcune parti del programma possono andare in crash mentre la parte che
deve resettare il clock sta ancora operando. Il WT controllato da un registro che include due bit di controllo
e sei bit di time-setting.

PROGRAMMARE I MICROCONTROLLORI
Il progettista di un applicazione deve avere cura di selezionare correttamente i pin di input e output, poich
alcuni pin devono essere connessi a speciali periferiche come ADC, Timer, ecc.
il programma che configura i pin in modo che abbiano il corretto comportamento elettrico, e che elabora i
dati per produrre unappropriata risposta ai segnali di ingresso. Poich la scheda progettata per avere
meno elaborazione elettronica possibile, tutta lelaborazione compiuta dal software. Programmare il
processore pertanto lattivit del progettista, che dovr scegliere gli strumenti adatti, perch il tempo di
sviluppo del programma e del suo test possono variare molto a seconda della piattaforma e del linguaggio
usato.
Il linguaggio compreso dalla CPU il linguaggio macchina ovvero una sequenza di numeri binari. Il
linguaggio Assembly leggermente pi evoluto nel senso che le istruzioni sono formate da insiemi di
caratteri, ma c ancora una corrispondenza biunivoca tra istruzione Assembly e istruzione linguaggio
macchina. I linguaggi pi evoluti (Fortran, Pascal, C,C++) sono compilati: cio ogni istruzione tradotta in

Introduzione ai Microcontrollori 1

una sequenza di istruzioni in linguaggio macchina. Un programma scritto in ANSI C portabile su qualsiasi
computer dotato di compilatore C, non dipende cio dallarchitettura.
Il linguaggio Assembly il linguaggio nativo di ogni C, necessita molta attenzione e molte linee di
codice. Oggi si usano quasi esclusivamente linguaggi ad alto livello, ma, in quasi tutte le applicazioni, ci
sono parti di codice che richiedono Assembly:
nei linguaggi ad alto livello, alcune parti di inizializzazione del programma, come lorganizzazione della
memoria (indirizzi della ROM, della RAM, e dei vettori di reset e interrupt), sono fornite in linguaggio
Assembly, che deve essere adattato allapplicazione;
alcune ISR che richiedono unelaborazione molto veloce;
alcune funzioni ripetitive che vengono richiamate spesso sono in tal modo ottimizzate in termini di
velocit. In questo caso il programmatore deve leggere con attenzione sul manuale del compilatore ad
alto livello la modalit di fornire gli argomenti alla funzione e leggerne le uscite.
Sviluppo del programma Consiste di tre fasi:
1. analisi il programmatore definisce ci che deve fare il programma, i dati di ingresso e uscita, la
memorizzazione e gli algoritmi necessari;
2. scrittura del codice la traduzione del punto 1 nel linguaggio scelto, usando un text editor. Il risultato
il codice sorgente ed alcuni file che pilotano gli strumenti di programmazione. Lassembler e il linker ne
controllano la correttezza sintattica;
3. debugging rimuove gli errori di programmazione relativi alla logica, alla coordinazione e al
processamento dei dati. Gli strumenti usati sono il Simulatore e lEmulatore circuitale.
Quando il programma perfettamente funzionante viene memorizzato in un EPROM, esterna o interna.
Questo produce un prototipo che deve essere ancora testato prima di applicarlo.
Assembler un programma che traduce un file di testo, che rappresenta il codice sorgente del
programma, in un file binario che contiene una serie di istruzioni specifiche per il C. Loutput
dellassembler essenzialmente il file object, che contiene i dati binari da processare ulteriormente, e non
pu essere letto. Se lassembler riscontra degli errori produce un report di errore.
Linker A meno che il codice sorgente che descrive lapplicazione sia molto piccolo, in genere conviene
dividerlo in diversi file, e assemblarli separatamente, in modo che, dovendo effettuare delle modifiche, si
cambia solo il file interessato, che viene ri-assemblato. Ogni file pu fare riferimento a variabili o righe di
codice (label) di un altro file, per cui nel codice si usano, attributi di tipo EXTERNAL, per dichiarare che una
label proviene da un altro codice sorgente, e PUBLIC per dichiarare che una label (o variabile) definita in
questo file sar utilizzata da un altro file. Il Linker lo strumento che concatena uno dopo laltro tutti i file
object in un unico file object, detto assoluto, e corregge gli indirizzi di memoria che si riferiscono ad oggetti
la cui locazione di memoria stata definita o cambiata dalla concatenazione. Le due operazioni
dellAssembler e del Linker devono essere eseguite da strumenti dello stesso produttore.
Il file object risultante pu essere scaricato in un emulatore per testare il programma, o nella memoria
EPROM di built-in del C, che contiene il codice dellapplicazione, usando uno strumento chiamato EPROM
burner. Questo accetta file object assoluti prodotti dal Linker dello stesso sviluppatore, ma spesso anche file
generati in un formato standard esadecimale, che consente di trasportare i file. Se il Linker non in grado
di generarli occorre usare un convertitore di codice.
Simulatori uno strumento per far girare un programma assoluto eseguibile. Come gli altri strumenti pu
essere utilizzato sul computer del programmatore. Interpreta il codice macchina specifico del C, e fornisce i
risultati sul display del computer, senza la necessit di utilizzare il C. Il programma pu essere eseguito
istruzione per istruzione, potendo monitorare i cambiamenti dei valori memorizzati nei registri o nelle
locazioni di memoria. Si possono inserire breakpoints nel programma, per interromperlo solo in quei punti
in cui necessaria unanalisi pi accurata di tipo passo-passo. Una finestra mostra il codice sorgente, ed un
cursore indica listruzione che viene eseguita. Altre finestre possono mostrare i contenuti dei registri e della
memoria. Lutilizzatore pu anche modificare contenuti di memoria, per correggere errori di programma, e
continuare lesecuzione con i valori corretti.
9

Introduzione ai Microcontrollori 1

Emulatori in-circuit Sono sofisticati strumenti hardware che si comportano come il C, e inoltre
consentono allutilizzatore di avere, in ogni momento, tutte le informazioni sullo stato degli ingressi e delle
uscite, e i contenuti di memoria. Lemulatore ha le stesse possibilit interattive del simulatore. La differenza
che in questo caso, quando viene eseguito il programma, il C interagisce con il mondo reale, processando
dati reali. La possibilit di operare un monitoraggio costante dei bus in tempo reale (real-time tracing), in
termini di dati, indirizzi e segnali di controllo, consente allutilizzatore di eseguire il programma alla
massima velocit, e poi dopo analizzare i dati registrati.

LINGUAGGIO C
oggi il linguaggio ad alto livello universalmente usato per programmare i C. Il vantaggio che ogni
istruzione implica centinaia di istruzioni in linguaggio macchina, sebbene il linguaggio rimanga comunque
vicino allhardware. un linguaggio strutturato, nel senso che il codice pu facilmente essere diviso in
blocchi che diventano funzioni, o il corpo di istruzioni condizionali o cicliche. Inoltre garantisce una
grandissima portabilit, sia da un computer allaltro, sia da un C allaltro. Ci consente, ad esempio, di
sviluppare il programma su un computer, prima ancora di avere il C, farne il debug, e poi successivamente
implementarlo su un C.
Un difetto del linguaggio che prevede un unico spazio di memoria, che contiene il codice e i dati di I/O,
sebbene nei C il codice risiede nella ROM, i dati nella RAM. Il C non in grado di specificare i rispettivi
indirizzi di memoria. Inoltre il C non ha istruzioni che prevedano interrupt. Tuttavia le diverse
implementazioni del linguaggio C per C prevedono soluzioni per ciascuno di questi casi, ma poich
variano da unimplementazione allaltra, possono causare la non portabilit del codice.
Lultimo argomento da considerare che solo utilizzando un ben strutturato e documentato linguaggio ad
alto livello come il C, si possono creare applicazioni in grado di soddisfare i sempre pi stringenti requisiti di
qualit, per esempio degli apparati sanitari.
Compiling and debugging in C Il compilatore C lo strumento che traduce il codice sorgente in
linguaggio macchina, trasformando un file testo in un file object. I diversi file object che compongono il
programma, detti anche moduli, dopo essere stati compilati separatamente, vengono linkati per produrre un
file object assoluto, come con lAssembler. Addirittura si possono scrivere alcuni moduli in C ed altri in
Assembly e poi linkarli per produrre un file object assoluto.
Un grande vantaggio nel fare il debug in C consiste nel fatto che ora possibile tracciare landamento anche
di variabili di tipo complesso come strutture, array e stringhe, riducendo molto il tempo di debug.

ISTRUZIONI E MODI DI INDIRIZZAMENTO


Modi di indirizzamento Se lindirizzo del byte da leggere o scrivere noto nel momento in cui viene
scritto il programma, viene usato il direct mode addressing, con due varianti: short per la pagina zero e long
per tutta la memoria. Se lindirizzo del byte non noto quando si scrive il programma, significa che il dato
ha una struttura complessa, ma poich il C elabora solo byte, necessario elaborare il dato un byte alla
volta, per cui lindirizzo del byte calcolato durante lesecuzione del programma. Lindirizzo pu anche
essere letto da un registro di indice (indexed addressing) o da una cella di memoria (indirect addressing). Le
istruzioni che caricano costanti in un registro usano limmediate addressing, che significa che il dato viene
caricato nella program memory subito dopo listruzione. Le istruzioni di salto spesso saltano ad un indirizzo
che vicino allindirizzo dellistruzione. Per risparmiare memoria, se lindirizzo del salto non molto
lontano (es. salto condizionato), si usa il relative addressing, contenuto in un byte, detto displacement.
Lultima modalit, detta inherent addressing, significa che non necessario assegnare un indirizzo al dato
contenuto nellistruzione (es. incremento).
Codifica delle istruzioni Le istruzioni vengono codificate da byte memorizzati nella program memory. I
byte sono 4: prefix byte (opzionale) operation code (opcode) e uno o due byte di indirizzo (opzionali).

10

Microcontrollori TI-MSP430 2

CAP. 2

MICROCONTROLLORI TI-MSP430

Gli MSP430, nati nel 1990 da un gruppo di progettazione della TI hanno trovato spazio nelle applicazioni
emergenti che richiedono bassi consumi. MSP430 sta per Mixed Signal Controller, ovvero paragonabile ad
un DSP (Digital Signal Processor) ed ha unarchitettura a 16bit RISC (Reduced Instruction Set
Controller) di tipo Von Neumann, in cui un unico spazio di memoria viene utilizzato sia per la memoria
programma che per la memoria dati.
Sotto certi aspetti larchitettura von-Neumann porta a essere meno efficienti in termini di accesso ed
elaborazioni dati, vista la presenza di un solo bus. Tuttavia la semplicit di utilizzo tale che molti
programmatori programmano gli MSP430 in assembly. A supporto della facilit di utilizzo, gli MSP430
hanno un set di solo 27 istruzioni. Larchitettura interna, grazie ai numerosi registri general purpose e al
constant generator, permette al compilatore o ai programmatori abili di ottimizzare il codice al pari di
architetture Harvard.
Gli MSP430 integrano al loro interno molte periferiche analogiche, rendendoli unottima scelta per
applicazioni con sensori. Tra le altre:
! ADC 10 bit (di tipo SAR)
! ADC 12 bit (di tipo SAR)
! ADC 16 bit (di tipo Sigma Delta, con ingressi single ended e differenziali)
! ADC 24 bit (di tipo Sigma Delta, con ingressi single ended e differenziali)
! Comparatori
! Amplificatore Operazionali
! Cap Touch (Sensori capacitivi)
! Scan Interface (periferica simile ai moduli per encoder)
! Transceiver RF (Modulo RX e TX per frequenze sub 1GHz)
Tra le periferiche ed interfacce digitali di maggior interesse si trovano:
! Timers, Capture e Compare, PWM
! UART, I2C, SPI,USB
! Moltiplicatore 32x32 bit
! DMA (Direct Memory Access)
! Clock ortogonale
! Modulo Data Encription
! Interfaccia LCD
LMSP430 nasce per applicazioni Mixed Signal, ovvero per quelle applicazioni che rappresentano il punto
dincontro tra il mondo analogico e digitale. Gli MSP430 rappresentano il punto di partenza per applicazioni
embedded destinate ad essere alimentati a batteria. Lessere stati pensati per applicazioni a bassi consumi
permette di avere unintera famiglia dalla quale poter selezionare il C migliore, garantendo di poter
ottimizzare la propria applicazione. La TI ha recentemente rilasciato una nuova famiglia di C MSP430
facente uso della memoria FRAM (Ferroelectric RAM) permettendo di abbassare ulteriormente i consumi in
Active Mode (ovvero quando viene eseguito il codice) ed ampliare le applicazioni in cui poter utilizzare gli
MSP430.

FAMIGLIE MSP430
La famiglia MSP430 si divide in diverse sottofamiglie in particolare presente la MSP430F1xxx,
MSP430F2xxx, MSP430G2xxx, MSP430C3xxx, MSP430F4xxx, MSP430F5xxx, MSP430F6xxx. Da queste
famiglie principali ci sono alcune sottofamiglie specifiche per alcune applicazioni tra cui la FRAM.
Diversamente da quanto si possa pensare, le sottofamiglie non sono nate in ordine crescente e ogni
sottofamiglia non rappresenta esattamente un modo per rendere obsolete le altre. La prima famiglia ad

11

Microcontrollori TI-MSP430 2

essere commercializzata stata la famiglia MSP430C3xxx, caratterizzata dallavere una memoria OTP (One
Time Programmable memory) e ROM programmate tramite maschere direttamente in fase di produzione del
Chip. La C indica proprio il fatto che si ha a che fare con la memoria OTP o ROM. Nella stessa famiglia
possibile trovare anche versioni cancellabili con i raggi ultravioletti, ovvero i famosi dispositivi con la
finestrella (EPROM). Queste varianti furono pensate per favorire la fase di prototyping (la sigla di questi
MSP430 PMS430E3xx, dove la prima P sta appunto per Protoyping e la E per EPROM). Sebbene i C con
memoria OTP non siano presenti nelle altre versioni, la TI offre la possibilit di richiedere la versione ROM
di qualunque C. Questo viene per fatto solo per grandi volumi, visto che la memoria ROM costa meno
della memoria Flash. Per applicazioni che siano sensibili ai costi e in cui non sia richiesto aggiornare il
Firmware, pu risultare una opzione vantaggiosa. In ambito hobbystico o in fase di sviluppo le memorie
OTP, UV EPROM, rappresentano una pessima scelta, visti i tempi di sviluppo richiesti. Per tale ragione
quando la memoria Flash divenne una soluzione economicamente vantaggiosa in termini di vantaggi/costi
(fine anni 90), fu introdotta la famiglia MSP430F1xxx, dove la F indica appunto che si ha a che fare con una
memoria Flash. Attualmente, (ottobre 2011) gli MSP430 supportano fino a 256KB di Flash, ma larchitettura
della nuova famiglia extended MSP430X (indirizzamento a 20 bit piuttosto che a 16) supporta fino a 1MB di
Flash. Dopo la famiglia MSP430F1xxx, stata introdotta la famiglia MSP430F2xxx pensata quale
ottimizzazione della precedente famiglia. La famiglia F2xxx, sebbene non sostituisca la F1xxx, possiede
diversi modelli 1:1 compatibili con la versione F1xxx ma ad un costo inferiore. Dopo la versione F2xxx stata
introdotta la famiglia MS430F4xxx, che praticamente uguale alla famiglia F2xxx ma introduce il modulo
LCD per pilotare direttamente i display LCD. Le famiglie F2xxx e F4xxx sono ottimizzate per applicazioni
che devono operare in Sleep Mode gran parte del tempo, ovvero eseguire del codice solo per brevi tempi. La
famiglia ha introdotto anche il primo modello di C con ADC Sigma-Delta a 16bit, recentemente introdotto
anche nella famiglia MSP430AFE2xxx con una risoluzione a 24bit. Il convertitore Sigma-Delta, avendo
ingressi sia single-ended che differenziali, si presta per una lettura diretta di sensori a ponte ovvero il cui
segnale differenziale. La versione AFE, che sta per Analog Front End, oltre a possedere la versione ADC
Sigma-Delta, possiede anche amplificatori operazionali a guadagno variabile, al fine di poter amplificare
direttamente il segnale proveniente da un sensore (ovvero senza dover far uso di componenti esterni). In
seguito stata introdotta la famiglia MSP430F5xxx e MSP430F6xxx; analogamente alle versioni F2xxx e
F4xxx, la differenza tra la famiglia F5xxx e la famiglia F6xxx risiede nella presenza o meno del modulo LCD.
Larchitettura F5xxx (uguale a F6xxx) stata ottimizzata per applicazioni che devono operare in Active
Mode (ovvero che devono eseguire del codice) per tempi lunghi. Tale ottimizzazione stata fatta anche per
la recente famiglia MSP43FR57xx, basata sullarchitettura F5xxx ma utilizzante la memoria FRAM piuttosto
che la memoria Flash. Da circa un anno la Texas ha introdotto la Value Line, ovvero la famiglia
MSP430G2xxx, caratterizzata dalla stessa architettura della famiglia F2xxx, basata anche essa su memoria
flash, ma ottimizzata per avere costi comparabili con i C a 8 bit (la G sta per Gate). Il vantaggio della
famiglia Value Line, almeno dal punto di vista hobbystico, il fatto che i vari modelli sono stati introdotti
con un package DIL (Dual In Line Package) ovvero utilizzabili su normali breadboard, senza far stancare il
nostro occhio o richiedere mani da chirurgo (le altre famiglie sfortunatamente non hanno il package DIL).
Nel nostro percorso di studio verranno sviluppate applicazioni facendo prevalentemente uso della famiglia
Value Line e del tool di sviluppo Launch Pad. Per applicazioni pi complesse si far uso delle Experimenter
board.
Oltre alle famiglie appena introdotte ci sono alcune varianti parzialmente descritte. Di rilievo oltre a quelle
accennate la variante MSP430FW4xx ottimizzata per il Water metering (con periferica Scan Interface). La
variante MSP430FE4xx con Front End analogico e ADC Sigma Delta. La variante MSP430FG4xx ottimizzata
per Glucose Meter (la G della value Line). La variante MSP430C09x, caratterizzata dal poter funzionare con
una sola batteria senza richiedere charge pump interni. Suo unico difetto che per poter funzionare a
tensioni cosi basse la memoria flash sacrificata ed esiste solo la versione ROM. Si ricorda poi la variante
CC430Fxxx, con modulo RF sub 1GHz. Il nome di questa variante discende dallaver integrato in un unico
chip il C MSP430 con il transceiver CC1101 Sub 1GHz; la CC sta per ChipCon, ovvero la societ
scandinava acquisita nel 2006 dalla TI.

12

Microcontrollori TI-MSP430 2

ARCHITETTURA E PERIFERICHE ULTRA LOW POWER


In questa sezione si analizzer sinteticamente l'architettura degli MSP430 e le periferiche disponibili. La
descrizione sincentrer prevalentemente sulla famiglia MSP430F2xx ed in particolare la Value Line.
Ciononostante sintrodurranno anche le differenze e miglioramenti apportati nelle altre famiglie, in maniera
da poter permettere al lettore unappropriata scelta dell'MSP430 che permette di risolvere le problematiche
associate
alla
propria
applicazione.
Sebbene larchitettura delle varie famiglie MSP430 sia in pratica uguale, bene precisare che la parola
architettura fa riferimento alla CPU. Le periferiche e i moduli che arricchiscono il C non sono
necessariamente uguali come la CPU, per cui come visto in precedenza, sono presenti molte varianti di
MSP430.
Sebbene questo capitolo possa essere una valida guida all'architettura e periferiche degli MSP430, non deve
essere considerata lunica fonte dinformazione, ma piuttosto un punto di partenza. Molti dettagli sui
registri e le impostazioni sono volutamente tralasciati per non ripetere dettagli che saranno trattati nei
capitoli dedicati alle varie periferiche. Come detto ogni famiglia MSP430 possiede una Family User Guide.
La User Guide contiene la descrizione dell'architettura e introduce le varie periferiche e registri presenti nella
famiglia stessa. Non tutte le periferiche introdotte in una User Guide sono effettivamente presenti in ogni
C. Normalmente, per ragioni di prezzo, sono presenti diverse varianti di C che possono o meno avere
determinate periferiche. Oltre alla User Guide, bisogna fare riferimento al datasheet del relativo C
utilizzato. Nel datasheet sono presenti altre informazioni specifiche del dispositivo o variante della famiglia.
In particolare la piedinatura, periferiche effettivamente disponibili, le caratteristiche elettriche e
configurazioni dei pin associati alle periferiche.

ARCHITETTURA DELLA CPU


Gli MSP430 sono stati progettati Ultra Low Power sin dalla prima versione. Effettivamente, come vedremo
in varie parti del corso, tutte le scelte sono guidate tenendo a mente la necessit di essere Ultra Low Power.
Architettura e periferiche giocano un ruolo importante al fine di avere un sistema a basso consumo. Infatti
ci che alla fine interessa non solo che la CPU consumi poco ma che lintero sistema abbia consumi
contenuti. Nella prima parte del corso abbiamo brevemente introdotto larchitettura degli MSP430
accennando solamente che era di tipo RISC (Reduced Instruction Set Computer) von-Neumann a 16 bit.
Maggiori dettagli possono essere visti nello schema a blocchi di un MSP430 riportato in Figura. E possibile
vedere la presenza di un solo bus dindirizzo (MAB, Master Address Bus) e un Master Data Bus MDB.
Entrambi sono a 16 bit ma il MDB ha alcune parti ad 8 bit per ospitare le periferiche ad 8 bit. Si noti che
linterfaccia JTAG utilizzata per la programmazione e Debug del sistema ha accesso ad entrambi i bus,
permettendo di effettuare Debug real Time senza interferire con l'esecuzione del programma.

Figura 2: Schema a blocchi di un MSP430.

13

Microcontrollori TI-MSP430 2

Larchitettura di von-Neumann ha la caratteristica di avere una sola area di memoria dove risiedono sia il
programma che i dati. Questo, se da un lato non rappresenta unottimizzazione da un punto di vista della
manipolazione dei dati, come per larchitettura Harvard, rappresenta una soluzione ottimale per ridurre al
minimo la complessit della CPU. Ridurre la complessit si traduce nellutilizzare meno circuiteria e
questo permette, in generale, di ridurre i consumi. La scelta dellarchitettura von-Neumann discende anche
da una ragione di semplicit concettuale che permette ai programmatori di utilizzare direttamente
lAssembly senza eccessivo stress. A supporto di questa semplicit gli MSP430 possiedono solo 27 istruzioni,
che permettono praticamente di essere imparate a memoria in poche ore di pratica. Sebbene i compilatori in
C integrati in CCS e IAR siano ottimizzati per raggiungere un buona efficienza di compilazione, scrivere in
Assembly permette in generale di raggiungere codici ottimizzati allestremo (presupponendo che il
programmatore sia esperto). Ottimizzare significa spesso cercare di ottenere un codice di dimensioni ridotte
o un codice che permetta di ridurre i consumi. Tuttavia, se non necessario, scrivere il codice in C consente
una pi facile leggibilit del codice, e la possibilit di impostare il compilatore al fine di ottimizzare in
termini di dimensioni del codice, velocit o consumi. Alcune volte le ragioni dello scrivere il codice in
Assembly risiedono nel fatto che i compilatori Assembly sono gratuiti, mentre quelli per linguaggio ad alto
livello sono a pagamento. Grazie ad ununica area di memoria stato inoltre possibile ottimizzare il
trasferimento dati, rendendo larchitettura ortogonale, nel senso che i dati possono essere letti, scritti e
trasferiti da varie parti di memoria per mezzo di una sola istruzione MOV. Le varie tipologie di
indirizzamento (addressing modes) sono sette, ma si rimanda alla User Guide per maggior dettagli (scrivendo
il codice in C, l'utilizzo delle varie modalit trasparente). Ogni istruzione, sebbene larchitettura degli
MSP430 sia a 16 bit, pu essere utilizzata sia in operazioni a 16 bit che a 8 bit. Per la natura dellarchitettura
MSP430, molti registri sono a 16 bit, ma in diverse applicazioni ritorna utile scrivere e leggere dati in formato
ad 8 bit (in Assembly le istruzioni su byte sono caratterizzate dallavere un .B dopo listruzione stessa).
Larchitettura MSP430, sebbene sia semplice a tal punto da permettere di essere programmata in Assembly,
ottimizzata per permettere a compilatori di alto livello di realizzare codice ottimizzato. In particolare
possiede 16 registri interni nominati da R0 a R15, che permettono un movimento rapido dei dati nello
svolgimento di molte operazioni. I registri da R4 a R15 sono per utilizzo generico, mentre i registri R0R3
hanno una funzione speciale. R0 rappresenta il Program Counter (PC), ovvero il registro che punta
all'indirizzo di memoria che contiene la prossima istruzione da eseguire. Sebbene larchitettura sia a 16 bit, la
memoria in realt strutturata in byte, dunque ogni istruzione occupa due byte o meglio 2,4,6 a seconda
della tipologia dell'istruzione stessa. Durante lesecuzione di un programma il PC viene automaticamente
incrementato di 2, al fine di puntare listruzione successiva; in particolare il suo bit meno significativo
sempre 0, ovvero allineato ad indirizzi pari. Il registro R1 rappresenta lo Stack Pointer (SP), ovvero il
registro che tiene lindice sullo Stack in cui vengono memorizzati gli indirizzi di ritorno, dopo lesecuzione
di una subroutine dinterrupt o semplice funzione (anche le istruzioni PUSH e POP fanno uso dello Stack).
Diversamente dai PIC della Microchip, lo Stack non ha uno spazio limitato e predefinito, bens deve essere
inizializzato allavvio di un programma. In Assembly questo obbligatorio, mentre in C viene fatto in
automatico e posto alla fine della memoria RAM. Questo deriva dal fatto che lo SP viene decrementato ad
ogni nuovo indirizzo da memorizzare nello Stack, e incrementato quando questo deve essere riscritto sul PC
(non si considerano le istruzioni PUSH e POP che permettono di memorizzare nello Stack anche dei dati). Il
registro R2 rappresenta lo Status Register (in figura).
Questo registro di particolare importanza poich, oltre ad avere i bit classici di uno Status Register (Z, N,
C), possiede anche i bit relativi alle modalit a bassa potenza, scelta che, come si vedr nel paragrafo
dedicato alle modalit Ultra Low Power, permette di semplificare il passaggio da una modalit allaltra. Il
registro R3 rappresenta il Constant Generator, ovvero generatore di costanti. Tale registro tanto semplice e
banale quanto efficace. Ha il compito di generare le costanti 0,1,2,4,8. Se unistruzione contiene una di queste
costanti, permette di ridurre la dimensione di questultima risparmiando il byte per memorizzare la stessa.
Gli MSP430 sono a 16 bit, ed in particolare lAddress Bus (MAB, Master Address Bus) e il Data Bus (MDB,
Master Data Bus) sono a 16 bit. Questo significa che possibile indirizzare uno spazio di memoria fino a
64KB (tra Flash e RAM). I primi MSP430 avevano una memoria Flash di pochi KB e solo in seguito si
arrivati a 64KB per poi superarli. Gli MSP430 sin dallinizio sono nati senza paginazione della memoria,
ovvero, diversamente dai PIC16, in cui per esempio necessario impostare il banco della memoria, gli
MSP430 hanno un unico spazio di memoria, che semplifica molto la stesura dei programmi anche in
Assembly. Quando la memoria degli MSP430 ha superato i 64KB indirizzabili dai 16bit, la soluzione stata
14

Microcontrollori TI-MSP430 2

quella di mantenere gli MSP430 senza paginazione della memoria; questa scelta ha richiesto notevoli
modifiche alla CPU. Lestensione della memoria ha portato alla nascita dellarchitettura MSP430X (estesa) a
20 bit che permette un indirizzamento di memoria massima fino a 1MB. Tutti i registri interni (e quasi tutti i
registri delle periferiche), ad eccezione dello Status Register, sono stati estesi da 16 a 20 bit, inoltre sono state
introdotte nuove istruzioni al fine di poter gestire operazioni a 20 bit. I dettagli sulle nuove istruzioni
disponibili si trovano allinterno della User Guide. Qualche nota merita il fatto che il PC, nel caso si
verifichino delle interruzioni, viene salvato come in precedenza, ma i 4 bit in eccesso sono salvati allinterno
dello Status Register, nei bit che nella vecchia architettura risultavano riservati. Questo permette di
risparmiare loverhead derivante dal salvataggio dei 20 bit del PC, infatti lo Status Register viene salvato
anche nell'architettura MSP430 Standard. Questa la ragione per cui lo Status Register rimasto a 16 bit,
infatti in questo modo salvare il PC e lo SR non porta nessun overhead rispetto alla struttura tradizionale
degli MSP430. Lo spezzettamento e ricomposizione del PC nello Stack trasparente al programmatore. Il PC
nel caso di semplici chiamate di funzioni invece salvato facendo uso di 4 byte, ovvero senza utilizzare lo
Status Register, che infatti non viene messo nello Stack.

Alcune istruzioni con il formato X sono state ottimizzate al fine di ridurre l'overhead derivante dalla nuova
architettura a 20 bit. Queste ottimizzazioni permettono di eseguire unapplicazione in maniera praticamente
inalterata sia in CPU con architettura a 16 bit classica che con architettura a MSP430X (almeno da un punto
di vista temporale). La nuova architettura talmente trasparente al programmatore che anche sul catalogo
non sono riportati i dettagli sul tipo di CPU, MSP430 o MSP430X, ma vedendo la dimensione della memoria,
si intuisce subito che larchitettura sicuramente MSP430X qualora la memoria Flash+RAM sia maggiore di
64KB. Per memoria inferiore a 64KB non detto, prendendo per esempio la nuova famiglia MSP430FR5739
con memoria FRAM, che, pur essendo a 16KB, possiede unarchitettura MSP430X. Infatti gli MSP430
con FRAM hanno unarchitettura ereditata dalla famiglia MSP430F5xxx. Dal lato della programmazione,
utilizzare unarchitettura MSP430 o MSP430X non porta molti cambiamenti, ma effettivamente bisogna
saperlo al fine di una corretta programmazione, qualora si voglia ottimizzare il codice o si voglia mantenere
compatibilit tra un codice scritto (in Assembly) per MSP430 e MSP430X. In particolare i compilatori
permettono di selezionare se utilizzare lo spazio di memoria ridotto o esteso. Se si fa uso del modello ridotto,
compatibile sia con gli MSP430 che MSP430X, possibile fare salti di memoria in un range di 64KB, per cui
bisogna fare attenzione che tutte le funzioni di gestione delle interruzioni risiedano nella parte bassa della
memoria Flash, come anche puntatori o variabili di grosse dimensioni. Nel caso degli MSP430 della Value
Line, utilizzati nella scheda LaunchPad, non ci sono problemi o domande da porsi visto che larchitettura
di tipo MSP430 e non estesa. Lutilizzo o meno di unarchitettura MSP430X si sente di pi qualora si stia
scrivendo il codice in Assembly. Infatti in questo caso necessario specificare il tipo di chiamata alle

15

Microcontrollori TI-MSP430 2

funzioni, estesa o non estesa, come anche utilizzare il formato corretto con o senza X. Qualora si faccia uso di
unarchitettura MSP430X, ma si utilizzano solo istruzioni Assembly dellarchitettura MSP430, il programma
pi facilmente trasferibile, in quanto compatibile, allinterno della famiglia MSP430 e MSP430X. Maggiori
dettagli sullarchitettura MSP430X possono essere trovati nella User Guide della famiglia MSP430F5xx.

ORGANIZZAZIONE DELLA MEMORIA


Dal primo sguardo sullarchitettura abbiamo visto che gli MSP430 possiedono una sola area di memoria e un
solo bus dindirizzamento, per cui larchitettura di tipo di von-Neumann. La memoria interna pur avendo
un'unica area dindirizzamento suddivisa in varie parti. La divisione differente a seconda del C, ma a
grandi linee possibile mostrare le principali varianti (per maggiori dettagli far sempre riferimento al
datasheet del C utilizzato). Lorganizzazione pi semplice della memoria quella presente nella Value
Line. In particolare lo spazio di memoria diviso in area Flash e RAM. Osservando la Tabella 1 possibile
vedere lo spazio dedicato allInterrupt Vector e alla Program Memory. Tale memoria risiede nella Flash. In
seguito presente la memoria nominata Information Memory, che pur essendo di tipo Flash ha qualche
peculiarit. La Program Memory divisa infatti in pagine da 512 byte, mentre la Information Memory in
pagine da 64 byte. Questo stato fatto per semplificare il processo di scrittura in tale area di memoria. In
particolare potrebbe essere utilizzata come memoria anche una EEPROM, considerando per che per
cancellare una locazione di memoria necessario cancellare una pagina intera. Le pagine dellInformation
Memory sono nominate A,B,C e D. La pagina A ha la caratteristica di possedere i valori di calibrazione che
sono salvati allinterno dellMSP430 (i valori presenti dipendono dal C). Di seguito presente la RAM, di
dimensioni variabili a seconda della variante del C. In ultimo sono presenti i registri associati alle
periferiche interne ovvero gli SFR (Special Function Registers). Maggiori dettagli sono riportati in Tabella 1.

Tab. 1 Organizzazione della memoria negli MSP430G2xx1.

Lorganizzazione della memoria leggermente diversa per dispositivi non della Value Line e con memoria
inferiore a 64KB (in Tabella 2 riportato un esempio). Si noti in particolare la presenza della Boot Memory.
Questa area di memoria di tipo ROM, ovvero scritta direttamente da TI in fase di produzione dellMSP430.
Questarea possiede il boot loader anche noto come bootstrap loader. La versione standard fa uso della
porta seriale per poter programmare il dispositivo senza far uso del programmatore. Varianti della famiglia
MSP430F5xx e MSP430F6xx con modulo USB, possiedono il boot loader USB e larea definita Boot Memory,
che di tipo Flash.

Tab. 2 Organizzazione della memoria negli MSP430F23x0.

Unultima organizzazione della memoria che bene tener conto quella dei dispositivi che hanno unarea di
memoria maggiore di 64KB, ovvero con architettura MSP430X (alcuni dettagli sono riportati in Tabella 3).

16

Microcontrollori TI-MSP430 2

La differenza maggiore, oltre alla diversa organizzazione degli spazi, legata alle nuove dimensioni, la
presenza della memoria RAM mirrored, ovvero riportata a specchio. La sua presenza serve solo per
compatibilit con larchitettura MSP430 e il desiderio di non avere la memoria RAM spezzettata in vari spazi
della memoria.
Unarchitettura come quella degli MSP430, sebbene larea Flash e RAM sia distinta, permette facilmente di
eseguire il programma sia in Flash che in RAM. Normalmente i programmi che vengono eseguiti in RAM
consumano molta meno potenza. Grazie ai soli 100nA necessari per mantenere i dati in memoria RAM,
trovare applicazioni in RAM, ovvero che partano dalla memoria Flash e vengano poi caricati in RAM, non
inusuale qualora si faccia uso degli MSP430.
In ultimo, ma non meno importante, bene ricordare che i nuovi MSP430 con FRAM rivoluzionano
totalmente la filosofia di organizzazione della memoria. Infatti la memoria FRAM pu essere usata sia in
veste di memoria Flash, RAM ed EEPROM, ed possibile partizionarla a piacimento come si farebbe con un
hard disk. Gli MSP430FR5xx possiedono comunque memoria RAM aggiuntiva, ma il compilatore CCS, ogni
qual volta venga creata una variabile, la alloca in FRAM e non in RAM.

Tab. 3 Organizzazione della memoria negli MSP430F2x1x.

CIRCUITERIA DI RESET
Ogni C possiede una circuiteria di Reset, ovvero quella parte del sistema che permette di inizializzare il C
al fine di avviarlo con uno stato noto. Sebbene l'azione da compiere sia relativamente semplice, il segnale di
Reset viene generato da una circuiteria pi o meno complessa a seconda dei C. Gli MSP430, come
praticamente tutti i C, sono caratterizzati dall'avere un pin esterno per la circuiteria di Reset, nominato
RST/NMI. La sua funzione duplice: all'avvio impostato come pin di Reset, ma potrebbe essere utilizzato
anche per ricevere un segnale associato internamente ad una interruzione non mascherabile (si veda il
paragrafo sulle interruzioni per maggiori dettagli). Il pin di Reset deve avere un resistore di pull-up di
47Kohm e un condensatore verso massa di 10nF (massimo di 2.2nF nel caso si faccia uso di programmazione
on-board, come per esempio nel caso di schede di sviluppo). Oltre al segnale di Reset, due segnali interni di
particolare importanza, legati al Reset stesso, sono il segnale POR (Power On Reset) e il PUC (Power Up
Clear).
Il segnale POR viene generato ogni qual volta:
Il C viene alimentato.
La linea di Reset RST viene portata a livello logico bassa.
Il modulo SVS (Supply Voltage Supervisor) rileva un problema di alimentazione e PORON=1.
Il segnale PUC viene generato quando:
Viene generato un POR.
Il Watchdog ha un overflow.
La password associata al Watchdog non corretta.
Si accede ad un indirizzo vietato della Flash (oltre il confine della memoria disponibile)
Se un'istruzione viene caricata da un indirizzo della memoria compreso da 0h a 01FFh.
Quando viene eseguito il Reset, viene eseguito un POR che causa le seguenti inizializzazioni:

17

Microcontrollori TI-MSP430 2

Il pin RST configurato come Reset.


I pin I/O sono configurati come ingressi (a meno di periferiche speciali).
Lo Status Register SR viene resettato.
Il Watchdog viene avviato.
Il PC viene caricato con il Reset Vector 0FFFEh
Dopo l'indirizzo associato al Reset Vector sono presenti altri vettori delle interruzioni, per cui, nel Reset
Vector, qualora si stia scrivendo in Assembly, bisogna scrivere un salto all'origine del programma principale.
Tra le altre operazioni da compiere dopo il Reset vi quella di inizializzare lo Stack Pointer in cima alla
RAM e disabilitare il Watchdog qualora non sia richiesto. Dopo queste istruzioni base si deve avviare la
procedura di inizializzazione delle periferiche richieste nella particolare applicazione che si sta sviluppando.
Il valore con cui viene inizializzato un registro dopo un evento di Reset riportato nella User Guide ed
contenuto tra parentesi tonde sotto la spiegazione del registro stesso. Spesso il valore pari a 0, dunque si
trova il valore (0).

MODULI E PERIFERICHE
Nonostante gli MSP430 siano C ricchi di periferiche, per ragioni di prezzo, ovvero per permettere al
progettista di ottimizzare i costi del proprio sistema, non tutti gli MSP430 hanno tutte le periferiche descritte
nella User Guide. In particolare come visto in precedenza, alcune periferiche sono presenti solo in alcune
famiglie MSP430, ma anche all'interno di una stessa famiglia sono presenti differenze. Nella famiglia
MSP430F2xx possibile trovare diverse periferiche base quali Timer, PWM (Pulse Width Modulation),
Moltiplicatore, ADC, DMA, SPI, I2C, UART, come anche periferiche speciali DAC, ADC Sigma Delta a 16 bit,
amplificatori operazionali, comparatori, sensore di temperatura, predisposizione per pulsanti capacitivi.
Nelle famiglie pi grosse possibile trovare anche altre periferiche come USB, modulo per criptare i dati,
transceiver radio e altro ancora.
La caratteristica di ogni modulo, i principali dei quali saranno trattati in capitoli dedicati, quella di essere
progettato per supportare applicazioni a bassa potenza. Oltre ad avere un semplice Enable per attivare e
disattivare il modulo stesso (ovvero eliminare i consumi del modulo), sono presenti altre opzioni quali
quella di permettere al modulo di essere coordinato da altri moduli o essere attivato/disattivato in
corrispondenza di segnali provenienti da Timer o altre periferiche. La caratteristica dei moduli interni agli
MSP430 quella di essere uguali anche tra le varie famiglie. In particolare un Timer_A uguale in tutte le
famiglie, indipendentemente che sia integrato in un MSP430F2xx o in un MSP430F5xx. Questo permette di
semplificare molto la portabilit di un codice da un C ad un altro. Versioni diverse di Timer sono presenti
quale normale evoluzione legata al miglioramento delle periferiche stesse. Versioni diverse del Timer, come
anche di altri moduli, sono caratterizzate da una nuova lettera. In particolare sono presenti per esempio il
Timer_B, Timer_C e Timer_D. Normalmente sebbene molti degli aspetti concettuali di ogni periferica
rimangano invariati, una nuova lettera sul nome del modulo porta sempre dei miglioramenti e
caratteristiche in pi che rendono spesso il codice scritto per un modulo incompatibile con un altro a meno
di aggiungere le impostazioni delle funzioni aggiuntive. Quando possibile le funzioni aggiuntive hanno
valori di Default tali da permettere il funzionamento di un modulo come il proprio predecessore. In alcuni
casi, nuove versioni di un modulo possono causare il cambio del nome di un determinato bit, pur
mantenendo la stessa funzione. Per tale ragione sempre necessario far riferimento alla User Guide qualora
ci si accinga ad utilizzare un nuovo modulo o nuova versione di un modulo precedentemente utilizzato. Le
User Guide possiedono, all'inizio del capitolo dedicato a un modulo, un pratico schema a blocchi che
permette facilmente un confronto tra due moduli di versione diversa. Oltre allo schema a blocchi sono
spesso riassunte anche le differenze del modulo con la versione precedente. Lo schema a blocchi include
anche il nome dei bit interni ai vari registri SFR dedicati alla configurazione della periferica. Come si vedr
con i primi esempi di programma, il nome dei bit interni ai registri SFR sono definiti all'interno del file
header associato al C utilizzato.
Il nome uguale al datasheet, ma il file header contiene anche altri parametri che permettono di semplificare
impostazioni associati a multiplexer interni, ovvero qualora siano presenti pi bit associati ad una
impostazione.

18

Microcontrollori TI-MSP430 2

DISTRIBUZIONE DEL CLOCK


Ogni C, per quanto semplice possa essere, richiede un clock al fine di permettere alla CPU di eseguire ogni
qualsivoglia operazione. Nonostante molte delle nozioni relative all'architettura facciano riferimento agli
MSP430, le problematiche risolte permettono di comprendere anche altri C e relativi moduli di
distribuzione del clock. Il paragrafo spiegher in maggior dettaglio il modulo Basic Clock usato nella
famiglia MSP430F2xx e nella Value Line, ma introdurr anche l'Unified Clock System (UCS), usato nella
famiglia MSP430F5xx, MSP430F6xx e nella nuova famiglia basata su FRAM. In ultimo, verranno spiegate le
modalit Low Power, quale forma di armonizzazione del clock e la sua distribuzione.

BASIC CLOCK MODULE


Gli MSP430 possiedono, in base alla famiglia di appartenenza, un modulo di distribuzione del clock
differente. La famiglia MSP430F2xx e la Value Line possiedono il cosiddetto Basic Clock Module (BCS).
Sebbene il clock non rappresenti altro che un onda quadra, che scandisce le varie sequenze interne della
CPU, al fine dello svolgimento di una qualsivoglia operazione, in applicazioni Ultra Low Power, le esigenze
e compromessi da soddisfare sono piuttosto complessi e contrastanti. Da un lato si vuole un clock rapido
(alta frequenza), ma dall'altro si vogliono anche avere bassi consumi (bassa frequenza del clock). Il Basic
Clock Module, nella sua semplicit, cerca di soddisfare tutte le esigenze. Il suo schema a blocchi
rappresentato in figura. subito possibile notare che sono presenti pi sorgenti per generare il Clock, come
anche pi uscite.

Le uscite del modulo rappresentano i 3 clock, che sono rispettivamente ACLK (Auxiliary Clock), MCLK
(Master Clock), SMCLK (Sub Master Clock). La CPU utilizza MCLK, mentre le periferiche possono
utilizzare a seconda delle esigenze sia ACLK che SMCLK. L'ACLK viene normalmente utilizzato per
applicazioni a bassa frequenza a 32KHz, mentre l'SMCLK viene utilizzato per le periferiche a media
frequenza. La caratteristica di MCLK e SMCLK di poter operare al massimo della frequenza di clock, che a
seconda dei modelli pu variare da 8MHz a 25MHz.
Normalmente ogni periferica ha la flessibilit di utilizzare una qualunque delle uscite appena citate, al fine
di poter compiere la propria mansione. Questo permette di ottimizzare le risorse energetiche, disattivando
un clock e mantenendone attivi altri, per il funzionamento delle periferiche dinteresse, permettendo ad ogni
periferica di operare in diverse modalit a basso consumo.

19

Microcontrollori TI-MSP430 2

Le tre uscite del clock possono essere associate a sorgenti diverse a seconda delle esigenze, dando una
maggior flessibilit nell'assegnare le risorse, ed in particolare gestire i consumi. Questo viene fatto per mezzo
del multiplexer presente prima dell'uscita stessa, in particolare del divisore di frequenza per 2, 4, 8.
Alte frequenze di clock si traducono in un elevato MIPS (Milioni di istruzioni per secondo), ma il modo
con cui questo avviene dipende molto dallarchitettura. Gli MSP430 possiedono un ciclo istruzione pari alla
frequenza di clock, quindi MIPS e Frequenza di clock coincidono. Strutture come PIC18 hanno invece il
famoso numero 4 che divide il clock, ovvero sono necessari 4 cicli di clock per compiere una istruzione.
Questo implica che i MIPS attuali di un PIC18 sono 4 volte inferiori alla frequenza di clock. I PIC24 e i dsPIC
possiedono unaltra architettura, in particolare hanno bisogno di 2 cicli di clock per istruzione.
Oltre a quanto appena visto necessario anche considerare il numero di cicli istruzioni necessari per
compiere una determinata operazione. Infatti, a seconda delle operazioni da compiere, sono necessari un
numero di cicli istruzioni diversi. Dettagli sul numero di cicli/istruzione sono riportati nella User Guide della
famiglia utilizzata.
A solo scopo di completezza, si fa notare che le fasi tipiche per eseguire un'istruzione, ovvero fase di Fetch
(caricamento), Decode (decodifica) ed Execute (esecuzione), non sono identificabili qualora si abbia
l'esecuzione di un'istruzione in un solo ciclo di clock. Ciononostante, sebbene il tutto avvenga via hardware,
concettualmente le tre fasi esistono ancora seppur non distinguibili temporalmente.

OSCILLATORI DEL CLOCK


Tra le possibili sorgenti di clock vi sono il VLO (Very low power / Low frequency Oscillator) e il DCO (Digital
Controlled Oscillator), entrambe interne al C. Queste permettono di evitare l'utilizzo di un quarzo esterno e
ridurre la lista della spesa (Bill of Material, BOM).
L'oscillatore VLO ha un valore fisso di frequenza nominale di 12KHz, ma il minimo e massimo varia tra
4KHz e 20KHz, dunque con una possibilit piuttosto ampia di errore. Questo oscillatore bene usarlo in
quelle applicazioni in cui una base dei tempi stabile non strettamente necessaria. Sebbene questo oscillatore
sia apparentemente di bassa qualit, ha il notevole vantaggio di essere a basso consumo, perch in generale
lavorare a frequenze pi basse permette di ridurre i consumi. Nel datasheet del MSP430F2231 per esempio
riportato che nella modalit LPM3, in cui si mantiene l'oscillatore VLO attivo, possibile avere dei consumi
nominali di 500nA (la famiglia MSP430 nominata Wolverine abbassa ulteriormente questi consumi). Sono
proprio queste basse correnti operative che rendono instabile il VLO, infatti basta avere correnti di leakage di
poche decine di nA, che la frequenza cambia. Normalmente le correnti di leackage aumentano con la
temperatura, per cui non deve sorprendere che il VLO vari la propria frequenza al variare della temperatura,
con una tolleranza dello 0.5%/C (grazie ad alcuni collegamenti interni tra ACLK e il capture input del Timer
anche possibile calibrare il VLO facendo uso del DCO, o meglio misurare la sua frequenza).
Qualora sia richiesta una frequenza pi elevata, possibile utilizzare il DCO interno, che permette frequenze
operative fino a 16MHz (a seconda della famiglia MSP430 utilizzata, questo valore pu variare, per cui
occorre far sempre riferimento al datasheet dell'MSP430 utilizzato). Il DCO, diversamente dal VLO, oltre a
raggiungere frequenze pi alte, ha la caratteristica di poter variare la propria frequenza in maniera dinamica.
Questo permette di adattare l'oscillatore in base alle esigenze dell'applicazione. Tra le sue caratteristiche
principali vi quella di attivarsi in pochi s, particolare importante in applicazioni Ultra Low Power, dove
bisogna attivarsi rapidamente ed andare in Sleep Mode altrettanto rapidamente. Il DCO inoltre collegato
ad un modulatore che permette di alternare la frequenza del DCO tra due step n e n+1. Per esempio,
supponendo di impostare il DCO alla frequenza di 1MHz e il passo successivo sia ipoteticamente a 1.1MHz,
il modulatore permette di variare il clock da 1MHz a 1.1MHz ogni x periodi impostabili nel registro MODx.
Questa variazione del clock crea un jitter voluto, che in alcuni casi potrebbe anche non essere desiderato, ma
permette di limitare le radiazioni elettromagnetiche generate dal clock della CPU. Infatti, lenergia irradiata
dovuta al clock interno si distribuisce su uno spettro pi ampio, riducendo il picco massimo, in altre parole
semplificando eventuali certificazioni Europee (CE) o Americane (FCC).
Un'altra caratteristica importante del DCO quella di entrare in funzione in circa 1.5us, se il C posto in
LPM3 o LPM4. Questo particolarmente importante in applicazioni Ultra Low Power, poich significa che il
controllore pu rimanere in Low Power Mode, svegliarsi molto rapidamente, eseguire le operazioni
necessarie, per poi tornare in Low Power Mode altrettanto rapidamente.

20

Microcontrollori TI-MSP430 2

Il DCO possiede alcuni valori calibrati a determinate frequenze, che, se caricate negli appositi registri di
controllo, permettono di impostare il DCO ad un valore noto di frequenza con una tolleranza di pochi
centesimi percentuale. Il valore di frequenza calibrato pu essere trovato allinterno dellheader file associato
al C utilizzato. Gli MSP430 della serie Value Line possiedono solo il valore 1MHz quale valore calibrato.
Gli MSP430 della famiglia MSP430F4xx possiedono unarchitettura pi complessa per il clock ed in
particolare possiedono un FLL (Frequency Lock Loop), molto simile ad un PLL (Phase Lock Loop), ma il cui
controllo avviene sulla frequenza e non sulla fase. Questo permette di semplificare larchitettura di controllo
ottenendo consumi ridotti.

OSCILLATORI AL QUARZO
Qualora queste caratteristiche non siano sufficienti possibile utilizzare un quarzo esterno (crystal oscillator).
A seconda del modello dell'MSP430 possibile avere uno o due buffer interni per quarzi esterni, XT1 e XT2.
La Value Line possiede attualmente solamente XT1 e con l'opzione LF (Low Frequency, LFXT1). La
frequenza dipende dalle applicazioni, ma spesso conveniente usare una frequenza binaria 215=32768 Hz
(0x8000), spesso indicata come 32 kHz (massima frequenza 50KHz). La famiglia MSP430F2xx ha in generale
XT1 con opzione sia a bassa che ad alta frequenza, potendo supportare quarzi con frequenze operative di
16MHz (alcuni MSP430 possiedono frequenze operative maggiori). L'utilizzo di un quarzo esterno permette
di raggiungere frequenze di clock molto stabili sia nel tempo che al variare della temperatura e tensione,
pagando il prezzo di usare componenti esterni e un aumento della corrente di sistema.
Utilizzare cristalli esterni richiede in generale un maggior tempo di stabilizzazione della frequenza. Per
esempio un cristallo da 32KHz pu richiedere centinaia di ms prima di poter essere operativo. In LPM3,
come vedremo a breve, il buffer di XT1 non viene disattivato, e un risveglio da questa modalit molto
rapido, ma un risveglio da una modalit LPM4 richiederebbe molto pi tempo (XT1 viene infatti
disattivato).
L'utilizzo di un cristallo esterno richiede un'opportuna capacit di carico al fine di poter permettere una
corretta oscillazione dello stesso. Gli MSP430 possiedono alcune capacit interne che possono essere abilitate
al fine di evitare l'utilizzo di ulteriori condensatori esterni (valori disponibili: 1pf, 6pf, 10pf, 12pf), attraverso
le costanti XCAP_x. Quando si abilitano queste capacit bisogna tener presente che gli stessi pin del C
rappresentano delle capacit di carico in parallelo, che dunque si devono sommare al totale (2 pF per pin).
Quindi se si seleziona la capacit da 12pF la capacit totale sar di 14pF. Qualora la capacit di carico
richiesta da un determinato quarzo sia maggiore di quella selezionabile internamente, necessario
aggiungere la differenza esternamente.
Sebbene la massima frequenza operativa sia di 16MHz, bisogna fare attenzione alla tensione di
alimentazione, al fine di poter lavorare alla massima frequenza. Infatti in applicazioni Ultra Low Power si
tende ad abbassare le tensioni operative al fine di ridurre i consumi, ma queste potrebbero non essere
compatibili con la frequenza di clock a cui si vuole operare. In Figura riportato un dettaglio dei limiti
appena citati per la famiglia MSP430F2xx.

Massima frequenza di clock e minima tensione operativa.

21

Microcontrollori TI-MSP430 2

Si noti in particolare che presente un limite operativo per la tensione anche per quanto riguarda la
programmazione, ovvero, qualora la propria applicazione voglia scrivere dei dati nella memoria Flash,
necessario che operi ad una tensione minima di 2.2V. Questi limiti, che affliggono molti C basati su
memoria Flash, non sono presenti nella famiglia MSP430 Wolverine, basata su FRAM (Ferro-Electric RAM).

UNIFIED CLOCK SYSTEM


Le famiglie MSP430F5xx e MSP430F6xx con memoria FRAM possiedono il modulo Unified Clock System
(UCS). La caratteristica di questa architettura, pi recente, quella di essere pi flessibile, infatti ogni uscita
del clock pu essere impostata su un qualunque clock interno o esterno (per tale ragione, come per gli
indirizzamenti interni, si parla anche di struttura ortogonale, ovvero tutte le combinazioni sono possibili).
L'architettura del modulo Basic Clock, sebbene semplice ed essenziale, permette di raggiungere un buon
compromesso tra semplicit e flessibilit. L'architettura Unified Clock System (UCS) cerca di completare la
precedente architettura, fornendo la totale flessibilit all'utente. In particolare ogni uscita del clock ACLK,
MCLK e SMCLK pu essere impostata su una qualunque sorgente di clock ed avere una divisione della
stessa. Per tale ragione il tutto rimane molto simmetrico.
Il modulo UCS introduce l'oscillatore REFO calibrato a 32KHz in fabbrica ed utilizzabile con il Real Time
Clock Calendar (RTC) interno agli MSP430. Anche l'oscillatore MODOSC nuovo, ed possibile utilizzarlo
come sorgente per vari moduli, tra cui l'ADC. In particolare viene utilizzato anche dal Flash controller
durante la fase di scrittura della memoria flash, permettendo di semplificare tale fase e garantire che i
prerequisiti del clock siano sempre rispettati (nella famiglia MSP430F2xx necessario garantire che, in fase
di scrittura, il clock utilizzato dal modulo flash controller sia entro un determinato intervallo).
La parte relativa al DCO pi complessa, dal momento che viene utilizzata l'architettura degli MSP430F4xx,
ovvero DCO pi il modulo FLL (Frequency Lock Loop). Il modulo FLL permette di controllare il modulo
DCO, garantendone la sua stabilit in frequenza rispetto a variazioni della temperatura e tensione, ammesso
che la sorgente di riferimento del FLL sia stabile. Tra le sorgenti di riferimento del modulo FLL possibile
scegliere sia un cristallo esterno che il modulo REFO.
Una novit introdotta dal modulo UCS il clock on demand, ovvero il clock su richiesta. Come vedremo a
breve, in base alla modalit Low Power selezionata, si disattivano una alla volta le varie sorgenti di clock.
Nella famiglia MSP430F2xx ci si traduce nel fatto che ogni volta che bisogna utilizzare un modulo e una
determinata sorgente di clock, necessario garantire che quest'ultima sia sempre attiva, e non si disattivi a
causa di un determinato stato Low Power. Questa attenzione da parte del programmatore, sebbene sia un
buon criterio di programmazione, viene garantita dal modulo UCS, il quale grazie ai segnali ACLK_REQ,
MCLK_REQ, MCLK_REQ e MODOSC_REQ, permette di tenere attivo un clock o riattivare un clock
disattivato da uno stato Low Power, ogni qual volta lo richieda una periferica. Una volta terminata l'esigenza
del clock, quest'ultimo viene disattivato in accordo con lo stato Low Power (i segnali di Clock Request non
sovrascrivono infatti lo Status Register). Se da un lato questo rappresenta un aiuto, al fine di garantire che le
periferiche non si trovino mai senza clock quando ne hanno necessit, bisogna tenerlo a mente qualora si
vedono consumi apparentemente eccessivi in un determinato stato Low Power. Se infatti il C nello stato
LPM3, ma l'ADC impostato ad usare il MCLK a 16MHz, durante la fase di sampling dell'ADC, anche se si
in LPM3, viene attivato il MCLK. Questo aumenta i consumi che si soliti vedere in LPM3, ma garantisce il
corretto funzionamento dell'ADC. La funzione di clock on demand, qualora non sia richiesta e si voglia
avere il controllo delle periferiche e clock senza alcun aiuto dell'hardware, pu essere disattivata.
L'architettura degli MSP430F5xx introduce nuove caratteristiche anche nel Power Management Module
(PMM), che, abbinate al modulo UCS, permettono una notevole flessibilit delle varie modalit Low Power
(le modalit LPMx.5 non sono presenti nell'architettura MSP430F2xx). Per maggiori dettagli sul modulo UCS
e i fail modes, si rimanda alla User Guide della famiglia MSP430F5xx e MSP430F6xx.

MODALIT LOW-POWER
Un programma rappresenta una successione di istruzioni, che la CPU esegue una alla volta. In molte
applicazioni le operazioni da compiere sono eseguite talmente rapidamente che la CPU pu rimanere in

22

Microcontrollori TI-MSP430 2

attesa di altre operazioni da compiere. In questi casi, e soprattutto in applicazioni in cui il sistema
alimentato a batteria, si preferisce mandare la CPU in stato di sleep, ovvero in uno stato che limita la
corrente assorbita dalla CPU stessa. Gli MSP430 sono stati concepiti fin dall'inizio per applicazioni a batteria,
per cui sono ottimizzati per varie situazioni a basso consumo. Le varie modalit sono rispettivamente
nominate LPM0, LPM1, LPM2, LPM3, LPM4 (alcuni MSP430 possiedono anche le modalit LPM3.5 e
LPM4.5).
Lo stato a bassa potenza pi utilizzato probabilmente il LPM3, visto che la CPU disattiva, ma presente
ACLK clock a bassa frequenza, che pu fornire il clock ad un Timer e permettere un risveglio periodico del
C. In particolare questo significa che possibile avere un cristallo a 32KHz o il VLO come sorgente. Si
ricorda che un sistema digitale consuma meno al diminuire della frequenza di clock. In applicazioni che
devono durare 15-20 anni, e prelevare l'energia da una semplice batteria a bottone, non si pu pensare di
eseguire in maniera continua applicazioni con clock dell'ordine dei MHz.
Lo stato a maggior risparmio energetico il LPM4, almeno per la famiglia MSP430F2xx. In questo stato,
disattivando ogni sorgente di clock, si possono raggiungere correnti di circa 100nA mantenendo i dati in
RAM. Questo un aspetto importante in quelle applicazioni come i data logger, o quelle in cui non si
vogliono inizializzare le periferiche ad ogni risveglio. I C della Microchip scendono rispetto a queste
correnti a circa 20nA, ma sacrificano i dati in RAM (a meno di non utilizzare altre modalit Low Power a
maggior consumo). Per ovviare a questo problema i C Microchip con XLP possiedono la possibilit di una
seconda batteria, utilizzata per alimentare un numero limitato di celle RAM, in cui possibile preservare
qualche dato importante, prima di andare in Deep Sleep mode. Altra differenza importante tra il Deep Sleep
mode e LPM4 il fatto che gli MSP430 possiedono la circuiteria BOR sempre attiva per monitorare la
tensione della batteria, mentre in Deep Sleep mode il modulo BOR non incluso. Si capisce che a seconda
delle applicazioni si potrebbe preferire un C rispetto all'altro.
Nella famiglia MSP430F5xx e MSP430F6xx, per dare ulteriore flessibilit al progettista, sono state introdotte
le modalit LPMx.5 come riportato in Figura. Il .5 sta ad indicare che LDO interno (Vcore) viene disattivato
per cui vengono persi i dati in RAM, qualora si abiliti la modalit LPM3.5 o LPM4.5. In particolare la
modalit LPM4.5 si comporta come il Deep Sleep Mode dei C Microchip con tecnologia XLP.
Maggiori dettagli su queste modalit possono essere trovati nella User Guide della famiglia MSP430F5xx e
MSP430F6xx. La nuova famiglia basata su FRAM possiede le modalit in figura, per, qualora si usi la
memoria FRAM per le variabili, anche entrando in modalit LPMx.5, i dati vengono preservati. Infatti la
memoria FRAM, pur potendo essere utilizzata come RAM, una memoria non volatile. Anche alcune
varianti degli MSP430F5xx possiedono l'opzione di una seconda alimentazione, al fine di preservare un
numero limitato di dati in un'area RAM speciale.

RISVEGLIO DEL MICROCONTROLLORE


Il C entrato in una modalit Low Power si risveglia per mezzo di un qualunque interrupt di una periferica,
per mezzo di un segnale esterno, un Reset o un Power Cycle. Il raggiungimento di bassi consumi si basa
proprio nello sfruttare questa possibilit. Per esempio se si vuole campionare un segnale analogico,
possibile impostare un Timer che avvii periodicamente l'ADC, il quale a fine conversione genera un
interrupt per svegliare la CPU dallo stato di Sleep.
Al verificarsi di un'interruzione, il risveglio del C legato al fatto che, durante la fase di riconoscimento di
un interrupt, lo Status Register viene azzerato, il che equivale allo stato attivo. Lo Status Register con le
informazioni dello stato a bassa potenza viene per salvato nello Stack, per cui, al ritorno dall'ISR, lo Status
Register viene ripristinato, e il C torna automaticamente nello stato a bassa potenza presente prima
dell'interrupt. Durante l'ISR, si pu anche cambiare il valore dello Status Register nello Stack, al fine di
alterare lo stato di bassa potenza al ritorno dall'ISR.
L'utilizzo di una determinata modalit Low Power dipende dall'applicazione, infatti le risorse necessarie
potrebbero non essere disponibili o meglio potrebbero essere rese disponibili in tempi troppo lunghi. Come
accennato, ogni modalit a basso consumo ha tempi di risveglio diversi, per cui bisogna valutare se questi
sono compatibili o meno con la propria applicazione. Infatti, sebbene sia importante mantenere il C in stato
di sleep al fine di ridurre la corrente consumata in stato attivo (Active Mode), anche altrettanto importante
che il C si risvegli rapidamente. Questo si traduce nell'avere un clock stabile in tempi rapidi, al fine di

23

Microcontrollori TI-MSP430 2

permettere un rapido utilizzo delle periferiche di sistema. Un risveglio rapido permette anche di risparmiare
energia.
A seconda della modalit Low Power selezionata, i tempi di risveglio possono oscillare da 1s fino al ms. Si
fa presente che, qualora si faccia uso del quarzo esterno da 32KHz, possono essere necessari centinaia di ms
prima che questo oscilli in maniera stabile (questo una propriet fisica dei quarzi a 32KHz e dei processi di
fabbricazione).
Al fine di ridurre i consumi non basta solamente scegliere un Low Power Mode adeguato, ma bisogna anche
prendere le precauzioni giuste. In particolare tutti i pin non utilizzati devono avere uno stato logico fisso
(essere posti come uscite o come ingressi con resistori di pull-up o pull-down). Altre precauzioni devono
essere prese anche a livello software. CCS, a partire dalla versione 5.2, possiede il plug-in ULP (Ultra Low
Power Advisor), che permette di controllare il programma durante la fase di compilazione. Il plug-in
controlla il programma in base a delle regole di buona programmazione e suggerisce i cambiamenti
necessari al programma al fine da ridurre al minimo i consumi.

LE INTERRUZIONI
Oltre alle istruzioni condizionali e ai loop, un programma pu avere il proprio flusso alterato a causa delle
interruzioni, ovvero eventi esterni o interni al C stesso, che richiedono particolare attenzione e l'esecuzione
di un programma o funzione speciale, nominata Interrupt Service Routine (ISR). L'ISR ha il compito di
gestire l'interruzione, ma deve essere scritta in maniera pi breve possibile. Infatti, quando in esecuzione
un ISR, vengono disattivate le interruzioni, per cui non possono essere gestiti altri eventi. Essere veloci
dunque di importanza fondamentale, al fine di garantire una rapida risposta ad eventi esterni.
Il segnale di Reset precedentemente spiegato rappresenta uninterruzione, infatti un programma in
esecuzione, qualora venga generato un Reset, viene interrotto e inizia il programma a partire dal Reset
Vector.
Al fine di permettere l'esecuzione del codice relativo allISR, qualora il C sia in sleep mode, qualunque
modalit Ultra Low Power viene lasciata per andare in Active Mode (AM).
Sul datasheet del dispositivo MSP430 usato si trova una Interrupt Vector Table, salvata in una zona
prefissata della memoria, che contiene gli Interrupt Vector corrispondenti ad ogni interrupt, ovvero gli
indirizzi di memoria flash nel quale vengono memorizzati gli indirizzi di memoria in cui inizia l'ISR, al quale
viene posizionato il PC al verificarsi della corrispondente interruzione. Quando si scrive in C non bisogna
tenere conto di questi dettagli, ma scrivendo in Assembly necessario scrivere all'interno dell'Interrupt
Vector Table l'indirizzo d'inizio dell'ISR.
La caratteristica dell'architettura degli MSP430 quella di avere delle priorit fisse, in particolare la priorit
associata all'indirizzo stesso in cui posizionato il vettore delle interruzioni. I vettori delle interruzioni
sono posizionati a partire dall'indirizzo 0FFC0h fino a 0FFFEh. Maggiore il valore dell'indirizzo, maggiore
la priorit associata all'interruzione stessa. Si noti in particolare che il valore a pi alta priorit
all'indirizzo 0FFFEh che associato al Reset della CPU stessa. Maggiori dettagli sugli interrupt e il vettore
associato ad ogni interruzione, possono essere trovati all'interno del datasheet del C utilizzato. In generale,
facendo uso dei nomi definiti all'interno degli Header File, non ci si deve preoccupare molto della posizione
dei Vettori delle interruzioni a meno di non voler sapere il loro livello di priorit.
In Figura riportato un dettaglio del vettore delle interruzioni dell'MSP430G2553. Si pu notare come le
porte P1 e P2 abbiano due vettori diversi, ma i singoli pin fanno riferimento ad uno stesso vettore. Come si
vedr pi avanti, il Timer A ha due vettori delle interruzioni, uno associato al Capture Compare Module 0, e
il secondo al resto dei Capture Compare Module (in questo caso solo 1). Questo discende da alcune
peculiarit del modulo CC0, i cui dettagli verranno discussi nel capitolo dedicato ai timer.
Qualora l'interrupt richieda calcoli lunghi, preferibile impostare un Flag all'interno dell'ISR, ed eseguire
una semplice macchina a stati all'interno del main. Sebbene le interruzioni siano disabilitate durante
l'esecuzione di un ISR, questo non vuol dire che altri eventi d'interruzione siano persi, ma soltanto che non
potranno essere serviti immediatamente. Infatti ad ogni sorgente d'interruzione associato un Flag
d'interruzione, che viene attivato anche se le interruzioni sono disattivate. Una volta che le interruzioni sono
riattivate, un Flag d'interruzione posto ad 1 fa generare una nuova interruzione. Per tale ragione

24

Microcontrollori TI-MSP430 2

importante, alla fine dell'esecuzione di un ISR, resettare il Flag delle interruzioni, al fine di evitare che
vengano eseguite in maniera continua nuove interruzioni a causa di un Flag che rimane sempre ad 1.
Se due interrupt sono generati o attivi in contemporanea, quello a pi alta priorit viene servito prima.
Sebbene si possa pensare che due segnali difficilmente possano arrivare in contemporanea, la possibilit
che due Flag delle interruzioni siano attivi in contemporanea non remota. Basti pensare al fatto che durante
l'esecuzione di un ISR si possono verificare altre interruzioni in tempi diversi. All'uscita dall'ISR si
verrebbero ad avere pi Flag attivi in contemporanea. Indipendentemente dal tempo di arrivo, viene servita
l'interruzione a pi alta priorit.

MSP430G2553 Interrupt Vector Table.

Gli interrupt interni agli MSP430, come per molti C, sono suddivisi in interruzioni mascherabili e
interruzioni non mascherabili. Le interruzioni non mascherabili sono quelle che non vengono attivate per
mezzo del bit GIE (General Interrupt Enable) presente nello Status Register, ma hanno un bit di abilitazione
dedicato. Le Interruzioni non mascherabili sono associate agli eventi di Reset, violazione di accesso Flash e
problemi associati all'oscillatore. Le interruzioni mascherabili, oltre a possedere un bit di abilitazione,
possono tutte essere mascherate per mezzo del bit GIE. Questo significa che se si attivano per esempio le
interruzioni associate ad un Timer, ma il bit GIE non abilitato, sebbene il Flag associato alle interruzioni
del Timer verr abilitato, non verr attivata nessuna interruzione.
Qualora l'evento che ha generato l'interruzione ha un vettore delle interruzioni dedicato, il Flag associato
all'interruzione viene azzerato automaticamente all'avvio della funzione di gestione dell'interruzione.
Qualora invece un vettore delle interruzioni sia associato a pi sorgenti di interruzioni, il Flag deve essere
azzerato nel programma stesso (via software). Un esempio di vettore associato a pi sorgenti d'interruzione
quello della PORT1. Infatti ogni pin ha un bit di abilitazione delle interruzioni e un Flag di avvenuta
interruzione. Alcune periferiche, pur avendo un solo Interrupt Vector associato a pi eventi, possiedono una
Tabella Secondaria delle Interruzioni, che permette di riconoscere facilmente la sorgente che ha causato
l'interruzione. La presenza o meno di questa tabella deve essere verificata nel datasheet del dispositivo
utilizzato.

INTERRUPT HANDLING PROCEDURE


Il riconoscimento di un'interruzione preventivamente abilitata, comporta le seguenti operazioni:
Se linterrupt di una periferica abilitato, un segnale appropriato setta il Flag di interrupt in uno dei
registri della periferica.

25

Microcontrollori TI-MSP430 2

Se il processore permette gli interrupt, la presenza di un Flag di interrupt da una periferica setta un Flag
di interrupt del processore.
Il processore termina l'esecuzione dell'istruzione corrente.
Il processore salva nello stack (pushing onto the stack) sia il PC, che punta alla prossima istruzione, che il
suo stato attuale, cio lo Status Register.
L'interrupt con maggiore priorit viene eseguito, se pi interrupt sono stati generati in contemporanea
(vedere lista priorit sul datasheet).
L'interrupt Flag viene resettato automaticamente, se la periferica ha un unico vettore delle interruzioni,
altrimenti va resettato via software.
Lo Status Register viene resettato (cleared), cosa che disabilita ulteriori interrupt durante lesecuzione
dellISR (disabilita GIE). Solo le interruzioni mascherabili sono disabilitate dal reset di SR, mentre quelle
non mascherabili (come il Reset) sono ancora possibili. Inoltre costringe il processore a tornare nello
stato attivo, se era in uno stato LPM.
Lindirizzo dellinterrupt vector (prima istruzione dellISR) copiato nel PC.
Quando viene generato un interrupt sono necessari 5 cicli di clock nella CPUX e 6 cicli di clock nella CPU
standard prima che venga eseguita la ISR (il tempo tra il riconoscimento di un interrupt e l'inizio
dell'esecuzione delle istruzioni nell'ISR detto tempo di latenza).
Una volta eseguita lISR possibile tornare alla normale esecuzione del programma. Il ritorno viene avviato
per mezzo dell'istruzione RETI (Return from Interrupt) necessaria solo se si programma in Assembly (in C
viene introdotta automaticamente in fase di compilazione). Le operazioni compiute durante il ritorno
dall'interrupt sono:
Ripristino dello Status Register e del PC, recuperandoli dallo stack (popping).
Nella CPU standard sono necessari 5 cicli di clock per permettere il ripristino della situazione prima
dell'interrupt, mentre nella CPUX sono necessari 3 cicli di clock.
Una volta ripristinato lo Status Register, la CPU torna nell'eventuale stato a basso consumo che era
impostato prima dell'interrupt, indipendentemente dallo stato utilizzato durante la gestione
dell'interruzione. Si ricorda comunque che lo Status Register memorizzato nello Stack potrebbe essere
cambiato a piacimento e alterare dunque lo stato di ritorno della CPU.

26

Hardware e Software di sviluppo 3

CAP. 3
HARDWARE E SOFTWARE DI SVILUPPO

SOFTWARE DI SVILUPPO
I C rappresentano una soluzione tecnica che richiede lo sviluppo sia di hardware che di software. In questo
paragrafo descriveremo le varie soluzioni che sono disponibili al fine di sviluppare software.
Gli MSP430, come gi accennato, sono stati progettati con numerosi registri interni general purpose, al fine
di favorire possibili ottimizzazioni da parte del compilatore. Si capisce quindi che, sebbene il C sia
facilmente programmabile anche in Assembly, ottimizzato per linguaggi ad alto livello come il C. Tra gli
ambienti di sviluppo principali bene ricordare Code Composer Studio (CCS), rilasciato dalla stessa TI e
IAR rilasciato dalla IAR Systems.
CCS basato sullIDE Eclipse. Dalla versione 5 CCS supporta anche il sistema operativo Linux. CCS integra
un completo ambiente di sviluppo e permette di programmare e fare Debug di applicazioni scritte sia in
Assembly che in C. Non possiede il simulatore, ma permette di fare il Debug dell'applicazione sfruttando
direttamente il modulo EEM (Embedded Emulation Module) interno agli MSP430. CCS rappresenta la
piattaforma di sviluppo della TI per mezzo della quale possibile programmare non solo gli MSP430 ma
anche gli altri C TI, quali la famiglia Stellaris, basata su Core ARM M3 e M4, la famiglia C2000, nonch i
processori high hand C54x, C55x, C6000, DaVinci, TMS470, TMS570, Sitara e OMAP. CCS possibile
scaricarlo in due versioni, quella Platinum, ovvero completa ma con un periodo di prova di 60 giorni, o la
versione Code Size Limited (Dimensione del codice limitata). Questa seconda licenza pur avendo tutte le
funzionalit attive, permette di scrivere codici per un massimo di 16KB.
Oltre al CCS possibile usare lambiente di sviluppo IAR. Anche in questo caso sono presenti due versioni.
La versione full, scaricabile gratuitamente, ha una licenza valida solo per 30 Giorni. La seconda versione
disponibile (KickStart Edition) ha un limite sulle dimensioni del codice; fino a 4KB per gli MSP430 standard,
tra cui la Value Line, e 8KB per gli MSP430X ovvero con architettura estesa (MSP430 con memoria maggiore
di 64KB sono automaticamente con architettura MSP430X). Programmi in Assembly possono essere scritti
senza limite sulla dimensione del codice, ma per applicazioni complesse pu essere poco pratico. Il
vantaggio principale di IAR sta nel fatto che oltre al Debug possiede anche il Simulatore, cosa utilizzata da
diversi sviluppatori di software. Altro vantaggio del compilatore IAR quello di ottenere, mediamente, un
livello di ottimizzazione del codice compilato, migliore rispetto al CCS. Questo discende prevalentemente
dal fatto che gli investimenti effettuati da IAR, essendo una societ che vende software sono mirati per
ottenere performance migliori. Avere un codice ottimizzato pu permettere di utilizzare C con meno
memoria Flash (pi economici) e permette, per una medesima applicazione, di eseguire meno linee di
codice, quindi permette di risparmiare cicli di clock, ovvero energia (sebbene tali affermazioni siano valide
in generale, dovrebbero essere verificate applicazione per applicazione). Questo potrebbe essere un aspetto
particolarmente importante in applicazioni che devono essere alimentate a batteria (per maggiori
informazioni si veda la Brief Note BN0014 Progettare sistemi embedded a batteria). Sebbene IAR non sia un
tool ufficiale, il suo supporto per il programmatore di TI praticamente come per il CCS (usano lo stesso
driver) per cui non bisogna temere che IAR non abbia tool aggiornati. La TI fornisce molti esempi per ogni
C sia scritti per lambiente di sviluppo IAR che CCS. IAR oltre a supportare gli MSP430, supporta altri C
di varie marche, tra cui Microchip, Atmel, ST, Renesas, Freescale, EnergyMicro e altri. Questo permette ai
team di sviluppo che fanno uso di C di diversi produttori di utilizzare un unico ambiente di sviluppo. Lo
svantaggio di IAR forse solo uno, ovvero il prezzo, generalmente maggiore rispetto ai prodotti offerti dalle
stesse case produttrici di C.
Nel seguente corso si utilizzer il CCS poich permette di programmare la famiglia Value Line senza alcun
limite. La versione del CCS sar la versione 5.5. La versione CCS 5.x differisce dalla CCS 4.x per pochi
aspetti, per cui il passaggio dalluna allaltra versione piuttosto indolore. Ciononostante alcuni aspetti delle
impostazioni, almeno da un punto di vista grafico, sono cambiati, per tale ragione, al fine di non rendere
obsoleti gli screen shot, si far uso dell'ultima versione (maggiori informazioni verranno date nella
successiva parte del corso dedicata all'impostazione dei vari strumenti di lavoro).

27

Hardware e Software di sviluppo 3

Oltre al CCS e IAR ci sono tool non ufficiali supportati dalla community intorno agli MSP430. In particolare
ci sono compilatori Open Source e Sistemi operativi Real Time. A proposito dei sistemi operativi Real Time
bene ricordare SYS/BIOS della stessa TI. Tale sistema operativo compatibile sia con gli MSP430 che con
altri C TI ed gratuito. Nella versione CCS 5.x pu essere selezionato per essere installato assieme lIDE.
Oltre ai tool ora descritti, dal momento che si utilizzer CCS e gli MSP430 della Value Line, bene ricordare
GRACE Tool (Graphical Peripheral Configuration Tool), disponibile come plug per CCS 4.x, mentre
integrato nel pacchetto d'installazione di CCS 5.x. Questo Tool permette d'impostare il C facendo uso di
uno strumento grafico. GRACE permette diniziare la programmazione degli MSP430 senza dover conoscere
in dettaglio le periferiche interne e le loro impostazioni. Sfortunatamente non ancora disponibile per le
altre famiglie, ma dal momento che si far uso prevalentemente della serie Value Line, questo non
comporter un grande disagio. Tale strumento pu tornare utile quale supporto nella scelta delle
impostazioni delle periferiche.
HARDWARE DI SVILUPPO
Programmatore per lo sviluppo Diversamente da molti produttori di C, gli MSP430, essendo utilizzati
prevalentemente in ambito professionale, non sono caratterizzati dallavere una miriade di programmatori
fatti in casa. Gli MSP430 sono supportati da un unico programmatore della TI noto come
programmatore FET (Flash Emulation Tool), presente sia in versione USB che per porta parallela. Il
programmatore fa uso dell'interfaccia JTAG e permette di programmare tutta la famiglia MSP430. in
particolare supportato sia dall'ambiente di sviluppo CCS che IAR. Infatti la TI fornisce i driver per il
programmatore, per cui altri fornitori di ambienti di sviluppo hanno sempre garantita la possibilit di
fornire strumenti di sviluppo al passo della stessa TI. Il programmatore FET, oltre a funzionare come
programmatore, fornisce la possibilit di eseguire anche il Debug del programma, ovvero permette la
lettura/scrittura del modulo di Debug interno agli MSP430 (EEM, Embedded Emulation Module).
Schede di sviluppo La TI produce diverse schede di sviluppo, specifiche per determinate applicazioni. Le
esperienze pratiche di questo corso sincentreranno prevalentemente sull'utilizzo della scheda di sviluppo
LaunchPad. Sebbene la scheda non possieda molto hardware a bordo, la presenza del package DIL (Dual in
Line package), del Debugger e del programmatore (per cui non richiede il programmatore FET) la rendono
utilizzabile anche per altri MSP430 Value Line. Uno dei suoi grandi vantaggi il suo prezzo irrisorio
inferiore a 10 Euro.
LAUNCHPAD KIT
La scheda Launchpad supporta tutta la famiglia MSP430G2xxx composta da package DIL sia a 14 che 20 pin.
Il kit di sviluppo fornito con due C della serie Value Line, uno dei quali gi montato e con un firmware
di esempio, che possono essere facilmente scambiati grazie allo zoccolo DIL. I due modelli di MSP430 sono
rispettivamente MSP430G2211 (1KB Flash) e MSP430G2231 (2KB Flash), entrambi a 14 pin. In particolare il
C MSP430G2231 possiede sia il sensore di temperatura, che il modulo USI (Universal Serial Interface), per
facilitare limplementazione di protocolli di trasmissione seriale sincrona SPI/I2C. La scheda fornita con il
cavo USB e una breve documentazione. Tuttavia nelle release pi recenti della scheda sono forniti invece
due C a 20 pin, il MSP4302452 (8KB Flash) e il MSP4302553 (16 KB Flash), questultimo con il modulo USCI
per implementare protocolli di trasmissione seriale sincrona e asincrona SPI/I2C/UART.

G2231

La scheda viene fornita con degli strip da collegare lateralmente. Sono forniti sia gli strip maschio che
femmina (da saldare). Sebbene i connettori femmina possano essere pi utili qualora si voglia interfacciare la
scheda con una breadboard di espansione, si consiglia di usare gli strip maschi, visto che le schede di
espansione per il LaunchPad (per esempio il Capacitive Touch boosterpack) montano il connettore

28

Hardware e Software di sviluppo 3

femmina, per cui sul LaunchPad necessario quello maschio. Visto il costo irrisorio della scheda si pu
anche considerare lacquisto di due schede.
In ultimo, nel kit presente un quarzo da 32 KHz, necessario qualora si vogliano sviluppare applicazioni in
cui sia richiesta una base temporale affidabile (per esempio trasmissione seriale).
La scheda di sviluppo LaunchPad, sebbene sia la pi piccola della famiglia, possiede una community che
condivide diversi progetti sviluppati. presente anche una Wiki Page, dalla quale poter partire per
attingere informazioni e spunti applicativi.

Installazione del kit LaunchPad Prima di collegare il LaunchPad bene avere preventivamente installato
lambiente di sviluppo CCS. Infatti con linstallazione di CCS vengono installati i driver necessari al
LaunchPad come anche per le altre schede di sviluppo supportate da CCS. Il driver per il LaunchPad
relativo al ponte seriale-USB TUSB3410, usato dal Debugger e dal Programmatore.

CODE COMPOSER STUDIO (CCS)


In questa parte del corso si descriveranno in maggior dettaglio gli strumenti sia hardware che software che
accompagneranno buona parte delle esperienze pratiche che seguiranno. bene iniziare con il software,
perch non installare lambiente di sviluppo potrebbe portare problemi con l'installazione dell'Hardware,
non essendo presenti i driver necessari per il riconoscimento delle schede di sviluppo.
SOFTWARE PER INIZIARE
Per iniziare a programmare gli MSP430 della Value Line sufficiente la versione CCS limitata nella
dimensione del codice compilabile. Infatti tale versione, seppur limitata nelle dimensioni del codice
compilato, permette di ottenere codici fino a 16KB, che rappresenta la dimensione massima della memoria
flash disponibile negli MSP430G2xxx (Value Line). La versione del CCS utilizzata nel corso la 5.5. Sebbene
non ufficialmente rilasciata per scopi di produzione, praticamente alla sua ultima fase beta (Dicembre
2011). Altri ambienti di sviluppo come IAR, MPLAB, in cui la compilazione del codice e la programmazione
del C sono fasi piuttosto rapide, la versione 4.x di CCS pu dare un po' fastidio, mentre la versione CCS 5.5
non ha pi nulla da invidiare agli altri ambienti di sviluppo. La versione da scaricare quella per Windows,
ma formalmente i linuxiani potrebbero utilizzare anche la versione per Linux. Salvo l'installazione, gli altri
passi necessari per creare un progetto sono indipendenti dal sistema operativo utilizzato.
29

Hardware e Software di sviluppo 3

AMBIENTE DI SVILUPPO ECLIPSE


Prima di iniziare a parlare di CCS, bene spendere due parole sull'IDE (Integrated Development Environment)
Eclipse, sul quale basato appunto CCS. Eclipse un ambiente di sviluppo multi-piattaforma (Windows,
Linux, Mac) e multi-linguaggio, ovvero utilizzabile per differenti linguaggi di programmazione. Venne
inizialmente sviluppato dalla IBM, che successivamente ha reso disponibile il progetto come Open Source.
Attualmente molte altre grandi societ collaborano nello sviluppo di tale progetto, oltre naturalmente a
persone singole che nel nome dellOpen Source rendono disponibili molti plug-in. Eclipse pensato per
permettere un ambiente di sviluppo comune nel quale poter integrare, per esempio per mezzo di plug-in,
altre funzioni. Fondamentalmente Eclipse potrebbe essere considerato un editor dove poter scrivere il
proprio codice, ma grazie alla sua integrazione con compilatore, linker, programmatore, deve essere
considerato pi propriamente come IDE, ovvero ambiente di sviluppo integrato. La TI ha iniziato ad
utilizzare Eclipse come piattaforma comune per i propri microprocessori, ma anche altre societ hanno fatto
uso di Eclipse come ambiente di sviluppo integrato a supporto dei propri microprocessori e compilatori.
Utilizzare un ambiente di sviluppo come Eclipse permette alle societ che lo utilizzano di avere un editor
all'avanguardia, oltre agli strumenti sviluppati da terze parti. Essendo Elicpse disponibile con licenza che
permette di rilasciare prodotti commerciali, d anche la possibilit di far uso di un tool che rappresenta pi o
meno uno standard negli ambienti di sviluppo di sistemi embedded.
Oltre all'IDE Eclipse, bene anche ricordare lIDE Netbeans supportato da societ come Oracle, Sun (java).
Questo ambiente di sviluppo per esempio utilizzato dalla Microchip nella sua nuova versione dell'IDE
MPLAB X, che come la versione CCS 5.5 supporta sia Windows che Linux.
INSTALLARE CCS
Dopo aver scaricato la versione di CCS 5.5, possibile procedere alla sua installazione. Dopo aver avviato
linstallazione si ottiene la finestra di selezione. Non richiesta linstallazione di tutti gli strumenti presenti,
visto che il corso sar esclusivamente sugli MSP430. Premendo Next compare la seconda finestra di
selezione. Per i compilatori e la documentazione sufficiente installare i pacchetti relativi agli MSP430.
Premendo nuovamente Next, si ottiene un'altra finestra di selezione. Come detto in precedenza, dalla
versione 5.1, CCS istalla anche GRACE tool (cosa non fatta nella precedente versione CCS 4.x), la nuova
documentazione MSP430ware e il sistema operativo SYS/BIOS che come detto un sistema operativo real
time Free, che pu essere utilizzato con diversi C della TI, tra cui gli MSP430. Premendo nuovamente Next
si ottiene un'ultima finestra. Il programmatore che necessario installare il FET programmer (MSP430
USB FET). Il programmatore FET per porta parallela non normalmente selezionato, ma per coloro che
volessero realizzarlo in casa, bene installare subito il driver al fine di evitare problemi in un secondo
tempo. Gli altri programmatori della lista fanno riferimento ai C che sono stati selezionati in precedenza,
che come detto non saranno descritti in questo corso.
AVVIO DI CCS
Come abbiamo visto CCS basato sull'IDE Eclipse, per cui il suo avvio consiste nell'avviare Eclipse. La
prima volta che si avvia pu essere richiesta la licenza che si vuole attivare, in particolare scegliere la
modalit limitata in codice a 16 kB (la licenza completa dura 90 giorni). Dopo questo primo passo che non si
presenter una seconda volta, si ha l'avvio vero e proprio di CCS. Ad ogni avvio verr richiesto il Workspace
che si vuole aprire (conviene non disattivare questa funzione). Il Workspace rappresenta la directory in cui
verranno salvati i progetti. Ogni Workspace rappresenta dunque, in generale, una raccolta di progetti. Pi
Workspace possono essere creati, al fine di poter classificare vari progetti. Per esempio un determinato
progetto di un Robot potrebbe rappresentare un buon esempio di Workspace in cui racchiudere vari progetti
e programmi da caricare nei diversi C utilizzati. Sebbene il Workspace rappresenti una directory, Eclipse, e
dunque CCS, crea al suo interno diversi file che contengono le informazioni relative ai vari progetti, per cui
non si pu creare un Workspace semplicemente creando una directory e copiando le varie sottocartelle che
contengono i progetti. Per fare questo bisognerebbe manualmente creare anche i file associati al Workspace
contenuti nella directory .metadata (in ambiente Linux rappresenta una cartella nascosta), per cui bene fare
queste operazioni sempre per mezzo dell'IDE. Qualunque nome andr bene. Volendo, una volta impostato il
Workspace, possibile impostarlo come default, ovvero fare in modo che venga aperto automaticamente
all'apertura di CCS. Tuttavia non conviene usare questa opzione se si vuole la possibilit di cambiare
30

Hardware e Software di sviluppo 3

Workspace in fase di avvio. In ogni modo anche dopo l'avvio possibile chiudere un Workspace ed avviarne
un altro.
CREARE UN PROGETTO CON CCS
Una volta avviato CCS, apparir la schermata principale per mezzo della quale possibile avere le prime
informazioni su CCS ed esempi sui vari C. Per creare un nuovo progetto da questa schermata, possibile
andare direttamente su New Project, o per mezzo del menu File
New
CCS Project, o ancora per mezzo
del Menu Project
New CCS Project. A seconda della situazione pu tornare pi pratico l'uno o l'altro
metodo. necessario dare un nome al progetto (Project Name), impostare la famiglia (MSP430), la variante
(MSP430Gxxx), il C (es. MSP430G2231 per il LaunchPad) e la connessione (TI MSP430 USB1). In base al C
selezionato, la finestra in basso a sinistra viene aggiornata con le opzioni disponibili scegliere (Empty Project
with main.c). In particolare si noti che per gli MSP430 della Value Line disponibile Grace Tool, ovvero lo
strumento di impostazione grafico. Terminate le impostazioni, premere Finish.
Si noti che il nome del progetto appena creato riportato sulla parte sinistra dell'IDE. In questa sezione,
nominata Project Explorer, sono elencati tutti i nuovi progetti che si creano e che appartengono allo stesso
Workspace. All'interno di ogni progetto, pur non avendo scritto nulla, sono presenti gi dei file, ed in
particolare il Main contiene gi tutto il necessario. Si noti che il nome del progetto in grassetto. Sebbene
questo non sia ora importante, fondamentale quando si hanno pi progetti. Il nome del progetto in
grassetto rappresenta il progetto attivo, ovvero quello che effettivamente viene compilato e salvato
all'interno del C. Per compilare e rendere attivi altri progetti (uno alla volta) basta selezionare il nome del
progetto dalla lista.
Tra i file contenuti nel progetto vuoto vi sono alcuni file include, che rappresentano tutti i file necessari per
lavorare con il C scelto. Il file .cmd rappresenta il file per il linker, ovvero il file che contiene
l'organizzazione della memoria del C scelto. Le informazioni contenute al suo interno permettono in fase di
compilazione del programma di allocare il codice in modo opportuno. Facendo un doppio click sul file
possibile aprirlo direttamente in CCS e verificare i vari settori in cui suddivisa la memoria. Dettagli sulla
suddivisione della memoria verranno dati in seguito quando si parler dell'architettura degli MSP430.
L'ultimo file introdotto dal Project Wizard il file ccxml, in cui memorizzato il tipo di debugger utilizzato
e la particolare scheda utilizzata. Nel nostro caso, facendo uso del LaunchPad, non dovremo cambiare
questo file, infatti la scheda viene automaticamente riconosciuta tra le schede supportate.
COMPILARE UN PROGRAMMA CON CCS
Creato il primo programma, o meglio dopo aver fatto creare il primo programma nulla facente al Project
Wizard, possibile procedere alla sua compilazione premendo il tasto del martello. La compilazione verr
eseguita in pochi secondi e creer la sottodirectory Debug, in cui sono contenuti tutti i file del progetto, ed in
particolare il file obj utilizzato per essere caricato nel C.
Per procedere alla programmazione del C bisogna ora collegare la scheda LaunchPad via USB. La prima
volta che verr connessa potr richiedere un po' di tempo prima che possa essere propriamente riconosciuta
(richieder infatti che i driver installati con CCS vengano associati alla scheda). Una volta che la scheda
stata riconosciuta, possibile premere il tasto di Debug (scarafaggio) che permette di caricare il progetto nel
C. Qualora la scheda di sviluppo non sia provvista di modulo di Debug come la scheda LaunchPad, al fine
di programmare il C necessario un programmatore, in particolare, come detto, un FET programmer o
compatibile. La fase di compilazione per mezzo del martello potrebbe anche essere saltata premendo
direttamente lo scarafaggio verde. Infatti se il progetto stato cambiato e non compilato verr compilato
prima di essere scaricato nel C. Il martello utile per scegliere se compilare il programma per una fase di
Debug o Release. La compilazione per Debug caratterizzata dal fatto che non viene praticata nessuna
ottimizzazione, cosa utile in fase di esecuzione passo passo del programma. Eventuali livelli di
ottimizzazione possono essere scelti tra le opzioni del progetto, ed in particolare si possono fare
ottimizzazioni a favore di un ridotto uso della memoria flash, o a favore della velocit di esecuzione,
desideri che spesso sono in conflitto tra loro. Compilato il progetto, quello che si aveva di fronte non pi
presente. In gergo di Eclipse si dice che cambiata la prospettiva (perspective). In particolare sono presenti
due prospettive principali: C/C++ per scrivere il codice (CCS Edit) e Debug. Il passaggio dall'una all'altra
prospettiva sempre possibile (ammesso che la compilazione sia andata a buon fine e il C sia connesso ad
un Debugger).
31

Hardware e Software di sviluppo 3

Per avviare il programma necessario premere il tasto Play (triangolo verde), mentre per metterlo in pausa o
fermarlo, premere rispettivamente il tasto Suspend e Terminate. Qualora non si volesse eseguire il
programma in maniera continua, possibile farlo passo passo premendo rispettivamente i tasti Step Into o
Step Over (permette di considerare una funzione come se fosse una semplice istruzione). Durante la fase di
Debug inoltre possibile monitorare i valori dei registri interni o delle variabili.
APRIRE UN PROGETTO CON CCS
Come detto ogni progetto contenuto all'interno di un Workspace. Allo stesso modo del Workspace, anche
il progetto contiene delle informazioni speciali contenute all'interno della sottodirectory .settings
Diversamente da altri ambienti di sviluppo, non esiste un vero file di progetto, sul quale cliccare per poter
aprire in automatico il progetto stesso. Per poter aprire un progetto bisogna aprire il Workspace in cui stato
creato. Questo come detto pu essere fatto in fase di apertura di CCS o dal menu File
Switch Workspace.
Questo aspetto ereditato dalla filosofia di Eclipse. Per aprire un progetto sviluppato da altri, bisogna
importare il progetto all'interno del proprio Workspace. Per poterlo aprire e inserirlo nella lista dei propri
progetti bisogna andare sul menu File
Import. Dalla finestra bisogna selezionare la voce CCS Existing
CCS/CCE Eclipse Project. Premendo Next si ha la possibilit di selezionare il Workspace in cui contenuto
il progetto che bisogna importare. Una volta selezionato un Workspace dal quale importare un progetto,
compare la lista dei progetti contenuti nel Workspace. Dalla finestra possibile selezionare uno o pi
progetti che si vuole importare nel proprio Workspace. In particolare consigliabile selezionare la voce
Copy Project into Workspace, in maniera che sia creata una copia indipendente all'interno del proprio
Workspace. Allo stesso modo con cui si seleziona la directory di un Workspace, si pu selezionare anche la
directory di un progetto (per esempio se laltro utente fornisse solo la cartella del progetto e non l'intero
Workspace). Premendo il tasto Finish, i progetti selezionati che appartenevano ad un altro Workspace
compaiono nella lista del Project Explorer e si possono selezionare per renderli attivi e compilarli.
CREARE UN FILE .HEX CON CCS
Ogni qual volta si voglia sviluppare un'applicazione professionale con gli MSP430, si ha in generale
l'esigenza di dover programmare il C direttamente, senza dover aprire il progetto per mezzo dell'IDE
Eclipse. Per fare questo necessario poter creare un file esterno al progetto che sia possibile richiamare per
mezzo del programma dedicato al programmatore. Per questo scopo il formato normalmente utilizzato lo
standard Intel e viene salvato normalmente con l'estensione .hex (formato esadecimale). Il codice viene
salvato in formato ASCII scrivendo il valore esadecimale relativo al contenuto di ogni locazione di memoria.
Oltre al File in formato .hex possibile salvare il formato .txt della TI molto simile a quello in formato Intel.
Molti IDE generano automaticamente il file .hex che viene poi usato per programmare il C. Sebbene CCS
possa fare questo, richiamando il programma hex430.exe installato automaticamente con CCS, di default non
impostato per farlo. Infatti la programmazione del C dallIDE avviene per mezzo dellobject file (creato
nella directory Debug del progetto). Per creare il file .hex o .txt necessario andare nel menu
Project/Properties. Dalla Finestra che si apre necessario impostare la voce Build (in alto a sinistra). Alla
voce in basso a destra Apply Predefined Step necessario selezionare la voce Create flash image: IntelHEX. Una volta fatto questo, la linea Command e description vengono popolate. Il comando non fa altro che
richiamare il tool hex430.exe e passare in linea di comando l'object file da convertire. Una volta eseguite
queste impostazioni e premuto ok, il file .hex verr creato ad ogni compilazione del codice (la possibilit di
programmare il target device direttamente dallIDE, ovvero progetto, rimane invariata). Il file .hex viene
salvato all'interno della directory Debug del progetto impostato.
I passi appena introdotti devono essere fatti per ogni progetto per il quale si voglia un file .hex. Alcune
preferenze da applicare per ogni progetto possono essere impostate direttamente dal menu
Window/Preferences. Tra le preferenze non c la possibilit di creare il file .hex per ogni progetto, per cui
bisogna farlo manualmente. In ultimo, ma non meno importante, si ricorda che il file .hex risulta utile anche
nei casi in cui si faccia uso del bootloader. Infatti dal lato PC il programma inviato spesso in formato .hex o
.txt. Nel caso del file TI-TXT, il programma viene salvato facendo precedere i byte relativi al programma
stesso, dall'indirizzo nel quale doverli salvare. Nel formato Intel, i dati sono strutturati in maniera
leggermente diversa, ogni riga contiene il numero di byte contenuti nella stessa, l'indirizzo in cui iniziare a
scrivere i dati, e in ultimo un checksum ottenuto dai byte precedenti.

32

Elementi di Programmazione 4

CAP. 4

ELEMENTI DI PROGRAMMAZIONE

UTILIZZO ED IMPOSTAZIONE DELLE PORTE I/O


Sebbene le porte di input/output (GPIO General Purpose Input/Output) non facciano realmente parte
dellarchitettura di un C, la loro importanza e continuo utilizzo la integrano con questultima. Gli MSP430,
come anche molti altri C, raggruppano i pin di I/O in porte di dimensione massima di 8 bit (8 pin), ovvero
un byte. Il nome delle porte Px.y dove x rappresenta il numero della porta (parte da 1), mentre y il bit di
interesse nel range 0-7. Fanno eccezione i pin che possono essere associati all'interfaccia JTAG, la cui
nomenclatura PJ.y. Gli MSP430 della value line hanno 8 pin di I/O sulla porta P1.
REGISTRI ASSOCIATI ALLE PORTE DI I/O
Esiste una serie di registri a 8 bit associati alle porte di I/O (allocati nella parte bassa della memoria). Ogni bit
controlla il pin corrispondente della porta, per esempio il bit 0 controlla il pin P1.0, il bit 1 il pin P1.1 e cos
via.
PxDIR: registro i cui bit impostano il pin corrispondente come input o come output. La loro funzione
inoltre spesso multiplexata con altre funzioni associate ad altre periferiche, per cui i vari bit devono essere
propriamente impostati per un corretto funzionamento. Stato dei bit:
Bit 1: imposta il pin come output
Bit 0: imposta il pin come input.
Poich in altri C il contrario, per limitare i disagi, durante la programmazione bene usare le costanti
definite allinterno dell'header file, incluso nel programma o linkato in compilazione.
PxIN: registro di input, la cui lettura permette di conoscere lo stato della porta stessa. Lo stato dei bit il
seguente:
Bit 1: significa che il relativo pin collegato a Vcc o livello logico compatibile con il livello alto. Bit 0: significa
che il relativo pin collegato a GND o livello logico compatibile con il livello basso.
Si noti che il registro PxIN di sola lettura. Sebbene sia possibile scrivere in tale registro, durante la fase di
scrittura si ha in generale un aumento della corrente consumata dal C stesso. Inoltre la scrittura in tale
registro non causa la variazione delle uscite.
PxOUT: registro dove il C opera la scrittura dei dati in ouput. Lo stato dei bit il seguente:
Bit 1: porre a livello alto un bit significa avere loutput collegata a Vcc.
Bit 0: porre a livello basso un bit significa avere loutput collegata a GND.
PxREN: registro che abilita o meno il collegamento di ogni pin della famiglia MSP430F2xx e successiva,
qualora sia impostato come input, con un resistore di pull-up, tra il pin e Vcc, o ad un resistore di pulldown, tra il pin e la massa. Lo stato dei bit il seguente:
Bit 1: porre a livello alto un bit significa avere il resistore abilitato.
Bit 0: porre a livello basso un bit significa avere il resistore disabilitato.
Per i pin che sono impostati come input, la scelta della tipologia di resistore avviene per mezzo del registro
PxOUT. Il bit corrispondente determina se il resistore sar di pull-up o pull-down:
Bit 1: resistore di pull-up
Bit 0: resistore di pull-down.
Qualora si abilitino i resistori di pull-up o pull-down, bisogna fare attenzione quando si scrive sulla porta
PxOUT, per non alterare il valore dei bit associati ai resistori. Se non si pone questa attenzione, si rischia di
alterare la tipologia del resistore, ed eventualmente vedere il pulsante di ingresso non funzionare pi.
Nella figura successiva schematizzata, da un punto di vista hardware, la selezione di un pin di una porta
come Input o Ouptut.
P1IE, P2IE: la famiglia MSP430F2xx possiede sulla porta P1 e P2 delle linee d'interrupt, che possono essere
abilitate individualmente (alcune varianti della famiglia MSP430F5xx e MSP430FR5xx possiedono interrupt
anche sulle altre porte).
Bit 1: interrupt abilitato sul pin.
Bit 0: interrupt disabilitato sul pin.

33

Elementi di Programmazione 4

P1IES, P2IES: ogni interruzione oltre a poter essere abilitata ha la possibilit di avere selezionato il fronte sul
quale deve essere abilitata. In particolare sia il fronte di salita che discesa possono essere selezionati.
Bit 1: interrupt viene abilitato nella transizione da livello alto a livello basso.
Bit 0: interrupt viene abilitato nella transizione da livello basso a livello alto.
P1IFG, P2IFG: una volta che l'interrupt viene generato, il PC viene caricato con il valore contenuto
nellinterrupt Vector associato alla porta che ha generato l'interrupt. Le sorgenti per ogni porta sono
differenti, visto che ogni bit pu concorrere alla generazione dell'interrupt. Per questa ragione il valore del
bit che ha generato linterruzione memorizzato allinterno del registro P1IFG e P2IFG, ovvero il registro
degli interrupt Flag. Il bit che viene letto deve essere azzerato manualmente, visto che la sorgente delle
interruzioni multipla. Per interrupt che non hanno sorgenti multiple, il flag viene automaticamente
azzerato. Lo stato dei bit il seguente:
Bit 1: l'interrupt stato generato dal pin.
Bit 0: l'interrupt non stato generato dal pin.
PxSEL, PxSEL2: quasi ogni pin, oltre a poter essere gestito individualmente come semplice input o output,
spesso condiviso con altre funzioni speciali. Per esempio la porta seriale, SPI, I2C, uscite PWM e via dicendo.
Per selezionare quale funzione debba esser attiva, necessario impostare correttamente i registri PxSEL e
PxSEL2, che controllano il multiplexer di uscita associato alla porta dinteresse. Per esempio, nel datasheet
dellMSP430G2553 dove riportata la Tabella che indica le possibili associazioni di ogni pin, il pin P1.0 pu
essere usato sia come standard I/O, che per il clock del timer TA0, avere in output il clock ACLK, oltre ad
altre funzioni. La funzione di default indicata per prima, cos i pin della porta P1 sono pin di I/O, mentre
quelli di P2 sono collegati alloscillatore la quarzo. Per selezionare le varie scelte necessario impostare
propriamente il bit P1DIR.1 e P1SEL.1 e P1SEL2.1 come riportato alla destra della Tabella. Infine si ricorda
che queste impostazioni sono relative allMSP430 utilizzato, per cui non si trovano nella User Guide, ma nel
datasheet del dispositivo utilizzato, in particolare nella sezione Application Information, nella quale si
descrivono i vari pin.
Pin Oscillation
La famiglia MSP430F2xx possiede in alcune sue varianti lopzione Pin Oscillation (in particolare nella
Value Line). Questa funzione permette di implementare facilmente sistemi con Cap Touch (capacitivi). La
caratteristica dei pin che possiedono tale funzione quella di offrire tutta la circuiteria necessaria per far
oscillare un pin al cui input collegata una capacit, in generale formata semplicemente da una piazzola sul
PCB di dimensioni opportune.

MSP430

PxOUT

P1.x

CPU
PxIN

PxOUT

PxREN

PxDIR

Pull-Up
VCC
GND
Pull-Down

CONTROLLO DEI BIT ASSOCIATI ALLE PORTE (Flipping Bits)


In questo paragrafo vengono illustrate le tecniche di programmazione per cambiare il valore di singoli bit in
un registro. Si consideri per esempio un LED connesso tra la porta P1.4 e ground. Se P1.4 a 0V, il LED
spento, se P1.4 collegato a Vcc, il LED acceso. Pertanto occorre accedere al bit 4 nel registro P1 (il primo
bit il bit 0). Per variarne il valore esistono diverse tecniche:
1. Direct Assignment
Se per esempio si deve impostare solo il pin 4 come output e gli altri input:
P1DIR = 0b00010000;
// oppure P1DIR = BIT4 se si definisce la costante BIT4
Questo metodo funziona bene in fase di inizializzazione, ma non va bene se si vuole cambiare un solo bit e
lasciare gli altri inalterati.
2. Bitwise Logic Operators | and &

34

Elementi di Programmazione 4

Sono operatori logici tipo AND e OR, che per operano bit a bit (2 input, 1 output). Poich X|1=1, e X&0=0,
indipendentemente dal valore di X, si possono usare le seguenti istruzioni
P1DIR |= 0b00010000
// compatta, equivale a P1DIR = P1DIR | 0b00010000
P1DIR &= 0b11101111
// compatta, equivale a P1DIR = P1DIR & 0b11101111
eventualmente anche in esadecimale, oppure, usando la costante BIT4,
P1DIR |= BIT4
// imposta il pin 4 come output senza alterare gli altri
P1DIR &= ~BIT4
// imposta il pin 4 come input senza alterare gli altri
3. Switching bit con ^
Poich loperatore exclusive OR ha la propriet che X^0=X, ovvero lascia X invariato, mentre X^1=~X, ovvero
inverte (toggle) tutti i bit di X, listruzione
P1DIR ^= 0b00010000
// oppure P1DIR ^= BIT4
fa commutare il bit 4 e lascia gli altri inalterati.
Se si aggiunge un LED sulla porta P1.6, il codice seguente li fa commutare entrambi
P1OUT = 0;
// disattiva tutte le uscite (tutti input)
P1DIR = BIT4 + BIT6;
// =0b01010000 imposta le porte P1.4 e P1.6 come uscite
P1OUT |= BIT4 + BIT6;
// entrambi ON
P1OUT &= ~(BIT4 + BIT6);
// entrambi OFF
P1OUT ^= BIT4 + BIT6;
// entrambi invertiti (toggling)
ESEMPIO: ACCENDERE I LED (blinking leds)
Per iniziare a programmare, dopo aver creato un progetto vuoto, occorre copiare il file riportato sotto,
all'interno del file main.c del progetto. Dettagli sul programma saranno dati nel prossimo paragrafo.
#include <msp430.h>
int i = 0;
void main (void)
{
WDTCTL = WDTPW + WDTHOLD;
// stop watchdog timer
P1DIR |= BIT0 + BIT6;
// imposta P1.0 e P1.6 come output
P1OUT = 0x01;
// collega P1.0 a Vcc (1) e P1.6 a Gnd (0)
while (1)
{
for (i = 0; i <30000; i++);
// pausa
P1OUT ^= BIT0 + BIT6 ;
}
}
// inverte lo stato delle porte
Una volta copiato il file, salvare e compilare premendo il martello nella Tool bar. Il programma dovrebbe
essere compilato senza problemi (a partire dalla versione di CCS 5.2 presente il plug-in Ultra Low Power
Advisor che si presenter automaticamente dopo la compilazione, premere semplicemente avanti per
proseguire). A questo punto si pu collegare la scheda LaunchPad al PC per mezzo di un cavo USB. Se la
prima volta, bisogna dare tempo al PC di installare i driver associati alla periferica, permettendo a Windows
di installarli automaticamente. L'installazione dei driver avverr con successo se CCS stato installato prima
di collegare la scheda LaunchPad al PC. Infatti CCS installa i vari driver delle schede di sviluppo
della TI oltre ai diversi programmatori supportati. Finita l'installazione della scheda LaunchPad, possibile
procedere al caricamento del programma all'interno del C, premendo il tasto della cimice verde. Questo
tasto avvier la compilazione e caricher il programma nel nostro MSP430. Si sarebbe potuto premere
direttamente questo tasto senza passare per la compilazione preventiva. La sola compilazione utile per
vedere se sono presenti degli errori senza dover inizializzare lhardware (programmatore). A fine
programmazione la finestra di CCS cambia da Edit a Debug. A questo punto si pu eseguire il programma
semplicemente premendo il tasto Play (nominato Resume) o il tasto Funzione F8. Il programma inizier a
salutare il mondo facendo accendere in maniera alternata il LED rosso e il LED verde. Il programma caricato
nell'MSP430 rimane al suo interno anche una volta terminato CCS. Questo significa che attaccando
semplicemente la scheda ad un altro PC o presa USB, per avere l'alimentazione, i LED inizieranno a
lampeggiare. Allo stesso modo se si dovesse staccare il C e inserire in una Breadboard, il programma
continuer a funzionare. Questo perch la scheda LaunchPad pu essere utilizzata anche come
programmatore. Le linee di programmazione e alimentazione sono rappresentate dai 5 Jumper presenti sul
connettore J3. Facendo un prolungamento possibile anche programmare direttamente un MSP430 su
un'altra scheda o Breadboard (oltre alle linee del connettore J3 bisogna collegare la massa GND). Per i
collegamenti richiesti per la programmazione si rimanda allo schema elettrico della scheda LaunchPad e
all'Application Note slau320.

35

Elementi di Programmazione 4

ANALISI DEL PROGRAMMA


#include <msp430.h>
Il programma inizia includendo un header file, che contiene le informazioni del dispositivo utilizzato.
Quello incluso per generico e in realt non quello che contiene le informazioni del dispositivo. Per aprire
il file, posizionare il cursore sul nome msp430, premere il tasto destro del mouse, e cliccare su Open
Declaration, o premere semplicemente il tasto Funzione F3. Aperto il file, al suo interno sono presenti tutti
gli header file degli MSP430, tutti con sfondo grigio, tranne uno, ovvero quello associato all'MSP430G2231,
che effettivamente viene incluso.
#elif defined (__MSP430G2101__)
#include "msp430g2101.h"
#elif defined (__MSP430G2001__)
#include "msp430g2001.h"
#elif defined (__MSP430G2231__)
#include "msp430g2231.h"
#elif defined (__MSP430G2221__)
#include "msp430g2221.h"
#elif defined (__MSP430G2131__)
#include "msp430g2131.h"
Quando
viene
creato
il
progetto
e
selezionato
l'MSP430G2231,
viene
definita
la
costante __MSP430G2231__ per cui, per mezzo delle direttive #elif (ovvero else if), viene incluso il
file msp430G2231.h Questo file lo si sarebbe anche potuto includere direttamente nel nostro file main.c al
posto del file msp430.h Qualora non si abbiano le informazioni del progetto, il tipo di MSP430 utilizzato non
noto, per cui potrebbe essere utile anche la pratica di includere lheader file del dispositivo piuttosto che
quello generico. Aprendo il file msp430G2231.h possibile trovare altre informazioni importanti sul
dispositivo utilizzato, in particolare si trova la definizione dei bit associati alle varie periferiche disponibili e
definizioni utili per la programmazione, come per esempio i bit costanti (in esadecimale o binario):
// STANDARD BITS (bit costanti pari a 1 nella posizione corrispondente)
#define BIT0
(0x0001)
// 0b0000-0000-0000-0001 (il - non si mette!)
#define BIT1
(0x0002)
// 0b0000-0000-0000-0010
#define BIT2
(0x0004)
// 0b0000-0000-0000-0100
#define BIT3
(0x0008)
// 0b0000-0000-0000-1000
#define BIT4
(0x0010)
// 0b0000-0000-0001-0000
#define BIT5
(0x0020)
// 0b0000-0000-0010-0000
#define BIT6
(0x0040)
// 0b0000-0000-0100-0000
#define BIT7
(0x0080)
// 0b0000-0000-1000-0000
#define BIT8
(0x0100)
// 0b0000-0001-0000-0000
#define BIT9
(0x0200)
// 0b0000-0010-0000-0000
#define BITA
(0x0400)
// 0b0000-0100-0000-0000
#define BITB
(0x0800)
// 0b0000-1000-0000-0000
#define BITC
(0x1000)
// 0b0001-0000-0000-0000
#define BITD
(0x2000)
// 0b0010-0000-0000-0000
#define BITE
(0x4000)
// 0b0100-0000-0000-0000
#define BITF
(0x8000)
// 0b1000-0000-0000-0000
e i valori relativi alla calibrazione delle periferiche. I valori calibrati dipendono da dispositivo a dispositivo,
per cui l'header file pu essere utile per vedere quali valori sono disponibili (i valori sono riportati anche nel
datasheet del C utilizzato).
Oltre alle varie definizioni, viene incluso anche un header file molto importante:
#include "in430.h"
Aprendo il file troviamo le seguenti informazioni:
void _enable_interrupts(void );
void _disable_interrupts(void );
unsigned short _bic_SR_register(unsigned short mask);
unsigned short _bic_SR_register_on_exit(unsigned short mask);
unsigned short _bis_SR_register(unsigned short mask);
unsigned short _bis_SR_register_on_exit(unsigned short mask);
unsigned short _get_SR_register(void );
unsigned short _get_SR_register_on_exit(void );

36

Elementi di Programmazione 4

unsigned short _swap_bytes(unsigned short src);


void _nop(void );
void _never_executed(void );
Queste rappresentano le cosiddette Intrinsic Functions, ovvero delle funzioni offerte a supporto del
programmatore. In particolare ogni funzione potrebbe anche essere implementata facilmente dal
programmatore impostando i vari bit dei registri, ad eccezione della funzione swap, che effettua
un'operazione di manipolazione sul registro passato. Vedremo alcuni esempi di utilizzo di tali funzioni nei
prossimi capitoli.
Oltre a queste funzioni sono presenti anche le definizioni di alcune funzioni utilizzate in ambiente IAR.
Questo viene fatto per rendere quanto pi compatibili possibile i due ambienti di sviluppo.
Prima di andare oltre consigliabile leggere almeno una volta lheader file dell'MSP430 utilizzato, al fine di
avere una panoramica delle periferiche e del modo con cui vengono definite le varie costanti.
Il programma inizia con la funzione main, ovvero la funzione principale per un programma in C. La prima
istruzione :
WDTCTL = WDTPW + WDTHOLD;
Questa istruzione consiste nel caricare due costanti nel registro WDTCTL. I registri che terminano con CTL
rappresentano i registri di controllo, in particolare WDTCTL rappresenta il registro di controllo del
Watchdog. Si ricorda che il watchdog rappresenta un timer, che viene incrementato automaticamente, e, nel
passare da 0xFFFF a 0x0000, genera un Reset della CPU. In particolare la sua funzione quella di monitorare
la CPU da eventuali stalli di software, infatti il watchdog deve essere periodicamente resettato per mezzo del
bit WDTCNTCL prima che giunga a 0 a causa di un overflow e venga generato un Reset. Se questo non
dovesse avvenire, vorrebbe dire che il programma entrato in stallo (probabilmente), per cui il Reset
generato dal watchdog permette di riavviare il C. In molte applicazioni questo non serve, per cui pu
essere disattivato. Le ragioni che portano a disattivare il watchdog sono principalmente due:
1. non si ha bisogno di proteggere il software;
2. per risparmiare energia (vita pi lunga di una batteria).
WDTCTL punta alla parola (2 byte) che controlla il timer watchdog. Questo timer resetta il chip dopo che
passato un certo tempo. Per far continuare a girare il programma in oggetto, occorre disabilitare questo
timer. Gli 8 bit meno significativi (da 0 a 7) del registro WDTCTL controllano il comportamento del timer
watchdog. Nello specifico il bit 7, se viene posto a 1, pone il timer in hold (mette in pausa il contatore).
Nellheader file, WDTHOLD definito essere una costante di 2 byte con solo il bit 7 posto a 1 (cio 0b00000000-1000-0000, o in esadecimale, 0x0080). Scrivendo WDTHOLD su WDTCTL, si disattiva il timer.
WDTCTL usa solo 1 byte per il controllo; gli 8 bit pi significativi sono impostati come una chiave, per
evitare che un programma cambi inavvertitamente la sua configurazione. Quindi per scrivere su WDTCTL
serve includere una password, che il valore 0x5a, infatti lheader file definisce la costante WDTPW come
0x5a00.
#define WDTPW (0x5A00).
Aggiungendo questo valore a WDTHOLD, il risultato scrivere il valore 0x5a80 su WDTCTL, che contiene
la chiave necessaria e setta il bit di hold a 1. (Si noti che si poteva anche usare la notazione
WDTPW|WDTHOLD invece di addizionarli, ottenendo lo stesso effetto, tuttavia gli esempi della TI usano
laddizione).
Il watchdog non viene disabilitato durante la fase di programmazione (usando dei pseudo fusibili che
disattivano il timer), ma durante l'esecuzione del programma stesso. Dal momento che il watchdog serve per
garantire la sicurezza del programma in esecuzione, c' chi preferisce attivare o disattivare il watchdog in
fase di programmazione, ed avere impossibilitata l'opzione di attivare/disattivare il watchdog in fase di
esecuzione del programma, visto che un malfunzionamento del programma potrebbe disattivare il
watchdog. Gli MSP430 permettono di attivare/disattivare il watchdog via software, ma per garantire che non
venga disattivato inavvertitamente, la scrittura del registro di controllo pu avvenire solo previa scrittura
della password. I critici dicono che questa procedura non comunque sicura, visto che la password scritta
nel codice stesso, per cui ci potrebbe essere una remota possibilit che la password venga utilizzata da una
parte di codice impazzito e blocchi il watchdog. Questo aspetto filosofico-probabilistico non molto fondato,
primo perch molto improbabile, secondo perch in applicazioni ad alta affidabilit in cui l'applicazione
debba essere monitorata, sia che il watchdog sia attivabile solo durante la fase di programmazione, sia via
Software, se si vogliono ottenere alti standard di sicurezza, si fa uso di un watchdog esterno (per esempio in
37

Elementi di Programmazione 4

ambito automobilistico). In applicazioni di questo tipo non raro trovare due C che eseguono lo stesso
compito in maniera ridondante (alcune volte si usano anche due C di marche diverse al fine di ridurre la
probabilit di rottura di entrambi allo stesso tempo).
All'avvio dell'MSP430, il watchdog automaticamente attivato, per cui bisogna disattivarlo qualora non se
ne faccia uso. Nel registro di controllo del watchdog possibile impostare anche il clock da utilizzare per il
Timer, come anche impostare il watchdog per uso generico (semplice Timer). Oltre a queste funzioni, il
registro di controllo permette di impostare se il pin RST/NMI debba essere RST o NMI (di default
impostato come RST). Qualora si selezioni la funzione NMI (Interruzione non mascherabile), anche
possibile impostare il fronte sul quale debba essere generata l'interruzione.
L'istruzione successiva :
P1DIR |= BIT0 + BIT6;
// imposta il bit 0 e il bit 6 come output
Questa istruzione permette di impostare il registro della direzione associato alla porta P1, alla quale sono
collegati i LED del LaunchPad. In particolare il LED rosso collegato al pin P1.0 mentre il LED verde al pin
P1.6 (si veda la serigrafia sul LaunchPad). I due bit, per essere impostati come uscite, devono essere
impostati ad 1. Per fare questo, piuttosto che usare dei numeri, si fa uso delle costanti BITx, definite
all'interno dellheader file, e le si carica nel registro di direzione P1DIR, sommandole o concatenandole come
prima. Di default ogni pin impostato come input.
Dopo aver impostato la direzione come output, si inizializza il valore dei bit sulla porta di uscita, in
particolare si accende il LED rosso e si spegne il LED verde, facendo uso della seguente istruzione.
P1OUT = 0x01;
// imposta BIT0 a 1 e BIT6 a 0 (0x01=0b0000-0001)
Terminata l'inizializzazione delle porte si pu procedere al programma vero e proprio. Come verr messo in
evidenza nel prossimo paragrafo, l'inizializzazione siffatta non ottimale per minimizzare i consumi. Infatti,
se si esegue Ultra Low Power Advisor, tra le varie obiezioni su Hello World, dir che le porte non sono
propriamente inizializzate per minimizzare i consumi. La parte del programma che permette di far
lampeggiare i LED :
int i = 0;
while (1)
{
// ciclo infinito
for (i = 0; i <30000; i++);
// pausa (incrementa soltanto la i da 0 a 30000)
P1OUT ^= BIT0 + BIT6 ;
}
// inverte lo stato del bit
Ovvero un loop infinito composto da un while (1), all'interno del quale inserita una pausa realizzata con un
semplice for nullafacente (30000 operazioni a vuoto), seguita dall'inversione dei bit. Si noti che per invertire i
bit si fatto uso dell'operatore XOR sulla porta di uscita, in particolare facendo uso del formato compatto:
P1OUT ^= BIT0 + BIT6 ;
// inversione di valore su P1.0 e P1.6
La notazione compatta permette di ottimizzare il codice, ma guardando il codice assembly (menu Window
Show View Disassembly)
P1OUT ^= BIT0 + BIT6 ;
tradotto in:
E0F2 0041 0021 XOR.B #0x0041,&Port_1_2_P1OUT
mentre
P1OUT = P1OUT ^ (BIT0 + BIT6) ;
tradotto in:
E0F2 0041 0021 XOR.B #0x0041,&Port_1_2_P1OUT
che lo stesso. Quindi laver ottimizzato il codice non servito a nulla. Infatti i compilatori odierni sono
divenuti piuttosto svegli e riescono ad ottimizzare il codice senza doverglielo dire (ci sono inoltre delle
impostazioni ad hoc per il compilatore, per mezzo delle quali si possono dare alcune direttive di
ottimizzazione al fine di ottimizzare velocit, consumi o dimensioni del codice compilato). Ciononostante
dal momento che in ambiente MSP430 tutti gli esempi fanno uso della forma compatta ottimizzata si
continuer a usare tale forma.
Una domanda che spesso molti si chiedono la ragione per cui CCS non supporti l'accesso dei registri per
mezzo del bit field, ovvero poter scrivere qualcosa del tipo:
P1OUT.BIT0 = 1;
al fine di impostare ad 1 il valore del BIT0 di P1OUT.
soprattutto considerando che usare la tecnica del bit masking non proprio una buona pratica di
programmazione (rende il programma poco leggibile). L'ambiente di sviluppo IAR supporta invece

38

Elementi di Programmazione 4

l'accesso ai registri SFRs (Special Function Registers) per mezzo del bit field, rendendo, almeno all'inizio, il
percorso di apprendimento un po' pi indolore.
I programmatori C avranno notato che la variabile i usata nel loop del delay stata dichiarata prima del loop
stesso. Questo dal punto della sintassi C un errore, e se si prova a compilare programmi con MPLAB
mettendo la variabile i prima del while, piuttosto che subito dopo il main, si ha un errore di compilazione.
Questo non avviene con CCS, poich un compilatore C/C++, ed in particolare il poter posizionare una
variabile in qualunque parte del programma appartiene alla sintassi del C++. Questa viene considerata una
buona pratica di programmazione, visto che la variabile viene dichiarata vicino al punto del suo utilizzo,
rendendo pi chiara la sua esistenza. Dal momento che questa regola non accettata dai compilatori C,
sebbene sia stata usata a solo scopo dimostrativo, si sconsiglia di utilizzarla, al fine di mantenere il
programma conforme all'ANSI C; ovvero tutte le variabili devono essere dichiarate subito dopo le parentesi
graffe della funzione in cui vengono utilizzate.
ESEMPIO: USO DEI PULSANTI (Pushing Buttons)
In questo paragrafo si studiano in maggior dettaglio le impostazioni associate al C, non solo al fine di
scrivere dati in output, ma anche di leggere dati, come per esempio la pressione di un pulsante. Si illustra
inoltre con qualche dettaglio una corretta inizializzazione delle porte, al fine di ridurre i consumi in
applicazioni alimentate a batteria.
Come per l'esempio precedente si utilizzer la scheda LaunchPad, su cui montato l'MSP430G2231. Si crea
un nuovo progetto, si copia il sorgente sotto riportato, si compila e si esegue il programma. Il semplice
esempio permette di accendere il LED1 ogni qualvolta venga premuto il pulsante S2.
#include <msp430.h>
void main(void)
{
WDTCTL = WDTPW + WDTHOLD;
// stop watchdog timer
P1DIR |= BIT0;
// imposta P1.0 come output
P1REN |= BIT3;
// abilita resistore di input su P1.3
P1OUT = BIT3;
// seleziona resistore tipo pull-up su P1.3
while (1)
{
if (P1IN & BIT3)
P1OUT &= ~BIT0;
// spegne LED1
else
P1OUT |= BIT0;
}
}
// accende LED1
Al fine di comprendere le varie impostazioni, dal momento che si fa uso sia di linee di uscita che di ingresso,
bene avere sottomano lo schema elettrico del LaunchPad. In Figura riportato un dettaglio della parte
associata al solo MSP430 montato sulla scheda, ovvero non considerando la parte del ProgrammatoreDebugger presente sulla scheda stessa (la documentazione completa si trova pubblicata nella User Guide del
LaunchPad slau318).

Schema elettrico della scheda LaunchPad

39

Elementi di Programmazione 4

Si noti che il LED1 posizionato su P1.0, mentre i due pulsanti sono collegati tra un pin e massa, il pulsante
S1 alla porta di Reset, il pulsante S2 alla porta P1.3. I rispettivi pin hanno anche un resistore da 47 k verso
Vcc. I due pulsanti, quando sono sollevati (aperti) forniscono un livello logico 1, perch il resistore li
connette a Vcc (nel caso del Launchpad 3.6V), quando sono premuti, si chiudono verso massa, dando un
livello logico 0.
Il programma inizia disattivando il watchdog, successivamente si imposta il pin P1.0 come output. Il pin
P1.3 associato al pulsante non viene toccato, poich di default, dopo un PUC (Power Up Clear), i registri DIR
vengono impostati per avere ogni pin come input (inoltre BIT0 vale 00000001, per cui si ha il pin P1.0 come
output e tutti gli altri pin come input). Sebbene P1.3 sia gi impostato come input, necessario inizializzare i
resistori di pull-up, infatti il pulsante normalmente aperto e alla chiusura vincola P1.3 a massa. Per attivare
il resistore di pull-up, necessario impostare il relativo bit nel registro P1REN, per selezionare il tipo di
resistore bisogna impostare P1OUT, come riportato nel codice seguente.
P1REN |= BIT3;
// abilita resistore input
P1OUT = BIT3;
// seleziona resistore tipo pull-up
A questo punto importante ricordare che, quando si ha a che fare con i resistori di pull-up/pull-down,
bisogna fare attenzione quando si utilizza il registro PxOUT, per scrivere dei valori sulle periferiche di uscita
(per esempio per accendere i LED). Se si alterano anche i bit associati agli ingressi, e si cambia un resistore di
pull-up in pull-down, il pulsante non verrebbe pi letto. Per questo bisogna accedere ai registri facendo uso
degli operatori logici (tecnica del bit masking), come si mostrer a breve.
Il programma continua con un ciclo continuo in cui viene letto il pulsante, e a seconda del suo valore
(premuto o meno) viene acceso o spento il LED1.
while (1)
{
if (P1IN & BIT3)
// se pulsante non premuto P1IN & BIT3 = BIT3 > 0 --> if vero
P1OUT &= ~BIT0;
// spegne LED1
else
P1OUT |= BIT0;
}
// accende LED1
In questa descrizione, l'aver fatto l'AND con il BIT3 non sembra molto logico, visto che presente un solo
pulsante. Questo in realt non vero, perch gli altri pin sono impostati come ingressi e potrebbero avere
valori diversi da 0.
Si noti che per accendere e spegnere il LED1 sulla Porta 1 si fatto uso degli operatori bitwise al fine di non
alterare gli altri bit della Porta 1, ed in particolare i resistori di pull-up. Se per esempio si fosse scritto:
while (1)
{
if (P1IN & BIT3)
P1OUT = 0x00;
// spegne LED1 (0b00000000)
else
P1OUT = 0x01;
}
// accende LED1 (0b00000001)
l'esempio avrebbe funzionato per una sola pressione del tasto. Infatti una volta acceso il LED1, si avrebbe un
resistore di pull-down su P1.3, e il pulsante S2 non sarebbe pi leggibile, non potendo pi distinguere il caso
di pressione dal caso di apertura, visto che in entrambi i casi si avrebbe 0.
La tecnica del bit masking pi laboriosa, ma come detto facendo uso di IAR possibile accedere
direttamente ai singoli bit del registro. Dunque l'approccio del bit masking non deve essere visto come un
fastidio derivante dagli MSP430, ma dal compilatore. Infatti le porte di ingresso e uscita sono dei semplici
registri, da leggere e scrivere, qualunque sia il C. Il modo con cui possibile accedervi dipende dagli
strumenti che mette a disposizione il compilatore. Quando si programma in assembly, il bit masking il
metodo per eccellenza, dato che normalmente non sono presenti alternative (a meno di non creare delle
piccole macro).
Il programma appena scritto, sebbene sia corretto, non in realt ottimizzato per applicazioni Ultra Low
Power. Infatti, eseguendo il plug-in Ultra Low Power Advisor, verranno messi in evidenza vari punti da
modificare, al fine di ridurre i consumi. In questo paragrafo si metter in evidenza solo il fatto che
un'ottimizzazione richiesta nelle impostazioni delle porte (non si tratter ancora le interruzioni e le varie
modalit a basso consumo).
All'avvio del C si verifica un POR, che a sua volta genera un PUC. Questo pone i registri relativi alle porte
in stato di reset, ovvero 0x00 (eccetto il registro di uscita e il registro relativo ai fronti delle interruzioni).
Come abbiamo visto scrivere 0x00 nel registro PxDIR equivale a porre tutti i pin come ingressi. Questo
rappresenta uno stato di sicurezza che permette di evitare dei cortocircuiti non voluti prima che
40

Elementi di Programmazione 4

l'applicazione dell'utente venga effettivamente eseguita (si ricorda infatti che prima dell'applicazione viene
eseguito in generale il boot code interno all'MSP430).
In applicazioni in cui non vengono utilizzati tutti i pin, come per esempio nell'applicazione precedente, i pin
non utilizzati devono essere propriamente impostati al fine di ridurre i consumi. Per fare questo si hanno
due possibilit, o si pongono i pin non usati come output, oppure come input, ma con un resistore di pull-up
o pull-down. Lo scopo in entrambi i casi di evitare che i pin non usati cambino lo stato logico, creando
transizioni inutili, ovvero sprechi di energia. Impostando un pin come output, si garantisce in automatico
che l'output avr un valore fisso. Questa configurazione per alcune volte non piace poich si perde il
vantaggio di avere i pin come input che garantiscono una sicurezza in pi rispetto ad eventuali cortocircuiti.
Nel caso in cui si seleziona il pin come input, necessario usare un resistore di pull-up o pull-down al fine di
garantire che l'input non rimanga fluttuante. Qualora sia fluttuante, a causa di rumore esterno, si potrebbero
verificare transizioni inutili, che potrebbero aumentare i consumi. Questo sebbene sembri poco probabile, in
realt non lo per quei pin che si trovano in prossimit di segnali variabili. Si sono verificati diversi progetti
con problemi di consumi anomali, risolti semplicemente ponendo dei resistori di pull-up / pull-down nelle
linee d'ingresso. Quanto appena detto non deve essere visto come un problema degli MSP430, bens un
trucco o considerazione da applicare a qualunque C. Nel caso dell'esempio precedente, dal momento che
gli MSP430 possiedono resistori di pull-up/down interni, il codice si potrebbe riscrivere nel seguente modo.
#include <msp430.h>
void main(void)
{
WDTCTL = WDTPW + WDTHOLD;
// stop watchdog timer
P1DIR |= BIT0;
// imposta P1.0 come output
P1REN |= BIT1 + BIT2 + BIT3 + BIT4 + BIT5 + BIT6 + BIT7;
// resistori input su P1
P2REN |= BIT0 + BIT1 + BIT2 + BIT3 + BIT4 + BIT5;
// resistori input su P2
P1OUT = BIT1 + BIT2 + BIT3 + BIT4 + BIT5 + BIT6 + BIT7;
// pull-up resistor
P2OUT = BIT0 + BIT1 + BIT2 + BIT3 + BIT4 + BIT5;
// pull-up resistor
while (1)
{
if (P1IN & BIT3)
P1OUT &= ~BIT0;
// spegne LED1
else
P1OUT |= BIT0;
}
}
// accende LED1
Come visibile dal programma, rispetto al primo esempio, si sono solo impostati i registri PxREN e i registri
PxOUT al fine di abilitare tutti resistori di pull-up. Tutto funziona come prima, tranne per il fatto che questa
volta il LED2 lievemente acceso. Infatti P1.6 stato impostato come input ed ha un resistore di pull-up. La
lieve accensione del LED2 mostra la presenza del resistore di pull-up. Se non si volesse questo (e in generale
non lo si vuole) si potrebbe impostare un resistore di pull-down (eliminare BIT6) o impostare P1.6 come
output e porla a 0. In alternativa si pu togliere semplicemente il Jumper del LED2 (disconnesso).
Un programma completo visibile in pushy_G2211.c
Sebbene negli esempi precedenti si sia trattato ogni pin come semplice Input o Output, in realt ogni pin ha
quasi sempre pi funzioni condivise con vari moduli. Per decidere la funzione del pin, necessario
impostare anche il valore del registro PxSEL (di default i pin sono impostati come semplici I/O). Oltre a
questa possibilit, gli MSP430 prodotti negli ultimi anni possiedono anche il cosiddetto Port Mapping in cui
uno stesso pin pu essere mappato su pin diversi a seconda delle esigenze di layout. Alcuni MSP430
possiedono anche un buffer di uscita, al fine di pilotare carichi che richiedono maggiore corrente. Al fine di
ridurre i consumi, il buffer presente sui pin pu essere attivato o meno per mezzo del registro PxDS ovvero
Port Drive Strength. In generale bisogna far riferimento al datasheet per vedere se l'MSP430 scelto possiede
o meno queste funzioni (presenti sulla famiglia MSP430F5xx)
FILTRO ANTI-SPIKES
Sebbene gli esempi precedenti mostrano un'applicazione semplice e funzionante, i pulsanti vengono letti
generalmente in altro modo. Infatti un pulsante, essendo un componente meccanico, al momento della sua
chiusura e apertura crea molto rumore, visibile con un oscilloscopio come una tensione con andamento
irregolare, prima di raggiungere il valore VCC o GND, a seconda dei casi. Questo rumore pu essere fonte di
una mal interpretazione della pressione del pulsante, che potrebbe essere rilevato come premuto/rilasciato
pi di una volta. Per evitare di rilevare una falsa lettura del pulsante, si realizza spesso un filtro,
frequentemente ottenuto con un semplice ritardo, per ridurre o eliminare questo problema. Il filtro basato su
41

Elementi di Programmazione 4

un ritardo non fa altro che aspettare un po' di tempo, dopo aver rilevato la pressione del pulsante, e rileggere
nuovamente lo stato del pulsante, ritenendo la seconda lettura come quella da interpretare valida. Con
questo semplice filtro si eliminano anche i disturbi elettrici, che si dovessero accoppiare sulla linea del
pulsante. I tempi di ritardo del filtro sono generalmente di qualche decina di ms, a seconda della
sensibilit che si vuole avere. Tempi molto lunghi impongono che il pulsante rimanga premuto per un
tempo prolungato. Un esempio in cui si applica un filtro anti spikes riportato di seguito. Forse
leggermente pi complesso degli altri, ma a questo punto si dovrebbero avere tutti gli elementi per
comprenderlo.
#include <msp430.h>
void main(void)
{
int delay = 0;
WDTCTL = WDTPW + WDTHOLD;
// stop watchdog timer
P1DIR |= BIT0;
// imposta P1.0 come output
P1REN |= BIT1 + BIT2 + BIT3 + BIT4 + BIT5 + BIT6 + BIT7;
// resistori input su P1
P2REN |= BIT0 + BIT1 + BIT2 + BIT3 + BIT4 + BIT5;
// resistori input su P2
P1OUT = BIT1 + BIT2 + BIT3 + BIT4 + BIT5 + BIT6 + BIT7;
// pull-up resistor
P2OUT = BIT0 + BIT1 + BIT2 + BIT3 + BIT4 + BIT5;
// pull-up resistor
while (1)
{
// ciclo infinito
if ((P1IN & BIT3) == 0)
{
// attende rilevazione pressione del pulsante
for (delay = 0 ; delay < 10000; delay++);
// pausa
if ((P1IN & BIT3) == 0) {
// se dopo la pausa il pulsante ancora premuto
P1OUT |= BIT0;
}
// accende LED1
while ((P1IN & BIT3)== 0);
}
// attende rilascio del pulsante senza fare nulla
P1OUT &= ~BIT0;
}
}
// spegne LED1
Si noti che in questo ultimo esempio si posto P1.6 con un resistore di pull-down per cui il LED 2 rimane
spento. Premendo velocemente il pulsante, la sua pressione viene ignorata, ovvero considerata come
accidentale o dovuta a rumore. In questo esempio la pausa per il filtro stata scelta in maniera empirica,
visto che non si sono ancora fatti esempi con i Timer e non si sa ancora impostare il DCO interno al C.
Sebbene questo esempio sia funzionale e si usi spesso in altri esempi di programmazione, il suo utilizzo non
pratica comune. Infatti gli MSP430 sono spesso utilizzati in applicazioni Ultra Low Power, ed una lettura in
polling e loop di attesa per mezzo di cicli for, rappresentano un grande spreco di energia. Filtri pi
complessi fanno uso dei Timer interni e letture multiple, mediando il valore delle letture (risultato di
maggioranza). L'utilizzo degli interrupt e le modalit a basso consumo rappresentano la pratica comune, ma
vedremo degli esempi nei prossimi paragrafi.
CONTROLLO DI ATTUATORI TRAMITE LE PORTE GPIO
I pin GPIO dellMSP430 sono limitati nella massima corrente che possono erogare/assorbire. Per i C della
Value Line il limite di 48 mA. Pertanto, se si vuole pilotare linterruttore di un attuatore con una porta,
occorre verificare quanto assorbe. Nel caso lassorbimento sia eccessivo, si deve usare il classico relay (ved.
app. Componenti in G. Orengo Dispositivi, circuiti e sistemi elettronici), ossia un interruttore con un
circuito di controllo, che assorbe poca corrente, separato da quello di carico che assorbe invece la corrente
necessaria da una rete o da un circuito di alimentazione.
IMPOSTAZIONE DEL CLOCK

DCO
Il DCO la sorgente di clock pi facile da configurare, completamente controllata dal software. Viene
configurata con due registri: DCOCTL e BCSCTL1. Il range di frequenza diviso in 16 intervalli contigui
(sovrapposti agli estremi). Ogni intervallo ulteriormente suddiviso in 8 frequenze possibili, per un totale di
128 frequenze. Limmagine seguente spiega come selezionare le possibili differenti frequenze del DCO
(mostra 3 linee anzich 16). Per selezionare la frequenza si usano i bit RSELx, ovvero 0-3, in BCSCTL1
(selezione range 0-15), e i bit DCOx, ovvero 5-7, in DCOCTL (selezione step 0-7). Si veda in proposito la
Users Guide della famiglia utilizzata. La TI usa una coppia ordinata per descrivere la selezione:
DCO(Range,Step). Per esempio DCO(6,3) corrisponde a 800 kHz, DCO(13,3) a 7.8 MHz e cos via.
42

Elementi di Programmazione 4

Se MCLK pilotato dal DCO, come di default, possono esserci problemi se la frequenza supera il limite di 16
MHz del processore.

Per vedere limpostazione di default per il DCO, dal debugger si apre la visualizzazione dei registri, e si
trovano i registri BCS+ sotto System_Clock. Per il G2211, DCOCTL inizializzato a 0x60, e BCSCTL1 a 0x87,
e guardando i singoli bit, si vede che RSEL=0b0111 (7) e DCO=0b011 (3), ovvero DCO(7,3), che una
frequenza tra 0.80 e 1.50 MHz. Il valore attuale dipende da diversi fattori ambientali, e si pu vedere
sulloscilloscopio, perch SMCLK pu essere pilotato dal DCO e messo sul pin P1.4.
Per cambiare la selezione si usa il seguente codice
BCSCTL1 &= ~(BIT0 + BIT1);
// set bits 0 and 1 to 0
BCSCTL1 |= BIT2 + BIT3;
// set bits 2 and 3 to 1
DCOCTL &= ~(BIT5 + BIT7);
// set bits 5 and 7 to 0
DCOCTL |= BIT6;
// set bit 6 to 1
L'oscillatore DCO viene calibrato in fase di produzione e test dell'MSP430. I valori di calibrazione sono
salvati nella flash (Info Memory A). I valori disponibili possono variare da modello a modello e possono
essere trovati all'interno del datasheet del C utilizzato. Altro modo per vedere le frequenze calibrate
all'interno dellheader file, incluso nel progetto stesso (basta cercare la parola chiave CAL). Un valore
calibrato del modulo DCO ha normalmente una tolleranza nel caso peggiore fino al 3% (intero intervallo di
tensione e temperatura operativa). I C della famiglia G2x11 hanno una sola frequenza calibrata a 1 MHz
(3%). Selezionare la frequenza calibrata molto pi facile, perch CCS fornisce gli shortcut:
BCSCTL1 = CALBC1_1MHZ;
DCOCTL = CALDCO_1MHZ;
dove CALBC1_1MHZ e CALDCO_1MHZ sono definite nell'header file (altri valori potrebbero essere presenti).
Il programma completo si pu trovare in dcodemo_g2211.c
TIMER
Una volta configurato il clock, si pu usare per controllare le periferiche, sia interne (buitl in) al C che
esterne. La prima da esaminare il Timer_A. In alcuni dispositivi, ma non nella value line, presente anche
il Timer_B, che piuttosto simile.
Un timer non nientaltro che un meccanismo di conteggio, legato ad un intervallo regolare fornito da un
clock. Il Timer_A a 16 bit, nel senso che conta da 0 a 0xFFFF (65535).
Il modulo diviso in due parti, la parte relativa al contatore vero e proprio, ovvero il Timer classico, e i
moduli CCR0, CCR1, CCR2, ovvero i moduli Capture e Compare. La famiglia MSP430G2xx possiede al
massimo 3 moduli Capture e Compare, mentre nella famiglia MSP430F5xx possibile trovare Timer_A
anche con 5 moduli Capture e Compare. I Timer_B hanno invece fino a 7 moduli Capture e Compare. Nella
descrizione che seguir, oltre a capire come funzionano i registri Capture e Compare, si capir anche perch
ne sono presenti 3 o 7. Per quanto riguarda la nomenclatura riportata nel datasheet, qualora siano presenti
due Timer_A, il loro nome sar Timer0_A e Timer1_A. In particolare il nome reale viene a dipendere anche
dal numero dei moduli Capture e Compare, che seguono la lettera A. Nel caso dellMSP430G2553 con due
Timer con 3 moduli Capture e Compare si ha rispettivamente Timer0_A3 e Timer1_A3 come riportato nel
relativo schema a blocchi del datasheet.
TIMER_A DELLA VALUE LINE
La parte superiore del Timer_A possiede la logica necessaria per la scelta del clock ovvero TACLK, ACLK,
SMCLK e INCLK. Il clock TACLK rappresenta il clock proveniente dal relativo pin esterno dellMSP430, per
43

Elementi di Programmazione 4

cui potrebbe essere utilizzato per esempio per misurare il numero di impulsi in un determinato lasso di
tempo. La possibilit di scegliere il clock ACLK e SMCLK permette al timer di lavorare a frequenze pi o
meno elevate, ma soprattutto di poter lavorare anche in diversi Low Power Mode. Per esempio se si volesse
usare il timer per far risvegliare il C ogni secondo e ci si trovasse in modalit LPM3, obbligatorio usare il
clock ACLK come sorgente per il timer, infatti il clock SMCLK viene disattivato in LPM3 mentre ACLK, sia
esso proveniente dalloscillatore interno VLO o il cristallo esterno da 32KHz, viene mantenuto attivo. Da
questo si capisce che se si volesse usare il Timer per risvegliare lMSP430 da un LPM4, lunico modo sarebbe
utilizzare il clock esterno TACLK, visto che tutte le altre sorgenti sono disattivate. Lultimo segnale di clock
utilizzabile per il timer INCLK. Tale sorgente dipende dal particolare dispositivo usato, in particolare per
lMSP430G2553 coincide con TACLK.
Dopo la scelta del clock vi un divisore di frequenza, che permette di dividere il clock selezionato per
1/2/4/8. La selezione dellopzione voluta avviene per mezzo dei bit IDx sempre presenti nel registro TAxCTL.
Per chi abituato a programmare con i microcontrollori PIC18 questo divisore di frequenza quello che
prende il nome di pre-scaler.
Il Timer vero e proprio, ovvero il TAR, non fa altro che contare ogni singolo impulso di clock. Il TAR un
registro a 16 bit e pu contare in vari modi selezionabili per mezzo dellopzione Count Mode, ovvero per
mezzo dei bit MCx (Mode Control), presenti sempre nel registro di configurazione principale TAxCTL.
MODALIT DI FUNZIONAMENTO DEL TIMER
Il Timer pu funzionare in 4 diverse modalit, Stop, Continuous mode, Up mode e Up/Down mode.
0FFFFh

0h

0FFFFh
TACCR0
0h

Continuous mode

0FFFFh
TACCR0
0h

Up mode

Up/Down mode
La modalit Stop quella all'avvio del microcontrollore. Il Timer disabilitato
e non avviene nessun
conteggio. Tale impostazione paragonabile ad un Enable del modulo stesso e deve essere impostata per
disabilitare il Timer al fine di risparmiare energia.
Nella modalit Up Mode il Timer, o meglio registro il registro TAR, viene incrementato per mezzo del clock
selezionato. Il valore raggiunto pari al valore contenuto nel registro (TAx)CCR0 (la x indica il numero del
Timer). Una volta raggiunto tale valore il conteggio riprende da zero, per cui il numero di conteggi effettuati
pari al valore CCR0+1.
Per quanto riguarda il flag delle interruzioni CCIFG, si ha che viene posto ad 1 quando il registro TAR
raggiunge il valore CCR0, mentre il valore TAIFG posto ad 1 quando il registro TAR viene posto
nuovamente a 0. Nel caso in cui il registro CCR0 dovesse essere variato durante il conteggio, si possono

verificare due casi: o il nuovo valore maggiore/uguale al registro TAR o minore. Nel secondo caso il
conteggio azzerato, al massimo entro il ciclo successivo del clock, mentre nel primo caso il conteggio
continua normalmente fino al nuovo valore. Tale modalit del timer pu essere utilizzata per generare una
base dei tempi costante. Facendo uso del clock ACLK si pu mettere il microcontrollore in LPM3 e
mantenere il conteggio tramite il Timer. Le interruzioni da parte del Timer possono risvegliare il
microcontrollore in maniera regolare al fine di eseguire determinate attivit. Come si vedr nei prossimi
capitoli, il Timer pu essere utilizzato per avviare in maniera automatica la conversione da parte del modulo
ADC, senza dover eseguire alcun codice all'interno dell'ISR. Indirettamente, pur non avendolo specificato, il
modulo CCR0 deve essere impostato per lavorare in modalit Compare, ovvero il bit CAP del registro
CCTLn (la x indica il numero del Timer mentre la n il numero del modulo Capture e Compare) deve essere
posto a 0. Infatti in questa modalit il registro TAR che effettua il conteggio viene confrontato (Compare) con
i relativi registri interni al modulo Capture e Compare abilitati in modalit Compare.
Nella modalit Up-Down Mode, il registro TAR viene incrementato per mezzo del clock selezionato fino al
raggiungimento del valore contenuto nel registro CCR0. Una volta raggiunto tale valore, il registro TAR
viene decrementato fino al raggiungimento del valore 0x0000, raggiunto il quale il registro TAR viene
nuovamente incrementato ripetendo il tutto ciclicamente. Per quanto riguarda il flag delle interruzioni si ha
che il bit CCIFG del registro CCR0 viene posto ad 1 quando il registro TAR raggiunge il valore CCR0,
mentre il valore TAIFG posto ad 1 quando il registro TAR raggiunge nuovamente 0x0000. Nel caso in
cui il registro CCR0 dovesse essere variato durante il conteggio, si possono avere diverse situazioni: nel caso
44

Elementi di Programmazione 4

in cui il timer stia nella fase di decremento il tutto continua normalmente fino al raggiungimento dello
0x0000, indipendentemente dal nuovo valore che si vuole scrivere; qualora il timer stia nella fase
d'incremento, il comportamento uguale alla situazione della modalit Up Mode.
Tale modalit del Timer pu essere utilizzata per generare segnali PWM complessi come quelli necessari per
pilotare un ponte H, ovvero sia necessario un tempo morto (dead time), ovvero una pausa, per evitare che i
transistor posti sullo stesso ramo del ponte non risultino attivati contemporaneamente, causando un
cortocircuito. Maggiori dettagli su come creare un segnale PWM complementare sono dati in seguito.
In Continuous Mode il timer esegue un conteggio continuo fino a 0xFFFF, ovvero il valore massimo
contenuto in un registro a 16 bit. Ogni volta che il conteggio arriva a 0xFFFF il registro TAR viene resettato
permettendo un nuovo conteggio a partire da 0. Dal momento che il modulo CCR non usato, l'unico flag
associato a tale modalit il flag TAIFG. I vari moduli CCRx possono essere abilitati in modalit compare in
maniera da ottenere diverse interruzioni in tempi diversi. I moduli CCRx possono essere impostati in
modalit Compare, per mezzo del bit CAP del registro CCTLn, impostandolo a 0. Abilitando il modulo CCR
in modalit Compare, anche il relativo flag CCRIF sar abilitato al verificarsi dell'evento di uguaglianza tra il
registro (contatore) TAR e il valore del registro CCRn.
Il Timer_A ha la possibilit di inserire dei checkpoint nel conteggio (2 nella Value Line), chiamati registri
capture/compare. Luso principale di questi registri quello di impostare i valori ai quali il contatore
manda un Flag al processore per fare qualcosa di speciale. Per esempio, il registro CCR0 pu essere usato
per fissare un limite superiore al conteggio. Il registro CCR1 manda solo un flag al processore e permette al
timer di proseguire il conteggio (questo anche il comportamento del primo in continuous mode). Questa
la modalit compare: si imposta un valore che confrontato con il valore attuale del conteggio; se il valore
uguale, lo segnala al processore. Nella modalit capture, quando arriva un particolare segnale, il timer
memorizza il valore attuale del conteggio, senza interromperlo.
Il Timer possiede un Interrupt Enable e relativo Interrupt Flag che segnala un evento di overflow. In
particolare ogni modulo CCR ha anche un suo interrupt Enable e relativo Flag, che segnala altri eventi di cui
si vedranno i dettagli a breve.
Analizzando lInterrupt Vector Table dellMSP430G2553 possibile vedere che i due Timer, Timer0_A3 e
Timer1_A3, possiedono diversi Interrupt Vector ed in particolare il Timer1_A3 ha maggiore priorit del
Timer0_A3. Il Flag associato alloverflow del registro TAR del Timer1_A3 associato al vettore con priorit
28 ed raggruppato con gli eventi delle interruzioni dei relativo moduli CCR1 e CCR2. Il modulo CCR0 ha
invece un vettore delle interruzioni dedicato con maggior priorit, ovvero di priorit 29.
Questo discende dal fatto che il modulo CCR0 ha un ruolo un po particolare visto che influenza le modalit
Up Mode e Up/Down Mode.
MODALIT COMPARE
Ogni timer possiede dei moduli Capture e Compare che confrontano il valore del Timer (TAR) con il valore
scritto nel relativo registro CCRn. A seconda del valore del registro TAR e della modalit selezionata
vengono attivati in maniera opportuna i segnali di interrupt. I relativi flag possono essere letti per verificare
il verificarsi di un determinato evento o potrebbero essere utilizzati per richiamare una ISR, qualora siano
attivati i relativi interrupt.
Oltre a questi aspetti base e fondamentali per un Timer, i moduli Capture e Compare possiedono un modulo
di uscita collegato ad un pin del microcontrollore. Il modulo di uscita pu dunque pilotare direttamente il
pin senza l'intervento del software. Questa funzione viene in particolare usata in applicazioni come il
semplice lampeggio di un LED o per generare un segnale PWM. Dal momento che ogni Timer ha pi moduli
Capture Compare, si capisce che si possono generare segnali PWM complessi, o semplicemente pi segnali
PWM con la stessa base dei tempi. Ogni modulo di uscita associato ad un Timer Capture Compare pu
essere impostato in varie modalit per mezzo dei bit OUTMODx presenti nel registro CCTLn.
Per meglio capire come si comportano le varie modalit, ovvero come l'uscita del modulo viene impostata in
funzione del valore del Timer bene vedere due esempi. Nel caso il Timer sia in modalit Up Mode o Up
Down Mode, le varie modalit di uscita controllano il relativo pin di uscita come riportato nelle figure,
rispettivamente, di sinistra e di destra.

45

Elementi di Programmazione 4

Prima di procedere oltre bene cercare di comprendere i diagrammi temporali riportati sopra. La loro
comprensione permette infatti di capire come sia possibile generare il segnale PWM. Si fa presente che per
permettere all'uscita del modulo Capture e Compare di pilotare direttamente il pin di una porta di uscita
necessario impostare in maniera opportuna il registro PxSEL e PxSEL2 (se presente) associati alla porta
d'interesse.
Una volta compreso come sia possibile controllare i pin di una porta bene riprendere brevemente quanto
precedentemente accennato relativamente ad un segnale PWM complesso ovvero complementare (con Dead
Time). Impostando CCR0 in maniera tale da avere la frequenza d'interesse per il segnale PWM possibile
impostare CCR1 e CCR2 per controllare due pin differenti. Impostando CCR1 in modalit di uscita 6 e CCR2
in modalit di uscita 2, possibile ottenere i diagrammi temporali della Figura successiva.

Tali diagrammi temporali rappresentano proprio due segnali PWM complementari con un Dead Time e un
Periodo pari a (Ttimer il periodo del clock o base tempi):
Tdead = Ttimer ( TACCR1 TACCR2 )
TPWM = 2 Ttimer TACCR0
MODALIT CAPTURE
Il Timer_A degli MSP430, sebbene sia il meno complesso della sua famiglia, possiede molte funzioni e
flessibilit. Oltre a poter generare una base tempi e controllare dei pin in uscita (Modalit Compare), il
modulo CCR associato al Timer permette di lavorare anche in modalit Capture, ovvero permette di
utilizzare il Timer per misurare dei tempi o velocit. La modalit Capture viene selezionata ponendo il bit
CAP del registro CCTLn ad 1. Il fronte sul quale attivare il trigger interno pu essere impostato per mezzo
dei bit CM del registro CCTLn, e pu essere impostato rispettivamente sul fronte di salita, discesa o
entrambi.
In modalit Capture, il Timer, impostato in una delle modalit precedentemente descritte, effettua il
conteggio liberamente. Al verificarsi di un evento di trigger, ovvero che permette di avviare un Capture, il

46

Elementi di Programmazione 4

valore del registro TAR viene salvato nel registro CCRn. Il segnale di trigger, ovvero di Capture, proviene
dalle linee CCIxA o CCIxB.
Questi segnali possono avere un collegamento sia interno (per esempio essere collegati all'uscita di un altro
modulo CCR o Timer) o collegati a un pin esterno. Il segnale di trigger pu essere selezionato per mezzo dei
bit CCIS del registro CCTLn. Per la corrispondenza tra CCIxA e CCIxB e segnali interni/esterni bisogna far
riferimento al datasheet del C utilizzato.
Nel caso nell'MSP430G2553 i segnali di trigger per il Timer0_A3 sono riportati in Figura. Le prime tre
colonne a sinistra rappresentano i pin associati all'ingresso del modulo CCR, ovvero i segnali di trigger del
Modulo Capture. Il segnale CCI0A nominato TA0.0 per esempio collegato alla porta P1.1 (nel package
PW20) ovvero il pin numero 3 da cui la scrittura P1.1-3. Le uscite del modulo CCR sono riportate nelle
ultime tre colonne. I dettagli relativi ai pin CCIxA e CCIxB possono essere visti nel datasheet anche alla
sezione in cui si descrivono i vari pin e funzioni (inizio del datasheet).

REGISTRI DEL TIMER_A


Se ne usano almeno 7 con la periferica Timer_A.
TACTL Il registro di controllo per attivare il collegamento tra un timer ed un clock, e selezionare la
modalit duso.
o I bit TASSELx (8 and 9) dicono al timer quale clock usare come sorgente.
o La frequenza di clock pu essere divisa di un ulteriore fattore 2, 4 o 8 usando i bit IDx (6 e 7). Si
noti che questa unulteriore divisione rispetto a quella che si pu operare sul clock stesso; in
totale la frequenza delloscillatore pu essere divisa per 64.
o I bit MCx (4 e 5) selezionano la modalit del timer. Si noti che impostare a 0 questi bit (come nel
POR) equivale a fermare il timer.
o TACLR il bit 2. Se questo bit vale 1, il processore prima resetta il timer e poi il bit.
o TAIE e TAIFG (bit 0 e 1) controlla la capacit del timer di triggerare le interruzioni.
TAR Questo registro contiene il valore attuale del conteggio.
TACCRx Sono i registri di capture/compare, di cui due nella value line, TACCR0 e TACCR1.
TACCTLx registro che controlla le modalit duso capture/compare
o CMx (bit 14 e 15) cambia il tipo di segnali che dicono al timer di andare in modalit capture.
o CCISx (bit 12 e 13) seleziona dove vengono presi i segnali di input.
o SCS e SCCI (bit 11 e 10) cambiano il sincronismo (il timer normalmente opera in modo asincrono
rispetto ai segnali di ingresso) ?
o CAP (bit 8) seleziona la modalit capture (1) o compare (0).
o OUTMODx (bit 5-7) seleziona le diverse modalit di output per il segnale CCR, quando il timer
manda un flag per un evento capture o compare.
o CCIE e CCIFG (bit 4 e 0) sono ulteriori interrupt associati con il CCR.
o CCI e OUT (bit 3 e 2) sono gli input e gli output per il CCR.
o COV (bit 1) diventa 1 per segnalare due capture simultanei, prima che il valore del primo sia
pronto per essere letto.

47

Timers
TASSELx
divider
/1/2/4/8

Timer block

16-bit timer register

IDx

TACLK
ACLK
SMCLK
INCLK

MCx
TAR

TAIFG

timer
clock

EQU0

Capture/compare channel 1

CMx
capture
mode

EQU1

comparator

CCISx

capture

TA1
inputs
CCI1A
CCI1B
GND
VCC

289

output
mode

latch

OUT1

OUTMODx

TACCR1

CCI

TA1
output

CAP

TACCR1
CCIFG
(CCIFG1)
SCCI

Figure 8.5: Simplified block diagram of Timer_A showing the timer block and
capture/compare channel 1. The circles show external signals that may be brought
out to pins of the device.
Figure 8.5 shows a simplified block diagram of the timer block and a typical
capture/compare channel. Many of the internal signals are omitted for clarity and it
emphasizes different features from Figure 4.7. The internal compare signal EQU0 from
channel 0 is particularly important because it plays an important role in the outputs from
the other channels and controls the timer itself in Up and Up/Down modes.

8.3.1

Timer Block

This contains the 16-bit timer register TAR, which is central to the operation of the timer.
It is controlled by the register TACTL shown in Figure 4.8.
Remember that a timer is really no more than a counter and has no direct concept of time
(the Real-Time Clock is an exception). It is the programmers job to establish a relation
between the value in the counter and real time. This depends essentially on the frequency
of the clock for the timer. It can be chosen from four sources by using the TASSELx bits:
SMCLK is internal and usually fast (megahertz).
ACLK is internal and usually slow, typically 32 KHz from a watch crystal but may
be taken from the VLO in the MSP430F2xx family.

www.newnespress.com

Elementi di Programmazione 4

TAIV Il Timer_A Interrupt Vector Register contiene i dettagli sugli interrupt flaggati dal Timer_A,
poich possono essere di diversi tipi. Gli unici bit 1-3 (TAIVx) indicano il tipo di interrupt che capitato,
permettendo di scegliere lazione opportuna.
Si rimanda ai manuali del dispositivo per uninformazione pi completa su questi registri.

TIMER OUTPUT
I registri capture/compare (TACCR0 e TACCR1 nella value line) possono pilotare ognuno un proprio
outptut. Si pu programmare lMSP430 per regolare loutput ogni volta che il timer raggiunge il valore
memorizzato nel registro. Per esempio, si pu impostare loutput TA0.1 per settare (a 1) loutput, ogni volta
che il timer raggiunge il valore posto in TACCR1. Oppure si pu invertire loutput TA0.0 ogni volta che il
timer raggiunge il valore TACCR0. Inoltre si possono usare entrambi i registri sullo stesso output,
assegnandogli un valore a TACCR1 e un altro a TACRR0 (questa caratteristica usata nella Pulse Width
Modulation PWM). La tabella successiva riassume tutte le modalit di utilizzo degli output del timer.
La modalit capture del timer pu essere usata per la temporizzazione precisa di eventi. In questa modalit
il timer memorizza il valore del conteggio attuale (TAR) nel registro TACCRx, nel momento in cui la cattura
triggerata dal comparatore, in modalit rising/falling edge. Si noti che una temporizzazione accurata
richiede un clock calibrato, per cui in questo caso si usa il DCO calibrato a 1 MHz.

CLOCK ACCURATI
OSCILLATORI AL QUARZO (crystal oscillators)
Per avere un clock pi preciso, bisogna usare un quarzo. Quello montato sul LaunchPad ha unaccuratezza
di 20 ppm (0.002%) in un ampio intervallo di temperature. Gli svantaggi sono che i quarzi richiedono un po
di tempo per stabilizzarsi e aumentano il consumo di potenza. Inoltre, alla frequenza di 32768 Hz, non si
posso ottenere elevate frequenze di trasmissione. Tipicamente servono 16 cicli di clock per sincronizzarsi con
il bit iniziale e iniziare il campionamento al centro di ogni bit. Ne consegue che il bit rate pi alto che si riesce
ad ottenere 2048 baud. Tuttavia, se si accurati, si riescono ad ottenere bit rate fino a 9600 baud.
Il quarzo, per oscillare, necessita di essere collegato a due capacit (uguali). Quello inserito nel LaunchPad
ha bisogno di capacit di 12.5 pF. Si possono selezionare capacit di 1, 6, 10 e 12.5 pF attraverso i due bit
XCAPx nel registro BSCCTL3 (default 6 pF).
Se si usa un cristallo diverso, pu richiedere valori diversi di capacit (CSPEC), per cui occorre saldarne due
sugli appositi pad. Le capacit parassite delle tracce e dellMSP430 sono gi incluse nelle capacit (in
parallelo) selezionabili attraverso i registri (CSEL). Per calcolare il valore delle due capacit da montare sulla
scheda si usa la formula

C1 = C 2 = 2 CSPEC CSEL

Cos per un cristallo che richiede 18 pF, occorre montare due capacit da 30 pF, se si usano le capacit di
default di 6 pF.
La maggior parte di ci che serve per usare i quarzi forniti gi impostato come valori di default nel modulo
BCS+. In effetti occorre soltanto aggiungere due linee di programma:

48

Elementi di Programmazione 4

BCSCTL3 |= XCAP_3;
// 12.5 pF for LaunchPad crystal
__delay_cycles(55000);
// let crystal stabilize
La prima linea seleziona il valore della capacit, la seconda concede un tempo sufficiente al quarzo per
stabilizzare la propria frequenza operativa. Tipicamente servono poche centinaia di millisecondi.
Listruzione aspetta 55000 cicli di clock, che alla frequenza di default del DCO di 1.1 MHz sono circa 50 ms.
A seconda dellapplicazione si pu aspettare di pi se necessario.
Il codice seguente serve a verificare se il cristallo oscilla, sebbene non ne possa accertare laccuratezza. La
frequenza del cristallo misurata attraverso il clock ausiliario ACLK.
/* 32kcrystalF2132: quick program to test the functionality of a watch crystal
* soldered to the target board. Flashes an LED on P1.0 at 2 Hz. */
#include <msp430f2132.h>
#define LED BIT0
void main(void)
{
int i;
// internal counter
WDTCTL = WDTPW + WDTHOLD;
P1OUT = 0x00;
P1DIR = LED;
BCSCTL3 |= XCAP_3;
// xtal has 12.5 pF caps
TACCR0 = 16383;
// f_xtal/2-1 gives 2 Hz flashing
TACTL = TASSEL_1 + MC_1 + TACLR;
// ACLK + Up Mode + Clear timer
for (;;)
{
while ((TACTL & BIT0) == 0)
// wait for timer to reach TACCR0
TACTL &= ~BIT0;
// reset TAIFG
P1OUT |= LED;
// LED on
for (i=0; i<0x314; i++)
// short delay
P1OUT &= ~LED;
}
}
// LED off fine main
Si noti che si usa un OR (|) per cambiare solo i bit corrispondenti a XCAP_x nel registro BSCCTL3.
Ciascun timer pu essere configurato per staccarsi dai clock (MCLK, SMCLK, ACLK), impostando i bit
TASSELx nel registro TACTL. TASSEL_1 nellheader file configura il timer per distaccarlo dal clock
ausiliario connesso al cristallo. I bit MC_x impostano la modalit del timer. Sono inizializzati a 0 quando si
accende il C, mantenendo spento il timer per risparmiare energia.
MC_1 nellheader file configura il timer in Up mode, che conta fino a che raggiunge il valore specificato in
TACCR0. MC_2 imposta il timer in Continuous Mode, che conta da 0x0000 a 0xFFFF. Se nel codice si
imposta il continuous mode, la frequenza di flash del LED 1 Hz, mentre in up mode pu variare da 1 a 32
kHz.
Il bit TACLR azzera il timer, cosicch ricomincia da 0.
Il bit TAIFG (che il bit 0 in TACTL) settato quando il timer inverte il conteggio, sia al top in continuous
mode, che a TACCR0 in up mode. Il codice controlla il bit per sapere quando flashare il LED. Si noti che il
flag va resettato manualmente. Il timer continua a contare mentre il codice accende il LED, poi fa una pausa
(0x314 per un breve flash), e spegne il LED, cos quando il loop ricomincia, il timer gi attivo. Questo
aspetto molto utile per applicazioni con esatte temporizzazioni, perch non tengono conto dei tempi di
processo tra un loop e laltro. Fino a che il programma impiega meno tempo a girare degli intervalli tra un
set e laltro di TAIFG, il programma eseguir il codice esattamente nel tempo richiesto.
Infine il valore TACCR0 scelto come f/n-1, dove f/n un sottomultiplo della frequenza di oscillazione.
AUTO-CALIBRAZIONE DEL DCO
Le frequenze di trasmissione (baud rate) standard, tuttavia, non sono sottomultipli interi della frequenza del
quarzo, infatti 9600 baud corrispondono a 3.41 cicli di clock, 1200 baud 27.31, ecc. Anche dividendo per 27 si
otterrebbero 1213.6 baud, con un errore del 1.1%. In questo modo la precisione che si guadagnata con la
stabilit di frequenza del quarzo andata perduta. Ci sono tuttavia dei quarzi, la cui frequenza si divide
perfettamente per i baud rate standard. Per esempio un cristallo da 7.3728 MHz ha esattamente 768 cicli di
clock per bit a 9600 baud. Se si dispone di un C MSP430 che consente di utilizzare quarzi ad alta frequenza,
questa pu essere una scelta eccellente per la comunicazione seriale alle frequenze standard.
Unaltra opzione quella di usare il DCO. Sebbene si visto gli errori che comporta, sotto certe circostanze si
pu fare meglio. Per prima cosa i margini di errore riportati nei datasheet coprono lintero range di

49

Elementi di Programmazione 4

temperature, da -40 a 85 F, e di tensioni, da 1.8 a 3.6 V. Generalmente non si opera fino a questi estremi, e
anche se fosse, se le condizioni non cambiano durante lapplicazione, si pu ricalibrare il DCO per quelle
particolari condizioni di lavoro. In questo caso laccuratezza del clock sarebbe molto superiore al 3%. I
datasheet specificano che lerrore di calibrazione dello 0.5% a 3V. Ovviamente una tensione stabile
necessaria per avere una frequenza del DCO stabile.
Calibrare il DCO piuttosto semplice, ma richiede un clock molto accurato, come il quarzo, da confrontare
con il DCO. Lidea semplice: si usa il quarzo per misurare precisamente un intervallo (per es. 1 secondo).
Sapendo quante oscillazioni avvengono, per la frequenza del quarzo, in quellintervallo, si regola (adjust) la
frequenza del DCO, in modo che il suo numero di oscillazioni si avvicini il pi possibile a quello del quarzo.
Si ottengono cos i valori per DCOCTL e BSCCTL1 per unaccurata calibrazione del DCO. Il codice:
voidSet_DCO(unsigned int Delta)
{
// Set DCO to selected frequency
unsigned int Compare, Oldcapture = 0;
BCSCTL1 |= DIVA_3;
// ACLK = LFXT1CLK/8
TACCTL0 = CM_1 + CCIS_1 + CAP;
// CAP, ACLK
TACTL = TASSEL_2 + MC_2 + TACLR;
// SMCLK, cont-mode, clear
while (1)
{
while (!(CCIFG & TACCTL0));
// Wait until capture occurred
TACCTL0 &= ~CCIFG;
// Capture occured, clear flag
Compare = TACCR0;
// Get current captured SMCLK
Compare = Compare - Oldcapture;
// SMCLK difference
Oldcapture = TACCR0;
// Save current captured SMCLK
if (Delta == Compare)
break;
// If equal, leave "while(1)"
else if (Delta < Compare)
{
DCOCTL--;
// DCO is too fast, slow it down
if (DCOCTL == 0xFF)
// Did DCO roll under?
if (BCSCTL1 & 0x0f)
BCSCTL1--;
}
// Select lower RSEL
else
{
DCOCTL++;
// DCO is too slow, speed it up
if (DCOCTL == 0x00)
// Did DCO roll over?
if ((BCSCTL1 & 0x0f) != 0x0f)
BCSCTL1++;
}
}
// Sel higher RSEL
TACCTL0 = 0;
// Stop TACCR0
TACTL = 0;
// Stop Timer_A
BCSCTL1 &= ~DIVA_3;
}
// ACLK = LFXT1CLK
Questo codice stato ricavato dal codice dco_flashcal.c disponibile per la maggior parte di C MSP430,
sebbene non si trovi per i G2xx1. Questo stato ricavato da quello per gli F21x2, cambiando TACCTL2 in
TACCTL0, per essere compatibile con i C del LaunchPad.
ACLK configurato per usare la frequenza del quarzo divisa per 8, ovvero 4096 Hz. Il timer impostato in
modalit capture, triggerando un fronte di salita di CCI0B (dai datasheet del G2231 o G2211 corrisponde ad
ACLK). Il timer stesso conta i cicli di SMCLK, che connesso al DCO. Se si vuole calibrare la frequenza di 2
MHz, allora in un ciclo di clock di ACLK ci si aspetta 2MHz/4096Hz=488.3 cicli SMCLK. Si passa il valore
488 alla routine, che attiva i clock e i timer. Quando si ha un evento capture, controlla se i cicli SMCLK sono
di pi o di meno, e regola il DCO e RSEL di conseguenza. Si ripete questo finch si trova una configurazione
che fornisce 488 cicli nellintervallo. I valori in DCO e RSEL sono i valori di calibrazione da salvare nei
registri DCOCTL e BSCCTL1. Questa routine usata nel codice di esempio DCOcalibrate.c. Si pu misurare
la frequenza ottenuta con un oscilloscopio collegato a P1.4.
IMPOSTAZIONE DELLE MODALIT LOW POWER
La flessibilit che discende dal modulo UCS o Basic Clock rappresentano solo un aspetto delle esigenze Low
Power. Un corretto utilizzo delle varie opzioni, e il supporto di periferiche intelligenti e a basso consumo,
rappresentano anche un'importante aspetto da non sottovalutare.

50

Elementi di Programmazione 4

IMPOSTAZIONE DEI REGISTRI PER MODALIT LPM


Si visto che le varie sorgenti del clock possono essere impostate in vari modi e selezionate in base alle
esigenze. Tra le varie impostazioni non vi per la possibilit di attivarle e disattivarle, almeno nei registri
di controllo. Infatti tale possibilit presente, ma avviene stranamente per mezzo dello Status Register. Lo
Status Register possiede infatti, oltre ai bit standard che ci si aspetta di trovare (V, N, Z, C), anche i bit SCG0,
SCG1, OSCOFF, CPUOFF. Questi bit intervengono appunto nel gestire le varie sorgenti del Clock ed in
particolare anche la CPU. In base alla particolare combinazione di questi bit ci si trova in un particolare stato
a bassa potenza. La famiglia MSP430F2xx e la Value Line hanno 5 modalit a bassa potenza nominate LPM0,
LPM1, LPM2, LPM3, LPM4. Tale Figura mette in evidenza la combinazione dei bit SCG0, SCG1, OSCOFF,
CPUOFF che configurano un particolare stato a bassa potenza. Per esempio in LPM0 si ha che la CPU in
stato di off come anche il MCLK, mentre SMCLK e ACLK sono attivi, ovvero :
SCG0=0
SCG1=0
OSCOFF=1
CPUOFF=1

Normalmente il passaggio e attivazione dei vari stati a bassa potenza viene fatto utilizzando funzioni
dedicate, senza doversi ricordare il valore dei bit associati ad ogni Low Power Mode.
In ultimo bene riprendere quanto detto brevemente durante la discussione dell'architettura degli MSP430,
ovvero perch i controlli della modalit Low Power siano posti stranamente nello Status Register. La
ragione risiede nel fatto che lo Status Register, assieme al PC, viene memorizzato nello Stack al verificarsi di
un interrupt. Questo significa che quando si in Low Power mode, e si verifica un interrupt, lo Status
Register, prima di essere resettato, perdendo le informazioni memorizzate al suo interno, viene salvato nello
Stack. Resettare lo Status Register significa avere il C in Active Mode subito dopo un interrupt, ovvero
pronto per eseguire le operazioni necessarie. Quando l'Interrupt Service Routine (ISR) termina il proprio
lavoro e l'istruzione RETI (Return From interrupt) viene eseguita dalla CPU, il PC e lo Status Register sono
automaticamente ripristinati, ovvero il C ritorna in stato di Sleep in automatico senza dover compiere
alcuna operazione. Per mezzo delle seguenti funzioni possibile cambiare le impostazioni dello Status
Register senza dover usare i singoli bit.
unsigned short _bis_SR_register(unsigned short mask);
unsigned short _bis_SR_register_on_exit(unsigned short mask);
unsigned short _bis_SR_register(unsigned short mask);
unsigned short _bis_SR_register_on_exit(unsigned short mask);
unsigned short _get_SR_register(void);
unsigned short _get_SR_register_on_exit(void);
Queste funzioni accettano infatti il nome della modalit da applicare. Per esempio se si volesse abilitare
LPM3 e abilitare le interruzioni si potrebbe scrivere:
51

Elementi di Programmazione 4

_bis_SR_register(LPM3_bits + GIE);
Le funzioni che terminano con on_exit sono le funzioni che operano sullo Stack, ovvero cambiano il valore
dello Status Register memorizzato nello Stack, in modo tale che al ritorno dalla ISR il Low Power Mode sia
diverso da quello precedentemente impostato.
ESEMPIO DI UTILIZZO DEL CLOCK E LOW POWER MODE
Vediamo ora di dare un senso a quanto detto per mezzo di un semplice esempio, che, seppur di poca utilit
pratica, mostra alcuni aspetti fondamentali sul comportamento degli MSP430 e del modulo di distribuzione
del clock.
#include <msp430.h>
void main(void)
{
WDTCTL = WDTPW + WDTHOLD;
// stop watchdog timer
BCSCTL2 |= SELM_0;
// seleziona SMCLK -> DCO, MCLK->DCO
BCSCTL3 |= LFXT1S_2;
// seleziona ACLK -> VLO
BCSCTL1= CALBC1_1MHZ;
// imposta DCO a 1MHz (calibrato)
DCOCTL = CALDCO_1MHZ;
P1DIR |= BIT6;
// trigger oscilloscopio
P1OUT = 0x00;
P1DIR |= BIT4;
// SMCLK in output su PORT1 bit 4
P1SEL |= BIT4;
P1DIR |= BIT0;
// ACLK in output su PORT1 bit 0
P1SEL |= BIT0;
__delay_cycles (1000);
while (1)
{
P1OUT = BIT6;
_bis_SR_register (LPM3_bits);
P1OUT = 0x00;
}
}
// Operazione mai eseguita
Il programma inizia come al solito con il disattivare il watchdog, successivamente imposta
MCLK, SMCLK, ACLK con diverse sorgenti, in particolare il DCO e VLO. Il modulo DCO viene impostato
ad 1 MHz per mezzo del valore calibrato e memorizzato all'interno dell'Information Memory. Dopo questa
inizializzazione, si impostata la porta P1, che sebbene si possa impostare con una sola istruzione, per scopi
di chiarezza si sono messe in evidenza le singole inizializzazioni. Il BIT6 impostato come output ed
utilizzato per generare un trigger per l'oscilloscopio prima di andare in LPM3. Questo fatto al solo scopo di
effettuare la misura di figura, facendo uso del segnale di trigger esterno dell'oscilloscopio. Successivamente
la porta P1 viene impostata per avere il SMCLCK e ACLCK in output rispettivamente sul pin P1.0 e P1.6. In
questo esempio si scelto di fare questo, semplicemente per fare una misura dei due clock precedentemente
impostati. In generale questo pu tornare utile per fornire il clock interno a periferiche esterne per mezzo del
buffer rappresentato dalla porta stessa. Oltre a questa applicazione, un tale setup pu essere utile in fase di
debug, al fine di monitorare lo stato del C (in un certo qual modo quello che si fa in questo esempio). Per
porre in uscita il clock SMCLK, si fatto uso per la prima volta del multiplexer presente sulla porta di uscita,
ovvero del registro PxSEL. Le informazioni relative al multiplexer si trovano nel datasheet alla sezione
relativa alle applicazioni.
Per avere in output il clock SMCLK necessario porre ad 1 il bit 4 dei registri P1DIR e P1SEL, ovvero:
P1DIR |= BIT4;
// SMCLK in output su PORT1 bit 4
P1SEL |= BIT4;
In maniera analoga si fatto per il clock ACLK. Una volta impostato il clock si eseguito un ritardo di 1000
cicli di clock, ovvero di 1ms, considerando che la nostra CPU sta lavorando a 1MHz (MCLK impostato con
il DCO).
__delay_cycles (1000);
Questo ritardo serve semplicemente a rendere l'immagine dell'oscilloscopio di pi facile lettura. Dopo il
ritardo viene settato il pin BIT6, che permette di avviare la misura dell'oscilloscopio, impostato per misurare
SMCLK (DCO) sul canale CH1 (pin P1.4) e ACLK (VLO ) sul canale CH2 (pin P1.0). Dopo aver settato il pin,
si entra in LPM3, per cui l'istruzione successiva non verr mai eseguita (non sono presenti interrupt e cambi
di modalit), ovvero il LED2 posto su P1.6 rimarr sempre acceso. Anche il while (1) ha poco senso in questa
applicazione, ma mostra che viene eseguito un solo ciclo. Il risultato della misura riportato in Figura.

52

Elementi di Programmazione 4

La linea gialla (CH2) rappresenta il VLO. Considerando che la base dei tempi dell'oscilloscopio 200us e
sono presenti circa due cicli di clock, si ha che la frequenza si aggira intorno a 10KHz (si sono misurati
11KHz). Il rettangolo azzurro rappresenta la frequenza del DCO, che essendo circa 100 volte pi alta del
VLO, risulta di tinta unita per il numero elevato di cicli di clock in 200us. Si osservi ora che CH1 e CH2
iniziano con un livello 0 (C non alimentato). Quando viene alimentato il C, il modulo DCO e VLO si
avviano e sono impostati rispettivamente su SMCLK e ACLK, per cui sono misurati dall'oscilloscopio. Dopo
1000 cicli di clock (5 divisioni da 200us) viene avviata la modalit LPM3. Come visibile in Figura, in questa
modalit viene disattivato il modulo DCO, MCLCK e SMCLK, per cui sul canale CH1, una volta entrati in
LPM3, non vi sono pi oscillazioni (il modulo DCO spento). Diversamente il canale CH2, avendo ACLK e
il VLO che rimangono attivi in LPM3, mostra ancora le oscillazioni. Osservando il LED2, si noter che questo
rimane acceso anche in LPM3, per cui, quando si vogliono abbassare i consumi, necessario in generale
accertarsi che tutte le porte siano impostate per spegnere ogni carico, se non necessario.

LE INTERRUZIONI
CODIFICA DEGLI INTERRUPT
Come detto, quando viene generata uninterruzione, il programma viene interrotto, o il C viene risvegliato,
per eseguire le istruzioni contenute all'interno dell'ISR. Al fine di dichiarare una funzione come ISR,
necessario scrivere il seguente codice:
#pragma vector = <VECTOR_NAME>
// senza punto e virgola!
__interrupt void <ISR_NAME> (void)
{
// inizio ISR
Per esempio, gli MSP430 della Value Line possiedono linee di interruzione sulla porta P1 e P2. Ad ogni porta
associato un Interrupt Vector.
#pragma vector = PORT1_VECTOR
__interrupt void P1_ISR(void)
{
// inizio ISR
La parola chiave #pragma peculiare del compilatore, e permette di associare il codice della ISR che segue al
vettore delle interruzioni assegnato alla porta P1 (PORT1_VECTOR), come specificato nellheader file del
dispositivo. L'indirizzo di inizio della funzione utilizzata come ISR memorizzato nellindirizzo definito
dallheader file come PORT1_VECTOR. La funzione ISR deve essere dichiarata con parametro sia di ingresso
che di uscita pari a void, e deve essere preceduta dalla parola__interrupt. Il nome della
funzione (P1_ISR) pu essere cambiato a piacimento, ma frequentemente si mette il nome del modulo
seguito da ISR. Per il resto una normale funzione, in C non serve altro codice per definire la sequenza di
uscita dallISR, ma in assembly serve listruzione RETI. Occorre prestare attenzione che, per ogni
interruzione che viene chiamata, ci sia una ISR corrispondente, altrimenti non prevedibile cosa accada.
Nel caso dell'MSP430G2231 si hanno per esempio i seguenti vettori delle interruzioni:
#define PORT1_VECTOR
(2 * 1u)
// 0xFFE4 Port 1
#define PORT2_VECTOR
(3 * 1u)
// 0xFFE6 Port 2
#define USI_VECTOR
(4 * 1u)
// 0xFFE8 USI
#define ADC10_VECTOR
(5 * 1u)
// 0xFFEA ADC10
#define TIMERA1_VECTOR
(8 * 1u)
// 0xFFF0 Timer A CC1,
#define TIMERA0_VECTOR
(9 * 1u)
// 0xFFF2 Timer A CC0
#define WDT_VECTOR
(10 * 1u)
// 0xFFF4 Watchdog Timer
53

Elementi di Programmazione 4

#define NMI_VECTOR
(14 * 1u)
// 0xFFFC Non-maskable
#define RESET_VECTOR
(15 * 1u)
// 0xFFFE Reset [Highest]
mentre per l'MSP430G2553 si hanno i seguenti vettori delle interruzioni.
#define PORT1_VECTOR
(2 * 1u)
// 0xFFE4 Port 1
#define PORT2_VECTOR
(3 * 1u)
// 0xFFE6 Port 2
#define ADC10_VECTOR
(5 * 1u)
// 0xFFEA ADC10
#define USCIAB0TX_VECTOR
(6 * 1u)
// 0xFFEC USCI A0/B0
#define USCIAB0RX_VECTOR
(7 * 1u)
// 0xFFEE USCI A0/B0
#define TIMER0_A1_VECTOR
(8 * 1u)
// 0xFFF0 Timer0)A CC1,
#define TIMER0_A0_VECTOR
(9 * 1u)
// 0xFFF2 Timer0_A CC0
#define WDT_VECTOR
(10 * 1u)
// 0xFFF4 Watchdog Timer
#define COMPARATOR_VECTOR (11 * 1u)
// 0xFFF6 Comparator A
#define TIMER1_A1_VECTOR
(12 * 1u)
// 0xFFF8 Timer1_A CC1
#define TIMER1_A0_VECTOR
(13 * 1u)
// 0xFFFA Timer1_A CC0
#define NMI_VECTOR
(14 * 1u)
// 0xFFFC Non-maskable
#define RESET_VECTOR
(15 * 1u)
// 0xFFFE Reset [Highest]
Il numero dei vettori delle interruzioni dipende quindi dal particolare dispositivo utilizzato, visto che i vari
modelli possono o meno avere determinati moduli/funzioni. Occorre fare sempre riferimento al datasheet e
all'header file, al fine di utilizzare in maniera opportuna i nomi dei vettori.
APPLICAZIONI
Esempio 1: GPIO INTERRUPT
Ad ogni porta sono associati molteplici registri per le impostazioni. In particolare i 3 registri per le
interruzioni associati alla porta P1 rappresentano, rispettivamente, il registro per abilitare le interruzioni sui
singoli pin (P1EN), la selezione del fronte (P1IES), e il relativo flag delle interruzioni, un bit per pin, (P1IFG).
Qualora l'interrupt sia generato da un pin sulla porta P1, necessario controllare il Flag dei singoli pin al
fine di determinare la sorgente esatta. Questa operazione consiste generalmente nel verificare bit per bit il
registro P1IFG che ha appunto i flag relativi alle interruzioni della porta. Gli MSP430 di ultima generazione,
per limitare il numero di istruzioni eseguite, ovvero energia persa al solo scopo di capire quale pin ha
generato l'interruzione, possiedono una Tabella Secondaria delle Interruzioni, nominata P1IV, che in realt
rappresenta un offset, che si pu sommare al PC, gi posizionato sul vettore d'interruzione associato alla
porta P1. L'offset rispecchia praticamente il valore del bit che ha generato l'interruzione, e permette di
azzerare il flag delle interruzioni associato al bit stesso. Tale tecnica viene usata anche per altre periferiche, in
cui ad un solo vettore delle interruzioni fanno capo pi sorgenti d'interruzione. Maggiori dettagli possono
essere trovati nella User Guide dell'MSP430 utilizzato. L'importante sapere che in alcuni casi, oltre alla
possibilit di usare l'istruzione if per controllare i flag d'interruzione, si pu usare anche l'offset speciale
dedicato alla periferica, da sommare al PC.
Come esempio si riporta il codice per gestire interrupt sulla porta P1, ma pu essere cambiato facilmente per
qualunque altra porta. Questo codice mostrer come variare il DCO usando gli interrupt.
Prima bisogna configurare la porta per usare gli interrupt. Il pulsante del LaunchPad si trova sul pin P1.3,
che deve essere configurato come input. Poich il pin si trova nello stato logico 1 (pull-up attivata), una
transizione alto-basso (10) deve essere usata per segnalare linterrupt.
P1IES |= BIT3;
// high low is selected with IES.x = 1
P1IFG &= ~BIT3;
// to prevent an immediate interrupt, clear the flag for P1.3 before enabling interrupts
P1IE |= BIT3;
// enable interrupts for P1.3
Abilitati gli interrupt sulla porta 1, una transizione alto-basso, causata dalla pressione del pulsante, attiver il
flag di interrupt in P1IFG. Il processore, tuttavia, non impostato per riconoscere interrupt mascherabili
come P1IFG. Occorre abilitare gli interrupt.
__bis_SR_register (GIE); // abilita le interruzioni globali
o equivalentemente:
_enable_interrupt();
// Note the singular name here! It's not interrupts
Il primo metodo lequivalente dellistruzione bis.w in assembly, e serve a configurare lo SR in un passo,
insieme ad altre opzioni, come le modalit low power. Il secondo pi intuitivo, e va bene se non serve
abilitare modalit low power. LISR invece:

54

Elementi di Programmazione 4

#pragma vector = PORT1_VECTOR


__interrupt void P1_ISR(void)
{
switch(P1IFG&BIT3)
{
case BIT3:
P1IFG &= ~BIT3;
// clear the interrupt flag
BCSCTL1 = bcs_vals[i];
DCOCTL = dco_vals[i];
if (++i == 3)
i=0
return;
default:
P1IFG = 0;
// probably unnecessary, but if another flag occurs in P1,
// this will clear it.
// No error handling is provided this way, though.
return;
}
}
// fine P1_ISR
Linterrupt pu arrivare dagli 8 pin della porta, per cui lISR deve poter attivare la procedura corrispondente
ad ogni interrupt. Listruzione switch lideale in questo caso, ed usata per selezionare un flag. Se per
qualche ragione arriva un flag da un diverso interrupt su P1, listruzione azzera il flag e va avanti. Sebbene
nessuno degli altri flag abilitato, buona pratica prevedere questo (anzi si dovrebbe inserire una procedura
di gestione dellerrore). Si noti che, poich P1 ha 8 sorgenti di interrupt, occorre azzerare manualmente i flag,
altrimenti il codice continua ad avere interrupt.
Il codice fa uso di tre valori memorizzati per BSCCTL1 e DCOCTL, che insieme alla variabile i devono
essere dichiarati esternamente allISR (globali), e li fa scorrere ogni volta che accade un interrupt.
Esempio 2: TIMER_A
Questo esempio accende e spegne una luce usando un interruttore a relay. Il programma utilizza uno switch
montato sulla porta P1.6 del LaunchPad, usando un LED verde al posto di un vero relay. Nel caso si
disponga di un vero relay, occorre essere sicuri che sia montato correttamente, per evitare che assorba troppa
corrente dallMSP430.
Usando un DCO calibrato ad 1 MHz e diviso per 8 (125 kHz), il contatore conter fino a 62500 in 0.5 secondi,
e si user per accendere e spegnere la luce ogni minuto. Per fare questo, si utilizza il Timer_A in up-mode, e
linterrupt usando i registri CCR0. Il timer viene configurato con il codice seguente:
TACCR0 = 62500 - 1;
// a period of 62500 cycles is 0 to 62499.
TACCTL0 = CCIE;
// enable interrupts for CCR0
TACTL = TASSEL_2 + ID_3 + MC_1 + TACLR;
Lultima istruzione seleziona SMCLK come clock per il Timer_A, seleziona il divisore di ingresso pari a 8,
seleziona lup-mode, e azzera il conteggio nel TAR. Questi valori sono predefiniti nellheader file del C.
LISR diventa qualcosa del genere:
#pragma vector = TIMERA0_VECTOR
__interrupt void CCR0_ISR(void) {
// no flag clearing necessary; CCR0 has only one source, so it's automatic.
if (++i == 120)
{
P1OUT ^= RLY1;
i = 0;
}
}
// fine CCR0_ISR
Anche qui si usata una variabile globale di conteggio i, e presumibilmente si assegna BIT6 a RLY1
nellintestazione del programma. Ogni volta che capita un flag (2 volte al secondo), il contatore
incrementato. Quando si raggiunge 120 (in 60 s a 2 Hz), il relay di controllo commuta, e il contatore si azzera.
Esempio 3: PULSE WIDTH MODULATION (PWM)
Questa applicazione fa uso del Timer con due interruzioni, per costruire un modulatore PWM. Si potr
cambiare la luminosit di una lampada, o la velocit di un motore.
Basic PWM Signal
Un segnale PWM una forma donda rettangolare, di cui si varia il duty cycle, pur mantenendo costante la
frequenza.

55

Elementi di Programmazione 4

Per creare un segnale PWM si usano due interrupt su un timer, che sono disponibili nellMSP430, sebbene si
si potrebbe usare un solo interrupt, e regolare attraverso il software quando accade linterrupt. Un interrupt
imposta la frequenza del segnale, laltro il duty cycle. Usando il modo compare del timer in continuous
mode, il Timer_A ha tre interrupt. Ogni volta che il timer si azzera (roll over) dopo 65536 (o 216) cicli di clock,
inviato un interrupt, che verr usato per attivare loutput del PWM. Usando CCR0, si pu impostare il
duty cycle, resettando loutput dopo uno psecificato numero di cicli di clock. Se linterrupt di CCR0 si resetta
quando il registro contatore dle timer raggiunge 32768 (=65536/2), si ottiene un duty cycle del 50%. Il PWM
attivo per met periodo, e resettato per laltra met. Se si setta CCR0 a 6554, si ha un duty cycle del 10%.
Poich il modulo Timer_A ha due registri capture-compare, usando questo metodo si possono ottenere, da
ogni dispositivo MSP430, almeno due segnali PWM indipendenti.
Come primo esempio, si consideri LEDPWM_G2231.c. Se si fa girare sul LaunchPad, si vedranno entrambi i
LED accendersi, aumentare la luminosit fino ad un massimo, poi diminuirla fino a spegnersi, e ripetere il
ciclo. I segnali PWM sono impostati per aumentare il loro duty cycle da 0 al 100% e poi di nuovo a 0 ad ogni
ciclo. Si noter che le transizioni sono un po brusche, per renderle pi soft basta togliere il commento alle
linee di codice che impostano il DCO ad una frequenza pi alta.
Miglioramento del Segnale PWM
La frequenza del PWM in questo caso F/216, dove F la frequenza del Timer_A. Alla frequenza del DCO di
default di 1.1 MHz, la frequenza degli impulsi solo 17 Hz, rilevabile facilmente dallocchio umano. Se si
usa la frequenza massima del DCO di 16 MHz, allora la massima frequenza degli impulsi 244 Hz. Per
alcune applicazioni questa potrebbe essere sufficiente, ma in molte altre ne serve una di almeno 1 kHz o pi.
Poich non c modo di aumentare ulteriormente la frequenza del Timer_A, necessario che il conteggio del
timer sia inferiore. Questo si pu ottenere in up-mode invece che in continuous mode, ma serve usare CCR0
allo scopo, perdendo la disponibilit di un output. Inoltre si perde anche in risoluzione. Per esempio, se si
setta CCR0 a 10, ci sono solo 11 possibli duty cycle; CCR1 pu essere settato ad un numero inferiore o
uguale a 10. Questo significa che si ottengono 11 duty cycle tra lo 0 ed il 100%, spaziati ad intervalli del 10%.
Mettendo CCR0 a 100, si possono selezionare intervalli dell1%, a 1000 dello 0.1% e cos via. In pratica
maggiore la risoluzione ottenibile e minore la frequenza.
Questo secondo esempio mostrato nel codice LEDPWM2_G2231.c. Prima occorre settare maualmente le
linee PWM su un interrupt di overflow. LMSP430 fornisce delle modalit di output del timer che fanno uso
sia di TACCRx che di TACCR0, cos impostare la linea PWM diventa automatico. Non serve nessun
interrupt ! Questo aspetto rende il codice pi chiaro, e fa risparmiare molta memoria. Ovviamente se sono
necessarie altre funzioni oltre il PWM, un interrupt pu ancora essere tirggerato per gestirle.
Si potrebbe anche notare che una caratteristica degli schemi osservati che gli impulsi sono tutti allineati a
sinistra, perch ogni linea PWM settata alta nello stesso istante (quando il timer torna a 0). In alcuni casi
potrebbe essere utile avere gli impulsi allineati al centro, cio in modo che il centro degli impulsi sia nello
stesso istante. Questo set-up si ottiene usando il timer in up-down mode, e impostando la commutazione
degli output del timer sui rispettivi interrupt CCRx; se la linea comincia bassa (che pu essere assicurato
resettando a TAR=0 se necessario), allora loutput diventa alto quando CCRx raggiunto contando in salita,
e poi diventa basso quando raggiunto di nuovo contando in discesa. Questa tecnica centra gli impulsi sul
punto pi alto, consentendo ad una molteplicit di segnali PWM di essere centrati nello stesso istante. Poich
questa modalit richiede che TACCR0 setti il valore fino a cui il timer conta, non si pu usare TA0.0 per il
PWM neppure in questo setup.
Negli esempi visti, quando la durata dellimpulso piccola, in media il LED emette meno luce. Un duty
cycle del 10% significa che il LED emette luce solo per il 10% del tempo, tuttavia percepito dallocchio
umano come se avesse una luminosit del 10%, sebbene i LED abbiano un funzionamento digitale (cio on
o off, senza gradualit). Aumentando e poi diminuiendo il duty cycle, si crea un simpatico effetto usato in
molta elettronica commerciale, oppure per variare lintensit (dimmer) di una sorgente di luce a LED.

56

Elementi di Programmazione 4

Esempi di segnali PWM con 3 differenti duty cycles e livello medio (sinistra). Allineamento a sinistra e al centro (destra)

Esempio 4: RISVEGLIO DA PRESSIONE DEL TASTO


Si vede ora come unire i vantaggi delle interruzioni con le modalit a basso consumo. Si supponga di voler
spegnere il proprio sistema usando un semplice push-button, ovvero lasciare il sistema alimentato, ma
mandarlo in standby e risvegliarlo con un semplice tasto, come per una calcolatrice.
Si crei un nuovo progetto in CCS basato su MSP430G2553 o MSP430G2xxx e si copi il seguente codice
sorgente:
#include <msp430.h>
#define ON 1
// definizione costante ON
#define OFF 0
// definizione costante OFF
volatile unsigned char state = OFF;
void main(void)
{
unsigned char loop;
WDTCTL = WDTPW | WDTHOLD;
P1DIR |= BIT0;
//pin 0 output (LED)
P2DIR = 0x00;
//attivazione resistore per pulsante S2 (BIT3) e su ingressi non usati della PORT1
P1REN |= BIT1 + BIT2 + BIT3 + BIT4 + BIT5 + BIT6 + BIT7;
//attivazione resistore su ingressi non usati della PORT2
P2REN |= BIT0 + BIT1 + BIT2 + BIT3 + BIT4 + BIT5 + BIT6 + BIT7;
P1OUT |= BIT3;
// attiva pull-up sul pin 3
P1IE |= BIT3;
// abilita interruzione sul pulsante S2
P1IES |= BIT3;
// fronte HighLow
__bis_SR_register (GIE); // abilita le interruzioni globali
while (1)
{
switch (state)
{
case ON:
for (loop=0; loop<100; loop++)
{
__delay_cycles (1000);
}
P1OUT ^= BIT0;
break;
case OFF : P1OUT &= ~BIT0;
__bis_SR_register (LPM4_bits);
break;

57

Elementi di Programmazione 4

default: break;
}
}
}
//************ ISR ***************************************************
#pragma vector = PORT1_VECTOR
__interrupt void P1_ISR(void)
{
if (P1IFG & BIT3 )
{
__delay_cycles (10000);
// filtro antirimbalzo
if ((P1IN & BIT3))
{
if (state == ON)
{
state = OFF;
}
else
{
state = ON;
}
}
P1IFG &= ~BIT3;
// clear interrupt flag
__bic_SR_register_on_exit(LPM4_bits);
}
}
L'esempio leggermente pi complesso degli altri finora presentati, ma anche il primo esempio che in un
certo qual modo fa qualcosa di utile, ovvero disattivare il sistema e andare in Low Power mode.
Il programma inizia definendo due costanti, rispettivamente ON e OFF, per poter implementare una
semplice state machine. Lo stato del sistema viene dichiarato all'interno della variabile globale state. Tale
variabile stata definita globale (ovvero fuori dal main), in maniera da poter essere letta e scritta sia nel
main che all'interno dell'ISR. Dal momento che anche l'ISR pu accedere alla variabile, quest'ultima stata
definita volatile. In questo modo il compilatore non effettuer nessuna ottimizzazione basandosi sul valore
della variabile. Alcune volte il compilatore, se ha gi caricato la variabile in un registro Rxx, non la ricarica
per fare un nuovo confronto. Se la variabile definita volatile, l'assumere che la variabile non sia cambiata
potrebbe non essere corretto, per cui il compilatore non fa alcuna ottimizzazione.
Il main inizia con il disattivare il watchdog e inizializzare le porte P1 e P2. Si noti che entrambe le porte sono
state inizializzate impostando ogni pin come input, ad eccezione di BIT0 di P1 utilizzato per il LED.
Successivamente per ogni pin si attivato il resistore di pull-down, ad eccezione di BIT3 di P1 impostato con
un resistore di pull-up. Questo necessario perch il pulsante S2, alla sua pressione, collegato a massa. In
questo esempio si sono attivati tutti i resistori, in maniera da poter limitare i consumi in stato Low Power.
Tale funzione si legge come Bit Set Status Register (scrivendo __bi e premendo control + space compaiono
tutte le funzioni intrinseche per lo Status Register).
Effettuata l'inizializzazione delle porte, si abilitano le interruzioni sul pin BIT3, al fine di rilevare la pressione
del pulsante S2. Dal momento che presente un resistore di pull-up, e alla pressione del pulsante il pin BIT3
di P1 posto a massa, si impostato il fronte di riconoscimento falling edge (da alto a basso). Infine si sono
abilitate le interruzioni globali per mezzo del bit GIE dello Status Register, attivabile per mezzo della
funzione intrinseca:
_bis_SR_register (GIE);
Finite le impostazioni, il programma entra in un loop infinito creato per mezzo di un while. Ciononostante il
programma non sempre in esecuzione, infatti nel ciclo infinito viene controllata la variabile state, che di
default vale OFF. Il controllo avviene per mezzo dell'operatore switch (tale controllo implementa una
semplice state machine). Nel nostro caso la state machine ha solo due stati OFF e ON, ed il passaggio
dall'uno all'altro avviene per mezzo della pressione del pulsante. La prima volta che viene controllata la
variabile state, il valore OFF, per cui viene spento il LED ed eseguita l'istruzione (funzione intrinseca):
__bis_SR_register (LPM4_bits);
Questa permette di mandare il C in Ultra Low Power mode (si ricorda che le interruzioni sono abilitate, se
cosi non fosse solo un Reset o Power Cycle permetterebbe di riattivare il sistema). Per tale ragione il clock e
la CPU si disattivano, il programma si arresta, e i consumi del sistema si aggirano a circa 100nA.
Per continuare la spiegazione del nostro programma bisogna premere il pulsante S2. Tale evento genera
un'interruzione, che nel caso specifico non interrompe l'esecuzione del programma, ma un sonno profondo.
Il PC viene impostato per permettere l'esecuzione della funzione ISR che abbiamo nominato P1_ISR.
Sebbene nel nostro caso abbiamo una sola sorgente d'interruzione, bene sempre controllare il registro dei
flag, al fine di verificare quale pin o modulo ha generato l'interruzione, nel caso specifico il registro dei flag
P1IFG. Una volta accertati che il BIT3 di P1IFG attivo, viene inserito un piccolo ritardo di 10ms, al fine di
permettere la stabilizzazione della linea associata al BIT3 (non la soluzione ottimale per ottenere bassi
consumi, sarebbe meglio usare un Timer). Dopo il ritardo per implementare un filtro anti-rimbalzo, si
effettua la lettura del pulsante: se questo premuto, ovvero pari a 0, viene cambiato il valore della variabile
58

Elementi di Programmazione 4

state, da OFF a ON, o da ON a OFF, a seconda del valore corrente. Nel caso di prima esecuzione il valore
sar invertito da OFF a ON.
Dopo queste semplici operazioni viene azzerato il BIT3 del registro P1IFG. Nel nostro caso si sarebbe potuto
anche scrivere 0x00 nel registro, ma nel caso in cui vengano usate anche le altre linee di interruzione, bene
che solo il bit d'interesse sia azzerato. In questo modo, se durante l'esecuzione della ISR dovesse essere
settato un altro flag, l'informazione non verrebbe persa.
Prima di uscire dalla ISR viene eseguita una nuova funzione intrinseca:
__bic_SR_register_on_exit(LPM4_bits);
Questa permette di cambiare il valore dello Status Register memorizzato nello Stack, ovvero il valore che lo
Status Register aveva prima dell'interruzione. In questo caso, dal momento che il C proveniva da uno stato
di sleep, se il vecchio Status Register venisse ripristinato, l'MSP430 tornerebbe in stato di sleep (si ricorda che
la modalit Low Power memorizzata nello Status Register). La funzione intrinseca eseguita permette di
resettare i bit associati al Low Power mode LPM4, in modo tale che al ritorno dall'interruzione lo stato del
C sia AM (Active Mode), e il controllo della variabile state venga nuovamente eseguito.
Da quanto appena detto, usciti dall'ISR, il programma continua la sua esecuzione dall'istruzione che segue la
funzione intrinseca:
__bis_SR_register (LPM4_bits);
per cui viene eseguita l'istruzione break, e l'operatore condizionale switch viene nuovamente eseguito
dall'inizio. In questo secondo caso il nostro stato ON, per cui viene attivato il LED e fatto lampeggiare, fino
a quando una nuova pressione del pulsante cambier lo stato da ON a OFF, e il C torner a consumare circa
100nA.
In questo esempio stata trattata solo l'interruzione della porta P1, per cui stata inserita solo l'ISR per la
stessa. Per ragioni di sicurezza buon uso dichiarare anche le altre ISR, una per vettore, al fine di prevenire
comportamenti anomali della CPU, qualora venga generata un'interruzione imprevista da parte di
periferiche, che formalmente non dovrebbero generare alcuna interruzione. Questo non un evento raro in
sistemi a C, che devono eseguire codice 24 ore su 24, siano MSP430 o altri C. Infatti un registro RAM
potrebbe variare il proprio valore a causa di radiazioni o fenomeni elettromagnetici (il cellulare per
esempio).
Il programma di esempio ha messo in evidenza che, oltre ad abilitare le interruzioni sui singoli pin o moduli,
prima di andare in Low Power mode, necessario abilitare il bit GIE (General Interrupt Enable). Infatti
tutte le interruzioni dei moduli interni, salvo rare eccezioni, sono di tipo mascherabile, ovvero possono
essere abilitate e disabilitate per mezzo del bit GIE. Ogni volta che si sta eseguendo un ISR, viene
automaticamente disabilitato il bit GIE, per disabilitare interruzioni annidate. Interruzioni annidate
sarebbero possibili abilitando il bit GIE all'interno dell'ISR. Sebbene sia lecito farlo, gestire interruzioni
annidate pu causare comportamenti anomali, legati al fatto che non si sono considerati tutti i casi possibili,
in cui si possono verificare interruzioni annidate.
L'esempio mostrato rappresenta una buona pratica di programmazione, per quando riguarda la gestione
delle interruzioni, ovvero ridurre al minimo il tempo di esecuzione dell'ISR cambiando semplicemente lo
stato di una variabile che viene controllata nel loop principale del main.
MISURA DELLA CORRENTE
L'esempio appena spiegato, oltre a mostrare come poter gestire le interruzioni da parte di diversi moduli
interni del C, rappresenta anche un buon esempio di come utilizzare le modalit Ultra Low Power del C.
Nel caso specifico interessante misurare le correnti che si possono ottenere senza dover staccare
fisicamente il circuito dalla batteria per mezzo di un interruttore.
Prima di procedere alla misura della corrente necessario rimuovere il ponticello da J5, in particolare quello
per il LED verde (LED2), per evitare che possa circolare della corrente parassita. Il LED1 si pu lasciare
collegato perch il relativo pin del C impostato come output.
Successivamente necessario programmare il dispositivo e fermare il Debug. Staccare la scheda dalla porta
USB e riattaccarla. In questo modo si pu essere certi che la scheda non pi collegata con CCS e il modulo
di Debug disattivo. La porta USB servir solo per alimentare il sistema.
Per misurare la corrente sufficiente scollegare il ponticello Vcc da J3, e posizionare un amperometro tra i
due pin del ponticello, per alimentare nuovamente il sistema. L'amperometro deve essere impostato alla
portata pi piccola disponibile della scala DCA. Gli amperometri a 3 digit e mezzo con portata minima da
59

Elementi di Programmazione 4

200uA potrebbero avere qualche problema a misurare le correnti in LPM4, visto che la cifra meno
significativa vale gi 100nA.
Per mandare semplicemente il C in standby non serve un interruttore. Premendo il pulsante, i consumi
saliranno rapidamente a circa 1-2mA, ma, a causa del LED lampeggiante, il valore osciller piuttosto
rapidamente. Altri esempi di modalit Low Power verranno mostrati quando verranno descritti gli altri
moduli, i quali per esempio possono continuare a lavorare usando LPM0-LMP3, a seconda del clock attivato.
In ultimo si ricorda che i dati presenti in RAM vengono mantenuti anche in LPM4, ovvero non si perde
nessuna informazione. La famiglia MSP430F5xx introduce anche le modalit LMP4.5, con la quale si pu
disattivare anche la RAM, e scendere ulteriormente con i consumi a circa 50-20nA.
ESEMPIO 5 : LAMPEGGIO DI UN LED PER MEZZO DEL TIMER_A ED INTERRUZIONI
Si noti che il codice non ottimizzato per raggiungere bassi consumi, pur facendo uso della modalit LPM3.
Infatti gli I/O non sono tutti inizializzati. Questa scelta legata semplicemente al fatto di mantenere il codice
pi compatto e di facile comprensione.
L'esempio non fa altro che far lampeggiare il LED2 posto sul pin P1.6 della scheda LaunchPad. La base dei
tempi ottenuta per mezzo del Timer0, il cui clock fornito dal cristallo esterno (al fine di far funzionare
l'esempio necessario saldare il cristallo da 32KHz fornito con il KIT LaunchPad o cambiare il clock di
riferimento per ACLK con VLO piuttosto che XT). Il Timer0 viene impostato per lavorare in Up Mode, per
cui il tempo determinato dal registro CC0. Dal momento che il clock di 32768Hz, si capisce che
impostando il registro TA0CCR0 a 16384, si avr un evento Compare ogni 500ms. Il modulo Capture e
Compare inoltre impostato per generare un interrupt (bit CCIE del registro TA0CCTL0).
Il lampeggio del LED2 si ottiene nell'ISR, in cui invertito il valore del bit 6 della P1, per cui il LED
lampegger alla frequenza di 1 Hz. Si noti come non sia necessario resettare il flag CCIFG, dal momento che
l'Interrupt Vector TIMER0_A0_VECTOR associato solo a CCIFG, per cui viene resettato automaticamente
al verificarsi di ogni interrupt. Si ricorda che questo valido solo per gli Interrupt Vector che hanno una sola
sorgente dinterruzione.
Si osservi che il LED continua a lampeggiare, anche se si in modalit LPM3. Infatti, il cristallo da 32KHz
non disattivato in LPM3 e il clock ACLK anche disponibile. Per tale ragione il Timer continua il suo
conteggio e ha la possibilit di generare un Interrupt che porta la CPU ad eseguire l'ISR, all'interno della
quale viene invertito ciclicamente lo stato del LED2.
L'utilizzo del cristallo da 32KHz permette di ottenere una base dei tempi precisa, che in particolare pu
essere utilizzata per realizzare un orologio digitale. Si noti che per realizzare un orologio digitale non
sarebbe richiesta la CPU sempre attiva. Impostando CCP0 a 0x7FFF possibile ottenere un evento Compare
ogni secondo e, al risveglio del C, la CPU pu eseguire gli opportuni incrementi per Secondi, Minuti e Ore.
Alcune varianti degli MSP430 possiedono anche un RTCC (Real Time Clock Calendar) interno, per cui
l'orario viene mantenuto senza l'intervento della CPU.
Le impostazioni dei registri sono volutamente non commentate al fine di esortare il lettore a controllare il
loro significato direttamente nei registri di configurazione descritti nei paragrafi precedenti.
#include <msp430.h>
void main(void)
{
WDTCTL = WDTPW | WDTHOLD;
// stop watchdog timer
P1OUT = 0x00;
// impostazioni porte I/O
P1DIR = BIT6;
//

BCSCTL2 = SELM_0 | DIVM_0 | DIVS_0;


// impostazioni modulo clock
BCSCTL1 |= XT2OFF | DIVA_0;
//

BCSCTL3 = XT2S_0 | LFXT1S_0 | XCAP_1;


//

TA0CCTL0 = CM_0 | CCIS_0 | OUTMOD_0 | CCIE;


// impostazioni Timer0
TA0CCR0 = 16383;
//

TA0CTL = TASSEL_1 | ID_0 | MC_1;


//

while (1)
{
// applicazione principale (loop infinito)
__bis_SR_register(LPM3_bits + GIE);
}
}
#pragma vector=TIMER0_A0_VECTOR
// Timer0 CC0 ISR
__interrupt void TIMER0_ISR(void)
{
P1OUT ^= BIT6;
}

60

Elementi di Programmazione 4

ESEMPIO 6 : LAMPEGGIO DI UN LED PER MEZZO DELLE USCITE DEL MODULO CAPTURE E COMPARE
Nellesempio precedente si visto come far lampeggiare il LED, invertendo lo stato dell'uscita del pin a cui
collegato. Sebbene questo sia un modo efficiente, poich si usata una sola istruzione e in LPM3 per il
tempo restante, c' un modo ancora pi efficiente in cui si pu restare sempre in LPM3, senza far intervenire
la CPU. Questo modo sfrutta l'uscita le modulo Capture e Compare e richiede di abilitare la relativa uscita
sulla porta P1. Per fare questo, oltre ad abilitare il pin 6 di P1 come uscita, si impostato il bit 6 anche del
registro P1SEL, che permette appunto di avere in uscita il valore del modulo Capture e Compare.
Diversamente dall'esempio precedente non vi nessuna ISR, ed in particolare la CPU non inverte lo stato del
pin 6 della P1.
Oltre alle impostazioni precedenti, per il modulo Capture e Compare si impostato anche il modulo
Capture e Compare CC1, il quale controlla l'uscita alla quale collegato il LED1.
In questo caso, per avere il lampeggio di un secondo, si impostato CCR0 a 32768 (visto che si usa ancora il
cristallo da 32768Hz), mentre CCR1 impostato a 32000. In questo modo il LED2 viene acceso solo per 768
cicli di clock, ovvero per 23ms, creando il tipico lampeggio di sistemi a basso consumo. Il lampeggio
dovuto al fatto che l'uscita del modulo CCR1 impostata in modalit 3 ovvero Set/Reset.
Il risultato di questo esempio quindi un avviso di un LED che lampeggia, senza avere nessun codice in
esecuzione. Questa la ragione per cui i moduli degli MSP430 vengono spesso chiamati Smart Modules.
#include <msp430.h>
void main(void)
{
WDTCTL = WDTPW | WDTHOLD;
P1OUT = 0x00;
P1DIR = BIT6;
P1SEL |= BIT6;
P1SEL2 &= ~BIT6;
BCSCTL2 = SELM_0 | DIVM_0 | DIVS_0;
BCSCTL1 |= XT2OFF | DIVA_0;
BCSCTL3 = XT2S_0 | LFXT1S_0 | XCAP_1;
TA0CCTL0 = CM_0 | CCIS_0 | OUTMOD_0 ;
TA0CCR0 = 32768;
TA0CCTL1 = CM_0 | CCIS_0 | OUTMOD_3 ;
TA0CCR1 = 32000;
TA0CTL = TASSEL_1 | ID_0 | MC_1;
while (1)
{
__bis_SR_register(LPM3_bits);
}
}

// stop watchdog timer


// impostazioni modulo I/O
//

//

//

// impostazioni modulo Clock


//

//

// impostazioni Timer0
//

//

//

//

// loop infinito

ESEMPIO 7 : LAMPEGGIO DI UN LED E CONTROLLO DELLILLUMINAZIONE CON PWM


In questo esempio si sono apportate alcune modifiche al precedente, al fine di avere un'uscita PWM (Pulse
Width Modulation). Per fare questo si in primo luogo impostato il clock del Timer non pi a 32KHz, bens
ad 1 MHz, in particolare, se si osservano le impostazioni del modulo interno del Clock, si pu notare che
si fatto uso del valore calibrato di 1MHz. Questo non necessario per la funzione PWM, ma rende il
progetto pi completo. La frequenza pi alta permette inoltre di avere una migliore risoluzione del PWM,
permettendo un cambio d'intensit del LED pi continua e fluida, per cui si potrebbe anche usare il valore di
16MHz, ovvero la massima frequenza possibile con la serie MSP430G2xx. Ciononostante, si ricorda che con
l'aumento della frequenza aumentano in generale i consumi, per cui sempre bene utilizzare il valore
opportuno e non eccedere le reali performance richieste.
Senza entrare nel dettaglio, si ricorda che un segnale PWM ha generalmente una frequenza costante e il suo
Duty Cycle viene variato, ovvero il rapporto che esiste tra Ton e Toff della forma d'onda quadra in uscita al
modulo Capture e Compare. La massima intensit del LED si ottiene quando l'uscita del modulo CCR1
sempre ad 1, mentre per valori intermedi si noter una variazione dell'intensit del LED2. Al fine di far
partire il LED da spento, piuttosto che impostare CCR1 in modalit 3, si impostata la modalit 7.
La frequenza del segnale PWM dipende da quanto rapidamente viene resettato il contatore del Timer0, il
quale, essendo in modalit Up Mode, conter fino a CCR0. Dal momento che il clock usato di 1MHz, e gli
impulsi di clock contati prima del Reset sono 5000, si ha che la frequenza del PWM di 200Hz. Con
frequenze dell'ordine di 200Hz si evita che l'occhio possa percepire la frequenza del PWM. Il processo per il

61

Elementi di Programmazione 4

calcolo di CCR0 in generale inverso, ovvero si parte dalla frequenza PWM che si vuole avere, e in base alla
frequenza di clock fornita al Timer, si calcola il valore opportuno da scrivere nel registro CCR0.
Il Duty Cycle viene impostato per mezzo del registro TA1CCR1, ed in particolare il 100% si ha quando CCR1
uguale a CCR0. In questo esempio il Duty Cycle viene variato all'interno del loop while di una quantit
arbitraria pari ad 8.
In questo esempio la CPU sempre in esecuzione, per cui proprio un'applicazione Low Power. Volendo
risparmiare energia, ma dovendo tenere il modulo DCO attivo, si pu usare la modalit LPM0, che permette
di disattivare la CPU. In questo caso sarebbe per necessario usare un secondo Timer, che permetta di
risvegliare la CPU periodicamente ed effettuare l'aggiornamento del Duty Cycle.
All'interno del loop presente la funzione intrinseca __delay_cycles(5000), che permette di avere un ritardo
di 5000 cicli di clock. Visto che MCLK 1MHz, si ha un ritardo di 5ms. Ogni 5ms viene effettuato
l'incremento del registro CCR1, ovvero viene incrementato il Duty Cycle, il quale si riflette in un aumento
dell'intensit del LED2. Quando il Duty Cycle raggiunge il valore di TA0CCR0, il registro TA0CCR1 viene
posto nuovamente a 0.
#include <msp430.h>
void main(void)
{
WDTCTL = WDTPW | WDTHOLD;
// stop watchdog timer
P1OUT = 0x00;
// impostazioni porte I/O
P1DIR = BIT6;
//

P1SEL |= BIT6;
//

P1SEL2 &= ~BIT6;


//

BCSCTL2 = SELM_0 | DIVM_0 | DIVS_0;


// impostazioni modulo clock
if
(CALBC1_1MHZ != 0xFF)
{
//

DCOCTL = 0x00;
//

BCSCTL1 = CALBC1_1MHZ;
//

DCOCTL = CALDCO_1MHZ;
}
//

BCSCTL1 |= XT2OFF | DIVA_0;


//

k
BCSCTL3 = XT2S_0 | LFXT1S_0 | XCAP_1;
//

TA0CCTL0 = CM_0 | CCIS_0 | OUTMOD_0 ;


// impostazioni Timer0 (frequenza PWM 200Hz)
TA0CCR0 = 5000;
//

TA0CCTL1 = CM_0 | CCIS_0 | OUTMOD_7 ;


//

TA0CCR1 = 0x00;
//

TA0CTL = TASSEL_2 | ID_0 | MC_1;


//

while (1)
{
// loop infinito
TA0CCR1 += 8;
if (TA0CCR1 > TA0CCR0)
{
TA0CCR1 = 0x00;
}
__delay_cycles(5000);
}
}
ESEMPIO 8 : MISURA DELLA FREQUENZA DI UN SEGNALE DIGITALE
In questo esempio si riprende quanto gi scritto nell'Esempio 6, che permette di segnalare un sistema in
esecuzione, con l'aggiunta del controllo Capture per monitorare i fronti di discesa di un'onda quadra, in
particolare si usa il Timer1 modulo CC1. Per mezzo del secondo Timer e del modulo Capture e Compare
possibile misurare il tempo che intercorre tra due fronti di discesa di un'onda quadra in ingresso al modulo,
permettendo di rilevarne il periodo o la frequenza di quest'ultima. In particolare l'esempio permette di
riconoscere la frequenza di 100Hz attivando il LED1 rosso. Il programma pu essere facilmente cambiato per
riconoscere altre frequenze e rappresenta una base su cui partire per applicazioni simili.
Per avere misure precise si fa uso del cristallo esterno da 32KHz, ma questo pone il vincolo di poter misurare
frequenze non superiori a 10KHz. Formalmente il valore massimo o limite potrebbe essere fino alla
frequenza di clock, ma, per avere una minima risoluzione, bene non eccedere con la frequenza in ingresso.
Infatti, la misura consiste nel misurare quanti periodi del clock fornito dal cristallo da 32KHz entrano nel
periodo del segnale che si sta campionando. Si capisce che con questo principio, maggiore la frequenza di
riferimento rispetto al periodo dell'onda quadra in ingresso, migliore sar la risoluzione con cui si effettua la
misura. Facendo uso del valore calibrato di frequenza del DCO, si possono avere errori dell'1%, che pu
arrivare fino al 3% considerando le possibili variazioni di temperatura e di tensione operative dell'MSP430
utilizzato. Per mantenere l'errore all'1%, comunque possibile effettuare una calibrazione della frequenza
62

Elementi di Programmazione 4

del DCO facendo uso del cristallo da 32KHz, in particolare la calibrazione permetterebbe di mitigare la
variazione del valore calibrato originale, visto che verrebbe eseguita al valore di tensione e temperatura
operativa effettiva. Con variazioni di temperatura di 5-10 gradi sempre raccomandabile una nuova
esecuzione del processo di calibrazione. Routine per effettuare la calibrazione del DCO facendo uso del
cristallo da 32KHz si possono in questo testo.
In ultimo si fa notare che la frequenza misurata con il metodo che segue, rappresenta una misura istantanea,
infatti si determina la frequenza in base ad un solo periodo del segnale in analisi, mentre la definizione di
frequenza sarebbe il numero di periodi del segnale che si ripetono in un secondo (unit di tempo), per cui,
per misure reali di frequenza, si dovrebbe fare una misura di 1s. Spesso, per velocizzare le misure, si fa uso
di sotto intervalli temporali come 100ms.
Il programma inizia col definire alcuni parametri ovvero valori di frequenza. I valori delle costanti fanno
riferimento al caso in cui la frequenza di riferimento sia 32KHz. In particolare un segnale da 100Hz ha un
periodo di 0.01s, per cui in questo intervallo sono presenti 328 periodi di clock generato a 32768Hz.
Il valore Frequency Accuracy serve per creare una piccola finestra all'interno della quale si considera il
segnale campionato ancora a 100Hz. Piuttosto che cambiare il valore della frequenza in pi punti del
programma, qualora si volesse rilevare frequenze diverse, il valore di riferimento posto all'interno della
costante FREQUENCY_THRESHOLD.
Dopo la definizione delle costanti, sono definite delle variabili globali. La necessit di avere delle variabili
globali discende dal fatto che sono utilizzate sia dalla ISR, che dal programma principale. Inoltre dal
momento che queste variabili possono essere variate da una ISR, sono dichiarate volatili, per cui il
compilatore non effettua nessuna ottimizzazione sulla gestione delle stesse.
La variabile measure_A rappresenta il valore del registro CC1 sul primo fronte di discesa. La variabile
measure_B rappresenta il valore di CC1 sul secondo fronte di discesa. Dalla differenza dei due valori si
calcola la frequenza, o meglio il periodo, dell'onda quadra in ingresso al modulo Capture e Compare, ovvero
i periodi del clock da 32768Hz contenuti all'interno del periodo del segnale osservato. La variabile state
una semplice variabile di stato per determinare se si deve effettuare il calcolo della frequenza o meno,
ovvero se entrambe le variabili measure_A e measure_B sono aggiornate.
Il programma continua in maniera molto simile all'Esempio 6, con la differenza che viene impostata anche la
P2, visto che il pin P2.1 viene attivato come input per il modulo CC1 del Timer1. Successivamente si imposta
il modulo relativo al Clock e il Timer0 come nell'Esempio 6 . Dopo l'inizializzazione del Timer0, si inizializza
il Timer1 impostandolo in Continuous Mode e con il clock da 32KHz. Il modulo Capture e Compare
utilizzato CC1 ed impostato in Capture Mode (il bit CAP posto ad 1). Si sarebbe potuto utilizzare anche
CC0 con il suo Interrupt Vector dedicato, ma per ragioni didattiche si scelto CC1, in maniera di mostrare
come, nel caso di Interrupt Vector condiviso, sia necessario controllare il bit dell'Interrupt e resettarlo. In
applicazioni in cui il tempo di esecuzione importante ed eventi si possono susseguire molto rapidamente,
preferibile resettare il flag dell'Interrupt subito dopo averlo individuato. In queste applicazioni si potrebbe
inoltre preferire CCR0 per la presenza del Vettore delle interruzioni dedicato, risparmiando di dover
controllare e resettare il Flag d'Interrupt.
Una volta effettuate le inizializzazioni dei moduli e attivate le interruzioni, il programma entra in un loop
infinito, interrotto solo dal verificarsi dei fronti di discesa nell'ingresso del modulo Capture e Compare. Al
verificarsi di una transizione valida del fronte, viene eseguita la relativa ISR, all'interno della quale viene
salvato il valore del registro CC1. In base al valore di state, si salva rispettivamente CCR1 in measure_A o
measure_B. Il valore di state viene controllato anche nel loop infinito del programma principale, in maniera
di verificare se le due variabili measure_A e measure_B possiedono entrambe un valore valido. Quando
questo si verifica, viene calcolato il valore di frequenza (o meglio periodo) e confrontato con il range
impostato dalle costanti dichiarate ad inizio programma. Se il valore rientra nella finestra impostata, viene
attivato il LED1 rosso. Uscendo dall'intervallo prestabilito, il LED viene spento. Il programma non effettua
controlli di validit sulle variabili measure_A e measure_B, in particolare se si dovessero verificare due o pi
overflow il valore non sarebbe valido, ma l'applicazione dell'esempio potrebbe riconoscere valori di
frequenze pari a 100Hz anche per frequenze minori. Questo si verifica in caso di disturbi pi lenti di 1Hz.
Altri controlli che potrebbero essere implementati potrebbero essere il calcolo di un valore medio o
l'inserimento di un time out nel quale si resettano i valori di measure_A, measure_B e state.
#include <msp430.h>
#define FREQUENCY_50_HZ 656
63

Elementi di Programmazione 4

#define FREQUENCY_100_HZ 328


#define FREQUENCY_ACCURACY 5
#define FREQUENCY_THRESHOLD FREQUENCY_100_HZ
volatile unsigned int measure_A = 0;
volatile unsigned int measure_B = 0;
volatile unsigned int frequency = 0;
volatile unsigned char state = 0;
void main(void)
{
WDTCTL = WDTPW | WDTHOLD;
// stop watchdog timer
P1OUT = 0x00;
// impostazioni porte I/O
P1DIR = BIT6+BIT0;
//

P1SEL |= BIT6;
//

P1SEL2 &= ~BIT6;


//

P2OUT = 0x00;
//

P2DIR = 0x00;
//

P2SEL |= BIT1 ;
//

P2SEL2 &= ~BIT1;


//

BCSCTL2 = SELM_0 | DIVM_0 | DIVS_0;


// impostazioni modulo Clock
BCSCTL1 |= XT2OFF | DIVA_0;
//

BCSCTL3 = XT2S_0 | LFXT1S_0 | XCAP_1; //

TA0CCTL0 = CM_0 | CCIS_0 | OUTMOD_0 ; // impostazioni Timer0


TA0CCR0 = 32768;
//

TA0CCTL1 = CM_0 | CCIS_0 | OUTMOD_3 ;


//

TA0CCR1 = 32000;
//

TA0CTL = TASSEL_1 | ID_0 | MC_1;


//

TA1CCTL1 = CM_2 | CCIS_0| CAP |CCIE;


// impostazioni Timer1
TA1CTL = TASSEL_1 | ID_0 | MC_2;
//

// **********applicazione principale ****************************************************


__bis_SR_register(GIE);
while (1)
{
// loop infinito
if (state == 0)
{
if (measure_B > measure_A )
{
frequency = measure_B - measure_A;
}
else
{
frequency = (0xFFFF-measure_A) + measure_B;
}
if ((frequency > (FREQUENCY_THRESHOLD -FREQUENCY_ACCURACY)) &&
(frequency < (FREQUENCY_THRESHOLD + FREQUENCY_ACCURACY))) {
P1OUT |= BIT0;
}
else
{
P1OUT &= ~BIT0;
}
}
}
}
// fine applicazione
// Timer1 CC1 Interrupt Service Routine (ISR)
#pragma vector=TIMER1_A1_VECTOR
__interrupt void TIMER1_A1_ISR(void)
{
if (TA1CCTL1 & CCIFG)
{
TA1CCTL1 &= ~CCIFG;
if (state == 0)
{
measure_A = TA1CCR1;
state++;
}
else
{
measure_B = TA1CCR1;
state = 0;
}
}
}

64

Periferiche Analogiche e Digitali 5

CAP. 5

PERIFERICHE ANALOGICHE E DIGITALI

COMPARATORI
Un comparatore si pu considerare come un convertitore analogico/digitale ad 1 bit.
Alcuni dispositivi, come lMSP430G2211 fornito con il LauchPad, hanno internamente (built-in) un
comparatore chiamato Comparator_A+ (CA+), ma lo contengono anche molti altri C compatibili con il
LaunchPad. Come si vede dallo schema, il modulo CA+ molto flessibile, sia perch molti GPIO pin possono
essere connessi ai suoi ingressi, sia perch include una molteplicit di tensioni di riferimento interne.
Come si ricorda, un comparatore ha due input, chiamati rispettivamente non invertente (V+) e
invertente (V-). Linput V+ pu essere connesso a tre differenti pin per segnali esterni, il V- a sette pin (di
cui due in comune con V+) e alle tensioni di riferimento. CA+ ha un output che pu essere letto via software,
o triggerare un interrupt di CA+ o di un timer. Tutte queste opzioni garantiscono una grande flessibilit e
utilit.
Osservando il diagramma dei pin (pinout diagram) del C, si vedono quali pin possono essere connessi al
comparatore, denominati CAx. Cos tre pin (CA0-2) possono essere connessi a V+, mentre sette pin (CA1-7)
possono essere connessi a V-, essendo CA1-2 in comune; si noti che P1.7, oltre a fornire linput CA7, pu
essere configurato per leggere o portare esternamente il valore di uscita del comparatore (CAOUT).

CONFIGURAZIONE DEI REGISTRI


Il modulo CA+ ha tre registri di configurazione: CACTL1, CACTL2 e CAPD.
I bit di CACTL1 sono cos ripartiti:
I bit CAREFx (4-5) e CARSEL (6) controllano le tensioni di riferimento interne. CAREFx ne seleziona il
valore, mentre CARSEL seleziona quale input sia connesso alla tensione di riferimento. Ci sono tre valori
possibili: 1/2 Vcc, 1/4 Vcc (accuratezza 1%), in cui Vcc=3.6V se alimentato via USB, e la tensione diretta di
un transistor (0.55 V), anche se meno accurata.
I bit CAIFG, CAIE e CAIES (0-2) controllano linterrupt. CAIFG il Flag, che si azzera automaticamente
quando linterrupt viene servito, CAIE attiva linterrupt, e CAIES seleziona se un interrupt triggerato
sulla transizione low-high (rising edge) o high-low (falling edge).
Il bit CAON (3) accende (1) o spegne (0) il comparatore.
Il bit CAEX (7) usato per scambiare tra di loro gli ingressi. Nel caso loutput va invertito. Questa
funzione utile per confrontare valori molto vicini, ma usata raramente.
I bit di CACTL2 sono cos ripartiti:

65

Periferiche Analogiche e Digitali 5

I bit P2CAx (2-6) sono usati per selezionare i pin connessi agli ingressi del comparatore. P2CA0 (2) e
P2CA4 (6) selezionano quelli connessi a V+, P2CA1-P2CA3 (3-5) quelli connessi a V- (ved. tabelle
datasheet).
Il bit CAF (1) collega loutput a un filtro RC per smorzare eventuali rapide oscillazioni, se gli input del
comparatore sono molto vicini. Non sempre necessario, ma pu essere opportuno usando segnali
variabili lentamente.
Il bit CAOUT (0) rappresenta loutput del comparatore, che pu essere solo letto.
Il bit CASHORT (7) collega insieme gli input del comparatore.

Lo scopo del registro CAPD, i cui bit CA0-CA7 controllano gli input del comparatore, quello di
disconnettere i circuiti digitali dai pin GIO usati come ingressi analogici.

ESEMPIO 1: CONFRONTO CON TENSIONE DI RIFERIMENTO


Come esempio di programmazione, si veda il programma in bcompG221.c, in cui si misura una tensione
analogica, e si fa lampeggiare il LED se la tensione superiore ad un riferimento, in questo caso 1/2 Vcc (1.8
V). Per fare questo il programma usa il Timer_A per far lampeggiare il LED, mentre CA+ usa un interrupt
per attivare o disattivare il lampeggio del LED.
CACTL1 = CAREF1 + CARSEL + CAIE;
// 0.5 Vcc ref on - pin, enable interrupts on rising edge
CACTL2 = P2CA4 + CAF;
// input CA1 on + pin, filter output
CAPD = AIN1;
// disable digital I/O on P1.1 (technically this step is redundant)
Le costanti possono essere trovati nellheader file del G2211.
CAREF1 seleziona 1/2 Vcc, e CARSEL collega la tensione di riferimento allinput V-. P2CA4 seleziona linput
CA1 (P1.1) come V+. AIN1 definite come BIT1 per disabilitare permanentemente i circuiti digitali su P1.1.
Questo passaggio non sarebbe essenziale, in quanto non si cambiano gli ingressi in questo programma?.
Dopo aver configurato le periferiche, il comparatore viene acceso da
CACTL1 |= CAON
e il chip entra in LPM0.
Il Timer_A triggera periodicamente un interrupt e fa commutare loutput P1.0 con il valore memorizzato
nella variabile flash. Quando CAOUT 0, nella flash c 0 e quindi il LED non si accende. Quando CAOUT
1, flash collegata a LED1 (definito con BIT0) che lampeggia.
LISR per CA+ la seguente:
#pragma vector = COMPARATORA_VECTOR
__interrupt void COMPA_ISR(void)
{
if ((CACTL2 & CAOUT)==0x01)
{
CACTL1 |= CAIES;
// value high, so watch for falling edge
flash = LED1;
}
// let LED flash
else
{
CACTL1 &= ~CAIES;
// value low, so watch for rising edge
flash = 0;
// turn LED off
P1OUT = 0;
}
}
LISR prima esamina il valore di CAOUT, se vale 1, allora V+ > VIl prossimo interrupt accade quando il valore scende nuovamente al di sotto del riferimento, per cui si
seleziona il falling edge, si assegna alla variabile flash un valore diverso da zero, che fa lampeggiare il LED.
Se CAOUT 0, allora V+ al di sotto del riferimento. Si seleziona il rising edge, flash posta a 0, il LED non
lampeggia, e P1OUT azzerato per assicurare che il LED sia spento.
Per testare il programma si pu collegare un potenziometro (> 1 k) tra Vcc e ground, e collegarne il pin
centrale (wiper) al pin P1.1 sul LaunchPad. Variare il potenziometro per vedere il LED che comincia a
lampeggiare, indicando che la tensione superiore a 1.8 V.

ESEMPIO 2: CAPACITANCE METER


La scarica di una capacit C attraverso una resistenza R segue la ben nota legge esponenziale V = V0 e t RC ,
in cui V0 il valore della tensione allistante di inizio della scarica. Detto questo, si pu immaginare di

66

Periferiche Analogiche e Digitali 5

misurare il valore della capacit, misurando quanto tempo impiega a scaricarsi fino ad un determinato
valore di tensione. In questo caso, se si suppone che la capacit venga inizialmente caricata a V0=Vcc, si vuole
misurare il tempo di scarica fino alla tensione 1/4 Vcc. Per risalire al valore della capacit, basta invertire la
precedente relazione

t
t
C=
=
R ln ( V0 V ) R ln 4

TA0.0

V+

V-

CAOUT

VCC

Si pu usare il CA+ per triggerare il timer, quando la scarica raggiunge un valore di riferimento. Ovviamente
laccuratezza della misura dipende anche dalla tolleranza della resistenza R (1%, 5%, 10%).
Lidea semplice, tuttavia la misura pu essere falsata dal tempo che intercorre tra linizio della scarica e
linizio del conteggio del timer, oppure tra il segnale di trigger del comparatore e la lettura del conteggio
(timer capture). Inoltre occorre determinare quali capacit si possono misurare con una certa configurazione.
Il primo problema si risolve usando la caratteristica capture del timer.
Si noti che una temporizzazione accurata richiede un clock calibrato, per cui in questo caso si usa il DCO
calibrato a 1 MHz.
Per prima cosa si collega il resistore a un output del timer (TA0.0). La giunzione tra resistore e capacit
collegata allinput (V+) del comparatore (V- alla tensione di riferimento 1/4 Vcc), e laltro terminale della
capacit a massa. Si inizia con il caricare la capacit a Vcc, impostando a 1 TA0.0, e aspettando un po di
tempo per farlo caricare. A questo punto TA0.0 azzerato collegandolo a massa al tempo specificato in
TACCR0. Quando la tensione sulla capacit scende sotto il valore di riferimento, loutput del comparatore
scende a 0, triggerando un segnale di capture al timer, che memorizza il conteggio in TACCR1. La differenza
tra i tempi di TACCR0 e TACCR1 una misura accurata del tempo di decadimento (decay time).
Ovviamente questo tempo non pu essere superiore a 216 s, altrimenti il timer inverte il conteggio.
// Comparator configuration
void CAinit(void)
{
CACTL1 = CARSEL + CAREF_1;
// 0.25 Vcc ref on - pin.
CACTL2 = P2CA4 + CAF;
// input CA1 on + pin, filter output.
CAPD = AIN1;
}
// disable digital I/O on P1.7 (technically this step is redundant)
Il codice imposta il comparatore in modo che associ il pin CA1 (connesso a P1.1 nel G2211) su V+,
impostando il registro P2CA4 (P2CA0 dovrebbe essere azzerato).
// Timer configuration
void TAinit(void)
{
TACTL = TASSEL_2 + ID_0 + MC_0;
// use SMCLK, no division, stopped mode
TACCTL0 = OUTMOD_1 + CCIE;
// TA0 sets VCTL at TACCR0
TACCTL1 = CCIS_1 + SCS + CAP + CCIE;
}
// TAinit
Il timer impostato senza attivare il conteggio. Assegnando OUTMOD_1 a TACCTL0, imposta loutput
TA0.0 quando il conteggio (TAR) raggiunge TACCR0, che di default vale 0. Abilitare gli interrupt permette
di segnalare eventuali overflow. Nel registro TACCTL1 si seleziona la modalit capture impostando il
registro CAP. Per sapere come connettere il comparatore, occorre guardare la tabella Timer_A2 Signal
Connections, che corrisponde ai dispositivi con il COMP_A+. Nella colonna dei segnali di ingresso si trova
CAOUT (interno) e si osserva il nome del modulo di input nella colonna successiva: CCI1B. I bit CCISx in
TACCTLx selezionano linput, e per questa famiglia devono essere assegnati a 0b01 per selezionare CCIxB.
Nellheader file si trova che si possono assegnare questi bit con la costante CCIS_1.
Si noti anche che, impostando SCS, si sincronizza la cattura con il clock del timer. Finch non parte il timer
non inizia la cattura. Il codice aspetta che lutilizzatore prema il pulsante connesso a P1.3. Cos facendo il C
esce da LPM0. In seguito il timer viene attivato. Quando inverte il conteggio la prima volta, viene posto a 1
TA0.0 (P1.5), caricando il condensatore. Per farlo caricare completamente, il codice aspetta 10 overflow, che
corrispondono a 655 ms. A questo punto il comparatore attivato, e il timer resetta TA0.0 al prossimo
overflow, che pertanto diventano 11. Si imposta il timer capture quando la tensione sulla capacit (P1.1/CA1)
scende sotto Vref (1/4 Vcc). A questo punto si triggera un interrupt. LISR disattiva la cattura ed il timer. Il
codice fa un loop che torna indietro e ricomincia il processo, aspettando che venga premuto il pulsante per
iniziare la misura.
Il codice completo si trova in CMeterG2211.c (affinch il codice operi correttamente occorre rimuovere il
jumper TXD). Si pu usare un resistore da 10 k e una capacit da 100 nF. Il LED dovrebbe diventare da
67

Periferiche Analogiche e Digitali 5

verde a rosso e poi nuovamente verde, indicando che la misura terminata. Il timer ha catturato levento e
lha memorizzato in TACCR1. Per vederlo si mette in pausa il debugger, e si esamina i registri del timer_a2.
Si dovrebbe trovare un valore intorno a 1400 (si pu cambiare da esadecimale a decimale nel men del
registro col tasto destro del mouse).
Nel prossimo paragrafo si far in modo di visualizzare la misura del tempo in microsecondi sul display
LCD. Conoscendo il valore di R, si pu calcolare il valore di C dalla formula riportata allinizio del
paragrafo.
Sarebbe tuttavia pi utile visualizzare direttamente il valore misurato di C. Si possono effettuare operazioni
in virgola mobile con lMSP430, sebbene con scarsa efficienza, ma occorre visualizzare un numero in virgola
mobile su un display LCD. Listruzione sprintf supererebbe il limite di spazio di 2 kB permesso dal G2211.
Una modalit dimostrata nel codice CMeterLCMFull.c, che occupa 1934 byte, ed ha anche il beneficio di
regolare automaticamente il range di valori.

DISPLAY LCD
Per spiegare luso di un modulo LCD, di seguito indicato come LCM, stato scelto un display alfanumerico
con uno standard 16x2 come quelli della SparkFun. Si badi di sceglierne uno che funzioni a 3.3 V, e non sia
stato modificato per accettare input seriali.
I moduli LCM della SparkFun utilizzano un chip ST7066 di interfaccia, basato sullinterfaccia HD44780, che
usa una trasmissione parallela a 8 bit, pi 3 bit di controllo, per trasferire dati verso/da il display. In totale
servirebbero quindi 11 bit, che sono troppi per il G2211, anche non usando loscillatore esterno (rendendo
disponibili P2.5 e P2.6), perch in totale si hanno soltanto 10 pin GPIO disponibili. Per fortuna linterfaccia
HD44780 (e cos anche ST7066) prevedono la possibilit di inviare i dati in due blocchi di 4 bit ciascuno, per
cui, anche usando un oscillatore al quarzo, sono sufficienti gli 8 pin della porta P1 per le due periferiche, il
comparatore e LCM.
Il modulo standard LCM ha 16 pin (14 senza la retroilluminazione). Il pin 1 (Vss) va connesso al ground del
LaunchPad, il pin 2 (Vdd) a Vcc. Se si usa la retroilluminazione, il pin 15 (LED+) pure connesso a Vcc, e il
pin 16 (LED-) a massa. Il pin 3 (Vo) controlla il contrasto, e si pu collegare al centrale di un potenziometro
da 10k, di cui gli altri terminali sono a Vcc e ground, per regolarlo manualmente, altrimenti questo pin pu
essere collegato al ground, anche se laspetto non sar eccellente. I pin di controllo sono il 4 (Register Select,
RS), 5 (Read/Write, RW) e 6 (Enable, E). Il pin 5 pu essere collegato a massa, risparmiando un pin, perch in
questo caso non si ha necessit di leggere nulla da LCM, che quindi sempre in modalit Write. Infine i pin
7-14 sono le linee dati D0-D7. Poich i pin non sono sufficienti per collegare D0-D7 a P1, si usa la modalit a
4 bit con i pin D4-D7, lasciando disconnessi D0-D3.
Per il capacitance meter si deve cambiare la configurazione dei pin per adattare lLCM, usando P1.1 per
TA0.0 anzich CA1, e P1.2/CA2 come input V+ del comparatore. P1.0 controller RS, P1.3 controller E, e
P1.4-7 controller D4-7. Si noti che P1.3 anche connesso al pulsante con una pull-up, che causer E basso.
Occorre rimuovere i jumper per i due LED e sui pin TXD/RXD.
Per inviare comandi o caratteri a LCM, basta mandare un impulso su E, e mandare unistruzione sui pin dati
sul falling edge di E. Listruzione invia un comando se RS basso, un carattere se RS alto. Come esempio si
descrivono i comandi per impostare LCM in modalit a 4 bit. Listruzione 0b001nnnxx detta Function Set
(i bit n selezionano la modalit, i bit x non sono usati e possono essere 0 o 1). Il bit 4 seleziona la modalit di
interfaccia (18 bit, 04 bit). Prima si imposta la linea dati con
P1OUT |=0x20
// 0b00100000
che imposta anche RS basso (command mode) ed E basso, poi manda il comando con un impulso su E.
LCM non risponde istantaneamente al comando, perch RS deve essere impostato basso da un po di tempo
prima di mandare un impulso su E. La linea dati deve essere impostata da un po di tempo prima del falling
edge su E e deve rimanere tale anche dopo limpulso. In pratica tra due comandi devono passare circa 150
ms. Ricapitolando, queste sono le istruzioni:
__delay_cycles(10000); // wait for the LCM to settle on power-up
P1OUT |= 0x20;
// set to 4-bit instructions
P1OUT |= BIT3;
// E high
__delay_cycles(200);
68

Periferiche Analogiche e Digitali 5

P1OUT &= ~BIT3;


// E low
__delay_cycles(200);
P1OUT &= 0x0F;
// clear the upper 4 bits
Sebbene E possa essere impostato alto prima di impostare le linee dati, conveniente invertire lordine per
evitare incongruenze temporali. Lultima istruzione azzera la linea dati per prepararla al prossimo comando.
Ora LCM pronto per ricevere comandi a 4 bit. Questa modalit funziona mandando i 4 bit (nibble) pi
significativi con un impulso su E, e poi gli altri 4 bit con un secondo impulso:
P1OUT |= ( & 0xF0);
// send upper nibble
pulse();
P1OUT &= 0x0F;
// clear
P1OUT |= (( & 0x0F;) << 4);
// send lower nibble
pulse();
P1OUT &= 0x0F;
// clear
Qui si assunto di aver raggruppato i comandi per mandare limpulso su E, con i relativi ritardi, nella
funzione void pulse(void). Se si raggruppa il codice precedente nella funzione void SendByte(char), allora si
possono inviare i prossimi comandi di inizializzazione nel modo seguente:
SendByte(0x28);
// Function Set 4-bit, 2-line mode (for 2-line displays, of course)
SendByte(0x0E);
// Display on, underline cursor on, non-blinking
SendByte(0x06);
// Character entry mode: increment address, no display shift
Dopo aver mandato questi comandi, LCM pronto a visualizzare qualunque carattere si vuole mandare. Si
noti che, per mandare caratteri, i comandi sono simili, ma P1OUT deve anche impostare BIT0 (RS), per dire a
LCM di ricevere istruzioni di caratteri anzich comandi. Si veda il programma completo in lcddemoG2211.c

VISUALIZZARE UN VALORE INTERO


In C per visualizzare un valore intero si pu usare la libreria stdio e la funzione sprintf(); basta creare un array
di caratteri come print_time[10] e usare listruzione
sprintf(print_time, "%d", time)
mettere un intero dentro la stringa print_time e passarlo a PrintStr().
Sfortunatamente questo metodo presenta molti problemi per essere usato con i C. Prima di tutto ogni
codice che usi la funzione printf sar troppo largo. Nel programma LCD si supererebbero i 2kB di spazio
disponibile per il dispositivo, inoltre lo streamlining renderebbe difficile formattarlo correttamente. Una
specifica di formato del tipo %10d, per formattare il tempo in 10 cifre non implementata. Cambiare le
propriet di printf nel progetto aumenterebbe ancora di pi loccupazione di codice, oltre i 2 kB consentiti dal
dispositivo. Una soluzione consiste nellestrarre i singoli bit usando loperatore mod e la divisione tra interi.
Listruzione
x%10; restituisce lultimo bit del numero memorizzato in x.
x/=10; rimuove lultimo bit
e lascia per ultimo il secondo. Creando un loop sul numero fino a raggiungere la condizione x==0 (non ci
sono pi bit), si pu estrarre un bit alla volta da visualizzare. Il codice LCM fatto in modo che il pacchetto
di 4 bit meno significativi (lower nibble) corrisponde esattamente al valore della cifra digitale, cos 0x30+0
0, 0x30+7 7 e cos via. Lo svantaggio di questa tecnica che le cifre digitali sono estratte in ordine
inverso, da destra a sinistra. Fortunatamente LCM ha la possibilit di inviare i caratteri da destra a sinistra.

CREAZIONE DI UNA LIBRERIA


Per inviare testo ad un LCM potrebbe essere utile avere una libreria che pu essere chiamata, senza dover
copiare/incollare codice ogni volta.
I passi da compiere sono sinteticamente i seguenti:
1. creare un directory (folder) in cui salvare le nuove librerie, per esempio sotto la directory workpsace;
2. copiare ogni #include, #define, funzioni e variabili globali in un nuovo header file. Un esempio di
libreria mostrato in simple_LCM.h. Se si usano definizioni specifiche per gli MSP430, bisogna
includere lheader generale dellMSP430 (#include <msp430.h>);
3. copiare il rimanente codice (funzioni contenute nel codice) in un nuovo file .c con lo stesso nome
(simple_LCM.c in questo caso). In cima al file bisogna aggiungere #include<filename.h> con il nome del
file library al posto di filename. Si noti che questo file non deve avere una funzione main allinterno;

69

Periferiche Analogiche e Digitali 5

nel nuovo progetto, cliccare col tasto destro sulla directory di progetto e selezionare newfolder. Cliccare
il pulsante [Advanced >>], e selezionare Link to folder in the file system. Ora si pu andare alla
propria directory di libreria e aggiungere la directory. Ogni file nella directory di libreria disponibile
per luso nel codice. Il compilatore, tuttavia, deve conoscere il path di questa directory per trovarla;
5. cliccare col tasto destro del mouse sulla directory e selezionare le propriet. Aprire la finestra Build
C/C++, e nei Tool Settings cercare MSP430 Compiler Include Options, cos come MSP430 Linker
File Search Path: ad entrambi va aggiunta alla lista la directory di libreria, per poter compilare il codice.
Ora si pu costruire il Capacitance Meter usando lLCM. Il codice scritto in CMeterLCMG2211.c illustra
nuovi modi di usare lLCM.
4.

CONVERTITORI ANALOGICO - DIGITALI


Gli MSP430 contengono un convertitore analogico-digitale a 10 bit, denominato ADC10, che di tipo SAR
(Successive-Approximation-Register). LADC10 pu effettuare misure fino a 2105 campioni/secondo (200 ksps).
Per utilizzare lADC10 bisogna montare sulla scheda il chip G2231, perch il G2211 non lo contiene.
LADC10 richiede ovviamente un clock, che pu essere un quarzo, il DCO o il VLO. Inoltre anche lADC10
contiene un clock, che pu essere usato indipendentemente da quello di sistema. Questo clock lavora
tipicamente intorno a 5 MHz, ma non calibrato, pertanto pu variare da un chip allaltro, con la tensione
operativa e la temperatura. Il vantaggio maggiore di questo clock che pu rimanere attivo anche quando
gli altri clock sono disattivati da una modalit low-power (LPM).
LADC10 ammette fino a 16 differenti input. Tipicamente, 8 di questi sono input esterni, 4 sono interni, e 4
sono altre tensioni di riferimento (ma su alcuni dispositivi possono diventare altri 4 input esterni, per un
totale di 12).
Gli G2231 hanno 8 input esterni (P1), ed hanno anche un sensore di temperatura incluso (built-in) nel chip,
come input interno (insieme agli altri 3 che devono fare confronti con tensioni di riferimento).
LADC10 richiede due tensioni di riferimento, che rappresentano il limite inferiore (tra 0 e 1.2 V) e superiore
(tra 1.4 V e Vcc, max 3.6 V) di conversione. Ci sono due tensioni di riferimento disponibili nellADC10, 1.5 V
e 2.5 V, e si pu usare anche Vcc, oltre a tensioni di riferimento esterne.
Infine lADC10 ha 4 modalit di funzionamento, 2 denominate single channel e repeat single channel, in cui
viene campionato un solo canale, e 2 denominate sequence channel, che campionano in sequenza e
ciclicamente un gruppo specifico di input tra 16 possibili. Ogni campionamento singolo o sequenziale pu
essere fatto una volta sola o ripetuto. In modalit sequenziale si possono usare gli input secondo un ordine
stabilito, per esempio tre input sono necessariamente A0, A1 e A2. Purtroppo lunico modo di campionare
input arbitrari di usare la modalit single channel, e cambiare canale con il software.
Come esempio di utilizzo di un ADC10 si vuole trasformare il progetto di un Capacitance Meter in un
Voltage Meter, sebbene potr misurare soltanto tensioni comprese fra 0 e 3.3 V. Il risultato verr visualizzato
su un LCD, ma si potr anche mettere in pausa il codice per leggere il risultato nei registri, nel caso non si
abbia un LCD. Si user la modalit single channel, ripetendo la misura con il software, perch pi facile
usare il debugger per vedere il risultato.
Nella modalit repeat single channel la periferica viene attivata e abilitata. LADC10 triggerato per iniziare
la conversione, che una volta completata viene memorizzata. Se si usano gli interrupt, il flag attivato, e
lADC10 ritorna ad uno fra tre passi, a seconda di come impostato. Tutto ci avviene allinterno
dellADC10, lasciando libero lMSP430 di compiere altre operazioni se necessario. Dopo che i campioni sono
acquisiti, si possono usare gli interrupt dellADC10 per comunicare con il codice.
Il codice dellesempio usa invece la modalit single channel, che molto simile ma senza la ripetizione,
perch, non usando le modalit low power, pi facile coordinare la temporizzazione, in modo che un
campione non acquisito finch lLCD non aggiornato. Mentre in corso una nuova conversione, il codice
rimane in un loop, prima di scrivere il campione misurato sul display LCD, dopodich pu cominciare una
nuova conversione per aggiornare la misura.

70

Periferiche Analogiche e Digitali 5

CONFIGURAZIONE DEI REGISTRI


Ci sono 8 registri associati con lADC10, di cui 4 sono usati per configurare la periferica. Uno usato per
memorizzare i singoli campioni, e tre sono usati per controllare il trasferimento in memoria del dato
campionato. Due dei registri sono usati soltanto per configurare gli input. ADC10AE0 abilita il
funzionamento dei pin esterni. necessario perch P1SEL, essendo un bit, pu solo selezionare due
modalit, che non includono lADC. ADC10AE1 fa qualcosa di simile, ma solo per dispositivi con pi di 8
input.
ADCCTL0 gestisce la configurazione di base della periferica: le tensioni di riferimento, il tempo e la
frequenza di campionamento, lalimentazione e gli interrupt dellADC. In particolare:
SREFx (bit 15-13) seleziona una tra 8 differenti configurazioni per i limiti di tensione inferiore e
superiore.
SHTx (bit 12-11) seleziona il tempo di campionamento tra 4 possibili, cio 4, 8, 16 o 64 cicli di clock
dellADC10CLK.
REF2_5V, REFON (bit 6,5) seleziona una delle tensioni di riferimento 1.5V e 2.5V, e commuta on/off la
tensione di riferimento.
ADC10ON, ENC, ADC10SC (bit 4,1,0) accende lADC, abilita la conversione e inizia la conversione,
rispettivamente.
ADC10IE, ADC10IFG (bits 3,2) abilita gli interrupt e attiva il flag.
ADC10CTL1 controlla gli input, il clock, la modalit di campionamento, ed il formato dei dati.
INCHx (bit 15-12) seleziona il canale da campionare in modalit singola, e il primo canale da campionare
in modalit sequenziale.
ADC10DF (bit 9) seleziona tra il formato binario semplice e in complemento a 2.
SSELx, DIVx (bit 4-3,7-5) scelgono la sorgente del clock, e dividono la frequenza per un fattore da 1 a 8.
CONSEQx (bit 2-1) seleziona la modalit sequenziale.
BUSY (bit 0) un flag solo di lettura, che indica quando lADC nel mezzo di un ciclo di
campionamento/conversione.
Il registro ADC10MEM memorizza il valore della conversione, da cui pu essere letto (0x00 corrisponde alla
tensione di riferimento inferiore, 0xFF a quella superiore, gli altri sono i valori intermedi). Se ADC10DF
attivo (1), il valore memorizzato in complemento a 2.
Il codice relativo ad un Voltage Meter si trova in VMeterG2231.c. Richiede la libreria simple_LCM. Usa A1 su
P1.1. Si pu testare il codice usando un potenziometro connesso tra Vcc e massa, connettendo il centrale
(wiper) a P1.1.
Esercizio: il codice si pu modificare per misurare tensioni con range pi esteso, usando solo due
componenti passivi. Si possono anche misurare tensioni sia positive che negative usando un operazionale
(level shifter).

MEMORIA FLASH
TECNOLOGIE FLASH
La memoria Flash allinterno degli MSP430 chiamata NOR flash, per distinguerla dalla NAND flash che si
trova in altri dispositivi (es. pen drive). Funziona intrappolando carica in una regione isolata chiamata
floating gate. La carica nel floating gate cambia la soglia del transistor, e determina il valore letto dal bit.
Agendo in maniera simile ad uno switch, una carica positiva fa in modo che lo switch si chiuda, e si legge un
livello logico 1, mentre una carica negativa mantiene lo switch aperto, e si legge un livello logico 0.
Quando si cancella una memoria flash, ogni bit posto in uno stato per cui il floating gate carico
positivamente, per cui da una cella vuota (blank) si legge un valore 1, un byte un valore 0xFF. Si pu da
programma impostare un bit a 0, applicando una tensione alta al controllo di gate, che permette al floating
gate di attirare carica negativa attraverso il drain, che viene intrappolata (a 25C, il tempo di vita di un bit in
una flash di tipo NOR dellordine di 1000 anni). Il Flash controller ha la capacit di generare queste
tensioni finch la tensione operativa del C almeno 2.2 V.
La memoria Flash degli MSP430 organizzata in sezioni chiamati segmenti. La memoria principale divisa
in segmenti di 512 byte. Ci significa che ci sono 4 segmenti nella memoria principale dei G2211 e G2231, che

71

Periferiche Analogiche e Digitali 5

hanno 2 kB disponibili. Ogni segmento ulteriormente diviso in blocchi di 64 byte. Altri 512 byte sono
inclusi in ogni MSP430, divisi in segmenti sia di 64 che 128 byte, rispettivamente composti da 1 o 2 blocchi.
Questi ultimi fanno parte della Information Memory del C. I C della Value Line nel LaunchPad hanno 4
segmenti di information memory di 64 byte ciascuno, chiamati Segment A-D.
La struttura fisica delle celle di memoria flash importante per capire come gestirle. Prima di tutto, quando
si cancella una memoria flash, si pu cancellare solo un segmento alla volta. Questo significa che ogni volta
che dobbiamo cambiare un bit di una singola cella da 0 a 1, ogni cella del segmento deve essere cancellata.
Quando si programma una flash, la tensione alta applicata nello stesso tempo allintero blocco, anche se si
deve programmare solo una cella. Questa tensione causa uno stress delle celle flash, per cui non si pu
superare uno specifico tempo di programmazione cumulativo, tipicamente di 10 ms. Ogni volta che si
cancella, il tempo cumulativo si azzera di nuovo. Questi fattori influenzano la velocit di programmazione, e
la frequenza con cui i blocchi devono essere cancellati. Alla velocit tipica che si usa (tra 257 e 476 kHz), si
pu programmare un intero blocco due volte. Programmare in questo caso significa cambiare gli 1 in 0,
mentre per cambiare gli 0 in 1 bisogna cancellare lintero segmento. Non si pu, tuttavia, programmare lo
stesso byte senza programmare ogni altro byte del blocco. La regola pratica (rule of thumb) che si raggiunge
un tempo di programmazione di 10 ms o si scrive lo stesso byte due volte, il blocco (e lintero segmento per
la memoria principale) deve essere cancellato. Questo un vero problema, specialmente se si vogliono
conservare in memoria dei dati, per esempio i valori di calibrazione di un DCO. A causa di questo, si
raccomanda di usare i segmenti dellInformation Memory per memorizzare tutto ci che si vuole mantenere
fuori del programma nel C. Il processo che si user quello di trasferire lintero contenuto di un blocco in
un buffer (RAM), cancellare lInformation Segment, cambiare quello che necessario nel buffer, e riscrivere il
contenuto del buffer nel segmento. Sebbene possa sembrare complicato, esattamente quello che succede in
un PC quando si usa una flash drive.

A NOR flash bit is a transistor with a little pocket for storing charge.

INFORMATION MEMORY
I segmenti B-C-D dellInformation Memory hanno uguale dignit, mentre il segmento A (SegA) deputato
dalla TI per immagazzinare informazioni che devono essere conservate indipendentemente da qualunque
variazione nel programma, come ad esempio i fattori di calibrazione. Di conseguenza questo segmento
bloccato, e occorre impostare un bit particolare, prima di ogni istruzione di cancellazione o programmazione
in questo segmento. Inoltre, per garantire lintegrit dei dati memorizzati in SegA, una parola (2 byte) di esso
dedicata al checksum (letteralmente verifica di somma), una sorta di controllo di errore basato sulla
verifica della somma dei bit di un dato trasmesso o memorizzato.
Partendo da un qualunque programma, si pu entrare nella modalit debug, per vedere il contenuto della
memoria su un pannello a destra (se non visibile basta selezionare WindowShowViewMemory).
LInformation Memory allocata dallindirizzo 0x1000 a 0x10FF. Il SegA comincia a 0x10C0 e finisce a
0x10FF. Da questa finestra si pu anche salvare una particolare regione di memoria su un file testo: si clicca il
pulsante Save, si sceglie nome e directory del file, si inserisce lindirizzo iniziale e la lunghezza (in parole)
della regione (un segmento di 64 byte composto di 32 parole, ovvero 0x20).
La maggior parte della memoria in SegA vuota (blank), infatti contiene il dato 0xFFFF (una cella di flash
cancellata ha valore 1).
Il formato usato da TI per memorizzare informazione nel SegA denominato TLV (Tag-Length-Value). La
Tabella ne d un esempio. La prima parola del segmento (0x10C0 e 0x10C1) memorizza il checksum del resto
del segmento (da 0x10C2 a 0x10FF) specificato come complemento a 2 di unoperazione XOR bitwise del
contenuto delle celle. Il resto del segmento suddiviso in regioni occupate da dati di uno stesso tipo; la
prima parola della regione dedicata a specificare la lunghezza di memoria (upper byte) allocata dal tipo di
72

Periferiche Analogiche e Digitali 5

dato come numero di celle (byte), e quale tipo di dato (lower byte) vi memorizzato, secondo una codifica per
tipi, ovvero celle vuote (0xFE), dati di calibrazione di ADC (0x08), e DCO (0x01). Per calcolare il checksum si
comincia dalla seconda parola e si fa uno XOR (bitwise) con la terza parola, il risultato con la quarta e cos
via, alla fine, sommando il risultato con il checksum (in complemento a 2) deve risultare zero. Si noti che il
checksum di celle vuote (0xFFFF) in numero pari vale 0x0000, e poich x^0=x, questa operazione si pu
omettere nel calcolo. Lalgoritmo potrebbe essere:
int chksum = 0;
char passed;
int *i;
for (i=(int *)0x10C2; i<(int *)0x1100; i++) {
chksum ^= *i;
}
if (chksum + *(int *)0x10C0 == 0)
passed = 1;
else
passed = 0;
Address

S
e
g
m
e
n
t
A

0x10C0
0x10C2
0x10C4

0x10D8
0x10DA
0x10DC

0x10EA
0x10EC
0x10EE

0x10F4
0x10F6
0x10F8

0x10FE

Value
Upper Byte
Lower Byte
~chksum+1
0x16 (length)
0xFE (type)

Type
checksum
TAG_EMPTY

0xFFFF

0xFFFF

blank cells

0x10 (length)

0x08 (type)

TAG_ADC12_1

<CAL values>

<CAL values>

0x08 (length)

0xFE (type)

TAG_EMPTY

0xFFFF

0xFFFF

blank cells

0x08 (length)

0x01 (type)

TAG_DCO_30

<CAL values>

<CAL values>

PROGRAMMARE LA FLASH MEMORY


Si supponga di aver trovato una frequenza di calibrazione non standard del DCO di 7.3278 MHz con un
quarzo, e di volere memorizzare il valore dei registri del DCO nel segmento B, usando comunque lo
standard TLV (si potrebbe memorizzare anche nel SegA, ma essendo una frequenza non standard per
sicurezza preferibile il SegB). Il SegB ha un range di indirizzi da 0x1080 a 0x10BF. Il calcolo del checksum:
chksum = 0x38FE ^ {0xFFFF^0xFFFF^0xFFFF} ^ 0x0201 ^ {calibration values}
= 0x38FE ^ 0x0201 ^ {calibration values} = 0x3AFF ^ {calibration values}
Il valore da memorizzare nella cella 0x1080 quindi ~chksum+1.
Il passo successivo quello di cancellare il SegB, e scrivere i valori nelle celle come in tabella.
S
e
g
m
e
n
t

Address
0x1080
0x1082
0x1084

0x10BA
0x10BC

Value
~chksum+1
0x38FE (length 0x38=56 celle, type 0xFE)
0xFFFF

0xFFFF
0x0201 (length 0x02, type 0x01)

0x10BE

0x{CALBC1}{CALDCO}

Type
Checksum
TAG_EMPTY
0xFFFF

0xFFFF
1 DCO entry

Quando si richiamano i valori di calibrazione CALDCO_1MHZ e CALBC1_1MHZ nel codice dellMSP430,


questi sono in realt i puntatori agli indirizzi di memoria nel SegA dove i valori sono memorizzati. Non
facile trovare il valore di tali indirizzi, in quanto sono allocati nel command file del linker, specifico di ogni
C. Il file si trova in (non modificare questo file!):
CCSinstallDir/Texas Instruments/ccsv4/msp430/include/msp430g2231.cmd
Negli MSP430, una variabile intera occupa 16 bit, mentre una variabile carattere 8 bit. Quando occorre
riferirsi ad una specifica parola, si usa un puntatore di tipo intero. Se occorre riferirsi ad uno specifico byte,
come i valori di calibrazione del DCO, si usa un puntatore di tipo char.

73

Periferiche Analogiche e Digitali 5

Per assegnare uno specifico indirizzo a un puntatore, piuttosto che riferirsi allindirizzo di una variabile, si
usano le forme letterali:
(int *)0xAB3C
(char *)0x1AB4
Pertanto per assegnare un indirizzo nel SegB ai valori di calibrazione del DCO si usano le istruzioni:
char *CALBC1_UART = (char *) 0x10BE;
char *CALDCO_UART = (char *) 0x10BF;
altrimenti, se non si vuole usare memoria, si definisce nellheader file:
#define CALBC1_UART *(char *) 0x10BE
#define CALDCO_UART *(char *) 0x10BF

FLASH MEMORY CONTROLLER


Serve a gestire i trasferimenti da/verso la memoria flash. Poich pu essere pericoloso accedere direttamente
alla memoria, necessaria una chiave per accedere ad ognuno dei suoi 4 registri. Il valore della chiave
memorizzato nella costante FWKEY. La configurazione dei registri la seguente:
FCTL1
FWKEY (bit 15-8) memorizza il valore 0x96.
BLKWRT (bit 7) bit di controllo per bloccare la modalit write.
WRT (bit 6) abilita la modalit write, necessario prima di scrivere.
MERAS & ERASE (bit 2 e 1) controllano la modalit di cancellazione. Si pu cancellare un singolo
segmento, lintera memoria principale, o la memoria principale insieme ai segmenti di informazione
(dipende se SegA sbloccato).
FCTL2
FSSELx (bit 7-6) selezionano quale clock usare per la programmazione.
FNx (bit 5-0) permettono di divider la frequenza del clock sorgente per un fattore fino a 64 (si divide per
FNx+1)
FCTL3
FAIL (bit 7) quando attivo potrebbe significare che la sorgente del clock non funziona per qualche
motivo. Se accade deve essere resettata dal software prima di poter fare qualunque altra cosa.
LOCKA (bit 6) controlla se SegA pu essere cancellato/scritto (vedere nel caso la Users Guide)
LOCK (bit 4) quando =1 blocca la cancellazione/scrittura nella flash memory.
WAIT (bit 3) indica quando incorso una cancellazione/scrittura nella flash memory.
FCTL4 non presente in tutti i dispositivi.
Per operare sullInformation Segment, occorre prima impostare il clock per il controller. La frequenza reale
non importante, purch sia compresa tra 257 e 476 kHz. Limpostazione di default per il DCO dellMSP430
circa 1.1 MHz, per cui una divisione per 3 (FNx=2) funziona bene, fornendo circa 370 kHz. Nellesempio il
DCO verr calibrato a 7.378 MHz, per cui una divisione per 20 (FNx=19) dar circa lo stesso risultato.
Se occorre memorizzare qualcosa nel segmento, allora necessario salvare lintero contenuto in un buffer.
Probabilmente il SegB vuoto (blank), per cui si potrebbe procedere. In seguito se imposta il controller in
modalit ERASE. Non si vuole cancellare lintera memoria, ma solo alcuni bit. Si azzera il bit LOCK. Adesso
si inizia il ciclo di cancellazione scrivendo in ogni indirizzo del segment. Occorre avere il puntatore di
indirizzi che punta al segmento che si vuole realmente cancellare. Una volta cancellato, si riconfigura il
controller in WRITE mode, e si procede a scrivere i valori che servono, sia come byte che come word. Se i
contenuti precedenti sono stati salvati, un metodo migliore cambiare i contenuti con i nuovi valori e usare
il block write mode. Infine, quando ogni cosa scritta, si disattiva il bit write mode e di blocca la memoria.
Il programma uartcalG2231.c descrive la calibrazione del DCO per trasmissione con la periferica UART
(Universal Asynchronous Receiver Transmitter), e la salva nel Segmento B della flash usando il formato TI-TLV
(tag-length-value). Questo codice richiede loscillatore al quarzo. Se c gi qualcosa memorizzato in SegB,
sar perduto. Se si vuole memorizzare nel Segmento C (0x1040 to 0x107F) o D (0x1000 to 0x103F), basta
cambiare gli indirizzi nelle istruzioni di assegnazione. Se si sceglie di usare il Segmento A, vedere le
istruzioni nella Users Guide per sbloccarlo, anche se non raccomandabile, poich si sta calibrando una
frequenza non standard del DCO. In caso dellesempio lindirizzo 0x10BE per il valore CALBC1, e 0x10BF
per CALDCO.

74

Comunicazione Seriale 6

CAP. 6

COMUNICAZIONE SERIALE

INTRODUZIONE
Per estendere la gamma di esperimenti che si possono realizzare con lMSP430, pu servire connettere il C
ad una periferica con interfaccia digitale di tipo seriale (es. sensore), da cui acquisire nel tempo una notevole
quantit di dati, da memorizzare nella EEPROM interna se sufficiente, oppure trasferire in una EEPROM
esterna collegata attraverso una linea seriale sulla stessa board del C, e successivamente, terminata
lacquisizione, elaborare o trasferire ad ununit remota (es. PC), tramite una linea seriale di tipo USB o RS232. In tutti questi casi serve impostare e attivare un canale di comunicazione seriale.
Una prima classificazione tra sistemi di comunicazioni sincroni e asincroni, che possono essere
implementati in maniera hardware, grazie ad appositi moduli di interfaccia, insieme a semplici programmi
software di gestione dei dati, o, in assenza di hardware, anche totalmente via software, con una tecnica
denominata bit banging.
La comunicazione seriale asincrona viene usata per fare comunicare dispositivi distanti fra loro, per cui
trasmettitore e ricevitore hanno ciascuno il proprio clock, che non possono essere sincronizzati, tuttavia
devono essere molto accurati, per avere esattamente la stessa frequenza di trasmissione, stabilita prima di
cominciare la comunicazione. Se la frequenza non esattamente la stessa, si pu avere ripetizione o perdita
di un dato. Inoltre, poich la trasmissione avviene tra dispositivi lontani, occorre proteggere il canale di
comunicazione da interferenze elettromagnetiche, tramite luso di cavi schermati.
Un protocollo per la comunicazione asincrona la UART (Universal Asynchronous Receiver/Transmitter), che
pu essere implementato totalmente via software, oppure controllato da un interfaccia hardware.
I sistemi di trasmissione seriale di tipo sincrono hanno risolto il problema del sincronismo dei clock,
semplicemente usando lo stesso clock per trasmettitore e ricevitore. Ci implica tuttavia che una linea di
clock sia inviata in parallelo ai dati, ma, affinch non si accumulino ritardi rilevanti fra trasmettitore e
ricevitore, i dispositivi devono essere molto vicini, in pratica sulla stessa board (PCB).
I protocolli di trasmissione sincrona che verranno esaminati sono la SPI (Serial Peripheral Interface) e la IC (o
I2C Inter-integrated Circuit Bus). La differenza che la SPI un sistema di comunicazione master-slave di tipo
punto-punto, con linee separate per ricezione e trasmissione, mentre la IC un vero e proprio bus che pu
collegare un master a diversi slave con ununica linea.
I sistemi di comunicazione seriale possono essere implementati utilizzando moduli hardware di interfaccia,
oppure totalmente via software, sebbene questa modalit si utilizza in pratica solo per la UART, perch
risulterebbe assai onerosa per la SPI/IC. Un modulo hardware, ormai superato, la USART (Universal
Synchronous/Asynchronous Receiver/Transmitter), usato soprattutto per la comunicazione asincrona (RS-232),
mentre per quella sincrona (SPI/IC) si usa la USI (Universal Serial Interface), oggi sostituite dalla pi moderna
interfaccia USCI (Universal Serial Communication Interface), che ha due canali separati, uno per quella
asincrona (USCI_A), laltro per quella sincrona (USCI_B).
Se per qualche motivo occorre usare un C che non ha un modulo hardware per gestire la comunicazione
sincrona/asincrona, linterfaccia di comunicazione si pu emulare con il software usando il Timer, con
tecniche denominate bit-banging. Questo verr fatto nel caso della UART, ma non verr ripetuto per i
sistemi sincroni, poich la maggior parte dei dispositivi ha uninterfaccia hardware, che semplifica molto la
progettazione di un canale di comunicazione.
COMUNICAZIONE SERIALE ASINCRONA
Per capire il meccanismo di comunicazione asincrona, si pu immaginare che due persone si mettano
daccordo affinch, in uno specifico istante, uno invii un messaggio allaltro, una lettera alla volta, ad
intervalli di tempo regolari. Se listante di inizio messaggio pu essere determinato dal ricevente tramite un

75

Comunicazione Seriale 6

opportuno carattere, per cui non serve che gli orologi siano sincronizzati, gli intervalli di tempo con cui
vengono scandite le lettere devono essere assolutamente uguali, per evitare errori, saltando o duplicando
una delle lettere. Per ottenere ci, richiesto che il mittente e il ricevente abbiano orologi molto precisi, cio
molto stabili in frequenza.
La comunicazione seriale asincrona richiede una linea per ogni direzione pi una massa. Non ci sono di
solito altre linee di controllo. La trasmissione full-duplex, nel senso che i dati possono essere scambiati
simultaneamente in ambedue le direzioni, in maniera indipendente, diversamente dalla SPI. La rilevazione
degli errori operata dallapplicazione che gestisce la comunicazione. In genere un link asincrono connette
solo due dispositivi, ma esistono anche dei bus che utilizzano questa modalit.
Un tempo tutti i personal computer avevano uninterfaccia seriale asincrona per connettere le periferiche,
come la RS-232, molto semplice da implementare via software o attraverso un modulo hardware, oggi
sostituita dalla USB (Universal Serial Bus), che invece sarebbe molto complessa da implementare con il
software. In pratica, per utilizzare un protocollo seriale nella comunicazione verso un PC dotato di porta
USB, sufficiente utilizzare un convertitore seriale/USB per creare una porta COM virtuale. Tuttavia sulla
scheda LaunchPad presente uninterfaccia hardware per gestire direttamente la porta USB.
Sebbene esistano molti esempi di codici per gestire linterfaccia UART, bene comprendere a fondo i
meccanismi della comunicazione seriale, applicati nel caso dellMSP430. Si comincer con il protocollo di
trasmissione, poi quello di ricezione, per poi progettare un completo transceiver UART.
UART - PROTOCOLLO DI COMUNICAZIONE
Nei sistemi UART alla sequenza di simboli binari che codifica il dato vanno aggiunti almeno due bit, uno per
indicare linizio di un nuovo messaggio di dati da trasmettere (start), laltro per indicarne la fine (stop). La
sequenza composta dai bit di controllo, pi i dati in mezzo, si chiama frame.
Usando la codifica ASCII, spesso vengono inviati 7 bit + 1 di inizio e 1 di fine, e infine un decimo tra il dato e
lo stop, detto bit di parit, che serve a determinare se il dato corretto o no, nel senso che il ricevitore deve
controllare che il numero di 1 nei 7 bit di informazione sia in numero pari o dispari, a seconda dei casi. Nei
sistemi a 8 o 16 bit, come i C, i dati sono codificati in 8 bit, e si esclude il bit di parit per mantenere a 10 bit
la lunghezza del frame.
Per esempio, il codice ASCII, a 7 bit e controllo di disparit, per la lettera D 0x44 o 0b1000100.
Il protocollo usato in UART (codifica little endian) comincia a trasmettere lLSB, e usa il livello logico alto
come default o stato di quiete (idle) della linea, che pertanto ha una resistenza di pull-up collegata
allalimentazione. Quindi, per inviare la lettera D, si invia un bit di start pari a 0 (abbassa il livello della
linea), la codifica in ordine inverso, e un bit di stop pari a 1 (rialza il livello della linea). Finora il messaggio
00010001x1, dove x il bit di parit dispari. Poich il numero di 1 nella codifica (escluso lo stop) 2, si
aggiunge un 1, pertanto il messaggio diventa 0001000111. Se si codifica con 8 bit anzich 7 si ottiene
0b01000100, per cui, senza controllo di parit, il messaggio diventa 0001000101.
Sebbene la frequenza di trasmissione sia internamente arbitraria, per comunicare con dispositivi esterni,
come computer ecc., occorre usare una frequenza standard, come 300, 1200, 4800, 9600 0 115200 baud.

Representation of the ASCII character "D" in UART TTL.

76

Comunicazione Seriale 6

ACCURATEZZA DEL CLOCK


Per capire quanto deve essere accurato un clock per una trasmissione di tipo UART, si osservi la figura
successiva, relativa a due clock (transmitter e receiver) operanti esattamente alla stessa frequenza. In questo
caso non importa molto dove si campiona il bit in arrivo, purch si misuri dopo il bit precedente. Se invece
c qualche errore nella frequenza di un clock, se il clock del receiver pi lento misurer il bit sempre pi
tardi, finch non lo perder completamente. Un clock pi veloce, similmente, misurer sempre pi presto,
finch non acquisir lo stesso bit due volte. Non sapendo se il clock del receiver pi lento o pi veloce, ha
senso catturare il bit circa a met, in questo modo passer pi tempo prima di commettere un errore.

Guardando la figura si vede che usando dei clock che sono inattivi soltanto per il 6% del tempo, si pu
notare dove avviene il primo problema: il bit 9 viene completamente saltato, pertanto non stato inviato un
intero frame di dati (frame error). La scelta del 6% non arbitraria: infatti lerrore riportato nei datasheet
dellMSP430 assegna alle frequenze calibrate del DCO (per non parlare di quelle non calibrate) una
tolleranza con la temperatura ed altri fattori di 3%. Pertanto se sia il trasmettitore che il ricevitore usano il
proprio DCO calibrato come clock, si otterr un errore massimo del 6%, rendendo la trasmissione UART
completamente inaffidabile.
In realt questo il caso peggiore, perch, se si comunica con un PC, probabilmente il suo clock pi
accurato di questo. Una tolleranza del 5% tra i clock dovrebbe, in media, dare un errore nel frame sul bit 11,
mentre il 4% sul bit 13, pertanto se si ha una tolleranza migliore di questa, e un breve gap tra i frame per
risincronizzare, si dovrebbe riuscire a comunicare. Si noti che ci implica, da una parte, un limite alla
dimensione del frame che si pu trasmettere in maniera affidabile, dallaltra, che la reale frequenza di
trasmissione sar un po pi bassa a causa dei tempi di recupero del clock tra i frame. Se si vuole una
trasmissione pi veloce serve un clock pi accurato.

UART TRANSMITTER
Dopo aver visto come dotarsi di un clock accurato, il miglior modo di controllare la temporizzazione del
sistema di trasmissione quello di usare la periferica Timer_A, che occorre configurare.
Per prima cosa occorre assicurarsi che il DCO usi una frequenza calibrata:
#define CALBC1_UART *(char *)0x10BF
#define CALDCO_UART *(char *)0x10BE
BCSCTL1 = CALBC1_UART;
DCOCTL = CALDCO_UART;
Per rendere tutto pi semplice, si possono mettere le definizioni nellheader file (es. calibrations.h), e quindi
includerlo in ogni programma dove occorre usare una calibrazione custom del DCO.
A questo punto bisogna impostare il timer. Prima occorre scegliere la frequenza di trasmissione (data rate),
che, se si usa la conessione USB del LaunchPad, pu essere solo 9600 baud (sebbene teoricamente la
trasmissione dovrebbe funzionare ancora con una frequenza del DCO di 921600 baud!).
Poi occorre calcolare quanti sono i cicli di clock durante la trasmissione di un bit. Se si trasmette a 9600 bit/s,
e il clock ha una frequenza di 7.3728 MHz, si hanno 7372800/9600 = 768 cicli di clock per ogni bit. Questo
periodo spesso chiamato bit time.
bit_time = 24576;
// bit time per 300 baud
bit_time = 768;
// bit time per 9600 baud
bit_time = 64;
// bit time per 115200 baud
77

Comunicazione Seriale 6

Il LaunchPad impostato per inviare la trasmissione su P1.1. Guardando il datasheet, si vede che questo pin
pu essere usato per loutput TA0.0 del Timer_A, per cui si pu attivare questo output quando il contatore
TAR raggiunge il valore memorizzato in TACCR0, che corrisponde al bit time. Ci sono due opzioni per fare
questo: si pu usare il Timer in up-mode, in modo che il contatore si resetta quando raggiunge TACCR0,
oppure si pu usare il timer in continuous-mode e resettare TACCR0, per essere bit time cicli oltre il valore
corrente del TAR, quando triggera un interrupt (facendo questo automaticamente tiene conto del roll-over).
La prima sarebbe una soluzione semplice solo per il transmitter, ma poich si vuole impostare un
receiver/transmitter full-duplex, e il receiver guarda a P1.2 (naturalmente usando TA0.1, basato su TACCR1
per il timing), ha pi senso usare il secondo metodo, per non avere conflitti quando si imposta il receiver,
usando il timer per entrambi.
Quando si configura il timer, occorre assicurarsi che TA0.0 sia impostato su P1.1. Non sarebbe necessario
avviare il timer finch non si pronti a trasmettere, tuttavia si pu evitare di spegnerlo, a meno che non si
abbiano problemi di consumo di potenza. Il timer lavora in modo indipendente e non interferisce con il
programma. Tuttavia non si abilitano gli interrupt finch non si pronti a trasmettere.
Quando si pronti a trasmettere, occorre abilitare gli interrupt, e configurare il timer per resettare loutput
TA0.0, per segnalare linizio di un carattere. TACCR0 incrementato per il conteggio del bit time, cos il
prossimo interrupt avviene al tempo esatto dato dalla frequenza di trasmissione scelta. Si avanza del numero
di bit inviati, e si comunica allMSP430 se il prossimo bit un set (1) o un reset (0), a seconda della codifica:
se si usa lo standard 8N1 (codifica little endian), lLSB. Quando tutti gli 8 bit sono stati inviati, si imposta
TA0.0 per inviare un bit di stop, arrivando a un totale di 10 bit per carattere. Gli interrupt sono disabilitati,
finch non si ha un altro carattere da inviare.
Osservando il codice in uartTXG2231.c, si nota innanzitutto la routine DCO_init(). Questa include uninsidia,
in quanto si sta accedendo direttamente alla memoria flash, per cui si corre il rischio di impostare il DCO alla
sua frequenza pi elevata, se si usa un C in cui non sono stati memorizzati i valori di calibrazione del DCO
a quellindirizzo di memoria. Questo tipo di controllo impedisce questa eventualit, e conviene sempre
inserirlo, anche se si usano le calibrazioni del costruttore. Nel codice stato inserito un segnale del LED1
(periodico 3 flash + 1 pausa), che indica un errore di calibrazione del DCO. Non segnala un errore di codice,
ma solo un indirizzo ad una cella vuota. Anche se il DCO stato calibrato, si pu avere errore, perch CCS,
quando si programma il chip, cancella di default sia i segmenti della main memory, sia quelli
dellinformation memory (eccetto INFO_A), cosicch le calibrazioni in INFO_B vengono cancellate. Occorre
far girare di nuovo il codice UART calibration, e aprire Project Properties. Scegliere Debug nel men a sinistra,
e selezionare MSP430 properties. Qui nella lista di Download Options, occorre selezionare Erase Main Memory
Only e chiudere la finestra delle propriet. In questo modo quando si carica il programma nel C, la
information memory non viene cancellata.
Questo codice impostato per mostrare alcune modalit di trasmissione. Ogni loop avviato dalla pressione
del pulsante su P1.3. Prima si avanza di una stringa e si invia allUART un carattere alla volta. I singoli
caratteri sono inviati contando ripetutamente da 1 a 9 ?. Singole cifre possono essere inviate facilmente come
caratteri, nellordine in cui appaiono nel codice ASCII (0 codice ascii 48=0+48, 1 codice ascii 49=48+1
ecc.). Un numero a pi cifre pu essere inviato usando una codifica simile a quella usata per lLCD.
Ci sono alcune semplici modalit di inviare dati e messaggi attraverso lUART, che occupano poco spazio di
codice, come le routine tx_byte() e CCRO_ISR(). Quando si chiama la routine tx_byte(), questa prima
controlla il flag UART_FG e aspetta che si azzeri il bit TX. Il byte da inviare caricato su un buffer, e
formattato per includere il bit di stop. Il bit TX posto in UART_FG, un LED acceso per indicare la
trasmissione, e il timer configurato per resettarsi al prossimo interrupt. Un carattere non pu essere
trasmesso prima almeno del bit time dalla precedente trasmissione; poich il clock dellMSP430 molto pi
veloce della frequenza di trasmissione, si potrebbe avere il problema di iniziare linvio di un nuovo carattere
microsecondi dopo lultimo, piuttosto che lasciare almeno un bit time di intervallo fra i due. Per evitare
questo, ogni flag di interrupt presente viene azzerato, e il prossimo interrupt impostato un bit time dopo il
tempo presente. Questa configurazione lascia appena poco pi di un bit time tra i caratteri. Gli interrupt
chiamano la routine CCR0_ISR, che imposta il prossimo interrupt dopo un bit time, configura TA0.0 con il
prossimo bit per il prossimo interrupt, e shifta il buffer di trasmissione in modo che abbia per primo il
prossimo bit da inviare.

78

Comunicazione Seriale 6

Per vedere accadere tutto questo, occorre un terminale seriale come RealTerm o PuTTY. Quando il terminale
rivolto a guardare la porta, il LaunchPad impostato a 9600 baud, si dovrebbe vedere il messaggio sul
display, ogni volta che si preme il pulsante.

UART RECEIVER
Una volta che si costruito il transmitter, il receiver non molto diverso. Il modo in cui verr costruito terr
conto della prospettiva di mettere insieme transmitter e receiver nello stesso dispositivo.
Ci che uguale al transmitter il fatto di usare il Timer_A come temporizzatore, usando lo stesso bit time
del transmitter. Il timer ha bisogno di un clock accurato, pertanto si user la stessa frequenza calibrata per
lUART. LISR per il timer far tutte le operazioni per leggere il dato seriale, non appena trasmesso al
dispositivo.
Ci sono una serie di differenze su come il receiver gestito, ma la maggior parte sono di minore importanza.
Per prima cosa si usano gli interrupt su una porta GPIO, per triggerare la ricezione dei dati. Una volta che si
vede un fronte di discesa sulla GPIO (che segnala che un bit di start sta per essere ricevuto), occorre
attendere met del bit time, in modo da campionare circa al centro di ogni bit, per cui serve definire anche
lintervallo met del bit time (si potrebbe dividere il bit time per due, ma questo in effetti lento, meglio
definire unaltra variabile con il tempo esatto che si vuole). Tutto ci viene gestito da una ISR per la porta
GPIO.
Inoltre si vuole usare per la temporizzazione loutput TA1 anzich TA0 del timer, perch questo permette di
inviare e ricevere contemporaneamente, poich ognuno ha un timer indipendente. Tuttavia TA1 ha una
modalit leggermente differente di gestire lISR. Si noti che TA0 e TA1 possono essere scambiati. A seconda
di quale pin si connette al trasmettitore e quale al ricevitore dipende la rispettiva associazione di TA0 e TA1.
Sul LaunchPad, Tx connesso a P1.1 e Rx a P1.2. Dal datasheet dei dispositivi P1.1 connesso a TA0.0,
permettendo di usare le caratteristiche di uscita del Timer_A per cambiare il bit automaticamente. Unaltra
opzione connettere P1.5 alloutput TA0.0. P1.2, P1.6 e P2.6 possono essere tutti connessi a TA0.1, cos come
si pu cambiare CCR0 con CCR1 per il timer del transmitter. Il receiver legge una porta GPIO, e in linea di
principio potrebbe essere usato con qualunque pin GPIO. Allo stesso modo si vedr anche come trasmettere
su qualunque porta GPIO.
Specifiche
Prima occorre definire il bit time, cos come la sua met. Per 9600 baud, alla frequenza calibrata per lUART
di 7.3728 MHz, questi corrispondono a 768 e 384 cicli rispettivamente. Si fa ancora uso della variabile di flag
UART_FG, lasciando BIT0 per un flag di trasmissione, e usando BIT1 per un flag di ricezione. Il dato sar
caricato ancora in un buffer, prima di essere salvato, usando il valore intero RXBuffer. La variabile di
conteggio dei bit (bit_count) sar usata per scalare il conteggio dei bit man mano che vengono ricevuti.
Quando si combina il transmitter con il receiver, ne serve una per parte per un conteggio indipendente. Per
ora si usato lo stesso nome.
Linizializzazione del DCO e del Timer identica per il transmitter ed il receiver, le differenze sono tutte
nellISR. P1 configurata per leggere dal pin scelto per ricevere (P1.2), abilitando gli interrupt sui fronti di
discesa. Quando avviene linterrupt (presumibilmente il bit start della trasmissione), CCR1 inizializzato a
met del bit time, gli interrupt CCR sono abilitati, e gli interrupt su P1 sono temporaneamente disabilitati
finch il dato non stato ricevuto. Questo metodo evita di dover aspettare che si azzeri il flag di ricezione
prima del prossimo interrupt su P1, cosicch nessun loop while necessario qui.
Linterrupt CCR1 gestito in maniera diversa dal CCR0. Innanzitutto CCR0 triggera i flag di interrupt
TAIFG nel registro TACTL. Tutti gli altri registri capture/compare triggerano i flag nel registro TAIV. C un
singolo interrupt per questo registro, cos TAIV deve essere letto per sapere quale CCR ha causato linterrupt
(sebbene nei dispositivi del LaunchPad c solo CCR1, linterrupt deve essere gestito nello stesso modo come
se ci fossero altri CCR nel dispositivo). Nel codice in C questo stato fatto usando unistruzione switch, che
cerca un flag CCR1, che segnalato dal bit 1 in TAIV. Anche TAIFG appare in TAIV, ma poich ha il valore
0xA piuttosto che 0x2, cos due casi sono sufficienti per identificare un flag di interrupt CCR1. Il flag
azzerato automaticamente quando linterrupt servito. Allora vengono letti i bit dallinput GPIO in
intervalli dati dal bit time. Quando tutti e 10 bit sono stati letti, gli interrupt del timer vengono disabilitati, e
gli interrupt su P1 abilitati nuovamente.
79

Comunicazione Seriale 6

Il codice pu essere testato con uartRXG2231.c (senza dimenticare di configurare il dispositivo per non
cancellare i segmenti dellinformation memory). Guardando la funzione main, impostata per cercare
comandi di caratteri singoli, riconoscendo i caratteri r per accendere il LED rosso, g il LED verde, e z
per non fare nulla, ma andare in modalit low power e aspettare un comando. Se viene inviato un comando
invalido, il codice si ferma nello stato attuale di P1OUT e fa lampeggiare i LED per segnalare un errore e
ritornare allo stato salvato. Dopo ogni comando il dispositivo si rimette in sleep mode. Per usare il codice,
bisogna essere almeno sicuri di essere usciti dal debug, perch pu capitare che i terminali seriali non
riescano a comunicare attraverso la stessa porta che aperta in CCS in modalit debug; uscendo dal debug,
si libera la porta per luso.
Questa la modalit pi semplice di inviare comandi allMSP430. Ovviamente ci sono altre possibilit per
inviare comandi multi-carattere, o inviare direttamente i dati.

UART TRANSCEIVER
Se si combina il protocollo del trasmettitore con quello del ricevitore, si quasi realizzato un transceiver
UART full-duplex. Ci sono solo alcuni dettagli da tenere in considerazione affinch operi correttamente:
Serve un contatore di bit sia per trasmettere che per ricevere. Servono altri 8 byte di memoria.
Sebbene non serva al protocollo UART, il codice di esempio usa gli interrupt su due pin della porta P1.
Ogni output su una porta GPIO, anche se gli interrupt sono disabilitati su quel pin, alzer il bit
corrispondente di P1IFG su un fronte di salita o discesa, a seconda dellimpostazione del bit
corrispondente in P1IES. Poich vengono usati due LED, ogni volta che un LED acceso si ha un fronte
di salita su quella porta, che alza il bit in P1IFG. Il metodo usato in questo codice per gestire gli interrupt
su P1 fallisce in questi casi, poich P1IFG non sar esattamente P1RX o BTN1. Per fissare questo, si
introduce una maschera, in modo che listruzione di switch nellISR di P1 consideri soltanto i bit per
P1RX e BTN1, ignorando ogni altro bit di P1IFG. C uno svantaggio in questo particolare schema: se
lUART riceve un bit di start nello stesso tempo in cui si preme il pulsante, uno o entrambi saranno
ignorati, a seconda dellordine e della temporizzazione dei due eventi.
Viene introdotta una nuova funzione che gestisce linvio di una stringa di caratteri sullUART. Questa
funzione fa uso di un puntatore, che permette di scrivere nuovi valori sulla variabile message. Fare
attenzione a non sovrascrivere altri valori nella RAM. Inoltre la funzione assume una lunghezza di
messaggio di esattamente 16 caratteri, e scriver spazzatura o avanzi dellultimo messaggio se si scrive
un messaggio pi corto. Uno pi lungo verr troncato.
Poich il DCO sta eseguendo comandi ad una velocit maggiore di quella di trasmissione, viene
aggiunto un piccolo ritardo totx_byte nel bit time. Questo ritardo consente di iniziare la trasmissione
prima che il controllo ritorni alla funzione principale. Senza di esso il codice potrebbe chiamare tx_byte
di nuovo prima che la trasmissione inizi effettivamente, cambiando il valore del TXBuffer, prima che
abbia la possibilit di essere inviato.
Il codice del transceiver pu essere trovato in uartTXCVRG2231.c. Sono state aggiunte alcune caratteristiche
per dimostrare la capacit full-duplex del transceiver. I comandi r, g e z sono presi dal codice del
receiver. A questi stato aggiunto il comando c, che stampa un messaggio continuo finch non viene
inviato un altro comando (es. z per andare in sleep). Si noti che il comando funziona anche se viene inviato
nel mezzo di una trasmissione di byte, poich il ricevitore opera su una configurazione separata di
timer/interrupt. Questo si pu verificare con un Logic Analyzer.
Oltre al comando c, un altro comando, originato dalla pressione di un pulsante (usando un carattere ASCII
non da tastiera 0xAF), pu essere inviato in ogni momento, dimostrando che il codice pu anche lavorare
con interrupt provenienti sia da interfacce fisiche che dalla trasmissione seriale. Il pulsante interrompe anche
la stampa continua del comando c, cos come resetta a z la variabile di comando.
Occorre ricordarsi di usare la frequenza di trasmissione di 9600 baud e di configurare CCS per includere
nellheader file la calibrazione UART, cos come non cancellare lInformation Memory che contiene i dati di
calibrazione.

80

Comunicazione Seriale 6

MODALIT USCI / UART


Se nel C presente un modulo USCI, limplementazione diventa ancora pi semplice.

COMUNICAZIONE SERIALE SINCRONA


Ci sono numerosi sensori e dispositivi, progettati per operare con microcontrollori, che usano protocolli di
comunicazione seriale sincrona. La differenza primaria di queste modalit di comunicazione la
condivisione dello stesso clock tra trasmettitore e ricevitore, piuttosto che affidarsi alla precisione di due
clock distinti. Questo limita tuttavia la distanza tra i dispositivi, ma semplifica molto il processo di
comunicazione.
Esistono numerosi protocolli progettati per la comunicazione seriale sincrona, ma alcuni sono diventati uno
standard per molti dispositivi, come la SPI (Serial Peripheral Interface) e la IC (o I2C Inter-integrated Circuit
Bus).
La differenza tra i due protocolli risiede nel fatto che lSPI un sistema di comunicazione di tipo SingleMaster/Single-Slave, mentre l IC di tipo Single-Master/Multiple-Slave, come rappresentato nella figura,
cio un vero e proprio bus dati. Tuttavia anche la SPI pu operare con diversi dispositivi, ma necessita di
una linea dedicata al riconoscimento della periferica, perch non ha protocolli di indirizzamento e
riconoscimento come la IC. Inoltre nella IC, che ha una sola linea dati e una per il clock, i dati possono
viaggiare in entrambe le direzioni, ma solo una per volta, da cui la trasmissione definita half-duplex. Nella
SPI, che ha due linee dati (pi il clock), la trasmissione pu avvenire in entrambe le direzioni
simultaneamente, ovvero di tipo full-duplex. La SPI pertanto un protocollo pi semplice, ma richiede pi
linee di comunicazione, particolarmente adatta al trasferimento di grandi quantit di dati.

La maggior parte dei C MSP430 ha almeno una periferica di interfaccia per la comunicazione seriale. I
dispositivi meno costosi hanno il modulo USI (Universal Serial Interface), interfaccia hardware sufficiente per
la SPI, mentre richiede lausilio del software per la IC.
I dispositivi pi grandi hanno invece il modulo USCI (Universal Serial Communication Interface), interfaccia
hardware che gestisce tutti gli aspetti della comunicazione, per cui il software deve solo gestire i dati da
trasmettere e memorizzare i dati ricevuti, tipicamente attraverso due piccole ISR. Ogni USCI contiene due
canali, A e B, indipendenti, anche se condividono alcuni registri e vettori di interrupt:
canale asincrono USCI_A opera come una UART per supportare la comunicazione RS-232. Pu
determinare il baud rate di un segnale di ingresso, che abilita il suo utilizzo in una rete LIN (Local
Interconnect Network). Il segnale di uscita pu essere modulato per un diodo led infrarosso per operare in
modalit IrDA (Infrared Data Association), cos come pu essere decodificato il segnale di ingresso. Infine
pu anche gestire la SPI, sebbene sia sincrona, perch unestensione semplice e ampiamente usata.
canale sincrono USCI_B gestisce sia la SPI che la IC, sia come master che come slave. Contiene una
macchina a stati completa per gestire la comunicazione IC in accordo alle specifiche Philips (ora NXP).
Prima dellavvento della USCI esisteva solo il modulo USART (Universal Synchronous/Asynchronous
Receiver/Transmitter), che gestiva principalmente la comunicazione asincrona e la SPI, ma in alcuni dispositivi
anche la IC.
Per utilizzare questi protocolli in un esperimento occorre almeno uno di questi dispositivi:
Un altro dispositivo che comunica via SPI e IC, come una EEPROM seriale o un sensore con output
digitale.
Interfacce USB-SPI/ IC come i dispositivi FTDI (questi chip includono anche uninterfaccia UART a 9600
bps).
Un altro C MSP430.
Il LaunchPad ha gi due dispositivi che includono una USI, ma servono alcuni componenti per alimentare i
dispositivi fuori dal LaunchPad.

81

Comunicazione Seriale 6

Per cominciare si user una EEPROM seriale della Microchip (per es. la 25AA080C per la SPI e la 24AA08
per la IC).
Dei due protocolli seriali sincroni usati dagli MSP430, la IC probabilmente quella usata pi
frequentemente, per il motivo che il protocollo progettato per lavorare con molteplici dispositivi su una
stessa linea di comunicazione. Molti sensori e periferiche esterne che usano la IC sono gi disponibili.
Tuttavia la SPI un po pi semplice da capire, per cui si comincer a trattare questa.

SERIAL PERIPHERAL INTERFACE (SPI)

ARCHITETTURA DELLA SPI


Per capire il meccanismo di comunicazione SPI, si immagini che due persone scrivano ciascuna un
messaggio allaltra su un pezzo di carta, poi si scambino i messaggi passandoseli di mano in mano nello
stesso tempo. Anche in questo caso il messaggio inviato una lettera alla volta, sebbene sia possibile
scambiare pi lettere simultaneamente. Tuttavia nelle periferiche USI e USCI possibile solo la trasmissione
di un bit alla volta.
Si supponga che due dispositivi A e B abbiano ognuno un registri a 8 bit. Allarrivo di un dato segnale (es.
fronte di salita del clock), ognuno scrive lMSB (7) sulla propria porta di uscita. Al segnale successivo (es.
fronte di discesa del clock), ognuno shifta il proprio registro a sinistra di un bit, e scrive il proprio output
sullLSB (0) dellaltro dispositivo. Dopo 8 cicli di clock, il valore dellottavo bit che era nel registro del
dispositivo A ora nel dispositivo B, mentre il valore che era in B ora in A. Entrambi leggono il nuovo
messaggio (e probabilmente fanno qualcosa con esso), e poi possono scrivere un nuovo messaggio nei loro
registri per il prossimo scambio. Questo scambio di informazioni rende possibile inviare dati da un
dispositivo allaltro molto velocemente.
La chiave affinch il processo funzioni bene che gli shift dei due dispositivi avvengano nel medesimo
istante, per cui devono essere sincronizzati. Questo sincronismo ottenuto condividendo lo stesso clock tra i
due dispositivi. Uno dei due genera il proprio clock, e lo connette ad una propria uscita per farlo usare
allaltro. Per convenzione, ci ri riferisce al dispositivo che genera il clock come master, e ogni altro
dispositivo che lo usa come slave. Entrambi sono simultaneamente mittenti e riceventi. Uno dei dispositivi
ha la funzione di controllo dello scambio dei dati, mentre laltro dipende dal controllore per fare lo scambio.
Si pu anche immaginare un sistema in cui il clock esterno al master e allo slave, tuttavia in genere pi
semplice che sia uno dei due dispositivi a controllare il processo, e questo viene chiamato master.

Molti costruttori hanno definito differenti nomenclature per le linee tra i due dispositivi. La Texas
Instrument ha tre linee chiamate SDI (Serial Data In), SDO (Serial Data Out) e SCLK (Serial Clock). Come per
lUART, la SDI di un dispositivo collegata alla SDO dellaltro e viceversa. Si noti che, poich occorre
includere il clock, la SPI richiede tre linee (pi una per il ground), mentre la UART due (pi una per il

82

Comunicazione Seriale 6

ground). Ci sono alcuni casi in cui uno dei due dispositivi non deve comunicare nulla allaltro, per cui
bastano due linee. Tra due dispositivi A e B la configurazione della connessione quindi:
SDI_ASDO_B
SDO_ASDI_B.
La Motorola invece ha una linea MOSI (Master Out - Slave In) e MISO (Master In - Slave Out), piuttosto che
SDI e SDO. In questo modo le due connessioni hanno una nomenclatura omogenea:
MOSI_AMOSI_B
MISO_AMISO_B
Alcuni costruttori usano semplicemente la nomenclatura SI e SO.
Si pu anche incontrare SPSCK ed SCK per il clock.
La SPI si pu anche estendere alla comunicazione con pi di due dispositivi, includendo un pin di Chip
Select, in cui uno slave si mette in ascolto solo quando questo pin alto o basso, a seconda
dellimplementazione, per cui la comunicazione avviene solo tra il master e lo slave selezionato, anche se
altre periferiche slave sono collegate. Lo svantaggio di questa configurazione che richiede una linea
ulteriore per ogni dispositivo che necessita di comunicare indipendentemente. Dispositivi abilitati con un
comando di chip select possono essere etichettati come CS, SS (slave select), e qualche volta CE (chip enable),
come nel caso della TI.

MODALIT USI / SPI


La periferica USI dellMSP430 molto flessibile per adattarsi a tutte le possibili configurazioni della SPI,
quella che viene usata dipende dai dispositivi che sono connessi.
Ci sono 4 modalit principali di funzionamento per la SPI, che dipendono dalla polarit del segnale (clock in
quiete a livello alto o basso) e dalla sua fase (prima lettura poi scrittura o viceversa). Le notazioni comuni per
questi due valori sono CPOL e CPHA, rispettivamente. La TI usa tuttavia i valori CKPL e CKPH, in cui
CKPH invertito rispetto alla definizione standard di CPHA (cio se CPHA=0 CKPH=1 e viceversa).
Quando CPOL/CKPL=0 la linea in quiete bassa, e quando CPHA=0 prima si legge poi si scrive.
Un esempio di temporizzazione di una modalit che usa CPOL/CKPL=0 e CPHA=0 (CKPH=1 per gli
MSP430) mostrata nella figura successiva (sinistra). Si noti che la fase si determina osservando i fronti:
qualunque fronte avvenga in mezzo al bit una lettura, il fronte tra i bit una scrittura, e lordine con cui
questi avvengono determina la fase. Questa modalit chiamata Mode 0,0 o Mode 0.

Nella figura di destra riportato un altro esempio che usa CPOL/CKPL = 1. In questo esempio il fronte di
salita ancora nel mezzo del bit e il fronte di discesa tra due bit, ma la scrittura viene prima, la lettura
dopo. Si deve porre CPHA=1 (o CKPH=0). Questa modalit chiamata Mode 1,1 o Mode 3 (perch 0b11=3).
Lesempio della figura successiva mostra un caso reale di una SPI EEPROM Microchip 25AA080C con 1 kB
di memoria. Il diagramma specifica che si tratta di un Mode 0,0 (CKPL=0, CKPH=1), anche se in alternativa
pu essere impostato il Mode 1,1. Inoltre lordine di trasmissione specifica che si cominci dallMSB.

83

Comunicazione Seriale 6

CONFIGURARE I REGISTRI USI


La periferica USI dellMSP430 usa 6 registri. Un aspetto nuovo di questi registri che, poich ogni registro
da 1 byte, si possono accoppiare ed accedere ad essi come se fossero 3 registri da 2 byte, ovvero USICTL (USI
Control Register), USICCTL (USI Clock and Counter Control Register) e USISR (USI Shift Register). In alternativa
si possono gestire singolarmente, a seconda dei casi.
USICTL composto di USICTL0 (lower byte) e USICTL1 (upper byte).
USICTL0
USIPEx (bit 7-5) abilita le funzioni USI sui pin dellMSP430. Il G2211 e il G2231 usano i pin P1.5-7 per le
funzioni USI. Questi pin sono gi connessi ad altre periferiche attraverso il registro P1SEL, cosicch sono
configurati per lUSI attraverso un proprio registro, ogni pin individualmente.
USILSB (bit 4) quando vale 1 (set) i dati sono trasmessi cominciando dallLSB (di default si comincia
dallMSB).
USIMST (bit 3) quando vale 1 lMSP430 diventa master, e lSCLK connesso al clock USI come output. Di
default, quando vale 0 (cleared), il C slave, e SCLK in input.
USIOE (bit 1) quando vale 1 loutput abilitato; questa funzione equivalente al Chip Select in altri
dispositivi, poich lMSP430 non ha un pin esplicito per questa funziona, per cui si implementa via software,
se necessario.
USISWRST (bit 0) quando vale 1 fa un Reset via sofware dellUSI, per consentire operazioni di
configurazione. Per iniziare la comunicazione USI si azzera.
USICTL1
USICKPH (bit 7) imposta la fase del clock, invertita rispetto a CPHA. Se 1, consente i modi 0 e 2, se 0 i modi
1 e 3.
USII2C (bit 6) occorre azzerarlo per usare un protocollo SPI.
USIIE (bit 4) abilita gli interrupt per il contatore USI; un flag di interrupt inviato quando il numero
specificato di bit stato trasmesso.
USIIFG (bit 0) flag di interrupt per USIIE; questo flag pu essere azzerato automaticamente o
manualmente, sebbene sia preferibile manualmente.
USICCTL composto di due parti, una per il clock USICKCTL (lower byte), laltra per il counter USICNT
(upper byte).
USICKCTL
USIDIVx (bit 7-5) divide la frequenza di clock per potenze di 2, fino a 128.
USISSELx (bit 4-2) seleziona la sorgente di clock. LUSI ha una grande variet di scelte possibili, inclusi i
registri capture/compare del Timer_A. Questi bit non sono usati se lUSI usata in modalit slave.
USICKPL (bit 1) determina la polarit (1 idle high, 0 idle low)
USISWCLK (bit 0) Clock realizzato via software.
USICNT
USI16B (bit 6) quando 1, lUSI trasmette fino a 16 bit, quando 0, trasmette fino a 8 bit.
USIIFGCC (bit 5) quando 1, il flag di interrupt deve essere azzerato manualmente.
USICNTx (bit 4-0) scrivendo un valore su questi bit, inizia la trasmissione fino al numero di bit specificato.
USISR composto di due parti, USISRL (lower byte) e USISRH (upper byte). USISRH ignorato se lUSI
configurata a 8 bit, e USI16B azzerato in USICNT.

SPI EEPROM 25XX080


In questo paragrafo si configura un modulo USI per comunicare con una EEPROM seriale usando un
protocollo SPI. Si usa in particolare un chip 25AA080C della Microchip, ma in linea di principio si pu usare
qualunque altra EEPROM SPI della Microchip.
Istruzioni e lo Status Register
Il chip ha 6 istruzioni a 8 bit. In questo progetto il pin Chip Select in effetti usato per segnare la fine di
unistruzione, e pertanto un segnale necessario che deve essere abilitato (a 0) prima e disabilitato (a 1) dopo
ognuna delle seguenti istruzioni.

84

Comunicazione Seriale 6

READ (0x03) inviare questi 8 bit, seguiti dallindirizzo a 16 bit della locazione di memoria che si vuole
leggere, comanda alla EEPROM di inviare il valore a 8 bit della cella specificata nei successivi 8 cicli di clock.
Lintera istruzione impegna 32 cicli di clock (8 il comando, 16 lindirizzo, e 8 la lettura).
WRITE (0x02) questo comando pu essere usato in due modalit differenti. Nella prima, dopo aver inviato
il comando, un indirizzo a 16 bit inviato seguito dal valore a 8 bit da scrivere. Nella seconda, piuttosto che
scrivere un byte alla volta, il chip pu scrivere un gruppo di byte (chiamata pagina) in una volta. Il chip
25AA080C ha pagine di 16 byte, cosicch con una sola istruzione possono essere scritti fino a 16 byte. Questa
modalit usa i bit di comando, seguiti dallindirizzo iniziale (ogni byte viene scritto nellindirizzo successivo
allultimo), e i valori di ogni byte da scrivere. Si noti che la scrittura non inizia finch non viene disabilitato il
Chip Select (a 1). Inoltre il chip richiede un piccolo intervallo di tempo (circa 5 ms secondo il datasheet) per
effettuare la scrittura. Ogni comando successivo di scrittura deve essere inviato dopo questo tempo (settling
time) per evitare linterruzione del processo. Questa istruzione impegna 24+8n cicli di clock, dove n il
numero di byte da scrivere (massimo 16 in questo chip).
WRDI (0x04) questa istruzione da 8 bit disabilita la scrittura della EEPROM. I dati possono solo essere letti,
le istruzioni di scrittura non hanno effetto. Questa istruzione impegna solo 8 cicli di clock.
WREN (0x06) questa istruzione a 8 bit abilita la scrittura. Quando completato un ciclo di scrittura, il bit
(latch) di write-enable resettato, cos questa istruzione deve essere inviata prima di ogni ciclo di scrittura.
Impegna solo 8 cicli di clock.
RDSR (0x05) la EEPROM include uno Status Register che indica la configurazione del chip. Il valore a 8 bit
pu essere letto inviando questo comando seguito da 8 cicli di clock per leggere il valore. Pertanto impegna
16 cicli di clock.
WRSR (0x01) lo Status Register pu essere scritto direttamente sotto certe condizioni, si effettua una
scrittura inviando un comando WREN, seguito da questo comando e gli 8 bit da scrivere nello Status
Register. Impegna 16 cicli di clock, ma come listruzione WRITE deve essere preceduta da WREN.
Lo STATUS REGISTER usa 5 degli 8 bit in questo chip:
WIP (bit 0) un flag che vale 1 quando il chip in fase di scrittura nella memoria. Istruzioni di write non
dovrebbere essere inviate finch questo bit 0 (non dovrebbe impiegare pi di 5 ms a settarsi a 1).
WEL (bit 1) flag che si setta quando la scrittura sul chip abilitata. Un comando WREN setta questo bit,
mentre un WRDI lo azzera. La scrittura nella memoria avviene se questo bit settao. La scrittura sullo Status
Register pu essere possibile mentre il bit settato, a seconda della configurazione del chip (vedere WPEN).
BP0 e BP1 (bits 2-3) questi bit abilitano la protezione da scrittura per singoli blocchi della EEPROM. Questo
chip composto da due blocchi, che corrispondono alla met pi alta e a quella pi bassa della memoria.
Settando sia BPO che BP1 protegge dalla scrittura lintera memoria. Questi bit sono settati attraverso
istruzioni WRSR.
WPEN (bit 7) questo bit determina se il pin di Write Protect interessa anche la scritturabilit dello Status
Register. Se questo bit 0, lo Status Register pu essere scritto senza tenere conto dello stato di WP
(ovviamente non appena inviato il comando WREN). Se questo bit 1, allora lo Status Register pu essere
scritto se WP mantenuto alto, ma protetto se basso.
Tutte queste istruzioni e definizioni di registri possono essere incluse nellheader file del programma C.
Come esempio si pu vedere 25xx080c.h
Questo file scritto specificatamente per questo chip, ma pu essere generalizzato. Questo header file verr
usato in tutti gli esempi di codici presentati, insieme allheader file delle calibrazioni calibrations.h (se gli
header file sono linkati al programma, occorre impostare le propriet di CCS per cercare nelle dierectory
dove sono allocati, sia per il compilatore che per il linker, inoltre al solito settare il debugger perch non
cancelli i dati di calibrazione dalla memoria).
Comandi dellMSP430
Ogni dispositivo hardware della TI pu presentare dei bug per cause non definite. Esiste una lista di questi
possibili errori per il G2231 in MSP430G2231 Device Errata. In particolare, esiste un bug con la USI,
denominato USI5, quando usa il protocollo SPI. Per qualche motivo il dispositivo invia un ciclo di clock in
pi la prima volta che viene usato, cos invece di inviare 8 cicli di clock, dopo aver scritto 8 su USICNT, ne
manda 9.

85

Comunicazione Seriale 6

Ci sono due modi di aggirare il problema: il manuale suggerisce di scriverne uno di meno su USICNT la
prima volta, cio 7 anzich otto. Questo tuttavia rende il codice confuso, perch la prima trasmissione viene
trattata differentemente, per cui si preferito eliminare il bug in fase di inizializzazione dellUSI. Prima di
abilitare i pin SDI e SDO dellMSP430, ma dopo aver attivato lUSI, si manda un comando per trasmettere un
solo bit. In effetti sono inviati due impulsi, a causa del bug, ma poich P1.6 e P1.7 non sono ancora stati
abilitati, non ha nessun effetto su qualunque dispositivo collegato allMSP430. Una volta che i due impulsi si
sono azzerati, i pin vengono abilitati, e lMSP430 pu usare il modulo come previsto. Il codice usato per fare
questo il seguente:
void USI_init(void)
{
USICTL0 = USIPE5 + USIMST + USIOE + USISWRST; // Enable SCLK, Master mode, enable output and reset USI
USICTL1 = USICKPH + USIIE;
// Mode 0 requires CKPH=1, enable interrupt
USICKCTL = USIDIV_3 + USISSEL_2;
// SMCLK div 8 -> 921.6 kHz USI clock
// One write command should take ~50 us at this rate,
// while 1 bit via UART @ 9600 should take ~100 us.
USICTL0 &= ~USISWRST;
// Clear USI for use
USICNT = 1;
// clear 1st transmit due to errata USI5
__delay_cycles(50);
// finish clearing (minimum (n+1)*16 cycles)
USICTL0 |= USIPE7 + USIPE6;
// Enable SDI/SDO pins.
USICTL1 &= ~USIIFG;
}
// USI_init
Unaltra tecnica usata in questi esempio stata usata anche per lUART: un flag custom usato per indicare
quando in corso una trasmissione SPI. Questo flag usato per trattenere il programma finch la
trasmissione non stata completata, assicurando che USISR non cambiato finch tutti i bit non sono stati
trasmessi. Il flag azzerato nella ISR della USI. In ogni esempio riportato il Chip Select fatto manualmente.
Un'altra modalit suggerita alla fine.
Programmi di esempio Scrittura: Metodo 1
Il codice eepromwrite_spiG2231.c dimostra come scrivere su un dispositivo EEPROM. Si usa P1.4 come
segnale di Chip Select, e deve essere connesso al pin 1 della EEPROM. Bisogna connettere P1.5/SCLK a SCK
(pin 6), P1.6/SDO a SI (pin 5), e P1.7/SDI a SO (pin 2). Si ricorda che le funzioni SCLK, SDO e SDI sono
abilitate nellMSP430 nei registri USI, non in P1SEL. Occorre rimuovere il jumper al LED su P1.6 nel
LaunchPad. Inoltre, sebbene il codice possa girare senza, buona norma collegare la linea SDI-SO (pin 2) a
Vcc. LMSP430 non accende automaticamente questa linea quando inattiva, come uno si aspetterebbe,
pertanto il trucco inserire un semplice resistore (es. 10k). Allo stesso modo occorre collegare HOLD (pin 7)
a Vcc.
Una volta avviato, questo codice scriver i primi 1024 byte ricevuti dalla UART (a 9600 baud) sulla
EEPROM. Completato questo, si accende il LED su P1.0 e il C va in modalit low power, indicando che il
processo di scrittura terminato. Tuttavia esiste un problema con questo codice. A 9600 baud ogni byte
impiega poco pi di 1 ms ad essere trasmesso. Mentre questo pi del tempo necessario per inviare il
comando di write alla EEPROM alla frequenza di trasmissione della USI, non un tempo sufficiente per
impostare la scrittura (settling time): ci dovrebbero essere circa 5 ms tra i comandi di scrittura. Se si invia un
carattere alla volta dalla tastiera, probabilmente sarebbe sufficiente, ma si devono premere 1024 volte i tasti
per provare il codice, mentre non lo se si scrive un file testo di 1024 caratteri, e si vuole trasmetterlo
direttamente. Perch funzioni, occorre introdurre un ritardo tra i caratteri in trasmissione. Usando il
programma Windows RealTerm, oltre ad avere la possibilit di trasmettere da un file, si ha anche quella di
inserire un ritardo tra i caratteri. Con questa configurazione si riesce a trasmettere il file mspsci.txt e a
scriverlo sulla EEPROM.

86

Comunicazione Seriale 6

To get this file to display nicely, you need to enable the


newLine mode in RealTerm. Displaying an extra line or
two also helps.

Programmi di esempio Lettura


Il programma eepromread_spiG2231.c rilegge i contenuti della EEPROM e li invia attraverso una UART (a
9600 baud) sul display. Si abilitata la modalit newLine. Il carattere da trasmettere di default inizia soltanto
una nuova linea, senza un carattere di a capo; questa modalit aggiunge un a capo, in modo che il testo
venga visualizzato correttamente. Se si perde una linea display, occorre aumentare il numero di righe.
Programmi di esempio Scrittura: Metodo 2
Linserimento di una pausa di 5 ms tra un carattere e laltro riduce la frequenza di trasmissione effettiva a
200 baud! Per fortuna, potendo scrivere fino ad unintera pagina alla volta, anche dopo aver scritto 16 byte il
tempo di ritardo necessario ancora soltanto 5 ms. A 9600 baud, ogni carattere impiega poco pi di 1 ms a
essere trasmesso, cosicch finch si scrivono pi di 5 caratteri alla volta, il tempo di trasmissione e
memorizzazione supera il settling time tra i comandi di scrittura, per cui non necessario aggiungere ritardi.
Lo svantaggio, naturalmente, che occorre tenere im memoria tutti i caratteri in un buffer nellMSP430
mentre si aspetta. Per fortuna gli MSP430 hanno una RAM sufficiente per questo, comunque qualcosa da
tenere a mente, soprattutto in programmi pi complessi di scrittura su EEPROM.
In eepromwrite2_spiG2231.c si implementata la stessa tecnica del codice precedente, ma scrivendo una
pagina intera di 16 byte ala volta. I valori ricevuti sono memorizzati in un array in un buffer, e poi trasmessi
alla EEPROM per essere scritti. Il tempo che impiega a inviare un comando completo di write inferiore al
tempo che impiega a ricevere i successivi 16 caratteri, cosicch il codice ritorna al ciclo for per aspettare i
caratteri. Se si opera una SPI ad una frequenza di trasmissione pi bassa questa condizione non
soddisfatta?
Analisi dei segnali SPI
Le due immagini successive mostrano i segnali del primo programma eepromwrite_spiG2231.c. Le linee
sono etichettate come SPI dalla parte della EEPROM, e UART dalla parte dellMSP430. Si nota nella prima
figura che le linee SPI operano soltanto ogni 5 ms, tra un carattere e laltro. C molto tempo sprecato in
questo metodo. Il secondo pannello mostra le linee in dettaglio. Si noti che ci sono due istruzioni da usare,
poich CS mostra due impulsi. SCLK configurato correttamente per la modalit scelta, basso in quiete e
lettura sul fronte di salita del clock. In un comando write, la EEPROM non invia nulla indietro, cos la SDO
alta durante le istruzioni. La EEPROM vede listruzione 0x06 (WREN) seguita dalla 0x02 (WRITE). Allora
lindirizzo a 16 bit 0x06 ricevuto, seguito dal valore 0x2A (corrisponde allasterisco) da scrivere su
quellindirizzo.

87

Comunicazione Seriale 6

Per il programma eepromread_spiG2231.c, i segnali UART ora seguono le istruzioni SPI (vengono trasmessi
i valori letti) Ci sono molti meno tempi morti in questo programma, poich non sono necessari i settiling
time tra le letture. Osservando il pannello pi in basso, si vede una singola istruzione. 0x03 (READ) seguita
da un indirizzo, 0x08 in questo caso, e poi 8 bit di riempimento (dummy). Nello stesso tempo la linea SDO
in quiete fino agli ultimi 8 bit, a quel punto riporta il valore 0x2A come valore memorizzato allindirizzo
0x08 nella EEPROM. Dopo un breve ritardo (dovuto agli step nellMSP430), comincia la successiva
trasmissione UART.
Una cosa da notare che la linea SDI non in quiete (alta) per tutto il tempo. Questo dovuto alla stessa
ragione per cui si voluto inserire un resistore sulla linea SDO per connetterla a Vcc. Probabilmente meglio
fare questo per entrambe le linee. Lasciandole scollegate, si pu dimostrare che il processo funziona lo
stesso; solo una convenzione che richiede di avere uno specifico livello di quiete.

88

Comunicazione Seriale 6

Infine, ci sono tre pannelli per illustrare il funzionamento del codice eepromwrite2_spiG2231.c. Il primo e il
terzo sono analoghi a quelli mostrati prima. Si noti che quasi non ci sono tempi morti nei segnali ricevuti
sullUART. Il pannello di mezzo mostra che sebbene si debbano trasmetteer 16 byte di dati da scrivere, la
lunghezza totale dellistruzione write sulla SPI inferiore al tempo per ricevere un singlo byte sullUART. Se
cos non fosse, e si dovesse completare prima una trasmissione UART, si potrebbe rovinare la trasmissione
cambiando i valori nel buffer. La SPI pu operare pi velocemente di quanto mostrato in questo caso.

89

Comunicazione Seriale 6

INTER-INTEGRATED CIRCUIT BUS (IC)


Il protocollo SPI un sistema utile e semplice, ma non privo di svantaggi. Nellesempio visto si nota che in
ogni dato istante, nonostante la possibilit di scambiare messaggi contemporaneamente, solo il master o lo
slave inviano dati significativi. Questo sistema in grado di collegare insieme una molteplicit di dispositivi,
ma ogni dispositivo ha ununica linea privata, e il master seleziona con quale dispositivo comunicare in ogni
istante. Nel frattempo le linee che non vengono usate occupano spazio e risorse che potrebbero essere
destinate ad altre funzioni.
Nei sistemi elettrici una linea che collega pi dispositivi viene chiamata bus. Nella comunicazione seriale
un bus una linea su cui pi dispositivi possono cercare segnali o generarne di propri. Un sistema di tipo
bus riduce il numero di connessioni necessarie per collegare insieme vari dispositivi.
Il protocollo Inter-integrated Circuit Bus (IC) stato sviluppato negli anni 80 dalla Philips Semiconductors
(ora NXP). Garantisce la piena funzionalit di un bus, usando solo due linee, una per i dati (SDA) e una per
il clock (SCL). Ovviamente ci sono degli svantaggi. Il primo la lentezza, solo 100 kbit/s in modalit
standard, il secondo che bisogna seguire un protocollo di comunicazione. Serve pi hardware, perch la
comunicazione deve essere controllata da circuiti logici, ovvero da una macchina a stati. Questa pu essere
implementata nellhardware, come nella USCI_B, o nel software per la USI. I trasferimenti di dati avvengono
tra un master ed uno slave. Ogni slave ha un unico indirizzo, lungo solitamente 7 bit. Il master inizia il
trasferimento, fornisce il clock, indirizza un particolare slave, ed infine termina la comunicazione.

90

Comunicazione Seriale 6

I2 C Hardware
Linterfaccia elettronica di un bus I2C mostrato nella figura successiva per un master e due slave. La
maggior parte degli slave pu controllare solo la linea dati SDA e non quella di clock SCL.
Gli output digitali possono essere sia VSS (o ground) per uno 0 logico, che VCC per un 1 logico. Sarebbe un
problema se due output di valore diverso si connettessero simultaneamente allo stesso bus. Il bus I2C evita
questa eventualit usando gli elementi attivi (NMOS), nei driver dei dispositivi che si affacciano sul bus, solo
per abbassare le due linee SCL ed SDA, che sono mantenute alte dai resistori di pull-up. Tali elementi hanno
pertanto un output di tipo open-drain (o open-collector). Ci significa che c solo un NMOS tra loutput e il
ground. Il master pu leggere e scrivere sulle linee, lo slave in genere solo sulla SDA. Il resistore di pull-up
mantiene la linea a VCC finch non c nessuna attivit; se un singolo NMOS si attiva, la linea abbassata a
VSS (o ground) per dare il livello logico 0, e scorre una corrente verso il ground. Se un dispositivo cercasse di
scrivere un 1 sul bus ed un altro uno 0, vincerebbe il secondo, ma il primo output non verrebbe disturbato
dalla linea. Questo tipo di connessione si chiama wired and perch la linea si porta a 1 solo se tutti i
dispositivi sono a 1, altrimenti a 0. Nei bus con pi di un master si pu creare un problema se pi master
accedono al bus contemporaneamente, per cui serve un arbitrato. Un approccio simile viene usato ad
esempio nei bus CAN (controller area network), che non verr descritto qui. Il sistema wired-and evita
problemi di contenzioso ma ha due svantaggi. Il primo lo spreco di corrente quando la linea abbassata, il
secondo il ritardo temporale che si crea quando la linea si deve caricare a 1.

Problemi con un Bus Seriale


Per evitare interferenze, un bus affollato in un sistema elettrico ha bisogno di regolare chi pu parlare e
quando.
Problema 1 Poich molti dispositivi controllano la stessa linea, per evitare che un dispositivo abbassi il
livello della linea (pull-down), mentre un altro la alza (pull-up), causando pericolosi corti circuiti, la specifica
IC richiede che i vari dispositivi possano collegarsi alla linea SDA soltanto abbassandone il livello. La linea
collegata a Vcc attraverso una resistenza di pull-up, cos, se nessun dispositivo collegato, si trova di default
in uno stato alto (questo simile alla necessit di resistori nellesempio SPI). In termini delle porte GPIO
dellMSP430, ci equivale ad impostare un pin come output a livello basso, input a livello alto; quindi si far
commutare a seconda che il pin debba scrivere un dato sulla linea (output), abbassando il livello della linea,
o debba leggere un dato dalla linea (input), permettendo alla linea di restare a livello alto, evitando danni al
sistema, secondo una tecnica denominata del bit-bang IC.
Problema 2 La conseguenza di usare il resistore di pull-up sulla linea un limite nella velocit con cui si
pu comunicare con lIC. Ogni capacit sulla linea, combinata con il resistore, allungher i tempi di salita
della linea. Se la resistenza troppo elevata, la linea non si sollever abbastanza velocemente per raccogliere
il messaggio inviato, se troppo bassa, la linea assorbe troppa potenza attraverso il resistore quando bassa
(collegata a massa). La specifica standard per una IC limita la velocit ad un clock di 100 kHz (standard
mode). Con una alimentazione di 3V, usata in molti progetti con lMSP430, la resistenza tipicamente
compresa tra 1 e 10k; se si preferisce lalta velocit, si sceglier 1k. Altre specifiche permettono frequenze

91

Comunicazione Seriale 6

di clock fino a 400 kHz (fast mode) o 1 MHz (fast mode plus). Queste modalit richiedono resistenze pi
piccole, per cui consumano pi potenza.
Problema 3 Poich molti dispositivi sono in ascolto sulla stessa linea, l IC assegna un segnale di chiamata
(call sign), sotto forma di un indirizzo a 7 bit, ad ogni dispositivo, affinch una richiesta del master sia
indirizzata ad uno solo di essi. Il bit 8 aggiunto per specificare se il master vuole leggere o scrivere in quel
dispositivo. Questo indirizzo di solito codificato nel dispositivo in maniera hardware (dal datasheet).
Alcuni dispositivi hanno solo una parte dellindirizzo codificata in maniera hardware (fissa), la rimanente
pu essere codificata via software (variabile), nel caso che sulla linea possano trovarsi pi dispositivi dello
stesso tipo.
Problema 4 Oltre a sapere chi deve rispondere, i dispositivi (incluso il master) devono anche sapere
quando possono inviare segnali; quando la linea collegata ad un dispositivo, gli altri devono trattenere i
loro messaggi, finch la linea non segnala di essere di nuovo libera. Questa coordinazione si realizza
confrontando i tempi delle variazioni nella linea dati con il clock. La comunicazione nella IC inizia con un
fronte di discesa nella linea SDA, e ne marca la fine con un fronte di salita, molto simile alluso del chip select
nellesempio della SPI. IC specifica che una condizione di start avviene quando la linea dati abbassata
mentre il clock alto, quella di stop quando la linea viene sollevata mentre il clock alto. La trasmissione dei
dati avviene tra questi due eventi. I dati sono inviati dopo che:
1. aver segnalato una condizione di start, cambiando lo stato della linea mentre il clock basso
2. il dispositivo in ascolto legge lo stato mentre il clock alto (equivalente al Mode 3 in SPI).
essenziale che i dispositivi che inviano messaggi non cambino lo stato della linea dati mentre il clock alto,
a meno di non indicare una condizione di start o stop. Gli altri dispositivi devono accorgersi di condizioni di
start e stop, per evitare di mandare propri messaggi nello stesso tempo. Se un dispositivo vede una
condizione di start, ascolter lindirizzo: se non il suo, deve aspettare, finch la linea viene liberata da una
condizione di stop, prima di mandare il suo messaggio.

Protocollo IC
Si vede ora una tipica trasmissione IC e si analizza cosa accade.
Guardando limmagine della prima parte di una trasmissione IC (condizione di start e indirizzamento), si
nota che lo stato del clock (curva inferiore) in quiete a livello alto. Il clock non parte finch il master non
abbassa la linea SDA. La condizione di start segnalata dal fronte di discesa di SDA mentre SCL alto. Il
segnale di start seguito da 9 impulsi del clock su SCL. Si ricorda che a questo punto n il master n lo slave
cambiano lo stato di SDA mentre il clock alto. Notare che i cambiamenti in SDA mentre invia i dati
accadono fra gli impulsi (clock basso), mentre si pu leggere il valore trasmesso nei punti dove il clock alto.
In questo esempio, il dato si legge 0b10010001. I primi 7 bit sono lindirizzo dello slave (0b01001000), seguiti
da un 1, che indica un comando di lettura dallo slave. Il dato seguente sar inviato dallo slave, che segnaler
di essere pronto a inviare. Si noter che ci sono 9 impulsi, ma solo 8 bit; il 9 clock chiamato il bit di
acknowledge. In questo caso il master indirizza lo slave e rilascia la linea dopo aver mandato 8 bit. Se lo
slave riconosce la chiamata, abbassa la linea; il master legge il 9 bit per confermare che lo slave ha ricevuto
listruzione. Se lo slave non in ascolto o fallisce nel riconoscimento del segnale, poich la linea tenuta a
livello alto, e il master ha rinunciato al controllo, la linea automaticamente si tira su. Se il master legge il 9
bit e lo vede alto, interrompe la trasmissione (se tenta di nuovo o meno dipende da come fatto). A questo
punto, ammesso che lo slave abbia riconosciuto listruzione, importante che il master non segnali uno stop
(inviando un fronte di salita sulla SDA mentre il clock alto), per dare tempo allo slave di inviare i dati
richiesti.

92

Comunicazione Seriale 6

Il master scrive un indirizzo, che riconosciuto dallo slave, e legge un singolo byte dallo slave.
.

La figura successiva illustra un esperimento vero. Come si interpreta il resto dellistruzione dipende dal
dispositivo che deve essere letto. Per semplicit si supponga di comunicare con un dispositivo slave che ha
un singolo registro a 2 byte. Non necessario un indirizzamento ulteriore rispetto a quello del dispositivo
stesso (in questo caso 0b0100000-1), cos, quando lo slave riceve listruzione di lettura, invia il valore a 16 bit
che contiene il registro. Il dato trasmesso un byte alla volta, con un bit di acknowledge tra i byte. In questo
esempio si vede lo slave inviare i due byte (00100101) corrispondenti ai caratteri ASCII %%. Dopo il primo
byte, lo slave rilascia la SDA, e aspetta che il master gli mandi un acknowledge di ricezione del dato,
abbassando la linea SDA. Dopo lultimo byte, si noti che il bit di acknowledge diventa alto. Nellultimo ciclo
il master non invia un bit di acknowledge, segnalando che non attende altri dati. Infine il master tira di
nuovo gi la linea SDA, poi rilascia il clock in modo che SCL diventa alto, quindi rilascia la SDA per
mandare una condizione di stop. A questo punto ogni altro dispositivo che ha bisogno di iniziare una
comunicazione libero di farlo; se tra lo start e lo stop un altro dispositivo avesse avuto un messaggio da
inviare, avrebbe dovuto aspettare finch non avesse visto il segnale di stop.

MODALIT USCI / IC
In questa sezione si descrive limplementazione di una funzione single master I2C per un C MSP430, che
possiede un modulo USCI, che semplifica i codici di attivazione della comunicazione. La funzione garantisce
la corretta inizializzazione del modulo USCI, e implementa un protocollo di trasmissione e ricezione I2C,
usando un indirizzo a 7 bit. Si descrive anche una versione che utilizza un modulo DMA (Direct Memory
Access). Invece di dover configurare i diversi registri del modulo USCI, il programmatore pu facilmente
usare le funzioni fornite, con i parametri per iniziare la comunicazione. Queste funzioni servono solo per
impostare il modulo USCI. facolt del programmatore includere anche funzionalit low-power per
disattivare la CPU, o impegnarla in funzioni di calcolo, durante la comunicazione I2C.

93

Comunicazione Seriale 6

Trasmettitore master (il master indirizza uno slave e trasmette dati a esso)

Ricevitore master (il master indirizza uno slave e riceve dati da esso)
Occorre aggiungere al progetto il file TI_USCI_I2C_master.c, che implementa la comunicazione I2C usando
solo il modulo USCI, oppure il file TI_USCI_I2C_master_dma.c che utilizza anche il modulo DMA. Il
corrispondente header file TI_USCI_I2C_master.h o TI_USCI_I2C_master_dma.h deve essere incluso, per
accedere alle funzioni master I2C. Questo programma gira sul C master, connesso ad un C slave, su cui
gira la corrispondente versione slave (TI_USCI_I2C_slave.c).
Lapplicazione stata sviluppata per la famiglia 2xx, tuttavia pu essere facilmente modificata per utilizzarla
con un altro MSP430 con un modulo USCI.
Luso del DMA causa maggiore lavoro nellinizializzazione e nelle routine di interrupt, per cui
raccomandato solo se si deve trasferire un grande numero di byte.
Se lMSP430 non ha un modulo DMA, o ce lha ma non si vuole usare, si chiamano le funzioni senza _DMA
Esempio di ricezione di n byte
#include "msp430x26x.h"
#include "TI_USCI_I2C_master.h"
unsigned char array[5] = { 0, 0, 0, 0, 0 };
void main(void)
{
WDTCTL = WDTPW + WDTHOLD;
// stop WDT
_EINT();
// enable interrupts
TI_USCI_I2C_DMA_receiveinit(0x48,0x3f); /* initialize USCI and DMA module (0x48=7-bit slave address right
justified, 0x3f=prescale parameter: the resulting baud rate is the
quotient of DCO frequency and the prescale parameter) */
while ( TI_USCI_I2C_notready() );
// wait for bus to be free
TI_USCI_I2C_DMA_receive(3,array);
/* receive the first 3 bytes of array (3=number of bytes to be received,
array=pointer into an array variable that is used to store the received
bytes. Since I2C communication works bytewise, it makes sense to use a
field of bytes, for example, unsigned char values) */
LPM0;
}
// put CPU to sleep during communication
Esempio di trasmissione di n byte
#include "msp430x26x.h"
#include "TI_USCI_I2C_master.h"
unsigned char array[5] = { 0x1, 0x2, 0x3, 0x4, 0x5 };
void main(void)
{
WDTCTL = WDTPW + WDTHOLD;
// stop WDT
_EINT();
// enable interrupts
TI_USCI_I2C_DMA_transmitinit(0x48,0x3f); // initialize USCI and DMA module
94

Comunicazione Seriale 6

while ( TI_USCI_I2C_notready() );
TI_USCI_I2C_DMA_transmit(3,array);
LPM0;
}

// wait for bus to be free


// transmit the first 3 bytes
// put CPU to sleep during communication

Controllare (checking) la presenza di uno slave


Questo esempio mostra come controllare se uno slave con un certo indirizzo connesso o meno al bus I2C.
Questa funzione differisce dalle due precedenti, in quanto blocca la CPU durante lesecuzione e rileva se uno
slave ha riconosciuto o meno il master.
void main(void)
{
WDTCTL = WDTPW + WDTHOLD;
// stop WDT
TI_USCI_I2C_transmitinit(transmit_cb,0x48,0x2f);
_EINT();
if (!TI_USCI_I2C_slave_present(0x11))
// check for slave
while (1);
// trap CPU if slave with address 0x11 doesnt answer
LPM0;
}
// Enter LPM0 w/ interrupt

IC MASTER CON LA USCI_B0 SU UN FG4618


In questo paragrafo vengono illustrate due programmi per interfacciarsi con un termometro usando una IC
sulla Experimenter Board TI MSP430FG4618/F2013. Lestensione ad altre schede simili molto semplice.
Il C FG4618 il master, che riceve 2 byte di dati dallF2013, che lo slave. Le librerie di codice sono
disponibili in due Application Note della TI, Using the USCI I2C Master (slaa382), e Using the USCI I2C Slave
(slaa383).
Un test molto semplice per un master isolato consiste nellinviare un indirizzo per uno slave. Lindirizzo non
riconosciuto, ovviamente, perch non c nessuno slave, ed il master dovrebbe reagire di conseguenza. Il
test fallisce se gli output non sono abilitati o non ci sono resistori di pull-up sul bus.
La USCI_B ha una macchina a stati nellhardware. Quando il dispositivo uno slave, riconosce
automaticamente il proprio indirizzo, richiede un interrupt, e si configura come trasmettitore o ricevitore a
seconda del bit R/W. Come master, genera le condizioni di start e stop quando richiesto. In entrambi i casi i
bit di acknowledgment sono inviati e ricevuti automaticamente. La USCI_B usa alcuni registri propri della
modalit IC, ma molti sono in comune con la SPI, semplicemente rinominando i bit. Questo crea confusione
negli header file, perch richiedono due blocchi di definizione per molti registri. Per esempio, il bit 7 di
UCB0CTL0 chiamato UCCKPH e imposta la fase del clock nella SPI, ma diventa UCA10 per
lindirizzamento a 10 bit della IC. Ci sono alcuni altri registri:
(Own address) UCB0I2COA: contiene lindirizzo del dispositivo quando slave. C anche un bit chiamato
UCGCEN, che pu essere posto a 1 per abilitare il dispositivo a rispondere ad una call.
(Slave address) UCB0I2CSA: specifica lindirizzo dello slave quando il dispositivo un master. Serve un
registro separato perch un dispositivo pu funzionare sia da master che da slave in un sistema multi master
(C un piccolo errore nel datasheet e nellheader file dellFG461x, se non stato corretto: entrambi UCB0I2COA e
UCB0I2CSA sono elencati come byte invece che word).

(Interrupt enable) UCB0I2CIE: contiene il bit che abilita i 4 interrupt usati dallIC per segnalare
cambiamenti di stato: not-acknowledge, stop condition, start condition, e arbitration lost. I flag
corrispondenti UCNACKIFG, UCSTPIFG, UCSTTIFG, e UCALIFG sono in UCB0STAT. Il registro
UCB0STAT contiene anche il bit UCSCLLOW, che settato quando un altro dispositivo sul bus mantiene
SCL basso. utile in fase di debugging del sistema, quando il bus bloaccato perch uno slave rifiuta di
rilasciare SCL.
La USCI_B ha due vettori di interrupt, entrambi condivisi con la USCI_A. Uno usato per la ricezione, laltro
per la trasmissione nella SPI, ma sono allocati in maniera differente per la IC:
il vettore per la trasmissione (USCIAB0TX_VECTOR nellheader file) usato sia per trasmettere che
ricevere i flag UCB0TXIFG e UCB0RXIFG; questi sono negli stessi registri nella SPI. Ha senso
condividere questi flag perch la IC solo half duplex: i dati sono trasferiti in una direzione alla volta,
diversamente dalla SPI e dalla UART;
il vettore che normalmente usato per la ricezione (USCIAB0RX_VECTOR) invece usato per i flag dei
4 cambiamento di stato in UCB0STAT.
Prima di guardare il software utile sapere come che aspetto dovrebbero avere le transizioni.
95

Comunicazione Seriale 6

La figura mostra una traccia di oscilloscopio del master FG4618 che legge 2 byte dallo slave F2013. Essendo 2
byte, il master manda il lacknowledge dopo il primo byte. Si usato un valore fittizio di 0xA5AA per il dato
per enfatizzare vari aspetti del protocollo. Si ricordi che il master fornisce il clock durante tutta la
transazione. Qui ha una frequenza di 100 kHz, che il massimo per lIC standard.
C una sequenza di eventi:
1. Il master genera la condizione di start (S)
2. Il master invia lindirizzo, 0b1001000, seguito da R/W= 1 per comunicare che vuole leggere dallo slave.
3. Lo slave riconosce lindirizzo mandando un valore basso su A.
4. Lo slave invia il primo byte di dati, 0xA5.
5. Il master riconosce la ricezione del byte inviando un valore basso su A.
6. Lo slave invia un secondo byte di dati, 0xAA.
7. Il master non riconosce questo byte, per comunicare che ha ricevuto abbastanza dati, per cui invia un
valore alto su A.
8. Il master invia una condizione di stop (P) per concludere la transazione e rilasciare il controllo del bus.
Si noti la forma asimmetrica degli impulsi sia su SCL che SDA. I fronti di discesa sono tirati gi dai transistor
open-drain e sono quindi rapidi. Invece i fronti di salita dipedono dalla corrente che scorre attraverso i
resistori di pull-up per caricare le capacit del bus.
Per impostare la USCI_B0 occorre:
Control register 0, UCB0CTL0: si deve scegliere UCMODE_3 per lIC e UCSYNC per la modalit sincrona.
Il bit UCMST configura il modulo come master.
Control register 1, UCB0CTL1: seleziona la sorgente del clock, UCSSEL1 per SMCLK. Il bit UCSWRST deve
rimanere a 1 per mantenere il modulo nella sua condizione di reset, fino a che tutto stato configurato. Il bit
UCTR determina se il modulo trasmette o riceve (qui 0 perch riceve).
Baud rate control register, UCB0BR0 and UCB0BR1: seleziona il baud rate. Si scelto un divisore per 10 per
ottenere circa 100 kHz. Il valore minimo 4 (o 16 se ci sono pi master).
Own address register, UCBI2COA: questo dispositivo solo master, per cui non necessario definire il suo
indirizzo, che 0 di default. N si usano in questo esempio delle call generali, per cui il bit UCGCEN 0.
Il software di questo esempio non usa interrupt per la IC, per cui si lasciato il bit UCB0I2CIE nel suo stato
di default con tutti gli interrupt di cambiamento di stato disabilitati.
// usci2cmaster1.c - receive temperature over I2C using USCI_B0
// Master mode , receive two bytes from slave; needs pull -ups on SCL , SDA
// Simple control flow for I2C , all in main routine , no interrupts
// FG4619 on TI Experimenter's Board , 32KHz crystal , 1MHz DCO (default)
// SCL on P3.2, SDA on P3.1
// Display temperature on LCD , flash LED to show activity
// J H Davies , 2007 -12 -01; IAR Kickstart version 4.09A
// ---------------------------------------------------------------------------------------------------#include <io430xG46x.h>
// Needs corrected version for USCI!
#include <intrinsics.h>
// Intrinsic functions
#include <stdint.h>
// Integers of defined sizes
#include "LCDutils2.h"
// TI exp board utility functions
#define SLAVE_ADDRESS 0x48
// I2C address of thermometer

96

Comunicazione Seriale 6
#define LED P5OUT_bit.P5OUT_1
// Output pin for LED (active high). Pin is output low by default
void main (void)
{
volatile uint16_t i;
// Loop counter to stabilize FLL+
int16_t Temperature;
// Value received over I2C
WDTCTL = WDTPW | WDTHOLD;
// Stop watchdog timer
FLL_CTL0 = XCAP14PF;
// 14pF load caps; 1MHz default
do
{
// Wait until FLL has locked
for (i = 0x2700; i > 0; --i) { }
// One loop should be enough delay for FLL+ to lock
IFG1_bit.OFIFG = 0;
}
// Attempt to clear osc fault flag
while (IFG1_bit.OFIFG != 0);
// Repeat if not yet clear
PortsInit ();
// Initialize ports
LCDInit ();
// Initialize SBLCDA4
DisplayHello ();
// Display HELLO on LCD
P3SEL = BIT1 | BIT2;
// Route pins to USCI_B for I2C
// 7-bit addresses (default), single master , master mode , I2C , synch
UCB0CTL0 = UCMST | UCMODE_3 | UCSYNC;
// Clock from SMCLK , receiver (default), hold in reset
UCB0CTL1 = UCSSEL1 | UCSWRST;
UCB0BR1 = 0;
// Upper byte of divider word
UCB0BR0 = 10;
// Clock = SMCLK / 10 = 100 KHz
UCB0I2COA = 0;
// Ignore genl call; own address = 0
UCB0CTL1 &= UCSWRST;
// Release from reset
// Set up basic timer for interrupts at 2Hz (500ms)
BTCTL = BTHOLD | BT_ADLY_500;
// Hold , period = 500ms from ACLK
BTCNT2 = 0;
// Clear counter
BTCTL &= BTHOLD;
// Start basic timer
IE2_bit.BTIE = 1;
// Enable basic timer interrupts
for (;;)
{
// Transfers triggered by BT
__low_power_mode_3 ();
// Wait for BT (needs only ACLK)
LED = 1;
// Show start of activity
UCB0I2CSA = SLAVE_ADDRESS;
// Slave to be addressed
UCB0CTL1 |= UCTXSTT;
// Send Start and slave address
while (( UCB0CTL1 & UCTXSTT) != 0) { }
// Wait for address to be sent
if (( UCB0STAT & UCNACKIFG) != 0)
{
// Address NOT acknowledged?
UCB0CTL1 |= UCTXSTP;
}
// Send Stop condition and finish
else
{
// Address acknowledged: receive
while (IFG2_bit.UCB0RXIFG == 0){ }
// Wait for first byte
Temperature = UCB0RXBUF << 8;
// MSB of temperature
UCB0CTL1 |= UCTXSTP;
// Send Stop condition after byte
while (IFG2_bit.UCB0RXIFG == 0){ }
// Wait for second byte
Temperature |= UCB0RXBUF;
// LSB of temperature
DisplayCentiCels (Temperature);
}
// Display temp to 0.01oC
LED = 0; // Show end of activity
}
// ---------------------------------------------------------------------// ISR for basic timer: return to main routine
// ---------------------------------------------------------------------#pragma vector = BASICTIMER_VECTOR
__interrupt void BASICTIMER_ISR (void)
{
// Acknowledged automatically
__low_power_mode_off_on_exit();
}
// Return to start new I2C message

Le transazioni IC sono triggerate dagli interrupt dal Timer1 ogni 0.5 s. In questo programma vengono usati
dei cicli di polling per controllare quando completata ogni fase della transazione, ma in pratica si usano gli
interrupt. Questi sono gli step principali:
1. Lindirizzo dello slave scritto in UCBI2CSA; questo si pu fare in qualsiasi momento
2. La transazione inizia settando il bit UCTXSTT bit in UCB0CTL1. Si potrebbe settare anche il bit UCTR, se
il master vuole anche trasmettere dati. La USCI_B automaticamente controlla se il bus disponibile,
genera una condizione di start (S), trasmette lindirizzo, e riceve il bit di riconoscimento. Azzera il bit
UCTXSTT quando questa fase complete.
3. Se lo slave non riconosce lindirizzo, il flag UCNACKIFG nel registro di stato UCB0STAT impostato.
Viene anche richiesto un interrupt se il correspondente bit di enable UCNACKIE settato in UCB0I2CIE.
In questo programma si risposto settando il bit UCTXSTP in UCB0CTL1, che genera una condizione di
stop e conclude la transazione.

97

Comunicazione Seriale 6

4.

Se lindirizzo riconosciuto, la USCI_B procede automaticamente a ricevere il primo byte di dati. Il flag
di ricezione UCB0RXIFG si alza quando il byte completo ed stato copiato nel buffer del ricevitore
UCB0RXBUF. Un interrupt pu essere richiesto se abilitato. Di norma I dati dovrebbero essere letti
prima che il prossimo byte completo, cosa che azzera UCB0RXIFG. La temporizzazione abbastanza
tranquilla perch c un doppio buffering e la USCI_B aiuta ulteriormente trattenendo SCL se
UCB0RXBUF non staot letto in tempo. Cos non c bisogno di un flag di overrun.
5. La USCI_B automaticamente manda un bit di acknowledgment (A) e continua a ricevere il prossimo
byte. Qui il second byte anche lultimo, e quindi si settato il bit UCTXSTP, mentre questo byte viene
ricevuto. Questo dice alla USCI_B di inviare un bit di not-acknowledge (A) dopo la ricezione, e generare
una condizione di stop (P), piuttosto che tentare di ricevere un altro byte.
6. Il flag di ricezione UCB0RXIFG di nuovo alto quando il secondo byte completo, terminando la
transazione.
Se invece si vuole ricevere un solo byte, il bit UCTXSTP deve essere settato mentre questo byte viene
ricevuto, ma non c nessun flag UCB0RXIFG dal byte precedente per triggere questo. Invece bisogna
aspettare finch il bit UCTXSTT si azzera, controllare che UCNACKIFG basso per comunicare che
lindirizzo stato riconosciuto, poi settare UCTXSTP mentre il primo byte viene ricevuto. Questo naturale
facendo il polling, ma meno ovvio in un processo governato dagli interrupt.

MODALIT USI / I2C


Sulla USI di un MSP430 implementare un protocollo I2C pi complicato di una SPI. Uno dei problemi
principali che una parte dellimplementazione deve essere fatta via software; il modulo USCI, invece,
unimplementazione hardware pi completa, che rende pi facile la comunicazione I2C, ma non tutti i C
hanno questo modulo. Per quei dispositivi che non hanno nessun modulo seriale, si raccomanda di usare il
protocollo SPI. possibile implementare un protocollo I2C completamente via software con i timer, ma non
semplice. La tecnica del bit banging molto pi semplice con la SPI.
Quando si considera come un dispositivo reagisca ad un interrupt, ci sono tre possibilit. La prima che c
soltanto un task da eseguire. Questa reazione la pi semplice e diretta da codificare. Un esempio quello
di usare linterrupt del timer per commutare lo stato di un LED. La seconda possibilit quella di avere un
insieme di task possibili da dover eseguire, ed una serie di circostanze determina quale task va eseguito nel
momento dellinterrupt. Spesso questo viene realizzato con luso di istruzioni condizionali in C. Terza
possibilit, ci pu essere una sequenza di task da eseguire passo-passo, in ogni passo temporizzato da una
successione di interrupt. Questo caso viene anche descritto come macchina a stati.

Il modulo USI ha naturalmente un solo interrupt (in realt due, se il C configurato come slave, c un altro
interrupt per segnalare la condizione di start per una transazione I2C). Questo interrupt triggerato ogni

98

Comunicazione Seriale 6

volta che un valore non nullo scritto su USICNT. Ogni transazione I2C richiede pi di una scrittura su
USICNT, e si pu fare uso di questo per impostare il modulo come macchina a stati. Cos facendo diventa
pi facile capire cosa sta accadendo e i problemi di debug, e consente anche di rimanere pi tempo in
modalit low power. Esaminando la flow chart che descrive la macchina a stati per un master I2C, si
cercato di separare i diversi stati, cos come appaiono nel codice. Si noti in particolare che la logica che deve
analizzare il bit Ack/Nack nella stessa sezione di codice che gestisce la trasmissione/ricezione dei dati.
Inoltre, per non aumentare la complicazione del diagramma, esso non descrive la gestione di Ack/Nack nel
caso di dati costituiti da pi di un byte, anche se questo pu capitare.
Il diagramma della macchina a stati per una configurazione slave non molto differente, basta sostituire i
primi due blocchi con un indirizzo di ricezione e se necessario un ackowledge. Una risposta Nack
dovrebbe mandare direttamente nello stato di quiete (idle) in questo caso. Invece di generare una condizione
di stop, uno slave dovrebbe semplicemente ripulire i registri (magari prendendo una nuova misura o
altrimenti preparsi per il prossimo comando) e tornare nello stato di quiete.
Il concetto di macchina a stati si associa molto facilmente allistruzione id switch in C. Osservando il
diagramma a stati, si dovrebbe codificare linterrupt USI con 6 differenti stati. Si deve assegnare un numero
ad ogni stato (es. 1-5 + uno stato di default per lo stato di quiete), oppure usando i costrutti enumerativi in C,
rendendo il codice molto pi leggibile.
Configurare la USI per I2C
Il registro USICTL0 gestito in maniera simile al setup per la SPI. La differenza principale che non serve
pi P1.5, poich esiste solo una linea dati in I2C. In USICTL1 serve solo abilitare il bit USII2C. Per la I2C,
come si ricorda, si vuole la stessa polarit/fase del Modo 3 della SPI; USICKCTL dovrebbe essere connesso a
una qualunque sorgente di clock (frequenza divisa), con il bit USICKPL abilitato.
Infine, quando si usa la USI in modalit I2C, si deve disabilitare lazzeramento automatico dei flag di
interrupt, abilitando il bit USIFGCC in USICNT. Il motivo sar chiaro quando si esaminer la ISR. Poich
uno dei bit superiori di USICNT ora abilitato, si pu cominciare la trasmissione/ricezione, semplicemente
assegnando il numero di bit a questo registro. Per questa ragione, si attiver il clock usando un operatore
OR, per evitare di cambiare qualcuno dei bit superiori di questa configurazione.
Il codice per impostare la USI per I2C qualcosa del genere:
USICTL0 = USIPE6 + USIPE7 + USIMST + USISWRST;
// Enable ports and set as master
USICTL1 = USII2C + USIIE;
// I2C mode, enable interrupts
USICKCTL = USISSEL_2 + USIDIV_3 + USICKPL;
// Use SMCLK/8, Mode 3 phase/polarity
USICNT |= USIIFGCC;
// Disable automatic clear control
USICTL0 &= ~USISWRST;
// Enable USI
USICTL1 &= ~USIIFG;
// Clear any early flags
Set-up della ISR
Questo frammento di codice un modo semplice per impostare la ISR in modo che permetta sia la
trasmissione che la ricezione in I2C. Ci sono alcune cose da notare in questo codice:
Il formato per usare tipo enumerabile in C :
enum{item_1, item_2, ... , item_n} var_name = initial_state
La numerazione cos come fatta in questo esempio ridondante, ma serve a illustrare che si possono
assegnare valori espliciti alla enumerazione. Questa variabile dichiarata come statica, per conservare il
suo valore tra una chiamata e laltra dellISR.
Si ricorda che le condizioni di start e stop sono date dai cambi nella linea SDA mentre il clock alto.
Questo si gestisce abilitando il latch (registro a 1 bit) di uscita della linea SDA, usando il bit USIGE in
USICTL0. La linea SDA assumer immediatamente qualunque valore si trovi nel primo bit da inviare in
USISRL, pertanto questo registro impostato a 0x00 per lo start e a 0xFF per lo stop (sarebbe necessario
impostare solo il bit MSB, ma questo metodo ha il vantaggio di essere sia conveniente che facile).
Si ricorda inoltre che nella I2C la linea SDA pu essere cambiata sia dal master che dallo slave. In
ricezione, il controllo della linea deve essere abbandonato da chiunque sta trasmettendo. Questo si
realizza settando (1) il bit USIOE in USICTL0 per controllare la linea, e azzerandolo per rilasciarla.

99

Comunicazione Seriale 6

Al flag SCFG ,usato finora nei codici della comunicazione seriale, occorre aggiungere due bit in questo
caso: il bit 3 indica trasmissione quando 1, ricezione quando 0. Il bit 4 usato per indicare che un
nuovo valore ricevuto stato salvato nel buffer. I buffer ITXBuffer e IRXBuffer devono essere definiti
prima (i nomi sono stati assegnati pensando gi alluso della I2C in parallelo con lUART).
Anche la variabile, dal nome autoesplicativo, slave_address deve essere definita prima.
Si noti laggiunta di un ulteriore stato chiamata PrepStop, che necessario prima di inviare una
condizione di stop, poich la linea deve essere bassa prima di inviare uno stop. Si noti che ogni caso
dellistruzione switch termina settando uno o pi dei 5 bit in USICNT e settando lo stato successivo.
Dopo la pausa (break), USIIFG azzerato. Quando il modulo USI ha terminato di trasmettere ogni cosa,
si genera un interrupt, e la routine si porta nello stato successivo. Lo stato PrepStop necessario per
permettere che il precedente Ack/Nack finisca prima di interrompere la comunicazione.
Infine, lazzeramento dellUSIIFG eseguito manualmente al fine di allungare il clock quando
necessario per una comunicazione lenta. Se qualcosa ritarda il master, lazzeramento manuale assicura
che ogni passo della ISR stato eseguito, prima che un altro interrupt sia permesso.

EEPROM MICROCHIP 24XX08


Sebbene impostare una USI per il protocollo I2C sia pi complicato, il valore aggiunto di questa complessit
diventa evidente quando si applica ad unimplementazione reale del protocollo. La EEPROM Microchip
24AA08 lequivalente I2C della 25AA080 usata con la SPI. La versione I2C di questa EEPROM
sostanzialmente pi semplice.
Istruzioni
Questo chip, diversamente dalla sua controparte I2C non ha uno status register. La protezione dalla scrittura
gestita strettamente dallhardware, attraverso un singolo pin connesso o a ground (write enable) o a Vcc
(write protect). Ci sono effettivamente solo due istruzioni, e le istruzioni di Read e di Write possono
riguardare un singolo byte o fino a 16 byte (una pagina). Listruzione Seek (ricerca) non altro che
unistruzione Write senza inviare alcun dato da scrivere.
Seek La 24xx08 ha un registro che punta allindirizzo corrente della sua memoria per il prossimo
comando Read/Write. Si pu cambiare lindirizzo corrente inviando lindirizzo dello slave (EEPROM)
con un bit di write (0 nell8 bit inviato), seguito dallindirizzo della cella di memoria. Ognuno di questi
comandi, ovviamente, seguito da un bit di acknowledge. Il master invia una condizione di stop
immediatamente dopo lindirizzo di memoria.
Write Si scrive un valore nella 24xx08 inviando lindirizzo dello slave con il bit di write, seguito
dallindirizzo di memoria da scrivere. Questo seguito dal valore a 8 bit da scrivere, e da una condizione
di stop. Si possono scrivere fino a 16 byte in celle contigue di memoria, inviando i valori successivi prima
della condizione di stop.
Read Si legge un valore dalla 24xx08 inviando lindirizzo dello slave con un bit di read (un 1 nell8 bit
inviato). Dopo il segnale di acknowledge, lo slave invia il valore a 8 bit, letto dallindirizzo corrente, nei
successivi 8 cicli di clock, e incrementa il puntatore di indirizzo. Se il master manda lacknowledge, lo
slave ripeter loperazione inviando il valore successivo. Quando tutti i valori richiesti sono letti, il
master invia un Nack, indicando che nessun altro dato deve essere inviato, e poi comanda la condizione
di stop. Se viene richiesto un valore da un indirizzo diverso da quello a cui punta il registro della 24xx08,
bisogna inviare unistruzione Seek prima dellistruzione Read.
Design Considerations
Ci sono alcune cose da notare nel datasheet della 24xx08:
Lindirizzo a 7 bit dello slave per questo dispositivo 0b1010xBB. Il bit x pu assumere qualunque
valore. Gli ultimi due bit BB si riferiscono al blocco allinterno del chip. Lo spazio di memoria diviso in
4 blocchi da 256 byte. Ogni byte allinterno del blocco ha un indirizzo a 8 bit tra 0 e 255. Il bit x viene
usato in un dispositivo pi grande con 8 blocchi. Questo significa se solo uno di questi dispositivi pu
essere usato su un bus I2C. Aggiungere un altro dispositivo (es. unaltra EEPROM) creerebbe un conflitto
di indirizzi sul bus. Aggiungendo allindirizzo l8 bit di R/W, i valori del byte in esadecimale vanno da
0xA0 a 0xA7 se x=0, e da 0xA8 a 0xAF se x=1; questi ultimi sono pertanto equivalenti ai primi.
100

Comunicazione Seriale 6

La maggior parte dei package di questo chip hanno tre pin chiamati A0, A1 e A2, che non sono usati e
non sono connessi internamente. Dispositivi pi grandi usano questi pin al posto dei bit di blocco, per
determinare lindirizzo del chip, permettendo luso di 8 chip sullo stesso bus. Per il chip usato in questo
caso questi pin possono essere connessi al ground, a Vcc oppure lasciati sconnessi (floating).
La maggior parte delle specifiche temporali nel datasheet sono utili da vedere, sebbene non siano un
problema con le velocit di trasmissione usate in questa applicazione. Il dispositivo pu funzionare fino
a 400 kHz, e persino a questa velocit i requisiti minimi sono ben al di sotto di quelli effettivi. La cosa
importante per lo scopo di questa applicazione sono i tempi di salita e discesa TR e TF . Questo
dispositivo specificato con 10 pF di capacit sulle linee. I pin GPIO dellMSP430 sono specificati con 5
pF. Le linee che connettono i due chip aggiungeranno ancora un po di capacit. Se nel caso peggiore si
fa una stima di 30 pF, e tempi di salita/discesa massimi di 300 ns, si ottiene un valore massimo di
resistenza di 10 k (pull-up/down).
Velocit pi elevate, come si ricorda, traggono beneficio da resistenze pi basse, a discapito della
potenza consumata. I resistori interni disponibili nellMSP430 vanno da 20 a 50 k. Per velocit pi
basse questi probabilmente vanno bene, ma velocit pi alte richiedono un resistore esterno tra 1 e 10
k.
Come la versione SPI di questo chip, il processo di scrittura non inizia finch non si vede una condizione
di stop, e un intervallo di 5 ms (settling time) richiesto per operazioni di scrittura, sia per un singolo
byte che per una pagina da 16 byte. Usando un setup simile a quello del paragrafo precedente, occorrer
tenerlo presente, usando un UART a 9600 baud per inviare i dati da scrivere. Qualunque istruzione
venisse inviata a questo dispositivo durante questo intervallo, il chip non risponderebbe. Il master pu
identificare il Nack facendo un polling, in questo caso, e aspettare finch non viene ricevuto un bit di
Ack prima di inviare listruzione successiva.

Le connessioni del chip sono molto semplici, poich sono necessari solo quattro linee: Vcc, ground, SDA e
SCL. Il pin WP del 24xx08 va connesso a ground per operazioni che consentano la scrittura.
Esempi
Scrittura semplice: i2cerase_G2231.c
Questo esempio un buon punto di partenza, perch scrive un valore alla volta in qualunque cella di
memoria. Poich serve un intervallo di 5 ms tra i comandi di write, in questo codice si usa un semplice
ritardo. Idealmente si dovrebbe permettere allMSP430 di eseguire altri task nel frattempo, e avere un timer
che indica quando si di nuovo pronti a scrivere. Questo codice utilizza un valore 0xFF per cancellare la
flash (eeprom), ma tutti i valori, in esadecimale, decimale o caratteri C sono accettati.
Lettura semplice: i2cread_G2231.c
Questo esempio legge lintero contenuto di una EEPROM e scarica i dati attraverso la UART. Se si fa girare il
codice precedente i2cerase_G2231.c, si vede apparire sul terminale 1024 volte il valore 0xFF (ammesso che
abbia la possibilit di visualizzare questo valore, altrimenti si pu cambiare il valore di cancellazione della
memoria per avere un carattere visibile). In questo esempio si usata una modalit low power, per trattenere
il programma principale, finch un valore non viene letto dalla I2C. Togliendo il simbolo di commento dal
comando _low_power_mode_off_on_exit() alla fine della USI_ISR, segnala che il dato pronto per essere
trasmesso dalla UART. Un loop di ritardo usato ancora per ritardare la lettura successiva, sebbene anche
qui idealmente una modalit low power potrebbe essere usata.
Scrittura di pagina: i2cwrite_G2231.c
Lesempio finale realizza la scrittura in pagine intere di 16 byte. I primi 1024 caratteri ricevuti dalla UART
sono scritti nella EEPROM. Come nellesempio della SPI, si pu usare un text file per inviare i dati ASCII alla
EEPROM. Dopo aver inviato i dati, si usa ancora la i2cread_G2231.c per visualizzare il messaggio sul
terminale. La stessa scrittura di pagina usata per impostare il settling time della EEPROM: lintervallo di
tempo richiesto per inviare 16 byte a 9600 baud pi lungo dei 5 ms richiesti.
Problemi temporali (race condition)
In origine la funzione principale in i2cwrite_G2231.c era questa:
for(i=0; i<64; i++)
{
slave_address = SAdrs + i/16;
// Current Block Address
101

Comunicazione Seriale 6

pageBuffer[0] = (i%16)*16;
// EEPROM Write Address
for(j=1; j<17; j++)
{
_BIS_SR(LPM0_bits + GIE);
// Standby for UART RX
pageBuffer[j] = RXBuffer;
}
// Extract RXBuffer
while(SCFG & BIT4)
// Ensure previous transmission is complete
_nop();
SCFG |= BIT3;
// Mark transmission/write instruction
USICTL1 |= USIIFG;
}
// Set USI interrupt to start I2C communication
Il problema era che il primo elemento di pageBuffer sarebbe stato sovrascritto poco dopo che fosse partito
linterrupt della USI, ma prima che la USI potesse trasmettere il valore originale! Una volta che si comincia a
mescolare le periferiche e fare un uso intensivo di interrupt, bisogna stare attenti alle race condition: cio
quando i valori che si vogliono usare cambiano prima che vengano effettivamente usati. La soluzione
adottata in questo caso, come visto nellesempio i2cwrite_G2231.c, stata di non cambiare gli indirizzi dello
slave e della memoria, finch una nuova pagina di 16 byte non fosse pronta per essere inviata. Questo
previene la race condition, assicurando che il valore non sia cambiato, finch la USI non azzerata.
Si noti che c anche un pericolo di race condition dovuto alla UART: se si aumenta la velocit della UART
(probabilmente serve anche aumentare la velocit del bus I2C ?), si potrebbero ricevere nuovi dati, prima che
i vecchi dati siano stati trasmessi alla EEPROM. Naturalmente, aumentando la velocit della UART potrebbe
causare problemi anche con il settling time.

102

App.1 Richiami Sistemi digitali

APP. 1

RICHIAMI SUI SISTEMI DIGITALI

CODIFICA BINARIA
La codifica binaria (binary encoding) segue un criterio identico a quello della codifica decimale, solo che
anzich avere a disposizione 10 simboli 0,1,2,3,4,5,6,7,8,9 come la codifica in base 10, dispone di 2 simboli
soltanto, da cui il nome. Come per i numeri interi, anche per i numeri binari ogni cifra o bit (da binary digit)
ha un peso diverso in base alla posizione nel numero. Come il numero decimale
1024 = 1 103 + 0 102 + 2 101 + 4 100 , cos il numero binario 1011 = 1 23 + 0 22 + 1 21 + 1 20 = 8 + 0 + 2 + 1 = 11 . In
un numero binario il bit pi a sinistra (leftmost bit) chiamato MSB (most significant bit), quello pi a destra
(rightmost bit) chiamato LSB (least significant bit). Con la regola dei pesi appena enunciata, banale
trasformare un numero binario in un numero intero decimale. Appare chiaro dalla tabella che con N cifre
binarie si possono rappresentare i numeri interi da 0 a 2N-1.
4-bit
Binary
decimal

0000

0001

00

01

0010 0011 0100


02

03

04

0101 0110
05

06

0111 1000
07

08

1001 1010
09

10

1011 1100
11

12

1101 1110 1111


13

14

15

Viceversa se si vuole trasformare un numero intero in un numero binario, bisogna tenere presente che detto I
il numero, occorrono un numero di cifre binarie pari alla parte intera
Int {1 + log 2 I} . Le cifre binarie 0 ed
1 sono chiamati 0 ed 1 logico, per distinguerli dalle tensioni associate ad essi, che sono chiamate livelli
logici. Per esempio il livello logico 0V (ground) pu essere associato allo 0 logico, ed il livello logico +5V pu
essere associato all1 logico, o viceversa. Una parola di 8 bit viene anche chiamata byte.

Numeri negativi
Per rappresentare sia numeri positivi che negativi si pu usare una codifica di tipo sign-magnitude, in cui il
bit MSB rappresenta il segno (0 positivo, 1 negativo) e gli altri bit il modulo. Gli svantaggi di questa
rappresentazione sono che lo 0 ha due rappresentazioni, e che prima di fare operazioni matematiche bisogna
distinguere il segno degli operandi.
Invece nella rappresentazione in complemento a due, si usano i primi 2N-1 numeri per rappresentare i
positivi, gli altri per rappresentare i negativi sostituendone il modulo X con 2N-X. Per esempio con N=3 cifre
binarie, il numero +3!011, il numero 3!233=5=101=101. In pratica la codifica dei numeri negativi si
ottiene codificando il modulo in binario, quindi si invertono uno a uno tutti i bit, e infine si aggiunge un LSB.
Per esempio:
binary

3 011

bit inversion

+ LSB

100 100 + 001 = 101

I vantaggi di questa rappresentazione sono che lo zero ha ununica codifica e soprattutto che le sottrazioni
possono essere operate come somme, senza discriminare il segno. Per esempio per effettuare loperazione
+2-3=-1, sufficiente effettuare la somma binaria 010+101=111.
Binary value
Sign and magnitude
Twos complement

Representations
000
+0
+0

of signed numbers
001
010
011
+1
+2
+3
+1
+2
+3

103

100
-0
-4

101
-1
-3

110
-2
-2

111
-3
-1

App.1 Richiami Sistemi digitali

ALTRE CODIFICHE
Esistono poi altri tipi di codifica binaria, utilizzati da alcuni ADC, come la codifica decimale-binaria
decimale
(binary
coded decimal BCD), in cui le singole cifre
c
decimali sono codificate singolarmente con 4 cifre binarie.
binarie Per
esempio il numero 18 codificato 0001-1000.
0001
una codifica meno compatta di quella binaria, utilizzata per
applicazioni commerciali.
Un difetto della codifica binaria che richiede un numero elevato di cifre per rappresentare numeri decimali.
Una codifica pi compatta la codifica esadecimale, che prima divide le cifre di un numero binario in
gruppi di 4,, a partire dalle cifre meno significative,
si
aggiungendo eventualmente degli 0 a sinistra, poi
codifica ogni gruppo in base 16,, con i simboli 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,, premettendo 0x (0b nella
codifica binaria), o con lh finale.. Es.:
0

!""""# 0010
!""""# 11010001
!""""# !""""# = 0x02D1 = 02D1h
721 = 0b1011010001 = 0b 0000
02D1h = 2 16 2 + 13 16 + 16
Decimal Binary Hexadecimal
00

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
0

La codifica esadecimale viene usata soltanto a livello software per rappresentare in maniera compatta
numeri elevati. Per esempio, per codificare gli indirizzi delle celle di una memoria, con 16 linee di indirizzi
possibile codificare 65536 locazioni diverse, che vanno dallindirizzo 0000h
0000 allindirizzo FFFFh.
FFFF
Gli errori nella trasmissione dei bit vengono valutati attraverso il BER (Bit Error Rate). Per esempio un
BER=10-7 significa che in media si commette un errore ogni 107 bit. Esistono allora codici che permettono di
rilevare la presenza di errori. Tra i pi noti sono i codici a controllo di parit, che consistono nellaggiungere
un bit alla parola di codice, allinizio o alla fine in maniera prestabilita,, che vale 1 o 0 in modo tale che il
numero totale di 1 sia pari (parit pari) o dispari (parit
(
dispari). Se al ricevitore arriva una parola che non
soddisfa al criterio
riterio di parit scelto, esso manda una segnalazione di errore che implica la ritrasmissione della
parola. Ovviamente questo codice non pu rilevare un errore doppio con effetti opposti.
opposti
Le informazioni
ioni di carattere testuale vengono rappresentate con la codifica ASCII (American Standard Code
for Interchange of Information). Originariamente la codifica era su 7 bit, per caratteri alfanumerici (numeri e
lettere maiuscole e minuscole). In seguito stata
stat estesa ad 8 bit introducendo caratteri grafici e caratteri
speciali (accentati). In tempi pi recenti stata sviluppata la codifica Unicode, che utilizzando 2 byte, pu
rappresentare 65536 caratteri diversi.
diversi

104

App.1 Richiami Sistemi digitali

RAPPRESENTAZIONE NUMERICA IN VIRGOLA FISSA


In generale, per trasformare un numero reale in codifica binaria occorre distinguere la parte intera (prima
della virgola) da quella decimale (dopo la virgola). Laritmetica binaria in virgola fissa (fixed point) prevede
che siano assegnati un numero fisso M di bit alla parte intera I, che verr rappresentata dalla sequenza di bit
b0 b1 b2 ...b M 1 con significato

I = b0 + b1 21 + b2 22 + bM 1 2M1
ed un numero fisso N di bit alla parte decimale P, che verr rappresentata dalla sequenza di bit b1 b 2 ...b N
con significato

P = b1 2 1 + b2 2 2 + b N 2 N
Dato un numero in base 10, il valore di ciascun bit della parte intera coincide con il resto della divisione che
si ottiene dividendo successivamente per 2

I 2 = b1 + b2 21 + bM 1 2 M 2 (resto b0)

( I 2 b0 )

2 = b 2 + b 3 2 1 b M 1 2 M 3

(resto b1) e cos via, aggiungendo un 1 a sinistra, e completando con un numero sufficiente di 0. Il valore
di ciascun bit della parte decimale coincide con la parte intera che si ottiene moltiplicando successivamente
per 2 le cifre decimali

2P = b1 + b2 2 1 + b N 2 N +1
Esempio:

2 ( 2P b 1 ) = b 2 + b 3 2 + b N 2 N + 2 ecc.

Numero=60.12

M=8 , N=8

Empiricamente: 60=32+16+8+4=25 +2 4 +23 +2 2 =111100 00111100


Parte intera:
60:2=
30:2=
15:2=
7:2=
3:2=
1
Resto

0 (b0 )

0 (b1 )

1 (b2 )

1 (b3 )

1 (b4 )

11100

Risultato 00 + 1 + 11100=00111100
Parte decimale:

0.12 2=0.24

0.24 2=0.48

0.48 2=0.96

0.96 2 = 1.92

Intero

0 (b-1 )
0.92 2=1.84

0 (b-2 )
0.84 2=1.68

0 (b-3 )
0.68 2=1.36

1 (b-4 )
0.36 2 = 0.72

Intero

1 (b-5 )

1 (b-6 )

1 (b-7 )

0 (b-8 )

Risultato 00011110

Laritmetica in virgola fissa si implementa facilmente in poco spazio di memoria e si esegue velocemente, per
cui adatta ad applicazioni in tempo reale (real-time signal processing), ovvero quelle applicazioni che
prevedono lesecuzioni di operazioni su un flusso continuo di dati, su processori che non hanno un
coprocessore matematico in virgola mobile. Internamente lunit aritmetica (Arithmetic Logic Unit, ALU)
prende un valore intero, ma il programmatore separa la parte decimale da quella intera. Tuttavia quando il
risultato di un operazione eccede il range consentito dal numero di bit assegnati alla parte intera, laritmetica
in virgola fissa non consente di rappresentare correttamente il numero, ma tuttalpi fornisce una
segnalazione di overflow. Nei microcontrollori che eseguono solo operazioni a 8 bit necessario sviluppare
una serie di routine matematiche per eseguire operazioni con numeri pi grandi o con pi cifre decimali. Un
modo pu essere quello di rappresentare i numeri con 4 bytes (32 bit), di cui se ne possono usare 20 per la
parte intera e 12 per la parte decimale, ottenendo una precisione alla terza cifra decimale.

RAPPRESENTAZIONE NUMERICA IN VIRGOLA MOBILE


La virgola mobile (floating point FLP) consente di risolvere i problemi connessi con la gestione della
posizione della virgola nella sequenza dei calcoli. La convenzione pi recente quasi universalmente accettata
la IEEE 754-2008. Tra i vari formati possibili quelli pi usati sono singola precisione (32-bit) e doppia
precisione (64-bit), ma esistono anche in mezza precisione (16-bit) e quadrupla precisione (128-bit).
Partendo da un numero composto da una base o mantissa con segno, suddivisa in una parte intera ed una
decimale, e un esponente (o caratteristica), si procede alla normalizzazione della mantissa, modificando
lesponente in modo che la parte intera della mantissa sia sempre 1. Questo bit viene nascosto e quindi la
mantissa rappresenta sempre la sola parte frazionaria. Lesponente pu risultare positivo o negativo. Per
evitare il segno si usa un esponente con bias (offset) in modo che sia sempre positivo.
105

App.1 Richiami Sistemi digitali

Nella rappresentazione in virgola mobile lMSB rappresenta il segno, seguito dallesponente. La mantissa
rappresentata dai bit rimanenti.
Il valore di un numero si calcola da:
s
( 1) 2 exp onent exp onent bias mantissa

L'esponente Emin-1 (con bias=0) indica il valore 0 (se M=0) e numeri de-normalizzati (M0), mentre Emax+1
indica (M=0) o NaN (not a number) (M0).

num. normalizzato
num. denormalizzato

Singola precisione
Esp
M
0 < esp < 255
qualunque
esp = 0

Zero

esp = 0

M0
M=0

Infinito

esp = 255

M=0

NaN

num. normalizzato
num. denormalizzato

Valore
s

(-1) (1,M)2

(-1) (0,M)2

Zero

esp = 0

M0
M=0

Infinito

esp = 2047

M=0

NaN

esp = 2047

M0

-126

(-1) 0
s

(-1)
NaN

esp = 255
M0
Doppia precisione
Esp
M
0 < esp < 2047 qualunque
esp = 0

esp-127

valore
s

(-1) (1,M)2

esp-1023

(-1) (0,M)2

1022

(-1) 0
s

(-1)
NaN

Per esempio si voglia trasformare in binario a 32 bit il numero 14.235


La parte intera pu essere convertita nel numero binario: 1110
La parte frazionaria nel numero binario: 0.00111100001010001111
La rappresentazione del numero allora: 1110.00111100001010001111
La normalizzazione richiede che la virgola sia spostata a sinistra:
3

Segno: 0 (numero positivo)


Mantissa: 11000111100001010001111

1.11000111100001010001111 2
Esponente: 127 + 3 = 130 = 10000010
(dopo la virgola)

LOGICA COMBINATORIA
OPERAZIONI LOGICHE
Le principali operazioni logiche sono:

z = x AND y = x y

z = x OR y = x + y

z = x XOR y = x y

y = NOT x = x

che hanno, rispettivamente, il significato:


AND: z vero se sono veri sia x che y (o se sono tutti veri, o se nessuno falso);
OR: z vero se x o y sono veri (o se almeno uno vero, o se non sono tutti falsi);
XOR: z vero se x e y non sono entrambi veri (o non sono entrambi falsi, o x y)
NOT: y vero se non lo x (o se x falso).
Associando il simbolo 0 alla caratteristica falso ed il simbolo 1 alla caratteristica vero, si pu ottenere
la seguente tabella di verit (truth table):
x

0
0
1
1

0
1
0
1

AND

OR

NOT

XOR

xy

x+y

x y = x y + x y

0
0
0
1

0
1
1
1

x
1
1
0
0

106

0
1
1
0

App.1 Richiami Sistemi digitali

Ai simboli dellalfabeto binario si possono associare altrettanti livelli di tensione, detti livelli logici, secondo
una logica positiva o una logica negativa. Sono riportati i livelli logici secondo la logica standard CMOS e
TTL.
Positive Logic

Negative Logic

VHmax

VHmax
logic 1

VHmin

TTL standard

CMOS standard

VHmax

V
5

VHmax

V
3.3

VHmin

2.2

VHmin

2.2

VLmax

0.8

VLmax

0.8

VLmin

VLmin

logic 0
VHmin

unspecified

VLmax

unspecified

VLmax
logic 0

logic 1

Alle operazioni logiche elementari suddette si possono associare altrettante porte logiche (logic gates). La
presenza di un circoletto alluscita di una porta denota loperazione di inversione logica.
X1
X2 ..
..
.
Xn

X1
X2 ..
..
.
Xn

X1
X2 ..
..
.
Xn

X1
X2 ..
..
.
Xn

X1+X2++Xn

OR

XOR

X1X2Xn
X

X1X2Xn

X1
X2 ..
..
.
Xn

X1+X2++Xn

NOR

AND

NOT

X1X2Xn
NAND

ALGEBRA BOOLEANA
Le operazioni logiche sono regolate dai teoremi dellalgebra booleana, che sono sintetizzati nella tabella
seguente. I pi importanti sono quelli di De Morgan, che consentono di trasformare una rete OR in una rete
AND, e viceversa, utilizzando gli invertitori (NOT).
X1
X2 ..
..
.
Xn

NOR

X1
X2 ..
..
.
Xn

OR

X1+X2++Xn

_ _
_
X1+X2++Xn

X1
X2 ..
..
.
Xn

Leggi di
De Morgan

X1
X2 ..
Xn

x+0=x
x +1 =1
x+x =x

AND

_ _
_
X1 X2Xn

NAND

..
.

X1X2Xn

Teoremi dellAlgebra Booleana


x 1 = x
x 0 = 0

Identit
elemento nullo
Idem potenza

x+x =1

x x = x
x x = 0

x=x
x+y =y+x

xy = yx

Commutativa

x + ( y + z) = (x + y ) + z

x ( y z) = ( x y ) z

Associativa

(x + y ) (x + z) = x + y z

x ( y + z) = x y + x z

Distributiva

x1 x2 xn = x1 + x2 + + xn

x1 + x2 + + xn = x1 x2 xn

leggi di De Morgan

Complementi
doppia negazione

F ( x1 ,x2 , ,xn ) = x1 F (1,x2 , ,xn ) + x1 F ( 0,x2 , ,xn )

F ( x1 ,x2 ,,xn ) = x1 + F ( 0,x2 ,,xn ) x1 + F (1,x2 ,,xn )

107

leggi di espansione di
Shannon

App.1 Richiami Sistemi digitali

ELEMENTI DI MEMORIA (Storage Elements)


LATCH
Un dispositivo elementare di memoria, che memorizza un bit, costituito da un circuito bistabile, che ha due
stati stabili tra cui pu commutare in seguito a stimoli di ingresso. Nella figura successiva rappresentato un
latch SR. Questo circuito cambia stato in seguito ad una variazione di livello (level sensitive) sugli ingressi R
(Reset) ed S (Set). In pratica quando diventa alto il livello sullingresso S, luscita Q si porta nello stato di Set,
ovvero a livello alto, quando diventa alto il livello sullingresso R, luscita Q si porta nello stato di Reset,
ovvero a livello basso. Se i livelli di ingresso sono entrambi bassi, rimane nello stato in cui si trova
(memoria), se sono entrambi alti, entrambe le uscite si azzerano. disponibile anche una terminazione di
uscita Q di valore opposto a Q.
Il circuito appena descritto ha un funzionamento asincrono. Nella configurazione di destra (Gated latch),
realizzata solo con porte NAND, il funzionamento invece di tipo sincrono, in quanto i cambiamenti di stato
sono sincronizzati da un clock, definito da una forma donda rettangolare periodica dotata di una
determinata frequenza e duty-cicle, che li abilita solo per la durata di ogni impulso.
Si possono definire tabelle caratteristiche, analoghe delle tabelle di verit dei circuiti logici.
Un latch di tipo D un elemento che memorizza il dato di ingresso. Si ricava facilmente da uno SR unendo
gli ingressi e aggiungendo una negazione su R.
Un latch di tipo JK si ottiene da uno SR premettendo due porte AND come in figura. Il funzionamento
identico a quello SR, eccetto nel caso di ingressi entrambi alti, per cui luscita, anzich commutare a 0, si
inverte. Questa modalit detta toggle. I latch di tipo Toggle possono mantenere il dato, oppure invertirlo ad
ogni impulso di clock.
SR Latch (asynchronous)

Gated SR Latch (synchronous)


R

Q
CLK

_
Q

_
Q

S R CLK Q
- - 0 Q

S R Q Q
S

_
Q

S
0
0
1
1

0
1
0
1

Q Q
0 1
1 0
0 0

CLK
_
R Q

0
0
1
1

CLK
_
R Q

J
0
0
1

0
1
0
1

1
1
1
1

K CLK Q
- 0 Q
0 1 Q
1 1
0
0 1
1

1 1

108

D CLK Q
- 0 Q

_
Q

0
1

Q
0
1
0

CLK
R

CLK
_
K Q

1
1

D
D

T CLK Q
- 0 Q
0

App.1 Richiami Sistemi digitali

FLIP-FLOP
Il problema degli elementi di memoria sensibili ai livelli di ingresso che luscita pu variare durante
lintervallo in cui il clock attivo. Per risolvere questo problema esistono dispositivi, detti Flip-Flop (FF),
caratterizzati da due tipi di funzionamento: uno detto pulse-triggered ed uno detto edge-triggered.
I FF di tipo pulse-triggered si possono realizzare con unarchitettura di tipo Master-Slave composta da due
latch, di cui in figura mostrato un esempio con FF D. Il latch master memorizza il dato di ingresso
mentre il clock basso. Quando il clock diventa alto, il dato viene trasferito al latch slave, ovvero
disponibile in uscita, ma contemporaneamente il latch master viene disabilitato (fase di hold). Serve a
prevenire che instabilit sul dato di ingresso possano trasferirsi in uscita, che si mantiene stabile durante
lintervallo di clock, e pu commutare soltanto ad ogni nuovo impulso di clock. uno schema simile a quello
del Sample and Hold negli ADC.
Per impedire che il dato di ingresso venga letto dai latch interni durante un intero semiperiodo di clock,
causando possibili instabilit, larchitettura Master-Slave stata in gran parte sostituita dai FF di tipo edgetriggered, in cui il dato di ingresso viene registrato solo durante il fronte di salita positivo (positive edge
transition) o negativo (negative edge transition) del clock. Larchitettura in figura mostra un esempio di FF di
tipo D.
Il simbolo circuitale dei FF edge-triggered si distingue da quello dei circuiti sensibili al livello per un
triangolino posto allingresso del clock.
Un FF pu prevedere anche due ulteriori input asincroni: Preset, che quando attivo pone luscita ad 1
indipendentemente dal valore degli altri ingressi, e Clear, che quando attivo pone luscita a 0 qualunque sia
il valore degli altri ingressi. Non ha senso porre ad 1 contemporaneamente questi ingressi.
Master
D

Slave

QM

CLK

QS

_
S

CLK

CLK

_
R

_
Q

Edge-triggered D Flip-Flop !
D

REGISTRI
Con una serie di n FF D si ottiene un registro a n bit, che memorizza una parola (word), affinch i singoli bit
non varino istantaneamente durante lelaborazione, come ad esempio in un DAC. In figura rappresentato
un registro ad 8 bit.
R7

R6

R0

R
8

CLK

..

CLK

Q
clock

CLK

Register
8

Q7

Q6

Q0

109

App.1 Richiami Sistemi digitali

SHIFT REGISTER
I registri a scorrimento, o shift register, servono a caricare un dato in maniera seriale o parallela, a seconda
della modalit programmata. Sono possibili quattro modalit: input seriale e output seriale, input seriale e
output parallelo, input parallelo e output parallelo, input parallelo e output seriale. La figura illustra le varie
modalit di funzionamento di uno shift register a 4 bit.
Data
in

Data
out

Data
in

Serial in/shift right/serial out

Data
out

Serial in/shift left/serial out

Data in

Serial in/parallel out


Data
out

Data
in

Parallel in/serial out

Data out

Data in

Data out
Parallel in/parallel out

Rotate right

Rotate left

La figura successiva mostra la realizzazione pratica di uno shift register universale a 4 bit. Una linea di
Preset enable serve a caricare simultaneamente un dato di input in modalit parallela. Le linee Qi sono gli
output paralleli. Dopo che il dato memorizzato nel registro, un segnale di controllo, applicato alle porte di
uscita (non mostrato), abilita il trasferimento dal registro alla linea del bus parallelo.
PRE3

PRE2

PRE1

PRE0

Preset
enable
Q3
Serial
input
Shift
pulse

PRE
D
Q
CLK
CLR

Q2
PRE
D
Q
CLK
CLR

Q1
PRE
D
Q
CLK
CLR

Q0
PRE
D
Q
Serial
CLK
output
CLR

Clear

MEMORIE
Tra i vari tipi di memoria, le memorie elettroniche, ovvero a stato solido (semiconduttori), hanno accessi
veloci, ma sono pi costose. Possono essere permanenti o volatili. Le memorie magnetiche (hard disk) hanno
accessi pi lenti, ma sono economiche e permanenti. Sfruttano le propriet di magnetizzazione permanente
dei materiali ferromagnetici. Le memorie ottiche (CD, DVD, ecc) sono lente e molto economiche, pi
difficilmente modificabili. Nei sistemi di elaborazione si ha spesso una combinazione di queste tecnologie,
attraverso una gerarchia di memoria, al fine di ottenere unottimizzazione dei costi e delle prestazioni, come
velocit di accesso e capacit di memoria. Parametri caratteristici delle memorie sono i tempi di accesso,
distinti in tempo di lettura, scrittura e cancellazione.
In generale la memoria si rappresenta come una matrice di parole (word), ciascuna identificata da un
indirizzo che ne seleziona la cella corrispondente. Lindirizzo (memory address) della parola viene codificato

110

App.1 Richiami Sistemi digitali

e trasportato da un bus, per poi essere decodificato per selezionare la riga di memoria. Se N sono le parole,
la codifica dellindirizzo richiede K = log 2 N bit. Si possono avere parole di 1 bit, oppure parole di 1, 2 o 4
byte.

MEMORIE ELETTRONICHE
Le memorie elettroniche si possono classificare principalmente in base a tre caratteristiche:
1) accesso, da cui si hanno quelle ad accesso casuale come le RAM (Random Access Memory), che significa
che i tempi di accesso non dipendono dalla locazione di memoria, e quelle ad accesso seriale,
caratterizzate da limitazioni nella modalit di accesso, per ottenere velocit superiori o risparmi di area,
in cui i dati sono accessibili secondo un ordine prestabilito, come le memorie FIFO (First In First Out) e
LIFO (Last In Last Out), usate per realizzare stack e registri a scorrimento, in applicazioni in cui i dati
sono trasmessi sequenzialmente.
2) lettura/scrittura, da cui si hanno memorie a sola lettura (Read Only Memory, ROM) e quelle riscrivibili
(Read/Write Memory, RWM).
3) latenza, ovvero permanenza o meno del dato in memoria quando viene staccata lalimentazione, per cui
si hanno memorie non volatili e memorie volatili.
Vengono ora esaminati i principali tipi di memoria:

SRAM (Static RAM) Memorie volatili dette statiche perch mantengono il dato finch sono alimentate;
possono essere rese permanenti con laggiunta di una batteria al litio. Per le loro elevate prestazioni, piccole
porzioni di questa memoria vengono usate nei processori per contenere le informazioni cui si accede con
maggiore frequenza. I tempi di lettura e scrittura sono paragonabili. In combinazione con le CAM
costituiscono le cosiddette memorie cache. Hanno costi elevati.
DRAM (Dynamic RAM) Memorie volatili che mantengono il dato solo per un breve periodo. Sono presenti
circuiti che eseguono operazioni di refresh per mantenere il dato (dopo un certo numero di microsecondi),
ma durante il refresh i dati non sono disponibili. Hanno elevata densit e bassi costi, pertanto vengono usate
nelle memorie centrali di grande capacit dei calcolatori. Tuttavia i tempi di accesso sono variabili, per cui
non sono adatte nelle applicazioni ad elevate prestazioni.
SDRAM (Synchronous DRAM) Le operazioni sono scandite da un segnale di sincronizzazione generato dal
sistema, eliminando i segnali di controllo: ci ha consentito di migliorare ulteriormente la velocit di
trasferimento, in conseguenza della richiesta di sempre pi elevate prestazioni. Esistono versioni ancora pi
performanti alloggiate nella scheda madre (motherboard) dei PC dette DIMM.
CAM (Context Addressable Memory) Dette anche memorie associative, confrontano una parola di ingresso
con tutte le parole contenute in memoria, fornendo un segnale (match) e lindirizzo della parola in caso di
riscontro positivo. Questo tipo di accesso viene usato nelle memorie cache.
Cache Combinazione di memoria SRAM e CAM, in cui il dato memorizzato nella SRAM, lindirizzo
fornito dalla CAM.
ROM (Read Only Memory) Memorie di tipo permanente programmabili una sola volta, o in fase di
produzione (masked), o a posteriori dallutente (PROM), tramite applicazione di una tensione elettrica
superiore a quella del normale funzionamento, che brucia dei fusibili. Consentono elevate densit, bassi costi
e alta affidabilit. Vengono anche chiamate dispositivi OTP.
EPROM (Erasable PROM) Memorie di tipo permanente ad elevata densit, cancellabili dallutente tramite
esposizione del chip a raggi ultravioletti (UV), per poter essere riprogrammate.
EEPROM (Electrically Erasable PROM, o E2PROM) Memorie di tipo permanente che non necessitano di
dispositivi esterni di programmazione, ma sono modificabili direttamente dal sistema digitale (in-system
programmable). Possono subire fino a 105 cicli di programmazione. Tuttavia hanno media affidabilit, bassi
costi e bassa densit.
FLASH Memorie di tipo permanente che si basano sullo stesso principio delle memorie E2PROM, ma
raggiungono densit superiori. Vengono usate ad esempio nelle Flash card o nei dispositivi USB Flash drive

111

App.1 Richiami Sistemi digitali

(pen drive o lettori mp3). Tuttora in evoluzione, hanno bassi costi, elevata densit, bassi consumi, discreta
affidabilit.
In tabella riportata una classificazione sintetica delle memorie a semiconduttore.
MEMORIE a Semiconduttore (solid-state memory)
Latenza
volatili
permanenti
Lettura/Scrittura
R/W
programmabili
read only
FIFO, LIFO, CAM SRAM EPROM, EEPROM mask ROM
Nome
DRAM
Flash
PROM
Shift Register
Accesso
non casuale
casuale

MEMORIE DI MASSA EMBEDDED


Si tratta essenzialmente di due tipi di memorie:

microdrive con tecnologia magnetica (816 GB) usati in molte applicazioni embedded portabili,
soprattutto relativi ad elettronica di consumo come fotocamere, videocamere, riproduttori multimediali;

memorie allo stato solido, costituite da memorie Flash con interfacce compatibili con quelle degli hard
disk (es. Serial ATA, SATA). Hanno tempi di accesso e consumi di potenza dimezzati rispetto ad un hard
disk. I fattori cruciali dello sviluppo di questa tecnologia sono i costi, le dimensioni, il consumo di
potenza, e soprattutto laffidabilit, che determineranno la prevalenza sugli hard disk a tecnologia
magnetica.

112

App. 2 Linguaggio C

APPENDICE 2

LINGUAGGIO C

Il linguaggio compreso dalla CPU il linguaggio macchina ovvero una sequenza di numeri binari.
Il linguaggio ASSEMBLY leggermente pi evoluto nel senso che le istruzioni sono formate da insiemi di
caratteri, ma c ancora una corrispondenza biunivoca tra istruzione assembly e istruzione linguaggio
macchina.
I linguaggi pi evoluti (Fortran, Pascal, C,C++) sono compilati: cio ogni istruzione tradotta in una
sequenza di istruzioni in linguaggio macchina.
In questa appendice si forniscono gli elementi essenziali del linguaggio ANSI C (American National
Standard Institute). Un programma scritto in ANSI C portabile su qualsiasi computer dotato di
compilatore C, non dipende cio dallarchitettura.

VARIABILI, IDENTIFICATORI, TIPI


Per variabile si intende unarea della memoria RAM nella quale pu essere conservato un valore.
Una variabile identificata da un nome detto appunto identificatore ovvero una sequenza di lettere
(maiuscole o minuscole) + _ (underscore) + cifre.
Un identificatore pu iniziare solo con una lettera, non con una cifra ( meglio evitare anche _ come primo
simbolo).
Un identificatore di una variabile non pu essere una parola chiave (vedere tabella), perch queste hanno un
particolare significato.
Non sufficiente attribuire un nome ad una variabile per poter memorizzare un valore, ma occorre definirne
il tipo. Il tipo delle variabili numeriche fondamentale nel calcolo scientifico; infatti ad ogni tipo associato
un range di valori rappresentabili.
Parole chiave
auto break case char const continue default double do else enum extern float for goto if int long register return short
signed sizeof static struct switch typedef union unsigned void volatile while
Esistono molte altre parole riservate che non possono essere usate come identificatori di variabili: funzioni
delle librerie (per esempio sin, cos, etc). In caso di dubbio si possono consultare le tabelle apposite sui
manuali o sui libri di testo, vale sempre la regola di personalizzare i nomi delle variabili, in modo che
ricordino il significato del contenuto.

TIPI DI DATI
void
puntatori

tipi scalari
tipi aritmetici
interi floating point

enum

tipi aggregati
arrays strutture unioni

Oltre ai numeri interi e ai puntatori (che vedremo in seguito) i tipi scalari comprendono anche i tipi
enumerativi. Solitamente non vengono molto usati per il calcolo scientifico, ma a volte servono come
supporto.
I tipi enumerativi sono utili quando si associa ad una particolare variabile un insieme di valori possibili:
enum {verde,giallo,rosso,blu} colore;
colore= giallo;
// corretto
colore=1 ;
// conflitto di tipo !!!

109

App. 2 Linguaggio C

Interi

TIPO
char
unsigned char
short int
int
long int
unsigned short
unsigned int
unsigned long
TIPO

Floating-point

float
Double
long double

tipi aritmetici
BIT BYTE Contenuto
8
1
carattere ASCII
8
1
16 2
intero con segno
32 4
intero con segno
32 4
intero con segno
16 2
intero senza segno
32 4
intero senza segno
32 4
intero senza segno
valore minimo
BIT BYTE
(non standard)
-38
32 4
1.1755 10
-308
64 8
2.2251 10
-4932
80 10
3.3621 10

Range
-127 +127
0 +255
15
15
-2 +2
31
31
-2 +2 -1
31
31
-2 +2 -1
16
0 +2 -1
32
0 +2 -1
32
0 +2 -1
valore massimo
(non standard)
38
3.403 10
308
1.7977 10
4932
1.1897 10

Le costanti numeriche seguono delle regole simili a quelle delle variabili:


55 una costante intera
55L una costante intera di tipo Long int
55U una costante intera di tipo unsigned int
55. 55.0 3.e-5 2.e13 sono costanti di tipo floating-point (default=double)
Sono esempi errati di costanti float i seguenti numeri:
354 42,100.00 4e 5e1.3
Dichiarazioni di tipo
Tutte le variabili usate in un programma vanno dichiarate e se necessario possono essere inizializzate ad
un valore.
int i,j;
float radius,sum=0.;
double area;
char carattere=65,car=A;
Espressioni
Unespressione una combinazione di operatori , numeri e nomi che permette di calcolare un valore.
Sono espressioni valide:
5
costante
j
variabile
5+j
costante + variabile
5*j+7 costante moltiplicata ad una variabile + costante
Gerarchia dei tipi di dati
long double double float unsigned long int long int unsigned int int
Operatore di assegnamento
variabile = espressione ;
Questa istruzione ha il seguente significato: viene valutata lespressione, il valore risultante viene
memorizzato nellarea di memoria identificata dalla variabile. Quindi la parte destra (rvalue) un valore, la
parte sinistra (lvalue) corrisponde al contenuto di un indirizzo di memoria.
x=10;
x=x+5;
// ha senso perch si legge da sinistra a destra
Commenti
Se occupa solo una riga si premette al commento il segno //, se occupa un blocco di righe si ripete // ad ogni
riga, oppure si premette /* si conclude con */ e all'inizio delle righe intermedie solo *.
istruzione;
// commento

110

App. 2 Linguaggio C

istruzione;

/* commento
commento
commento */

Conversioni di tipo implicite e casting


Il linguaggio C permette di combinare tipi aritmetici nelle espressioni con pochi vincoli, operando nel caso si
trovi con costanti e/o variabili di tipo diverso una conversione automatica, che va capita e sperimentata per
evitare errori banali e perdite di tempo.
Esiste una gerarchia tra i tipi aritmetici: se si combinano tipi differenti il risultato dellespressione
convertito al tipo con gerarchia pi alta. Per esempio:
float x,y;
float x;
int i;
int n=256;
x=y/i;
x=1/n;
// equivale a x=1.0/n;
Questo in un certo senso pu far risparmiare chiamate a funzioni come la parte intera, ma pu essere
pericoloso se non lo si controlla.
Effetti indesiderati di perdita di informazione si superano operando un casting cio una conversione
esplicita del tipo di una variabile.
float x;
int i=1,n=256;
x= (float) i/n;
Operatori di casting pi utilizzati
(float) variabile; (float) (espressione);

(int) variabile;

(int) (espressione);

PRINTF E SCANF
Printf e scanf sono funzioni della libreria standard di input/output. Sono utilizzate per mandare
informazioni sullo standard output (video) o ricevere dati dallo standard input (tastiera).
Qui viene presentata la sintassi semplificata con alcuni esempi, per una descrizione pi dettagliata si pu
fare riferimento a qualsiasi manuale di C.
Per utilizzare queste funzioni occorre inserire allinizio del file che contiene il programma (file header) la
seguente direttiva al pre-processsore:
#include<stdio.h>
// Attenzione nelle istruzioni al pre-processore non c il ;
main ()
{
float x=100.0;
int i=2;
char c1=A;
// printf(stringa di testo: descrizione formato \n,variabile1,variabile2,...);
printf(Il valore della variabile x : %f \n,x);
// \n significa a capo
printf(valore di i (intera): %d \n,i);
printf(carattere :%c \n,c1);
printf( i:%d, x:%f \n,i,x);
}
Pi delicata la funzione scanf, infatti il valore che viene digitato deve essere registrato nellarea di memoria
corrispondente alla variabile in questione. Questa viene indirizzata ponendo il carattere & davanti
allidentificatore della variabile.
float a,b,c;
main ()
{
printf(Programma per il calcolo delle radici di unequazione di secondo grado \n);
printf(Inserire in ordine i valori di a,b,c: );
// non va a capo
scanf(%f %f %f,&a,&b,&c);
printf( a= %f , b= %f ,c =%f \n,a,b,c;
}
// verifica
se la variabili fossero invece double occorre cambiare il formato di input (%lf =long float)
scanf(%lf %lf %lf,&a,&b,&c);

111

App. 2 Linguaggio C

LA STRUTTURA DI UN PROGRAMMA C
#include <stdio.h>
// direttiva al pre-processore
#define PI 3.14159
// definizione di costante
main ()
{
istruzione;
}
// fine main

OPERATORI (esistono altri operatori, guardare sui manuali)


precedenza
moltiplicazione e divisione
*,/

somma e sottrazione
+,
incremento,decremento di 1
++,-=
relazionali
<,<=,>,>=

uguaglianza
==,!=

AND logico
&&

OR logico inclusivo
||
=
OR logico esclusivo
^^

NOT logico
!,

assegnamento (*)
=

(prefisso o suffisso)

Per la valutazione di una espressione vengono eseguite prima le operazioni con precedenza pi alta, a parit
di precedenza le espressioni che contengono questi operatori (escluso lassegnamento) sono valutate da
sinistra a destra. Esempio:
a=b+c-d;
equivalente a
a=(b+c)-d;
a=b=c;
invece equivalente a
b=c;
a=b;
Notazione compatta, incremento e decremento
a=a+5;

a+=5;
a=a-5;

a-=5;
a=a/5;

a/=5;
a=a*5;

a*=5;
a=a+1;

a++;
a=a-1;

a--;

OPERATORI BITWISE
C fornisce 6 operatori per la manipolazione dei bit, che possono essere applicati soltanto a operatori interi,
con e senza segno.
&
bitwise AND
|
bitwise inclusive OR
^
bitwise exclusive OR
<<
left shift
>>
right shift
~
complemento a 1 (unary)
Loperatore & spesso usato per impostare a 0 i bit selezionati (primi 7), lasciando gli altri uguali, per es:
n = n & 0x0177;
// in binario:
n = n & 0b 0000 0001 1111 1111; (senza spazi)
Loperatore | usato per impostare a 1 i bit selezionati e lasciare inalterati gli altri, per esempio:
x = x | SET_ON;
// pone a 1 i bit di x che sono 1 anche nella costante SET_ON.
Loperatore ^ imposta a 1 i bit che corrispondono a bit differenti (0 e 1) negli operandi, e zero gli altri.
Gli operatori << e >> operano uno shift a sinistra e a destra delloperando di sinistra del numero di posizioni
dato dalloperando di destra, che deve essere non-negativo, completando i bit vacanti a sinistra con 0.
Shiftare a destra equivale a moltiplicare per 2n dove n il numero di shift.
Loperatore ~ inverte ogni bit delloperando. Per esempio:
x = x & ~077
// x = x & ~000011111111 = x & 111100000000
pone gli ultimi 8 bit di x a zero.

112

App. 2 Linguaggio C

IL CONTROLLO DI FLUSSO
Le istruzioni in un programma C vengono eseguite in ordine di apparizione (programmi sequenziali).
A volte utile eseguire blocchi di istruzioni differenti in funzione di alcune condizioni oppure ripetere un
insieme di istruzioni per molte volte.
In questi casi il flusso delle istruzioni non sequenziale e si distinguono: la selezione condizionale e i cicli.
Sintassi dellistruzione if else
if (espressione)
istruzione 1;
// eseguita se espressione diversa da 0
else
istruzione 2;
// eseguita se espressione=0
istruzione 3;
// sempre eseguita
Se la scelta una solamente
if (espressione)
{
istruzione 1;
// eseguita se espressione diversa da 0
istruzione 2;
}
// fine if
istruzione 3;
// sempre eseguita
Se le istruzioni per ogni condizione sono pi di una devono essere racchiuse tra parentesi graffe. Esempio:
if (++i == 3)
// corrisponde a:
i = i+1;
if i == 3
i = 0;
return;
Usando la tabulazione per incolonnare le istruzioni il codice risulta pi leggibile.
Attenzione a non confondere == con = !!!
Esempio
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
// serve per utilizzare le funzioni matematiche
main ()
{
double numero;
printf(Dammi un numero positivo\n);
scanf(%lf,&numero);
if (numero < 0)
{
printf( Il numero negativo\n);
exit(0);
}
printf(La radice quadrata di %lf %lf\n,num,sqrt(num)); }
Sintassi dellistruzione switch
Listruzione switch una decisione multimodale che controlla se unespressione uguaglia una tra un certo
numero di costanti intere
switch (expression)
{
case const-expr : statements
case const-expr : statements
default : statements
}
Tutte le espressioni devono essere differenti. Il case default eseguito se nessuno dei precedenti si
verificato. Listruzione break causa unuscita immediata dallo switch,
Il seguente programma conta le occorrenze di ogni carattere (compresi spazi bianchi)
include <stdio.h>
main()
{
int c, i, nwhite, nother, ndigit[10];
nwhite = nother = 0;
for (i = 0; i < 10; i++)
ndigit[i] = 0;
while ((c = getchar( )) == EOF)
{
switch (c)
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':

113

App. 2 Linguaggio C

ndigit[c-'0']++;
break ;
case ' ':
case '\n':
case '\t' :
nwhite++;
break ;
default :
nother++ ;
break ;
}
}
printf ( "digits =" ) ;
for (i = 0; i < 10; i++)
printf(" %d", ndigit[i]);
printf(", white space = %d, other = %d\n, nwhite, nother);
return 0;
}

CICLI
I cicli (loop o iterazioni) permettono di eseguire un blocco di istruzioni fino a che non sia verificata una certa
condizione. Le istruzioni che permettono di fare ci sono tre: while, do ... while, for.
Sintassi dellistruzione while
while (espressione)
while (x<y)
istruzione;
x++;
La singola istruzione pu essere sostituita da un blocco di istruzioni tra parentesi graffe.
Sintassi dellistruzione do ...while
do
do
istruzione;
x++;
while (espressione);
while(x<y);
La singola istruzione pu essere sostituita da un blocco di istruzioni tra parentesi graffe.
La differenza tra while e do ... while che nella prima il controllo sullespressione viene fatto allinizio
mentre nella seconda alla fine; quindi se lespressione falsa, nel primo caso listruzione/i non mai
eseguita, mentre nel secondo eseguita almeno una volta. Queste istruzioni sono spesso utilizzate per
leggere dati da tastiera o da file, senza sapere quanti sono.
La sintassi dellistruzione for
for (espr1;espr2;espr3)
for (i=0;i<100;i++)
istruzione;
printf(i=%d\n,i);
Attenzione lerrore pi comune mettere un ; al termine del comando for, questo provoca un ciclo che non
fa nulla.
Anche se la sintassi dellistruzione for pu sembrare molto generica occorre fare alcune precisazioni:
Esiste un indice del ciclo (in questo caso la variabile intera i)
La prima espressione inizializza lindice del ciclo (i=0)
La seconda una espressione logica che verifica quando il ciclo termina (i<100)
La terza d la regola di incremento o decremento dellindice di ciclo
NON mai opportuno MODIFICARE lindice di ciclo.
Esempio: calcolo del fattoriale
int j,val;
long fact=1L;
printf(Digita un numero intero:);
scanf(%d,&val);
for (j=2;j<= val; j++)
fact*=j;
printf(%d\n,fact);

114

App. 2 Linguaggio C

Esercizio: provare a valutare il massimo valore di val nel caso avessi definito la variabile fact del tipo short
int.
Cicli annidati
// stampa una tavola pitagorica sfuttando i cicli annidati
int j,k;
printf( 1 2 3 4 5 6 7 8 9 10\n);
printf( -------------------------\n);
for (j=1;j<=10,j++)
{
// ciclo esterno
printf(%5d|,j);
for (k=1;k<=10;k++)
// ciclo interno
printf(%5d, j*k);
printf(\n);
}
Cicli infiniti
A volte sorge la necessit di creare un ciclo per eseguire un programma allinfinito, ovvero fino a che
lutente lo interrompe. A volte i cicli infiniti sono dovuti ad errori di programmazione. Alcuni esempi:
while (1)
istruzione;
for(;;)
istruzione;
for (j=0; j<10; j++)
{
.....
j=1;
}
// si modifica lindice del ciclo

ARRAY E PUNTATORI
Anche se gli array e i puntatori appartengono a tipi di dati differenti, i primi infatti sono un tipo di dato
aggregato e i secondi sono di tipo scalare, sono strettamente collegati e costituiscono una delle caratteristiche
pi potenti del linguaggio C.
Per array si intende un insieme di variabili dello stesso tipo memorizzate consecutivamente. E possibile
accedere ad una variabile di un array, detta elemento, attraverso una espressione detta indice. Per default
lindice 0 indica il primo elemento, lindice 1 lelemento successivo etc. Gli array hanno lo scopo di trattare
molti dati correlati tra loro, tipicamente una serie di misure.
Dichiarazione e inizializzazione
(Tipo) (Nome dellarray)[dimensione]= (valori iniziali separati da ,) ;
Es.
float temperature[4]= 10,20,30,40 ;
Puntatori
Si visto nellistruzione scanf che lindirizzo di memoria di una variabile si identifica premettendo una &
alla variabile (&nome_variabile). Il valore di questo indirizzo di memoria pu essere assegnato ad una
variabile di tipo puntatore. Invece lasterisco * viene usato per identificare il contenuto dellarea di memoria,
puntata dal puntatore. Inoltre * viene usato nella dichiarazione di una variabile di tipo puntatore.
Esempio:
int j=1, *ptr;
// puntatore e dato devono avere lo stesso tipo
printf(Il valore di j %d\n,j);
printf(Lindirizzo di j %p\n,&j);
// %p il formato dei puntatori
o in alternativa
ptr=&j;
printf(Lindirizzo di j %p\n,ptr);
Nel caso precedente se si assegna:
*ptr=2;
printf(%d\n,j);
// d in uscita il valore 2
Il linguaggio C definisce una aritmetica dei puntatori. Se ptr un puntatore, ptr+3 anchesso un puntatore,
che individua il terzo oggetto che segue quello individuato da ptr. chiaro che anche in questo caso

115

App. 2 Linguaggio C

fondamentale il tipo del puntatore, perch oltre ad essere coerente con il dato, si possono sommare o
sottrarre due puntatori solo se entrambi puntano allo stesso tipo di dato.
int *ptr1,*ptr2;
int j;
ptr2=ptr1+4;
j=ptr2-ptr1;
// j assume il valore 4
j=ptr1-ptr2;
// j assume il valore -4
Esiste il puntatore nullo (viene utilizzato per i controlli di flusso):
int *p=0;
Con i puntatori si pu accedere agli elementi di un array:
short a[4];
short *p;
p=&a[0];
// quindi *p contiene il valore di a[0]
a[1]=*(p+3);
// si ssegna il valore a[3] ad a[1]
*p = *p +1
// a[1]=a[1]+1 anche *p +=1 *p++ ++(*p)
Inoltre il nome di un array che non seguito da un indice viene interpretato come il puntatore allelemento
iniziale dellarray. Es. a equivalente a &a[0] e nellesempio sopra equivalente a p.
a[0]
a[1]
a[2]
a[3]
p=a
(p+3)
*p=a[0]
*(p+3)=a[3]
Array a pi dimensioni
In C si possono definire array a pi dimensioni (senza limite sul numero di dimensioni). Per esempio:
float matrice[2][3];
alloca in memoria una matrice con 2 righe e 3 colonne , ogni elemento di tipo float e viene indicato da una
coppia di indici. Per mantenere efficente laccesso alla memoria, bisogna considerare come sono memorizzati
i valori (scorre pi velocemente lindice di destra):
for (i=0;i<2;i++)
for(j=0;j<3;j++)
printf(matrice[%d][%d]=%f \n,i,j,matrice[i][j]);
stampa il contenuto della matrice, mantenendo lordine in memoria.
i=0,j=0 i=0,j=1 i=0,j=2
i=1,j=0 i=1,j=1 i=1,j=2
Linizializzazione di un array a pi dimensioni si pu fare in fase di dichiarazione: per esempio un quadrato
magico, ovvero la somma di ogni riga, la somma di ogni colonna e la somma di ogni diagonale hanno
tutte lo stesso valore (ogni riga racchiusa tra parentesi graffe):
int magic[5][5] =
= {{17,24,1,8,15},{23,5,7,14,16},{4,6,13,20,22},{10,12,19,21,3},{11,18,25,2,9}};
Array multidimensionali e puntatori a puntatori
Un array multidimensionale un array di array. Nellesempio sopra magic pu essere visto come un array
di 5 righe; ogni riga un array unidimensionale di dimensione 5.
Quindi magic[i] il nome del vettore unidimensionale che occupa la riga i ed quindi il puntatore al primo
elemento della riga i, pertanto lelemento magic[i][j] equivale a *(magic[i]+j).
Daltra parte magic (nome dellarray 2d ) un puntatore a puntatore e quindi si pu dire che lelemento
magic[i][j] equivalente a *(*(magic +i)+j)
Se si definisce un puntatore a puntatore nel modo seguente:
int **ptr;
si pu assegnare al puntatore a puntatore il nome della matrice:
ptr=magic;

FUNZIONI

Uno degli strumenti pi versatili e pi utilizzati nel linguaggio C, sono le funzioni.

116

App. 2 Linguaggio C

Alcune funzioni sono contenute in librerie (stdio,stdlib, math etc..) e possono essere chiamate dallutente
rispettando la loro sintassi. E pi interessante il caso in cui lutente definisce le funzioni da utilizzare o
allinterno del proprio codice o allesterno. Si veda il primo caso attraverso un semplice esempio:
#include <stdio.h>
int somma(int a,int b)
{
// tipo_del_risultato nome_funzione(tipo_argomento nome,..)
int c;
// corpo della funzione
c=a+b;
return (c);
}
// return (risultato)
main( )
{
int ris;
ris=somma(5,6);
printf(risultato %d\n,ris);
}
Le funzioni di tipo void
Una funzione pu anche non restituire un valore, in questo caso il tipo della funzione void, pi in generale
indica un puntatore generico.
#include <stdio.h>
void funzione(int a,int b)
// void nome_funzione(tipo_argomento nome,..)
{.... ; }
// { corpo della funzione; }
main( )
{
int par1=0,par2=1;
funzione (par1,par2);
}
Una funzione deve essere definita prima di essere utilizzata.
Esercizio: Convertire il primo programma C, quello che stampava un messaggio, in una funzione di tipo
void (oltre alla funzione main).
Osservazione: anche main una funzione, obbligatoria in ogni programma e pu avere o non avere
argomenti. Se non specificato di tipo intero e la scritta main () equivale a int main (void) (in questo caso
main non ha argomenti, void = puntatore generico).
Passaggio degli argomenti
Gli argomenti di una funzione rappresentano una modalit per passare dati alla funzione stessa. Molti
linguaggi di programmazione passano argomenti per indirizzo (by reference), il C passa gli argomenti per
valore (by value).
funzione chiamante
Parametro attuale

Parametro attuale

passaggio per indirizzo


Indirizzo
passaggio per valore
Valore

funzione chiamata
Parametro formale

Parametro formale

La differenza sostanziale che nel primo caso vengono modificati sia il parametro attuale, sia quello formale;
mentre nel secondo viene modificato solo quello formale.
Se si vuole che la funzione modifichi il parametro attuale si deve passare il puntatore alla variabile in
questione.
Eempio molto semplice di passaggio di parametri di tipo puntatore:
#include <stdio.h>
#include <stdlib.h>
void swap (int *x,int *y)
// funzione che non restituisce alcun valore
{ int temp;
/* variabile temporanea definita solo *
* internamente alla funzione */
temp=*x;
*x=*y;
*y=temp; }
main ()
{
int a=2,b=3;
swap(&a,&b);
printf(a= %d b=%d\n,a,b);
}

117

App. 2 Linguaggio C

Passaggio di array come argomenti di funzioni


Nel linguaggio C, un nome di un array utilizzato come argomento di funzione viene interpretato,
ovviamente, come lindirizzo del primo elemento dellarray. Nella funzione chiamata necessario dichiarare
largomento come un puntatore allelemento iniziale del vettore e questo si pu fare in due modi:
void func(float *ar)
oppure
void func(float ar[ ]) // nome dellarray senza dimensione
Mentre nella funzione chiamante si utilizzer listruzione passando il parametro che sar stato allocato in
memoria con una determinata dimensione.
float array[5]; ....
func(array);
Occorre porre attenzione che in questo modo linformazione sulla dimensione del vettore allinterno della
funzione persa e quindi, quando necessario, occorre passare la dimensione come ulteriore parametro.
Esempio:
#include ....
int somma(int *a,int size) // parametri: puntatore al vettore e sua dimensione
{ int sum=0;
for (i=0;i<size;i++)
sum+=a[i];
return(sum);
}
// restituisce la somma degli elementi di un vettore
main() {
int vettore[3]= 1,2,3 ;
printf(%d\n,somma(vettore,3)); }
Nellesempio sopra alla funzione somma occorre passare lindirizzo del primo elemento del vettore e la sua
dimensione.

ISTRUZIONE SIZEOF
Listruzione sizeof(espressione) restituisce un intero che corrisponde al numero di byte occupati in memoria
dallespressione tra le parentesi.
Lesercizio seguente tende a chiarire il passaggio dei parametri nelle funzioni in C:
#include <stdio.h>
#include <stdlib.h>
void stampa_dim(float arg[ ])
// funzione che stampa la dimensione dellargomento
{ printf(La dimensione dellargomento : %d\n, sizeof(arg) ); }
main()
{
float vettore[10];
printf(La dimensione di vettore : %d\n,sizeof(vettore));
stampa_dimensione(vettore);
}
La dimensione di vettore : 40
La dimensione dellargomento : 4

STRINGHE
Una stringa un array di caratteri terminati dal carattere nullo=\0
Una stringa costante definita da una serie di caratteri racchiusi tra doppi apici.
Esempio di dichiarazione di una stringa:
char mia_stringa [ ]=esempio di testo;
// questo array ha dimensione 17 (16+\0)
Le stringhe possono essere scritte e lette con le istruzioni printf e scanf usando lo specificatore di formato %s
Per quanto riguarda scanf, largomento deve essere il puntatore ad un vettore di caratteri di dimensioni
sufficienti a contenere la stringa (il carattere di terminazione viene aggiunto automaticamente).
Per quanto riguarda printf, largomento deve essere il puntatore ad una array di caratteri terminato dal
carattere nullo. Esempio:
#define max_char 80
main ()
{
char stringa[max_char];

118

App. 2 Linguaggio C

printf(introdurre una stringa:);


scanf(%s,stringa);
printf(%s\n,stringa);
}

// legge la stringa da tastiera


// la riscrive sul video

Funzioni per la gestione delle stringhe (includere <string.h>)


char *strcat(char *s1, const char *s2);
aggiunge una copia della stringa s2 alla stringa s1, sostituendo il carattere nullo di terminazione di s1 con il
primo carattere di s2, restituisce il valore di s1.
int strcmp(const char *s1,const char *s2);
confronta la stringa s1 con s2, restituisce un numero positivo se s1>s2, negativo se s1<s2, se s1=s2 la funzione
restituisce 0.
char *strcpy(char *s1, const char *s2);
la stringa s2 copiata nellarray puntato da s1, la funzione restituisce il valore di s1.
size_t strlen(const char *s);
restituisce la lunghezza in byte della stringa puntata da s (escluso il terminatore di stringa).
Queste funzioni vengono chiamate per esempio, nel seguente modo:
char stringa1[3]=aa,stringa2[3]=ab;
printf(%s\n,strcat(stringa1,stringa2));
printf(%d\n,strcmp(stringa1,stringa2));
printf(%d\n,strlen(stringa1));

I/O DA FILES
Finora si visto linput e loutput dei dati dalla tastiera (standard input) e sul video (standard output);
ovvio che questo pu essere molto scomodo se si devono inserire molti dati. Le stesse operazioni di I/O si
possono eseguire utilizzando files. Alcune definizioni:
Si dice stream una sorgente o una destinazione di dati.
Uno stream viene connesso ad un file o a un device tramite loperazione di apertura
Loperazione di chiusura termina questa connessione
Lapertura di un file fornisce come risultato un puntatore a file.
#include <stdio.h>
main()
{
file *f1,*f2;
f1=fopen(nome_del_file_input,r);
// deve esistere r=read
f2=fopen(nome_del_file_output,w);
// se non esiste viene creato w=write
fscanf(f1,specificatore di formato,&variabile1,...);
// legge da file
fprintf(f2,specificatore di formato,variabile1,...);
// scrive sul file
fclose(f1);
fclose(f2);
}

ALLOCAZIONE DINAMICA
Fino a questo punto sono state trattate le variabili e, in particolare i vettori, allocati in memoria in modo
statico, ovvero una determinata variabile o un vettore occupavano nel corso del programma un numero
fissato di bytes determinato dalla dichiarazione. chiaro che in alcuni casi, quando per esempio non si sa
quanti dati si deve trattare, molto pi versatile definire nel corso del programma la dimensione di una
variabile. Ci possibile con lallocazione dinamica. I prototipi delle funzioni dedicate a questo scopo sono
in stdlib.h e quindi per utilizzarle occorre includere questo file.
Le funzioni sono malloc() e free(). La prima alloca la variabile in memoria occupando il numero di byte
passati come parametro e restituisce il puntatore, free libera la memoria allocata con malloc, quando non
pi utilizzata.
#include <stdlib.h> .....
int dim;
float *vec;
// puntatore a vettore in modo statico float vec[dim]
printf( dammi la dimensione del vettore:);
scanf(%d,&dim);

119

App. 2 Linguaggio C

vec=(float*)malloc(dim*sizeof(float));
....
free(vec);

// allocazione pi casting
// il vettore vec si pu utilizzare nel solito modo
// si libera la memoria

STRUTTURE

Le strutture sono utilizzate per raggruppare pi dati che hanno un legame logico. Per esempio un numero
complesso rappresentato dalla sua parte reale e dalla sua parte immaginaria, un atomo caratterizzato dal
nome, dal simbolo, dal numero atomico, dalla massa atomica e dalla vita media.
Definizione di una struttura (struct la parola chiave per la definizione,atomo e complex sono i nomi di
queste due strutture):
struct atomo
{
char *nome;
char * simbolo;
int numero_atomico;
float massa_atomica;
}
struct complex
{
float reale;
float imm;
}
I campi delle struttura una volta dichiarati non possono essere modificati.
I dati di tipo struttura si dichiarano nel modo seguente:
struct atomo idrogeno;
struct complex numero;
idrogeno una struttura di tipo atomo con con 4 campi. Ogni campo pu essere indirizzato nel seguente
modo:
idrogeno.nome= idrogeno;
idrogeno.simbolo=H;
idrogeno.numero_atomico=1;
idrogeno.massa_atomica=1.008 ;
numero una struttura di tipo complex con 2 campi.
numero.reale=1.0;
numero.imm=2.0;
definisce il numero complesso 1 + 2 i
Esercizio: Creare alcune funzioni che eseguono le operazioni aritmetiche sui numeri complessi.
#include <stdio.h>
#include <stdlib.h>
struct complex
{
float re;
float im; };
struct complex somma(struct complex a, struct complex b)
{ struct complex somma;
somma.re=a.re+b.re;
somma.im=a.im+b.im;
return (somma);
}
main ()
{
struct complex c1,c2,c3;
c1.re=1.0;
// non possibile inizializzare una struttura nella dichiarazione
c1.im=0.;
c2.re=0.;
c2.im=2.;
c3=somma(c1,c2);
printf("%f %f \n",c3.re, c3.im);
}

120

App. 2 Linguaggio C

COMPILAZIONE

Codice sorgente

Compilatore

Codice oggetto

prova.c

gcc c prova.c

prova.o

Linker
Librerie utente
Librerie di linguaggio e di sistema
gcc o prova prova.o lm

Codice eseguibile

LIBRERIA MATEMATICA (math.h)

#define M_E 2.7182818284590452354


#define M_LOG2E 1.4426950408889634074
#define M_LOG10E 0.43429448190325182765
#define M_LN2 0.69314718055994530942
#define M_LN10 2.30258509299404568402
#define M_PI 3.14159265358979323846
#define M_TWOPI (M_PI * 2.0)
#define M_PI_2 1.57079632679489661923
#define M_PI_4 0.78539816339744830962
#define M_3PI_4 2.3561944901923448370E0
#define M_SQRTPI 1.77245385090551602792981
#define M_1_PI 0.31830988618379067154
#define M_2_PI 0.63661977236758134308
#define M_2_SQRTPI 1.12837916709551257390
#define M_SQRT2 1.41421356237309504880
#define M_SQRT1_2 0.70710678118654752440
#define M_LN2LO 1.9082149292705877000E-10
#define M_LN2HI 6.9314718036912381649E-1
#define M_SQRT3 1.73205080756887719000
#define M_IVLN10 0.43429448190325182765
#define M_LOG2_E 0.693147180559945309417
#define M_INVLN2 1.4426950408889633870E0
double sin(double x);
double sinh(double x);
double asin(double x);
double cos (double x);
double cosh (double x);
double acos (double x);
double tan (double x);
double tanh (double x);
double atan (double x);
double exp (double x);
double log (double x);
double log10 (double x);
double pow (double x,double y);
double sqrt (double x);
double fabs (double x);
double floor (double x);
double ceil (double x);

//

1 / log(10)

//

1 / log(2)

seno (argomento in radianti)


seno iperbolico
arcoseno: il risultato nellintervallo [-pi/2,pi/2]
coseno
coseno iperbolico
arcoseno: il risultato nellintervallo [-pi/2,pi/2]
tangente
tangente iperbolica
arcotangente:il risultato nellintervallo [-pi/2,pi/2]
esponenziale di x
logaritmo in base e di x
logaritmo in base 10 di x
x**y
radice quadrata non negativa
valore assoluto di x
pi grande intero non maggiore di x
pi piccolo intero non minore di x

121

prova.exe