Sei sulla pagina 1di 80

TEORIA RISORSE SPECIALE PRATICA

Prima parte
n° 252 - Giugno 2006
L’ambiente di sviluppo
Seconda parte
n° 253/254 - Luglio/Agosto 2006
MikroC:
Controllare le uscite del PIC
Terza parte
n° 255 - Settembre 2006
Gestione di pulsanti, tastiere
By Example:
e display LCD

D opo il notevole successo Dal momento che su Fare Elettronica si è già


parlato estesamente sia dei PIC che del linguag-
riscosso dalla serie di articoli gio C, non dedicheremo molto spazio alla teo-
sul MikroBasic, vogliamo ria, ma ci concentreremo soprattutto sugli
aspetti pratici, sulle applicazioni e sugli esempi.
presentarvi, a partire da questa
Inizieremo con dei programmi molto semplici,
puntata, il MikroC: un ambiente per cui anche chi si fosse perso qualcosa, non
di sviluppo, in ANSI C per PICmicro dovrebbe avere alcun problema a seguirci (alla
80
fine dell’articolo comunque verranno indicati
prodotto della MikroElektronika,
Teoria

alcuni riferimenti bibliografici per recuperare).


simile al MikroBasic per semplicità
IL MIKROC
di utilizzo ed efficienza. In questa Il MikroC è un ambiente di sviluppo prodotto
puntata conosceremo gli strumenti dalla Mikroelektronika, società jugoslava molto
conosciuta per altri suoi prodotti di successo
messi a disposizione dal pro-
quali il MkroBasic o le schede di sviluppo
gramma ed inizieremo ad usarlo EasyPIC. Il MikroC in particolare è un compila-
con alcuni semplici esempi. Nelle tore che è stato specificamente pensato per i
microcontrollori PIC, ed è quindi in grado di
prossime puntate utilizzeremo il gestire tutte le caratteristiche peculiari dei vari
MikroC per realizzare progetti modelli e produrre codice ottimizzato per que-
sti dispositivi. In realtà definirlo “compilatore C”
sempre più complessi ed interessanti. è piuttosto restrittivo, dal momento che il
MikroC integra anche una grande quantità di
strumenti ausiliari, sia per la scrittura del codice,
Quello che ci proponiamo di fare a partire da sia per soddisfare molte delle esigenze che sor-
questa puntata è mostrare come realizzare una gono quando si programmano sistemi a micro-
serie di interessanti applicazioni utilizzando i controllore. Altro aspetto molto interessante è la
microcontrollori PIC ed il linguaggio C. presenza di una completa documentazione
L’ambiente di sviluppo che utilizzeremo sarà il interattiva ed di una nutrita serie di funzioni di
MikroC v5.0, che inizieremo a conoscere in libreria pronte per essere utilizzate nelle applica-
tutte le sue caratteristiche già dal prossimo zioni. Grazie a queste caratteristiche, utilizzando
paragrafo. Com’è noto il linguaggio C, al pari di il MikroC è possibile scrivere applicazioni com-
FARE ELETTRONICA - GIUGNO 2006

altri linguaggi ad alto livello (Basic o Pascal), plesse in pochissimo tempo, come verrà
permette di scrivere programmi complessi in mostrato in questa serie di articoli.
maniera semplice e veloce, però rispetto a que- Nel prossimi paragrafi analizzeremo in dettaglio
sti produce un codice mediamente più compat- le principali caratteristiche dell’ambiente di svi-
to e veloce, simile a quello che si otterrebbe luppo.
programmando in assembler. Il C quindi per-
mette di sfruttare meglio le caratteristiche del Il compilatore
microcontrollore, e di controllare più accurata- Il MikroC comprende un compilatore ANSI C
mente il suo funzionamento e le sue periferiche. che implementa praticamente tutti gli aspetti
L’ambiente
di sviluppo di Antonio Di Stefano

standard del linguaggio, e che è stato apposita- L’ambiente di sviluppo


mente pensato per i microcontrollori PIC (tutti i La Figura 1 mostra un’immagine “panorami-
modelli della serie 12, 16 e 18). Il compilatore è ca” del ricco ambiente di sviluppo (IDE) offer-
in grado di generare dal codice C direttamente to dal MikroC. Nonostante l’abbondanza di
il codice oggetto (cioè i file .hex caricabili sul strumenti, scrivere e compilare un program-
PIC) o anche codice assembler leggibile, ed ma è molto semplice. L’area più grande visibi-
eventualmente modificabile dall’utente. le in figura è quella riservata alla scrittura del
Il compilatore supporta tutti i tipi di dati previ- codice (Code Editor). Il codice scritto viene
81
sti dall’ANSI C, sia quelli interi che quelli floating automaticamente “colorato” per distinguerne

Teoria
point, utilizzando per questi ultimi un formato a i diversi elementi (istruzioni, costanti, com-
32 bit compatibile con lo standard IEEE-754. In menti, tipi, etc.). Questo facilita molto la let-
quest’ultima versione è possibile richiamare le tura e la comprensione del codice, e può evi-
funzioni in maniera annidata e ricorsiva (nei denziare la presenza di eventuali errori di
limiti consentiti dallo stack del PIC). Per rendere scrittura. Esiste anche una funzione persona-
più efficiente il codice generato, il MikroC appli- lizzabile che corregge gli errori di digitazione
ca una serie di ottimizzazione automatiche in direttamente mentre si scrive il testo (!). Altre
fase di compilazione. funzioni utili sono quelle che permettono di
Oltre agli aspetti standard del linguaggio, il commentare o de-commentare le sezioni di
compilatore è dotato di alcune estensioni dedi- codice evidenziato, o di indentarlo.
cate, utili per interagire con i PIC. Ad esempio è Due strumenti utili mentre si scrive il codice
possibile gestire in maniera molto semplice le sono il “Code Assistant”, ed il “Parameter
routine d’interruzione o la posizione delle varia- Assistant”: due piccole finestre “pop-up” che
bili all’interno della memoria. servono a ricercare o a completare automati-
camente il nome delle
funzioni che si vuole
richiamare, o i loro para-
metri, evitando di consul-
tare continuamente la
guida o altri punti del
codice.
Sulla sinistra (Figura 2) si
trova il “Code Explorer”,
dove vengono riassunti
FARE ELETTRONICA - GIUGNO 2006

tutti gli elementi inseriti


nel codice: funzioni, eti-
chette, costanti, e varia-
bili con il loro valore e
ambito di visibilità.
Cliccando su una qualsia-
si delle voci verrà eviden-
ziato nel codice la sua
Figura 1 L’ambiente di sviluppo MikroC
posizione. Questo risulta
TEORIA RISORSE SPECIALE PRATICA

Prima parte MikroC By Example: L’ambiente di sviluppo

utilissimo sia per trovare velocemente gli ele- (usata in fase di simulazione e per determina-
menti nel codice, sia per evitare errori nel- re il valore di alcune costanti), e l’elenco dei
l’uso delle variabili globali, o con le ri-defini- files che compongono il progetto.
zioni e l’inizializzazione delle variabili. In basso trova posto la finestra dei messaggi,
Il tab posto accanto, denominato Qhelp, in cui vengono visualizzati i risultati di compi-
mostra un elenco di tutte le funzioni di libre- lazione e gli eventuali messaggi di errore.
ria integrate nel compilatore (sia quelle stan- Nella stessa zona è presente un pratico con-
dard del C che le librerie per le funzioni vertitore di dati decimali / binari / esadecima-
avanzate). Cliccando su una delle voci si apri- li, chiamato “Qconvertor”.
rà la guida in cui la funzione viene descritta
in dettaglio, spesso anche con degli esempi I tool aggiuntivi
di utilizzo. Come accennato prima, il MikroC offre una
Il piccolo riquadro sottostante riassume le grande quantità di strumenti aggiuntivi oltre a
proprietà del progetto corrente: il modello di quelli strettamente indispensabili per scrivere
PIC da utilizzare, la sua frequenza di lavoro il codice e compilarlo. Questi tool possono
aiutare moltissimo nella fase di creazione dei
82
programmi e nei test, e risulta molto comodo
Teoria

averli integrati in un unico ambiente.


Un primo strumento legato al compilatore è
quello che permette di analizzare il codice
prodotto, in termini di occupazione di memo-
ria (Fig. 3). E’ possibile tenere sotto controllo
l’occupazione della ROM (o Flash) e della RAM
del PIC, lo spazio occupato dalle diverse fun-
zioni del programma e la loro distribuzione in
memoria. Analizzando questi dati è possibile
in modo molto rapido capire come ottimizza-
re il codice dal punto di vista dell’occupazione
Figura 2 Il Code Explorer
di memoria.
Gli altri strumenti, dai più semplici a quelli più
FARE ELETTRONICA - GIUGNO 2006

Figura 3 Strumenti di analisi dell’occupazione di memoria


sofisticati, sono richiamabili dal menu Tools. anche altri tipi di terminali, che hanno un uti-
Tra quelli più semplici abbiamo una sempre lizzo simile, ma ambiti diversi: l’HID Terminal,
utile tabella ASCII ed un “editor” per caratteri usato per comunicare con dispositivi USB HID
a 7 segmenti (Fig. 4), che permette di dise- e che ingloba anche una funzione per la crea-
gnare il carattere voluto, ed ottenere il valore zione dei descrittori USB (la classe HID è utiliz-
numerico corrispondente ai segmenti accesi e zata da dispositivi come joysick, mouse o
spenti. Tra gli strumenti più sofisticati spicca- tastiere, ma è utilizzabile anche per trasmette-
no l’USART Terminal (Fig. 5) che può essere re in maniera semplice dati ad un PC via USB);
utilizzato per comunicare via seriale (RS232) l’UDP Terminal permette di scambiare dati
con un qualsiasi dispositivo o sistema esterno. utilizzando una connessione di rete ed il pro-
E’ possibile gestire la comunicazione impo- tocollo UDP. L’MMC Card Terminal invece è
stando tutte le consuete opzioni, inviare i dati utilizzato per leggere blocchi di dati da una
(digitandoli) e visualizzare in tempo reale memoria tipo Multimedia Card (MMC) o
quelli ricevuti dal dispositivo esterno. Esistono Secure Digital (SD). Come vedremo la presen-
za di questi strumenti è dovuta al fatto che il
MikroC offre delle funzioni di libreria per
83
interfacciarsi a questi tipi di dispositivi.

Teoria
Tra gli altri tools meritano una segnalazione
quello per creare immagini per display grafici
(Fig. 6), che tra i display annovera anche il
Nokia 3110, quello per gestire il bootloader,
per la formattazione dei dati della EEPROM, e
Figura 4 Editor per display a 7 segmenti
per la programmazione del micro via USB.

Le librerie
Uno degli aspetti più interessanti del MikroC è
che comprende un grandissimo numero di
librerie già pronte che implementano funzioni
complesse. Sarebbe troppo lungo descriverle
tutte, quindi qui ne daremo solo una veloce
panoramica (le utilizzeremo comunque nelle
prossime puntate). Chi desiderasse informazioni
più dettagliate può consultare la documentazio-
ne del MikroC, che è scaricabile dal sito del pro-
Figura 5 L’USART Terminal
duttore. Una prima classe di funzioni sono quel-
le per la gestione dell’hardware del PIC. In que-
sta classe sono comprese funzioni per la gestio-
ne della UART (sia hardware che emulata via
software), del generatore PWM e del convertito-
re Analogico/Digitale. Un'altra classe implemen-
ta algoritmi per la gestione di hardware esterno
FARE ELETTRONICA - GIUGNO 2006

comune: tasti, display a LED, display LCD e


display grafici, tastiere (a matrice o anche PS2),
generazione di suoni, EEPROM seriali, Flash, etc.
Un’altra classe ancora permette di gestire diver-
si tipi di interfacce e protocolli, anche molto
complessi, quali SPI, I2C, RS485, CAN, Ethernet,
memorie Compact Flash, e memorie MMC o SD
(con il supporto del file system FAT).
Figura 6 Editor per display grafici
Sono anche presenti delle librerie con funzioni
TEORIA RISORSE SPECIALE PRATICA

Prima parte MikroC By Example: L’ambiente di sviluppo

che facilitano la conversione tra tipi diversi (es. IL PRIMO PROGETTO


da numero float a stringa e viceversa), ed alcu- Per prendere confidenza con l’ambiente di svi-
ne operazioni trigonometriche. luppo iniziamo con un progetto molto sempli-
ce, il classico “Hello world!” del mondo dei
Il debugger microcontrollori: faremo lampeggiare un LED. Il
Il MikroC comprende anche un source code programma è molto semplice e si presta bene a
debugger cioè un simulatore che permette di ese- essere usato come “cavia” per le prime speri-
guire il codice creato sul PC, per capire se si com- mentazioni: una volta capito come fare lampeg-
porta come voluto. Questo strumento è molto giare un LED non è difficile creare pattern via via
importante per eseguire un primo test del pro- più complessi, iniziare a giocherellare con le
gramma o di alcune sue routine. Il funzionamen- altre periferiche, e creare programmi molto più
to è molto semplice: una volta compilato il pro- vari ed interessanti. Lo schema elettrico che uti-
gramma, è possibile eseguirlo (anche in modalità lizzeremo è quello mostrato in Figura 7: il PIC è
passo-passo), ed osservare il suo comportamento. collegato nel modo usuale all’alimentazione, ad
L’esecuzione del programma può essere seguita un quarzo da 4MHz, e sul primo piedino della
sia sulla finestra del codice, sia su delle finestre porta B è stato collegato un LED con una resi-
84
aggiuntive che mostrano il contenuto della RAM, stenza per limitare la corrente.
Teoria

quello dei registri interni e dello stack, e sulla fine-


stra del “clock”, che può essere usata per valuta- Va notato che useremo un PIC 16F84, ma que-
re il tempo di esecuzione delle routine. I valori sto particolare potrebbe essere del tutto irrile-
della RAM e dei registri possono essere modificati vante: un programma scritto in C infatti, a diffe-
dall’utente durante l’esecuzione, modificando renza di uno scritto in assembler è praticamente
quindi il comportamento del programma. Questo indipendente dal microcontrollore usato! Se scri-
risulta indispensabile in alcuni casi, in quanto il viamo bene il nostro codice, con piccole modifi-
debugger non simula in dettaglio il comporta- che esso potrà funzionare non solo su diversi
mento di alcuni dei circuiti interni del PIC, come modelli di PIC, ma anche su diversi tipi di micro-
ad esempio i timer, gli ADC, le interruzioni, etc. controllori o processori (ad esempio AVR, 8051,
Per facilitare l’osservazione dei valori assunti in ARM, etc.). Questo è uno dei maggiori vantaggi
particolari punti del programma, si possono piaz- offerti dal C.
zare dei breakpoint, che interrompono momenta- Per creare il nostro primo progetto iniziamo
neamente l’esecuzione. avviando il MikroC, e selezionando dal menu
Project la voce New Project. Si
aprirà la finestra visibile in Figura 8.
Inseriamo il nome del progetto (in
questo caso LEDBlink), il percorso
in cui vogliamo salvare i file pro-
dotti, una breve descrizione del
progetto (in generale documenta-
re i progetti è importante!).
Scegliamo come dispositivo un
PIC16F84 ed una frequenza di
FARE ELETTRONICA - GIUGNO 2006

clock di 4MHz. Dalle opzioni sotto-


stanti è possibile modificare i
“fuses” del dispositivo, in questo
caso usiamo il pulsante “Default”
per selezionare quelli predefiniti
(oscillatore HS, e Watchdog disabi-
litato). Dopo avere dato conferma,
potremmo scrivere nell’apposita
Figura 7 Schema usato per fare lampeggiare il LED
finestra il seguente codice:
TEORIA RISORSE SPECIALE PRATICA

Prima parte MikroC By Example: L’ambiente di sviluppo

void main() { basta farli precedere da “0b” (in questo caso ad


PORTB = 0; esempio potremmo scrivere 0b00000011). È
TRISB = 0; possibile anche usare il Qconvertor posto in
while(1) { basso per eseguire questo tipo di conversioni.
PORTB = 1; Una piccola nota importante: tutti i programmi
Delay_ms(500); che scriveremo saranno sempre racchiusi da, o
PORTB = 0; termineranno con, un loop infinito (eventual-
Delay_ms(500); mente vuoto).
} Questo è necessario perché altrimenti, una volta
} terminata la funzione main, il programma si tro-
verà in una condizione indefinita: potrebbe
Il codice non è scritto in modo molto elegante, bloccare il microcontrollore, compiere delle
però è utile per capire come funziona: la funzio- azioni imprevedibili, o ripetersi in modo non
ne main è quella che verrà eseguita all’avvio, corretto.
all’inizio viene assegnato il valore 0 a PORTB Chi conosce il C o ha già usato altri compilato-
(che indica il valore assunto dai piedini della ri, avrà notato che non è stato necessario inclu-
86 porta B del PIC), e TRISB (che determina la dire- dere nessuna libreria: il MikroC infatti provvede
Teoria

zione dei pin, 0 indica che sono stati configura- automaticamente a includere le definizioni rela-
ti tutti come uscite). A questo punto inizia un tive al PIC usato (come PORTB e TRISB), e diver-
ciclo infinito realizzato con l’istruzione while. se funzioni di libreria.
Dentro al ciclo viene assegnato il valore 1 a Per compilare il nostro programma, usiamo il
PORTB, cioè viene portato alto il piedino RB0. pulsante Build Project (quello con gli ingranag-
Quindi viene richiamata una routine che genera gi in alto), o l’equivalente voce del menu
un ritardo di 500ms (mezzo secondo), viene Project. Il report di compilazione sarà visibile
portato basso il piedino RB0, e vengono attesi nella finestra in basso.
altri 500ms, quindi il loop si ripete. A questo Se diamo un’occhiata alle statistiche sull’occu-
punto è utile una piccola spiegazione: per capi- pazione di memoria vediamo che il nostro pro-
re su quale piedino della porta B agiamo, dob- grammino occupa 74 word nella Flash del PIC.
biamo considerare l’espressione binaria del Proviamo a scriverlo in un modo un po’ più
numero che abbiamo utilizzato, ciascun bit rap- ottimizzato:
presenta ordinatamente un piedino della porta.
Se avessimo voluto accendere i pin RB0 ed RB1 void main() {
assieme (valore binario 00000011), avremmo PORTB = 0;
dovuto usare il valore decimale 3. Per facilitare TRISB = 0;
questo tipo di operazioni il MikroC riconosce while(1) {
anche i numeri scritti direttamente in binario, PORTB = PORTB^0b00000001;
Delay_ms(500);
}
}

Per ottenere il comportamento “intermitten-


FARE ELETTRONICA - GIUGNO 2006

te” abbiamo usato l’operatore XOR del C


(l’accento circonflesso “^”). L’XOR ha l’effetto
di invertire i bit della variabile in corrispon-
denza dei bit 1 indicati nell’operando, in que-
sto caso quindi invertiamo solo il primo bit,
quello corrispondente a RB0.
Alla seconda inversione, il bit torna come
prima, ottenendo l’effetto intermittente. Il
Figura 8 Finestra per l’immissione dei dati del progetto
programma quindi funzionerà esattamente
come prima. Quanto occupa il nostro pro- RIFERIMENTI
gramma questa volta? Soltanto 42 word! • Il MikroC può essere scaricato dal sito Internet
Questo risultato è dovuto sia alla maggiore del produttore all’indirizzo:
compattezza del programma, sia al fatto che www.mikroelektronika.co.yu
Delay_ms è una macro, non una funzione, • Maggiori informazioni sui PIC possono essere tro-
quindi prima veniva effettivamente copiata vate sul corso “PIC By Example” pubblicato su
due volte nel programma! Fare Elettronica (numeri da 157 a 176) e sul CD-
ROM omonimo, o sul libro della serie “Conoscere
E se volessimo far lampeggiare due led, uno su ed usare” dal titolo “PICmicro” di Maurizio Del
RB0 e l’altro su RB1, in modo alternato? Corso e Tiziano Galizia, edito dalla Inware.
(Schema in Figura 9). Ecco come modificare il • Maggiori informazioni sul linguaggio C si pos-
programma: sono trovare nella serie di articoli “Vitamina
C” pubblicato su Fare Elettronica (numeri da
void main() { 217 a 246), o sul CD-ROM omonimo, o sul
PORTB = 0b00000010; libro della serie “Conoscere ed usare” dal
TRISB = 0; titolo “Il Linguaggio ANSI C” di Antonio Di
87
while(1) { Stefano, edito dalla Inware Edizioni.

Teoria
PORTB = PORTB^0b00000011; • Alcune spiegazioni più dettagliate sugli algo-
Delay_ms(500); ritmi e sui circuiti utilizzati in alcuni degli
} esempi (compresa la scheda EasyPIC) si posso-
} no trovare nel tutorial sul MikroBasic, pubbli-
cato su Fare Elettronica (dal numero 238).
Abbiamo modificato soltanto due parametri: il
valore iniziale (all’inizio un LED sarà già acce- CONCLUSIONI
so, l’altro spento), ed abbiamo fatto in modo Nella prossima puntata considereremo degli
che venissero invertiti entrambi i bit meno esempi un po’ più complessi, che partendo da
significativi. quelli appena visti ne espanderanno molto le
possibilità utilizzando istruzioni e costrutti più
Se vogliamo provare i nostri programmi con il potenti, per gestire il flusso del programma e le
debugger, possiamo richiamare la voce “Start temporizzazioni. Non mancate!
Debugger” dal menu Run, ed usare le opzioni
di step (stesso menù, oppure tasti F7 ed F8). Codice MIP252080
Se invece vogliamo prova-
re il codice direttamente
sul circuito vero e proprio,
dopo la compilazione avre-
mo a disposizione il file
.hex da caricare nel PIC.
Potremo usare lo stesso
MikroC se stiamo usando
una delle schede EasyPIC
FARE ELETTRONICA - GIUGNO 2006

(il download avviene via


USB!), oppure potremo
usare un programma ester-
no qualsiasi (come il noto
IC-Prog) ed il nostro pro-
grammatore preferito. I
LED dovrebbero iniziare a
lampeggiare immediata-
Figura 9 Schema per l’accensione di due LED alternati
mente, come voluto.
TEORIA RISORSE SPECIALE PRATICA

Prima parte
n° 252 - Giugno 2006
L’ambiente di sviluppo
Secondo parte
n° 253/254 - Luglio/Agosto 2006
MikroC
Controllare le uscite del PIC
Terza parte
n° 255 - Settembre 2006
Gestione di pulsanti, tastiere
by Example:
e display LCD

I n questa puntata scopriremo siderare, dal momento che ciascuna di esse può
adattarsi meglio a specifiche applicazioni.
come scrivere in C degli Supponiamo ad esempio di utilizzare 8 LED,
algoritmi dedicati alla gestione ciascuno collegato ad un piedino della porta B
come mostrato in figura 2, e di volerli accende-
delle uscite dei PIC,
re in sequenza nelle due direzioni (il classico
per comandare LED, display effetto “supercar”). Le alternative più conve-
ed altri dispositivi, utilizzando nienti sono due: in un caso possiamo utilizzare
104
gli operatori del C che permettono di manipo-
le tecniche più efficienti
Teoria

lare i bit, nell’altro possiamo utilizzare una


e gli strumenti messi tabella di consultazione (look-up table).
Consideriamo il primo caso, cioè partiamo da
a disposizione dal MikroC.

GENERAZIONE DI PATTERN
E SEQUENZE
Nella scorsa puntata abbiamo mostrato il codi-
ce per fare lampeggiare un LED collegato alla
porta RB0 del PIC. Per comodità riportiamo in
figura 1 lo schema elettrico del circuito e di
seguito il codice utilizzato:

void main() { Figura 1 Schema usato per fare lampeggiare il LED

PORTB = 0;
TRISB = 0;
while(1) {
PORTB = PORTB^0b00000001;
Delay_ms(500);
}
FARE ELETTRONICA - LUGLIO/AGOSTO 2006

Il programma è molto semplice: utilizza sola-


mente un’operazione di XOR (l’operatore “^”
del C) per ottenere l’inversione del primo bit
della porta B (e quindi il lampeggio del LED),
ed una routine di ritardo per determinarne la
frequenza. Se dovessimo generare delle combi-
nazioni più complesse (come avviene in molte
applicazioni reali) questo approccio potrebbe
non essere adeguato.
Figura 2 Schema usato per accendere i LED in sequenza
Esistono diverse possibilità che è il caso di con-
Controllare
le uscite del PIC
di Antonio Di Stefano

una configurazione delle uscite, ed otteniamo porta B sono state configurate tutte come usci-
le altre operando sui bit. Il codice è il seguente: te (TRISB=0), e soltanto il primo piedino è stato
posto alto (PORTB=0x01). Ricordiamo che il
void main() { MikroC permette di esprimere i valori numerici
char dir; anche in binario (anteponendo “0b” al nume-
ro), ma abbiamo preferito utilizzare la notazio-
/* Direzione di scorrimento ne esadecimale per rendere il codice maggior-
1=sinistra, 0=destra */ mente portabile. Per convertire questi valori in
105
dir=1; binario si può utilizzare il convertitore

Teoria
Qconvertor, che si trova in basso nella finestra
// Inizializz. porta B del messaggi del MikroC. All’interno del loop
PORTB = 0x01; principale viene dapprima verificato se il LED
TRISB = 0; attualmente acceso è il primo o l’ultimo, e in
ciascuno di questi due casi viene cambiata la
// Loop infinito direzione dello scorrimento. Per eseguire que-
while(1) { sto controllo è stata utilizzata l’istruzione if del
C: solo se la condizione dentro le parentesi è
// Raggiunti bordi? verificata l’espressione che segue vene esegui-
if (PORTB&0x01) dir=1; ta. L’espressione che viene controllata è l’AND
if (PORTB&0x80) dir=0; binario tra i bit della porta B e 0x01 (oppure
0x08). Questa espressione darà 0x01 solo se il
// Aggiornamento direzione bit meno significativo della porta B sarà alto (lo
if (dir) { stesso per il più significativo nel caso di 0x80),
PORTB = PORTB << 1; altrimenti zero. Per maggiore chiarezza avrem-
} else { mo potuto scrivere:
PORTB = PORTB >> 1;
} if ((PORTB&0x01)==0x01) dir=1;
if ((PORTB&0x80)==0x80) dir=0;
Delay_ms(100);
} ma ricordiamo che il C considera vera qualsiasi
espressione che non valga 0, quindi anche non
FARE ELETTRONICA - LUGLIO/AGOSTO 2006

}
indicando il valore atteso otteniamo lo stesso
Il funzionamento del programma dovrebbe risultato. Abbiamo usato lo stesso accorgimen-
risultare abbastanza intuitivo: inizialmente to per il frammento di codice successivo, che è
viene dichiarata una variabile (dir) per tenere quello che si occupa dell’aggiornamento della
traccia della direzione di scorrimento, in segui- posizione. Se la variabile dir vale 1 viene esegui-
to vengono inizializzate le variabili e le porte, e to uno scorrimento binario di una posizione a
poi viene eseguito il loop principale. Durante il sinistra (a destra nell’immagine), altrimenti
funzionamento la variabile dir assumerà soltan- (else) viene eseguito uno scorrimento a destra
to due valori (scelti da noi), quindi è conve- (a sinistra nell’immagine). Gli scorrimenti (ope-
niente dichiararla del tipo più piccolo possibile ratori “<<” e “>>” del C) produrranno la
gestito dal C, cioè char (8 bit). Le linee della sequenza di valori: 0x01, 0x02, 0x04, 0x08,
TEORIA RISORSE SPECIALE PRATICA

Seconda parte MikroC by Example: Controllare le uscite del PIC

0x10…, 0x80, e poi a ritroso (quando cambie- dichiarazione dell’array. Inoltre abbiamo dichia-
rà la direzione di scorrimento) fino a 0x01. rato un’altra variabile (n) che verrà utilizzata
Dopo ogni scorrimento attendiamo 100ms come contatore, cioè come indice per scorrere
usando l’apposita routine di libreria Delay_ms, la tabella. Nel loop principale assegniamo diret-
messa a disposizione dal MikroC, e ripetiamo tamente il valore corrente della tabella alla
l’intera routine, all’infinito. Consideriamo porta B, inoltre incrementiamo l’indice n (con
adesso un altro approccio per ottenere lo stes- l’istruzione n++). Quando l’indice raggiungerà
so risultato. In questo caso utilizzeremo una il valore 14, sarà riportato a 0, e quindi il con-
look-up table per memorizzare le configurazio- teggio ripartirà da questo valore. Notare la dif-
ni delle uscite che vogliamo ottenere. Il codice ferenza tra “==” ed “=” nell’istruzione if.
è il seguente: Ricordiamo che il primo è un operatore relazio-
nale, che controlla se il valore dell’espressione
void main() { di sinistra è uguale a quello dell’espressione di
char n=0; destra (restituendo un valore di verità). L’altro
char leds[14]={0x01, 0x02, operatore invece assegna un valore ad una
0x04, 0x08, 0x10, 0x20, variabile.
106
0x40, 0x80, 0x40, 0x20, Questa seconda versione del programma, seb-
Teoria

0x10, 0x08, 0x04, 0x02}; bene richieda un po’ più di memoria, ha un


vantaggio importante: basta cambiare i valori
// Inizializz. porta B in tabella per ottenere comportamenti diversi.
TRISB = 0; Si provi ad esempio ad utilizzare questi valori:

// Loop infinito char leds[14]={0x81, 0x42,


while(1) { 0x24, 0x18, 0x18, 0x24,
0x42, 0x81, 0x42, 0x24,
PORTB=leds[n]; 0x18, 0x18, 0x24, 0x42};

n++; Ci saranno due LED accesi che si muoveranno


if (n==14) n=0; in direzione opposta (basta convertire in bina-
rio i valori per rendersene conto).
Delay_ms(100); L’approccio usato nel primo programma invece
} risulta più flessibile quando le configurazioni
} utilizzate non sono sempre le stesse, ma varia-
no nel tempo, magari in base a condizioni
Per creare la tabella di consultazione abbiamo esterne.
utilizzato un array (leds[…]), in cui abbiamo
inserito manualmente le configurazioni delle PILOTARE I DISPLAY A
uscite volute (che sono le stesse che abbiamo 7 SEGMENTI
ottenuto col programma precedente). Ci I display a 7 segmenti rappresentano un econo-
FARE ELETTRONICA - LUGLIO/AGOSTO 2006

occorrono soltanto 14 voci per la tabella, che mico e funzionale dispositivo di visualizzazione.
sono state inizializzate direttamente nella Vedremo quindi in questo paragrafo come uti-

Figura 3 Schema di un display a catodo comune Figura 4 Schema di un display ad anodo comune
Codice MIP 253107
TEORIA RISORSE SPECIALE PRATICA

Seconda parte MikroC by Example: Controllare le uscite del PIC

lizzarli. Un display a 7 segmenti è composto da posito strumento messo a disposizione dal


8 LED disposti a formare 7 segmenti più un MikroC, richiamabile selezionando la voce
punto decimale (non sempre presente), con cui Seven Segment Convertor dal menu Tools
è possibile rappresentare numeri, lettere e sim- (figura 6). È sufficiente disegnare il carattere
boli grafici. I display a 7 segmenti sono dispo- che vogliamo visualizzare per avere il valore
nibili in due versioni, che differiscono per i col- numerico corrispondente ad uno dei due tipi di
legamenti dei LED: in entrambi i casi i LED pos- display. Nel nostro caso ricaveremo il valore per
sono essere pilotati indipendentemente da un le cifre da 0 a 9 e per le lettere da A, b, c, d, E
terminale, mentre l’altro è comune a tutti. Il ed F (che ci potrebbero servire per rappresen-
terminale comune può essere l’anodo o il cato- tare valori esadecimali). Un modo molto sem-
do dei LED (e da questo particolare deriva il plice per associare questi caratteri ai valori
nome del display). Quando il terminale comu- numerici consiste nell’utilizzare un array.
ne è il catodo (figura 3), quest’ultimo sarà col- Vediamo quindi come realizzare un programma
legato a massa, e per accendere un segmento che conta continuamente da 0 a 9:
sarà sufficiente fornire un’alimentazione positi-
va al rispettivo anodo (ad esempio utilizzando void main() {
108
un piedino di uscita portato a livello logico char n=0;
Teoria

alto). Nel caso di display ad anodo comune char segs[16]={0x3F, 0x06,


(figura 4), questo terminale verrà collegato al 0x5B, 0x4F, 0x66, 0x6D,
positivo dell’alimentazione, e per accendere un 0x7D, 0x07, 0x7F, 0x6F,
segmento sarà necessario portare a massa il 0x77, 0x7C, 0x39, 0x5E,
catodo del segmento che si vuole accendere 0x79, 0x71};
(usando un piedino di uscita a livello logico
basso). Per pilotare un display a 7 segmenti // Inizializz. porta B
avremo bisogno quindi di almeno 8 uscite TRISB = 0;
del PIC, e con queste potremo gestire soltan-
to un carattere. Per fare le prime prove colle- // Loop infinito
ghiamo il display al PIC come mostrato in figu- while(1) {
ra 5 (stiamo quindi utilizzando un display a
catodo comune): i segmenti verranno pilotati PORTB=segs[n];
tramite i piedini della porta B. Possiamo quindi
procedere come negli esempi visti fino ad ora, n++;
l’unico problema è che dobbiamo ricavare per if (n==10) n=0;
ogni carattere che vogliamo visualizzare il cor-
rispondente valore da inviare alla porta B (che Delay_ms(1000);
esprime quali segmenti sono accesi e quali }
spenti). Per fare questo possiamo utilizzare l’ap- }

Come si può notare il programma è quasi iden-


FARE ELETTRONICA - LUGLIO/AGOSTO 2006

tico a quello visto precedentemente, sono stati


modificati soltanto i valori contenuti nell’array,
che in questo caso
codificano i carat-
teri voluti. Se si
estende il conteg-
gio fino a 16 sarà
possibile visualizza-
re anche le cifre
Figura 6 Il convertitore per display esadecimali da A a
Figura 5 Collegamento al display a 7 segmenti 7-seg
F. Non possiamo
superare 16 perché altrimenti leggeremmo una char cifra[4]={1,2,3,4};
posizione dell’array che non esiste, ottenendo // Inizializz. porte
un risultato imprevedibile. E se volessimo utiliz- TRISA = 0;
zare più cifre, e quindi più unità a 7 segmenti? TRISB = 0;
Occorrerebbe utilizzare molte più porte di I/O,
oppure ricorrere alla tecnica del multiplexing, // Loop infinito
molto utilizzata in questi casi. Occorre collegare while(1) {
i display come mostrato in figura 7 ed utilizzare ds = 1;
soltanto 4 linee di I/O aggiuntive (questa volta for(n=0; n<4; n++) {
provenienti dalla porta A) per gestire le 4 cifre. PORTB = segs[cifra[n]];
Il funzionamento è semplice: le linee della PORTA = ds;
porta B saranno condivise da tutti i display, che Delay_ms(2);
quindi riceveranno gli stessi dati, ma soltanto ds = ds << 1;
un display alla volta sarà acceso, comandando }
attraverso la porta A i transistor BJT sui cui col- }
lettori si trovano i display. In questo modo riu- }
109
sciamo a comandare individualmente i singoli

Teoria
display: se vogliamo visualizzare un carattere Il codice somiglia molto a quello visto in prece-
sul display più a sinistra, dovremo inviare il denza, le uniche differenze riguardano l’utilizzo
codice del carattere sulla porta B e portare alto di un altro array (cifra[…]), che contiene le cifre
soltanto RA0. Ovviamente soltanto un display da visualizzare nei 4 display, e l’impiego della
alla volta potrà essere acceso. Occorre quindi porta A per selezionare il display. Il loop princi-
accenderli in successione ad una velocità tale pale contiene un ciclo for, che viene ripetuto
da non farne percepire all’occhio lo spegni- per 4 volte (una per ogni cifra), al cui interno
mento. Per ottenere questo effetto è sufficiente viene impostato il valore della cifra corrente
accendere ogni cifra almeno ogni 20ms. Dal sulla porta B, ed aggiornato il contenuto della
momento che le cifre sono 4, dovremo tenerle porta A. In pratica all’inizio la porta A conterrà
accese singolarmente meno di 5ms. Per evitare il valore 0b0001, alla porta B viene assegnato il
un effetto di “sfarfallamento” possiamo anche valore adatto a rappresentare la prima cifra, si
usare una velocità maggiore (senza esagerare, attende 2ms, al valore della porta A viene appli-
altrimenti otterremo una bassa luminosità). cato uno scorrimento a sinistra di un bit, otte-
Riassumendo: nendo 0b0010, ed il ciclo for riprende con la
• Sulla porta B dovranno essere inviati ciclica- seconda iterazione, in cui viene assegnato alla
mente i valori corrispondenti alle 4 cifre che porta B il codice per la seconda cifra. Si prose-
vogliamo visualizzare. gue così fino alla quarta, e quindi si esce dal
• Sulle porta A dovrà essere portata alta una ciclo for, ma non dal loop principale, che si
sola linea alla volta, quella corrispondente alla ripeterà all’infinito. Sul display dovrebbero
cifra da visualizzare (avremo quindi ciclica- apparire le cifre 1234 indicate nell’array cifra.
mente 0b0001, 0b0010, 0b0100, 0b1000). A questo punto potremo pensare di sfruttare il
FARE ELETTRONICA - LUGLIO/AGOSTO 2006

display realizzato per implementare delle fun-


Il codice che implementa questo comporta- zioni utili. Se ad esempio riuscissimo a visualiz-
mento è il seguente: zare il valore di una qualsiasi variabile, potrem-
mo usare il display come un comodo dispositi-
void main() { vo di output (sia nelle applicazioni, sia in fase di
char n, ds; debug). Per fare questo possiamo utilizzare
char segs[16]={0x3F, 0x06, alcune funzioni messe a disposizione dalle libre-
0x5B, 0x4F, 0x66, 0x6D, rie del MikroC, ed in particolare quelle che per-
0x7D, 0x07, 0x7F, 0x6F, mettono di creare delle stringhe a partire dal
0x77, 0x7C, 0x39, 0x5E, valore numerico di una variabile. Una di queste
0x79, 0x71}; funzioni è la seguente:
TEORIA RISORSE SPECIALE PRATICA

Seconda parte MikroC by Example: Controllare le uscite del PIC

void ShortToStr(short number, TRISA = 0;


char *output); TRISB = 0;

La funzione prende in ingresso una variabile di // Conversione


tipo short, ed un puntatore ad un array di 4 ShortToStr(val, cifra);
caratteri (come il nostro cifra[…]), e restituisce
nell’array di caratteri una stringa corrisponden- // Loop infinito
te al numero indicato come primo parametro. while(1) {
Ad esempio: ds = 1;
for(n=0; n<4; n++) {
short t = -24; PORTB=segs[cifra[n]-48];
char txt[4]; if (cifra[n]==32)PORTB=0x00;
if (cifra[n]==45)PORTB=0x40;
ShortToStr(t, txt); PORTA = ds;
Delay_ms(2);
In questo caso l’array txt[…] conterrà i seguen- ds = ds << 1;
110
ti 4 caratteri “ –24” (il primo carattere è uno }
Teoria

spazio). Esistono anche le funzioni IntToStr, }


WordToStr, FloatToStr, che lavorano nello stes-
so modo, ma usano tipi di variabili diversi e }
restituiscono un numero maggiore di caratteri.
Possiamo utilizzare questa funzione con il codi- In pratica è stata aggiunta soltanto la variabile
ce fino ad ora scritto? Si, ma dobbiamo appor- var, la chiamata alla funzione ShortToStr, ed
tare qualche piccola modifica. La funzione alcuni controlli nel loop che visualizza i dati. In
infatti restituisce una stringa, i cui valori sono particolare all’inizio è visualizzata la cifra (otte-
codificati in ASCII. Se diamo un’occhiata alla nuta dal codice ASCII meno 48), poi viene veri-
tabella dei codici ASCII (menu Tools/ASCII ficato se il carattere è uno spazio o un segno
Chart) vediamo che i codici che ci vengono meno, ed in questi casi viene corretto l’output
restituiti vanno dal 48 al 57 (decimale) per i sulla porta B con i caratteri corrispondenti (tutti
caratteri numerici, ma comprendono anche il i segmenti spenti nel caso dello spazio, o solo
32 dello spazio, ed il 45 del segno “–“. quello centrale acceso nel caso del meno).
Se escludiamo i due segni non numerici, i codi- Poi viene assegnato il valore appropriato alla
ci dei caratteri numerici possono essere ottenu- porta A, come accadeva nell’esempio preceden-
ti semplicemente sottraendo 48 al codice ASCII te. A questo punto possiamo visualizzare il con-
(o anche considerando solo i 4 bit meno signi- tenuto di qualsiasi variabile del nostro program-
ficativi, cioè eseguendo un AND con 0x0F). ma. Un’ultima nota interessante: non abbiamo
Il codice diventa quindi il seguente: mai utilizzato il punto decimale presente nei
display. Se lo volessimo accendere in una qual-
siasi delle quattro cifre, sarebbe sufficiente atti-
FARE ELETTRONICA - LUGLIO/AGOSTO 2006

void main() {
char n, ds, val; vare il bit più significativo della porta B.
char segs[16]={0x3F, 0x06, Questo può essere ottenuto eseguendo un OR
0x5B, 0x4F, 0x66, 0x6D, (operatore “|” del C) tra il codice del carattere
0x7D, 0x07, 0x7F, 0x6F, attualmente visualizzato ed il valore binario
0x77, 0x7C, 0x39, 0x5E, 0b10000000 (cioè 0x80 esadecimale):
0x79, 0x71}; if (punto) PORTB=PORTB|0x80;
char cifra[4];
TIMER ED INTERRUZIONI
val=-47; Il precedente esempio ha mostrato come sia pos-
sibile utilizzare un display a 7 segmenti per visua-
// Inizializz. porte lizzare il contenuto di una variabile. Come abbia-
mo visto per ottenere una buona visualizzazione prestabiliti. Nel nostro caso il codice all’interno
delle 4 cifre è necessario rispettare delle precise della routine dovrà aggiornare il display un digit
temporizzazioni. Risulta quindi piuttosto difficile alla volta. Occorrerà quindi impostare il timer ed
aggiungere altre funzioni al programma visto il suo prescaler in modo da generare un interrupt
prima, perché qualsiasi altro compito inserito nel ogni 2ms. Per abilitare le interruzioni associate al
loop principale altererebbe le temporizzazioni del TMR0 dobbiamo impostare ad 1 i bit GIE e T0IE
display, e risulterebbe a sua volta rallentato dai del registro INTCON, ed abilitare il prescaler col
tempi di attesa utilizzati per il display. Per risolve- fattore di divisione voluto sul registro
re questo problema è necessario separare e rende- OPTION_REG (per maggiori dettagli si veda il
re indipendenti le temporizzazioni e le funzioni di datasheet del PIC o il tutorial “PIC By Example”
gestione del display dalla normale esecuzione del già pubblicato su Fare Elettronica).
programma: questo può essere ottenuto utiliz- Se abbiamo una frequenza di clock di 4MHz, pos-
zando il timer del PIC e le interruzioni. siamo ottenere un’interruzione ogni 2ms usando
Il meccanismo è molto semplice: il PIC ha un un fattore di divisione pari a 8 (infatti
timer ad 8 bit (chiamato Timer0 o TMR0) che 4*256*8/4MHz=2ms).
conta da 0 ad 0xFF (in totale 256 valori), ed è Dopo avere impostato il corretto valore nei regi-
111
incrementato ad ogni ciclo macchina (4 cicli di stri, dobbiamo scrivere la routine di gestione delle

Teoria
clock) o suoi multipli. interruzioni. Per fare questo sarà sufficiente creare
Quando il valore del timer supera 0xFF, può esse- una funzione chiamata “interrupt”, il MikroC la
re generata un’interruzione. Utilizzando il timer è riconoscerà come routine di gestione delle inter-
quindi possibile richiamare un’apposita funzione ruzioni, e la richiamerà automaticamente in caso
(la routine d’interruzione) a intervalli di tempo di interruzioni. Il codice risultante è il seguente:

Software MikroC

Un potente
compilatore C
per PICmicro

✔ Code Editor
✔ Code Explorer
FARE ELETTRONICA - LUGLIO/AGOSTO 2006

✔ Debugger
✔ Statistiche

Tutto in un ambiente
Windows facile ed intuitivo

Un set di strumenti veramente indispensabili


per sviluppare applicazioni con i PICmicro
Codice MIP 253111

Ordinalo subito su www.farelettronica.com oppure telefona allo 02.66504755


TEORIA RISORSE SPECIALE PRATICA

Seconda parte MikroC by Example: Controllare le uscite del PIC

char n, ds, cifra[4];


char segs[16]={0x3F, 0x06, n++;
0x5B, 0x4F, 0x66, 0x6D, ds <<= 1;
0x7D, 0x07, 0x7F, 0x6F, if (n > 3) {
0x77, 0x7C, 0x39, 0x5E, n = 0;
0x79, 0x71}; ds = 1;
void main() { }
short val;
// Reset del timer
// Inizializz. porte TMR0 = 0;
TRISA = 0; // Reset flag TMR0IF/TMR0IE
TRISB = 0; INTCON = 0x20;
}
n=0;
ds=1; Le differenze rispetto alla versione preceden-
te sono essenzialmente 3.
112 Innanzi tutto alcune delle variabili utilizzate
val=-125;
Teoria

ShortToStr(val, cifra); prima sono state dichiarate fuori dalla funzio-


ne main, quindi risultano essere delle variabi-
OPTION_REG = 0x82; li globali: tutte le funzioni possono leggerle e
TMR0 = 0; modificarle.
INTCON = 0xA0; Questo risulta utile dal momento che devono
essere inizializzate o modificate all’interno
while(1) { della funzione main, ma devono essere lette
} ed utilizzate dentro la routine d’interruzione,
} inoltre il loro valore deve essere mantenuto
anche quando si esce dalle rispettive funzioni.
In questo modo all’interno del loop principa-
void interrupt() le si potrà assegnare un valore all’array cifra,
{ e la routine d’interruzione provvederà a visua-
PORTA=ds; lizzarlo indipendentemente.
PORTB=segs[cifra[n]-48]; La funzione main è quasi vuota: contiene
if (cifra[n]==32) PORTB=0x00; solamente le istruzioni di inizializzazione dei
if (cifra[n]==45) PORTB=0x40; registri e delle variabili, e finisce con un
FARE ELETTRONICA - LUGLIO/AGOSTO 2006

Figura 7 Schema del display a 4 cifre


loop infinito vuoto, che in pratica attende while(1) {
solamente il verificarsi delle interruzioni. La ShortToStr(val, cifra);
routine d’interruzione invece svolge le stes- Delay_ms(250);
se funzioni viste prima, solo che opera su val++;
una cifra alla volta: aggiorna il valore di }
PORTA e PORTB come nel programma pre-
cedente, modifica il valore di n e ds in modo Notare che in questo caso il ritardo di 250ms
da puntare al prossimo carattere, e prima di inserito non influisce completamente sull’ag-
terminare resetta il valore del timer e del gornamento del display, che continua a funzio-
suo flag d’interruzione. nare con le sue temporizzazioni!
Non è necessario utilizzare la funzione di
ritardo dal momento che i valori impostati CONCLUSIONI
sulle porte saranno mantenuti fino alla pros- Abbiamo visto alcuni esempi abbastanza utili e
sima interruzione, che si verificherà dopo versatili, in questo caso applicati al pilotaggio di
circa 2ms. LED e display a 7 segmenti, ma la cui struttura
A questo punto sarà possibile eseguire altre può essere applicata anche ad altri tipi di disposi-
tivi esterni (relè, motori, buzzer…). Nella prossi- 113
operazioni nel loop principale e modificare

Teoria
come si vuole l’array cifra[], ed il display ma puntata verranno presentate le routine in C
sarà aggiornato di conseguenza ed in per la gestione di dispositivi di input quali pulsan-
maniera automatica. ti, tastiere di varo tipo, e display LCD intelligenti.
Ad esempio si può realizzare un conteggio
inserendo nel loop il seguente codice: Codice MIP 253104

Scheda easyPIC3
La rivoluzionaria scheda
di sviluppo per PICmicro
✔ Programmatore USB2.0 on-board
✔ Tastiera a 32 tasti
✔ 32 LED per il monitoraggio degli I/O
✔ 4 cifre LED a 7 segmenti
✔ Predisposizione per moduli LCD alfanumerici
✔ Predisposizione per moduli LCD grafici
FARE ELETTRONICA - LUGLIO/AGOSTO 2006

✔ Predisposizione per comunicazione RS232


✔ Predisposizione per tastiera PS2
✔ Predisposizione per sensore di temperatura DS1820
✔ Supporto per tutte le famiglie PIC (anche PIC10F)*
✔ Predisposizione per comunicazione USB
✔ Alimentazione esterna o via USB
✔ Fornita con 16F877
✔ Disponibile con o senza display
Codice MIP 253113

Ordinala subito su www.farelettronica.com oppure telefona allo 02.66504755


DISTEFANO - MIKROC 3pt C 31-07-2006 17:54 Pagina 88

TEORIA RISORSE SPECIALE PRATICA

Secondo parte
n° 253/254 - Luglio/Agosto 2006
Controllare le uscite del PIC
Terza parte
n° 255 - Settembre 2006
MikroC
Gestione di pulsanti, tastiere
e display LCD
Quarta parte
n° 256 - Ottobre 2006
by Example:
Generazione di segnali PWM

I n questa puntata scopriremo pull-down poste tra i pulsanti e massa servono


per fissare a livello basso i rispettivi piedini di I/O
come gestire con il MikroC quando i pulsanti non sono premuti (in caso
alcuni semplici ma fondamentali contrario il potenziale dei pin potrebbe essere
facilmente influenzato da interferenze esterne).
dispositivi di input collegati al PIC,
Utilizzeremo il pulsante posto su RB1 per accen-
quali pulsanti e tastiere. dere il LED e quello posto su RB2 per spegnerlo.
Vedremo inoltre come utilizzare Il codice C che implementa questo comporta-
88
mento è il seguente:
un display LDC intelligente
Teoria

per visualizzare le informazioni void main() {


// Inizaliz. porta B
TRISB = 0x06;
PORTB = 0;
I PULSANTI // Loop infinito
Moltissime applicazioni richiedono l’utilizzo di while(1) {
interruttori o di pulsanti per rendere possibile if (PORTB&0x02) PORTB = 1;
l’interazione tra utente (o ambiente) e program- if (PORTB&0x04) PORTB = 0;
ma. La gestione di questi dispositivi potrebbe }
sembrare a prima vista molto semplice. In real- }
tà occorre tenere in considerazione molti aspet-
ti un po’ “nascosti” che potrebbero portare a Come si può vedere, per leggere lo stato dei due
risultati del tutto inaccettabili. Per capire quali pulsanti i piedini corrispondenti della porta B
sono questi problemi consideriamo il circuito sono stati configurati come ingressi, scrivendo il
mostrato in figura 1. valore corrispondente nel registro TRISB (il valo-
Abbiamo utilizzato un PIC16F84 collegando alla re 0x06 corrisponde al valore binario
porta B due pulsanti ed un LED. Le resistenze di 0b00000110, in cui gli 1 rappresentano i pin
configurati come ingressi). Per
controllare se i pulsanti sono pre-
muti viene testato il bit corri-
spondente della porta B con
l’istruzione if, e viene assegnato il
FARE ELETTRONICA - SETTEMBRE 2006

valore corrispondente alle uscite.


Tutto funziona senza problemi,
ma forse non come si può imma-
ginare. Per capire meglio quello
che succede proviamo a fare in
modo che alla pressione di uno
dei due tasti il LED venga acces-
so, ed alla successiva pressione
venga spento. Il codice è il
Figura 1 Accensione e spegnimento di un LED
seguente:
DISTEFANO - MIKROC 3pt C 31-07-2006 17:54 Pagina 89

Gestione di
pulsanti, tastiere
e display LCD di Antonio Di Stefano

void main() { un’istruzione while che ripete in continuazione


// Inizaliz. porta B un loop vuoto fino a quando il tasto risulta pre-
TRISB = 0x06; muto, al rilascio del pulsante si uscirà dall’istru-
PORTB = 0; zione e verrà ripetuto il loop principale. Ora il
// Loop infinito programma dovrebbe funzionare meglio, ma si
while(1) { possono notare ancora delle incertezze nella
if (PORTB&0x06) PORTB = PORTB^0x01; risposta.
} Queste imprecisioni sono dovute al “rimbalzo”
89
} meccanico dei pulsanti, che si verifica alla pres-

Teoria
sione del tasto e genera un segnale che può
Se carichiamo il programma sul PIC e proviamo oscillare diverse volte tra livello alto e livello
a premere un tasto, potremo osservare un com- basso prima di stabilizzarsi.
portamento diverso da quello atteso: il LED si Queste oscillazioni durano tipicamente alcuni
accenderà e si spegnerà in continuazione men- millisecondi e possono variare a seconda dalle
tre il tasto rimane premuto, ad una velocità tale caratteristiche meccaniche del pulsante.
da non vederne neanche il lampeggio, e sarà Vediamo come affrontare questo problema con-
impossibile fermarlo in uno stato prestabilito. siderando un circuito (figura 2) ed un program-
Qual è il problema? Dobbiamo ricordarci che ma un po’ più completo.
l’esecuzione del programma sul PIC è estrema- Nel circuito un display a 7 segmenti è collegato
mente più veloce di qualsiasi nostra azione, di alla porta B del PIC, mentre due pulsanti sono
conseguenza, anche premendo brevemente il collegati alla porta A. Quello che vogliamo otte-
pulsante, il LED verrà acceso e spento migliaia di nere è che ad ogni pressione del pulsante S1 il
volte! Un primo accorgimento per evitare que- numero sul display venga incrementato di uno,
ste ripetizioni consiste nell’attendere che il pul- mentre ad ogni pressione di S2 il conteggio
sante venga rilasciato prima di ripetere il ciclo. venga decrementato. Ecco il codice:
In questo modo dovremo effettivamente pre-
mere due volte il pulsante per accenderlo e spe- void main() {
gnerlo. Il codice è il seguente: signed char cont=0;
char segs[16]={0x3F, 0x06,
void main() { 0x5B, 0x4F, 0x66, 0x6D,
// Inizaliz. porta B 0x7D, 0x07, 0x7F, 0x6F,
TRISB = 0x06; 0x77, 0x7C, 0x39, 0x5E,
FARE ELETTRONICA - SETTEMBRE 2006

PORTB = 0; 0x79, 0x71};


// Loop infinito // Inizializz. porte
while(1) { TRISA = 0x03;
if (PORTB&0x06) PORTB = PORTB^0x01; TRISB = 0x00;
// Attesa rilascio PORTA = 0x08;
while(PORTB&0x06) {}; // Loop infinito
} while(1) {
} // Aggiorna display
PORTB=segs[cont];
Per attendere il rilascio del tasto è stata inserita // Controlla polsanti
DISTEFANO - MIKROC 3pt C 31-07-2006 17:54 Pagina 90

TEORIA RISORSE SPECIALE PRATICA

Terza parte MikroC by Example: Gestione di pulsanti, tastiere e display LCD

if (PORTA&0x02) cont++; Il problema di questo approccio (attesa rilascio


if (PORTA&0x01) cont--; + ritardo) è che occorre letteralmente fermare il
// Correzione contatore programma per qualche istante, ritardando sia
if (cont>9) cont=0; tutti gli altri compiti, sia la risposta stessa del
if (cont<0) cont=9; sistema alla pressione del tasto. Quest’ultimo
// Attende rilascio effetto può essere particolarmente fastidioso,
while(PORTA&0x03) {}; soprattutto nelle interfacce utente. Vedremo più
} avanti una soluzione più sofisticata ed elegante
} a questo problema.

La gestione del display a 7 segmenti è stata trat- TASTIERE A MATRICE


tata nella scorsa puntata, ed il codice utilizzato In alcune applicazioni può essere richiesta l’uso
è praticamente lo stesso. Le due istruzioni if di una testiera dotata di un discreto numero di
verificano se uno dei due pulsanti è premuto e tasti (circa 10 o 20). Ovviamente non è conve-
aggiornano la variabile cont di conseguenza. niente utilizzare un pin di I/O per ciascun tasto,
L’istruzione while seguente attende che i pul- si può invece adottare una tecnica simile a quel-
90 santi vengano rilasciati (il controllo viene fatto la utilizzata per i display a 7 segmenti: indirizza-
Teoria

su entrambi, per questo è stato utilizzato il valo- re separatamente le righe e le colonne della
re 0x03 nell’AND). Infine viene corretto il valo- tastiera. In questo modo è possibile gestire 16
re del conteggio se questo supera 9 o scende tasti utilizzando soltanto 8 I/O! Lo schema è
sotto 0 (per questo motivo la variabile di con- quello mostrato in figura 3, ed è comunemente
teggio è stata dichiarata come signed char). utilizzato in diversi dispositivi commerciali. I pie-
Provando il programma si può notare lo stesso dini da RB0 a RB3 sono configurati come uscite,
difetto visto prima: ad ogni pressione si posso- e pilotano le colonne della tastiera.
no avere variazioni del conteggio maggiori di 1. I pin da RB4 a RB7 sono configurati come
Occorre infatti risolvere ancora il problema dei ingressi e vengono utilizzati per leggere il
rimbalzi. Un metodo banale per fare questo valore presente sulle 4 righe. Quando i tasti
consiste nell’inserire un’attesa di qualche decina vengono premuti, cortocircuitano una riga
di millisecondi alla fine del loop, come segue: con una colonna. Dal momento che le uscite
… vengono attivate una alla volta (come accade-
Delay_ms(50); va con i display a 7 segmenti), leggendo il
} valore delle righe è possibile sapere quale
} tasto è premuto. Ad esempio, all’inizio della
scansione RB3-RB0 varranno
0b0001, quindi sarà selezionata
la prima colonna. Se leggiamo
0b0100 su RB7-RB4 significa
che il tasto premuto è T9.
Scrivere una routine per la let-
tura di una tastiera potrebbe
FARE ELETTRONICA - SETTEMBRE 2006

sembrare un’impresa difficile, o


quantomeno noiosa, ma per
fortuna non abbiamo bisogno
di farlo! Il MikroC infatti mette
a disposizione delle routine di
libreria molto semplici da usare.
Le funzioni in questione sono
Keypad_Init, Keypad_Read e
Keypad_Released. La prima
Figura 2 Conteggio up-down su display a 7 segmenti
serve per selezionare la porta
DISTEFANO - MIKROC 3pt C 2-08-2006 11:49 Pagina 91

del PIC che dovrà gestire la tastiera, la secon- TASTIERE PS/2


da restituisce un numero da 1 a 16 se uno dei Se abbiamo bisogno di un maggior numero di
tasti è stato premuto, 0 altrimenti, mentre la tasti e vogliamo utilizzare un minor numero di
terza esegue la stessa funzione, ma blocca pin di I/O è possibile adottare una soluzione
l’esecuzione del programma fino a quando un molto pratica: possiamo collegare al PIC una
tasto non è stato premuto e rilasciato. Un tastiera PS/2, del tipo utilizzato di solito per i
esempio di utilizzo è il seguente: PC. Sono sufficienti due linee di I/O: una per i
dati seriali, l’altro per il clock. Ad ogni pressione
… di un tasto la tastiera invierà il suo “scan code”,
// Tastiera su porta B sotto forma di una stringa di 11 bit, in cui saran-
Keypad_Init(&PORTB); no presenti un bit di start, 8 bit di dati, un bit di
… parità dispari, ed un bit di stop. Se il tasto viene
// Se è premuto un tasto… tenuto premuto, la tastiera invierà di nuovo il
c=Keypad_Read(); dato ogni 100ms circa.
if (c) { Quando il tasto viene rilasciato la tastiera invie-
… rà un codice 0xF0, seguito dallo scan code del
91
tasto. I tasti “speciali” (tasti F1-F12, tasti curso-

Teoria
} re, etc.) sono preceduti da un codice 0xE0.
// Oppure: Anche in questo caso non sarà necessario scrive-
// attende la pressione re a mano le routine di gestione, perché il
c=Keypad_Release(); MikroC mette a disposizione delle funzioni già
… pronte che rendono estremamente semplice

Compilatore MikroC

Un potente
compilatore C
per PICmicro

✔ Code Editor
✔ Code Explorer
✔ Debugger
FARE ELETTRONICA - SETTEMBRE 2006

✔ Statistiche

Tutto in un ambiente
Windows facile ed intuitivo

Un set di strumenti veramente indispensabili


per sviluppare applicazioni con i PICmicro
Codice MIP 255091

Ordinalo subito su www.farelettronica.com oppure telefona allo 02.66504755


DISTEFANO - MIKROC 3pt C 31-07-2006 17:54 Pagina 92

TEORIA RISORSE SPECIALE PRATICA

Terza parte MikroC by Example: Gestione di pulsanti, tastiere e display LCD

l’uso di questi dispositivi. Le funzioni che utiliz- identica a quella vista in precedenza, le uniche
zeremo sono Ps2_Init e Ps2_Key_Read. differenze risiedono nella chiamata alle due fun-
Queste funzioni restituiscono le informazioni sui zioni che gestiscono la tastiera. Inizialmente è
tasti premuti, e gestiscono anche la conversione stata richiamata la funzione di inizializzazione,
da scan code ad ASCII. indicando che la tastiera è collocata sulla porta
Per effettuare una prima prova consideriamo lo A. All’interno del loop principale viene richiama-
schema di figura 4, in cui un display a 7 seg- ta la funzione Ps2_Key_Read. Questa funzione
menti è collegato alla porta B del PIC, mentre la restituisce 1 se è stato premuto o rilasciato un
tastiera è collegata alle linee RA0 ed RA1 della tasto, oppure 0 in caso contrario.
porta A. Nel primo caso le informazioni sul tasto saranno
Il programma che segue legge i dati inviati dalla contenute nelle tre variabili, che vengono pas-
tastiera e visualizza sul display il carattere corri- sate per riferimento (e che quindi vengono
spondente se questo è un numero oppure una modificate dalla funzione). La variabile key con-
lettera da A ad F. tiene il codice ASCII del tasto premuto o rilascia-
to, la variabile special vale 1 se è stato premuto
void main() { un tasto “speciale” (e che quindi key non con-
92
unsigned short key, special, down; tiene un valido codice ASCII). La variabile down
Teoria

char tasto=0; vale 1 se il tasto è stato premuto, o 0 se è stato


char segs[16]={0x3F, 0x06, rilasciato.
0x5B, 0x4F, 0x66, 0x6D, Nel programma viene verificato prima di tutto
0x7D, 0x07, 0x7F, 0x6F, se si è verificata la pressione di un tasto (e non
0x77, 0x7C, 0x39, 0x5E, un rilascio), ed in seguito viene fatto un control-
0x79, 0x71}; lo sul valore ASCII del tasto: se questo corri-
// Inizializz. sponde ad un valore numerico o ad una lettera
TRISB = 0x00; dalla A alla F (sia minuscole che maiuscole),
// Inizializz. PS/2 viene generato il carattere corrispondente da
Ps2_Init(&PORTA); visualizzare sul display.
// Attende inizializz. Come si può vedere il funzionamento di queste
Delay_ms(100); routine è molto semplice e si presta ad essere
TRISA = 0; utilizzato in molte applicazioni.
PORTA = 15;
// Loop infinito DISPLAY LCD INTELLIGENTI
while(1) { I display LCD intelligenti sono un’altra categoria
// Legge tastiera e aggiorna carattere di dispositivi molto comodi e versatili. Essi permet-
if (Ps2_Key_Read(&key, &special, &down)) { tono infatti di visualizzare testo e numeri disposti
if (down) { su un certo numero di righe e di colonne.
if ((key>='0')&&(key<='9')) La programmazione di questi dispositivi non è
tasto=key-'0';
if ((key>='a')&&(key<='f'))
tasto=key-'a'+10;
FARE ELETTRONICA - SETTEMBRE 2006

if ((key>='A')&&(key<='F'))
tasto=key-'A'+10;
}
}
PORTB=segs[tasto];
Delay_ms(20);
}
}

Figura 3 Schema di una tastiera a matrice


La parte del programma che gestisce il display è
DISTEFANO - MIKROC 3pt C 31-07-2006 17:54 Pagina 93

complicatissima, ma può risultare sicuramente

2006
un tantino laboriosa. Anche in questo caso il
MikroC ci viene incontro fornendoci delle fun-
zioni di libreria già pronte che permettono di
utilizzare in modo molto semplice ed intuitivo MILANO 20-23 SETTEMBRE
questo tipo di display. Le funzioni permettono
di visualizzare sul display delle stringhe di testo
o dei caratteri, indicandone le coordinate inizia-
li se desiderato. 32a BIENNALE INTERNAZIONALE
Vediamo subito un semplicissimo esempio, che DELL’AUTOMAZIONE, STRUMENTAZIONE,
applicato allo schema di figura 5, visualizza sul MICROELETTRONICA E
display la stringa “Fare Elettronica!” disposta su ICT PER L’INDUSTRIA
due righe:

void main() {
char *testo1 = “Fare”; Registrati on-line all’indirizzo
char *testo2 = “Elettronica!”;
www.fieremostre.it/prebs
TRISB = 0x00;
Lcd_Init(&PORTB); e riceverai un’email che,
Lcd_Cmd(LCD_CLEAR);
Lcd_Out(1,1, testo1);
presentata alle reception
Lcd_Out(2,5, testo2); dei Padiglioni 11-9 e
while(1) {}
} 18 di Fiera Milano,
ti permetterà di
Dapprima viene inizializzato il display sulla
porta B, in seguito viene inviato un comando ritirare la tessera
per pulire lo schermo (i comandi disponibili
gratuita di accesso
sono parecchi, e si rimanda al manuale del
MikroC per un elenco completo), ed infine ven-
gono stampate le due stringhe, la prima nella
posizione iniziale (in alto a sinistra), la seconda
nella riga sottostante, spostata di 5 caratteri dal
Siamo presenti al
bordo sinistro.
Mettendo assieme quello che abbiamo impara-
to fino ad ora, possiamo realizzare un’applica-
padiglione 9
stand S15
zione leggermente più complessa, che permet-
te di apprezzare la versatilità delle funzioni di
libreria viste: proviamo a collegare al PIC una
tastiera PS/2 ed un display LCD, e creare un e abbiamo riservato
programma che ci permetta di scrivere sul
display tramite la tastiera, come avviene di soli-
to sullo schermo di un normale computer. Lo
un omaggio
schema da considerare è quello di figura 5, a
cui la tastiera può essere collegata alla porta A
per i nostri abbonati
del PIC, come visto in figura 4.

void main() {
char x, y;
unsigned short key, special, down;
ACCORRETE
NUMEROSI!
DISTEFANO - MIKROC 3pt C 31-07-2006 17:54 Pagina 94

TEORIA RISORSE SPECIALE PRATICA

Terza parte MikroC by Example: Gestione di pulsanti, tastiere e display LCD

// Inizializz. e viene attivato il cursore tramite gli appositi


TRISB = 0x00; comandi, poi ha inizio il loop principale, in cui
// Inizializz. display si verifica se c’è stata attività sulla tastiera, se è
Lcd_Init(&PORTB); stato premuto un tasto (variabile down=1), se si
// Inizializz. PS/2 tratta di un tasto alfanumerico o meno (variabi-
Ps2_Init(&PORTA); le special). Il carattere viene quindi inviato al
// Attende inizializz. display oppure gestito opportunamente se cor-
Delay_ms(100); risponde ad un tasto speciale. Come si può
Lcd_Cmd(LCD_CLEAR); vedere le coordinate del cursore vengono gesti-
Lcd_Cmd(LCD_UNDERLINE_ON); re dal programma, questo permette di evitare
che il cursore possa superare l’area visibile del
x=1; display. I caratteri sono stampati con la funzione
y=1; Lcd_Chr, che prende in ingresso le coordinate
// Loop principale (riga e colonna), ed il codice ASCII del carattere
while(1) { da stampare, che in questo caso è quello resti-
// Legge tastiera e visualizza tuito dalla tastiera. Tra i tasti speciali quelli rico-
94
if (Ps2_Key_Read(&key, &special, &down)) { nosciuti e gestiti sono i tasti cursore, che per-
Teoria

if (down) { mettono di spostarsi nelle quattro direzioni, ed


if (!special) { il tasto Enter, che viene utilizzato per cancellare
// Testo il testo visualizzato. Per evitare che il cursore
if ((key>31)&&(key<128)) { esca dall’area visualizzata (larga 2 righe di 16
Lcd_Chr(y,x,key); caratteri), sono presenti degli appositi controlli
x++; // Avanza cursore che limitano le coordinate x ed y del cursore. La
} funzione Lcd_Out, che di solito serve per visua-
} else { lizzare una stringa in una data posizione, qui è
if (key==30) x--; // Cursore sx usata soltanto per aggiornare la posizione del
if (key==31) x++; // Cursore dx cursore (infatti visualizza una stringa vuota).
if (key==32) y=1; // Cursore su
if (key==33) y=2; // Cursore giù ANTIRIBALZO CON INTERRUZIONI
if (key==13) { // Enter: cancella Come è stato accennato prima, implementa-
x=1; re la funzione di antirimbalzo ed antiripetizio-
y=1; ne (o ripetizione ad una velocità controllata)
Lcd_Cmd(LCD_CLEAR); può causare qualche inconveniente, se non si
} progetta bene il tutto.
} Esistono molte buone soluzioni a questo pro-
blema, e di seguito ne mostreremo una che
// Aggiornamento posizione può essere facilmente adattata alle diverse
if (x>16) x=16; esigenze e ad un numero di pulsanti qualsia-
if (x<1) x=1; si. L’idea è quella di utilizzare le interruzioni,
// Aggiorna posiz. cursore seguendo la stessa filosofia che abbiamo
FARE ELETTRONICA - SETTEMBRE 2006

Lcd_Out(y,x,""); usato per gestire i display a 7 segmenti di 4


} cifre: generare un’interruzione ad intervalli
} regolari con un timer, e verificare lo stato dei
} pulsanti in questi intervalli. In pratica ad ogni
} interruzione verifichiamo se lo stato dei pul-
santi è cambiato, in caso affermativo iniziamo
Come si può vedere il programma è un mix di a contare da zero, e continuiamo fintantoché
quelli presentati precedentemente. Il display è lo stato rimane uguale.
stato assegnato alla porta B mentre la tastiera In questo modo sapremo che se il contatore
alla porta A. Inizialmente il display viene ripulito raggiunge una certa soglia lo stato dei pul-
DISTEFANO - MIKROC 3pt C 31-07-2006 17:54 Pagina 95

realtà fa anche di meglio:


gestisce anche il ritardo con
cui aggiornare il valore quan-
do il pulsante rimane premu-
to! Infatti il valore caricato nel
contatore è differente a secon-
da che siano verificati dei cam-
biamenti di stato (pulsante
premuto o rilasciato) o che si
sia utilizzata la variabile.
In questo modo possiamo
ottenere una risposta molto
veloce alla pressione, ma una
velocità di ripetizione molto
più lenta quando il pulsante
Figura 4 Collegamento di una tastiera PS/2 al PIC rimane premuto, adatta quindi
95
ai tempi di reazione dell’uten-

Teoria
santi si è stabilizzato, e quindi possiamo con- te. Il codice che segue mostra la tecnica
siderare il loro valore valido. descritta applicata allo schema di figura 2, in
Una volta utilizzato il valore dei pulsanti, cui i pulsanti controllano l’incremento o il
azzeriamo il contatore in modo da prepararci decremento del numero visualizzato sul
alla successiva pressione. La nostra routine in display a 7 segmenti.

FARE ELETTRONICA - SETTEMBRE 2006


Codice MIP 255095
DISTEFANO - MIKROC 3pt C 31-07-2006 17:55 Pagina 96

TEORIA RISORSE SPECIALE PRATICA

Terza parte MikroC by Example: Gestione di pulsanti, tastiere e display LCD

char tasti, tpress; if (tasti&0x04) cont--;


void main() { tpress=0;
char cont=0; }
char segs[16]={0x3F, 0x06,
0x5B, 0x4F, 0x66, 0x6D, // Correzione contatore
0x7D, 0x07, 0x7F, 0x6F, if (cont>9) cont=0;
0x77, 0x7C, 0x39, 0x5E, if (cont<0) cont=9;
0x79, 0x71}; }
// Inizializz. porte }
TRISA = 0x03;
PORTA = 0x08;
TRISB = 0x00; void interrupt()
// Inizializz. registri {
OPTION_REG = 0x82; if (tasti==PORTA) {
TMR0 = 0; if (tpress<250) tpress++;
INTCON = 0xA0; } else {
96 // Loop principale tpress=180;
Teoria

while(1) { tasti=PORTA;
// Aggiorna display }
PORTB=segs[cont]; // Reset timer e flag
// Controlla polsanti TMR0 = 0;
if (tpress>200) { INTCON = 0x20;
if (tasti&0x02) cont++; }

Figura 5 Collegamento del display LCD al PIC

Rispetto al codice mostrato prima le differenze estesa a più pulsanti su più porte, e può essere
sono minime, e consistono nell’impiego della integrata con la routine di gestione del display
routine d’interruzione e delle variabili tpress, vista nella scorsa puntata (o con altre che richie-
FARE ELETTRONICA - SETTEMBRE 2006

che viene usata come contatore per lo stato dei dano simili tempistiche).
pulsanti, e la variabile tasti, che è in pratica una
copia della porta A. Come si può vedere quan- CONCLUSIONI
do viene rilevata una transizione (variabile tasti Nella prossima puntata vedremo come genera-
è diversa dal valore di PORTA), il valore del con- re con il PIC ed il MikroC dei segnali PWM e
tatori riparte da 180 e non 0, quindi per rag- come utilizzarli per delle interessanti applicazio-
giungere la soglia (200) impiegherà minor ni. Vedremo anche come generare del suoni e
tempo rispetto a quando il valore viene utilizza- melodie con il PIC. Non mancate!
to (tptress è riportato a 0).
Va notato che la routine può essere facilmente Codice MIP 255088
DISTEFANO - MIKROC 4pt C 13-09-2006 9:43 Pagina 92

TEORIA MHZ RISORSE SPECIALE PRATICA

Terza parte
n° 255 - Settembre 2006
Gestione di pulsanti, tastiere
e display LCD
Quarta parte
n° 256 - Ottobre 2006
MikroC
by Example:
Generazione di segnali PWM
Quinta parte
n° 257 - Novembre 2006
Realizzazione di un timer
digitale programmabile

L a modulazione PWM permette questa energia verrà dissipata (o utilizzata) dal


dispositivo stesso. L'energia media su un periodo
di generare e gestire segnali sarà quindi funzione del rapporto tra la durata del
analogici utilizzando le normali livello alto e quella del livello basso (questo rap-
porto è chiamato "duty cycle"). Se vogliamo otte-
uscite digitali del microcontrollore.
nere un valore "medio" stabile occorrerà che il
In questo articolo verranno descritte dispositivo collegato sia "sufficientemente lento",
diverse tecniche per generare in modo da eseguire proprio l'operazione di
92
"media" nel tempo.
segnali PWM utilizzano i PIC
Teoria

Nel caso di un microcontrollore (o altro dispositi-


ed il MikroC. vo digitale) potremo ottenere un valore di tensio-
ne analogico compreso tra circa 0V e 5V (o
comunque la tensione del livello logico alto), sem-
plicemente generando un'onda quadra e varian-
INTRODUZIONE do il duty cycle dallo 0% al 100%. Come già
Quella di generare o controllare segnali analogi- detto è necessario che il circuito collegato a valle
ci utilizzando un microcontrollore è un'esigenza abbia un comportamento "passa basso", in modo
abbastanza frequente. In molti casi infatti un'in- da rispondere con sufficiente lentezza. Se questa
formazione binaria tipo acceso/spento non è condizione non è verificata, basta aggiungere un
sufficiente, ma occorrere un segnale analogico filtro passa basso passivo come mostrato in segui-
vero e proprio al cui livello possa essere associa- to. Ma come si genera un segnale PWM? Il meto-
ta un'informazione. Quasi tutti i microcontrollo- do classico è illustrato in Figura 1.
ri mettono a disposizione dei convertitori È utile comprendere questo metodo perché sarà
Analogico/Digitali, ma quasi nessuno incorpora quello che utilizzeremo negli esempi seguenti
la funzione opposta. Una soluzione un po’ dra- (implementato via software o hardware). Si utiliz-
stica (dal punto di vista dei costi) potrebbe esse- za un contatore che viene costantemente incre-
re quella di utilizzare un convertitore mentato, ed un valore di soglia: se il valore del
Digitale/Analogico esterno. Per fortuna nella contatore è inferiore alla soglia, l'uscita sarà a livel-
maggior parte di applicazioni non occorre arriva- lo alto, se il contatore è maggiore o uguale, l'usci-
re a tanto, ed è possibile utilizzare i dispositivi ta sarà posta a livello basso. Per valori bassi della
messi a disposizione dal micro per ottenere que- soglia si otterranno quindi duty cycle bassi, e vice-
sta funzione. Per fare questo si ricorrere infatti alla versa. Nei prossimi paragrafi vedremo come
FARE ELETTRONICA - OTTOBRE 2006

modulazione PWM (Pulse Width Modulation, ossia implementare questo meccanismo in C, utilizzan-
modulazione a larghezza di impulsi), che consen- do diverse tecniche.
te appunto di generare dei segnali analogici par-
tendo direttamente da segnali digitali.
L'idea è la seguente: si utilizza un'onda quadra, di
solito a frequenza costante, scegliendo opportu-
namente il tempo in cui essa si mantiene a livello
alto in ciascun periodo. Questo tempo è quello in
cui in ogni ciclo effettivamente forniamo energia
Figura 1 Generazione di un segnale PWM
al dispositivo collegato, nel tempo rimanente
DISTEFANO - MIKROC 4pt C 12-09-2006 19:25 Pagina 93

Generazione di
segnali PWM
di Antonio Di Stefano

PWM CON CODICE SEQUENZIALE cont++;


Il codice riportato di seguito mostra com'è possi-
bile implementare in C un semplice generatore di i++;
segnale PWM. In questo caso il segnale viene if (i==200) {
costruito letteralmente come spiegato prima: level++;
viene usata una variabile per implementare il con- i=0;
tatore, una per la soglia ed una condizione che }
aggiorna il livello dell'uscita. Questo approccio ha }
93
il vantaggio di essere utilizzabile su qualsiasi PIC, }

Teoria
anche i più semplici, in quanto non si ricorre ad
alcuna periferica hardware specifica, ed è possibi- La variabile cont è usata come contatore, ed è
le avere l'uscita su qualsiasi piedino di I/O. Il quindi incrementata ad ogni iterazione. Dal
segnale PWM generato viene utilizzato nello sche- momento che è ampia 8 bit, raggiunto il limite di
ma di Figura 2 per fare lampeggiare un LED con 255 tornerà automaticamente a zero. La variabile
una variazione graduale della luminosità. Ci si level è usata come soglia. Se avessimo assegnato
potrebbe chiedere come mai non sia stato usato un valore fisso, avremo potuto ottenere un valore
un filtro passa basso. In effetti il LED è un disposi- stabile di luminosità del LED. In questo caso inve-
tivo abbastanza veloce, e non esegue affatto la ce il valore della soglia è variato progressivamen-
media del segnale, ma ne segue fedelmente il te, in modo da avere un duty cycle che inizia dallo
valore. Tuttavia il risultato che si ottiene è buono, 0% (level=0) al 100% (level=255). La soglia è
perché la media è eseguita dal nostro occhio, che incrementata ogni 200 iterazioni, in modo da ren-
ha un tempo di risposta di circa un ventesimo di dere visibile la variazione, per fare questo è stata
secondo! Il codice è riportato di seguito. utilizzata la variabile i, che serve proprio per con-
tare quante volte viene eseguito il loop.
void main() { Un paio di considerazioni importanti: quanti livel-
unsigned char cont, level, i; li di luminosità possiamo ottenere? Ben 256, cioè
tanti quanti sono i livelli del contatore PWM. Qual
TRISB = 0; è invece la frequenza della nostra onda PWM?
PORTB = 0; Qui la risposta è meno ovvia: per completare un

cont=0;
level=0;
FARE ELETTRONICA - OTTOBRE 2006

i=0;

// Loop infinito
while(1) {
if (cont>level) {
PORTB=0;
} else {
PORTB=1;
}
Figura 2 Schema per l'accensione del LED con onda PWM
DISTEFANO - MIKROC 4pt C 13-09-2006 9:44 Pagina 94

TEORIA MHZ RISORSE SPECIALE PRATICA

Quarta parte MikroC by Example: Generazione di segnali PWM

periodo è necessario che il contatore esaurisca il fatto che il segnale PWM generato ha una risolu-
conteggio, quindi serviranno ben 256 iterazioni zione di 16 livelli e non 256. Questo è stato otte-
del loop per completare un solo periodo! nuto incrementando il contatore di 16 invece che
Supponendo di utilizzare un PIC funzionante a di 1 (l'overflow si ha in 256/16=16 passi). Questo
4MHz, possiamo misurare quanto tempo impie- accorgimento, unito all'utilizzo delle interruzioni
ghiamo per eseguire un'iterazione del loop utiliz- permette di ottenere una frequenza PWM molto
zando il Debugger del MikroC, facendolo funzio- più alta.
nare in modalità step, e osservando il tempo di
esecuzione riportato in basso. Il risultato è 16us, unsigned char cont, level;
quindi 16us*256=4096us, cioè ben 4ms (244Hz)!
Giusto in tempo per non percepire un fastidioso void main() {
sfarfallio…Questo semplice calcolo mette in luce
un problema tipico del PWM: se vogliamo ottene- // Inizializz. porte
re una risoluzione alta, dobbiamo accontentarci di TRISA = 0x03;
frequenze basse, a meno di non "contare" a velo- PORTA = 0x00;
cità molto elevate (cosa non sempre possibile o TRISB = 0x00;
94
conveniente).
Teoria

// Inizializz. Registri
PWM CON INTERRUZIONI OPTION_REG = 0x80;
Uno dei problemi del codice presentato prima è TMR0 = 0;
che il periodo dell'onda PWM dipende dalla quan- INTCON = 0xA0;
tità delle istruzioni che sono presenti nel loop prin-
cipale. Ovviamente in programmi completi il loop cont=0;
sarà affollatissimo di istruzioni e routine e quindi la level=0;
frequenza del PWM risulterà bassissima e probabil-
mente non costante. Per risolvere questo inconve- // Loop infinito
niente, come abbiamo fatto in altre occasioni, pos- while(1) {
siamo utilizzare le interruzioni. Programmiamo il
timer TMR0 del PIC in modo da generare un'inter- if (PORTA&0x01)
ruzione ad intervalli regolari, ed utilizziamo la rou- level+=16;
tine di servizio per incrementare il contatore PWM if (PORTA&0x02)
ed aggiornare l'uscita. In questo caso la frequenza level-=16;
dell'onda generata sarà facile da calcolare essendo
data dalla frequenza delle interruzioni diviso il Delay_ms(100);
numero di passi del contatore. Questa frequenza }
risulterà inoltre indipendente dal contenuto del }
loop principale e dal resto del programma.
Il codice riportato di seguito implementa il meto- void interrupt()
do appena descritto: il segnale PWM viene utilizza- {
to per regolare la luminosità di un LED, il cui livel- if (cont>level) {
FARE ELETTRONICA - OTTOBRE 2006

lo può essere regolato manualmente agendo sui PORTB=0;


due pulsanti SW1 ed SW2, che permettono di } else {
aumentarlo o diminuirlo (lo schema è riportato in PORTB=1;
Figura 3). Si può notare come la gestione dei pul- }
santi sia fatta nel loop principale, dove per evitare
problemi di rimbalzo è stato inserito addirittura un cont=cont+16;
ritardo di 100ms! Tutto questo non interferisce
con la generazione del segnale PWM, che invece TMR0 = 0; // reset timer
segue tempistiche indipendenti. Un'altra differen- INTCON = 0x20; // cancella flag IRQ
za rispetto al codice presentato prima risiede nel }
DISTEFANO - MIKROC 4pt C 12-09-2006 19:25 Pagina 95

I pin RA0 e RA1 della porta A sono impostati niente prescaler, etc.). Nel loop viene soltanto
come ingressi ed utilizzati per rilevare la pressio- rilevata la pressione di uno dei due pulsanti ed
ne dei pulsanti. La porta B è utilizzata per la aggiornato il livello, nella routine d'interruzione
generazione dell'onda PWM (sul piedino RB0). invece si trova lo stresso codice che prima era
Sono abilitate le interruzioni relative al TMR0, nel loop principale. Come già detto il contatore
ed impostati il suoi parametri (clock interno, è incrementato di 16 unità invece che di 1, per
ottenere una risoluzione di 16 livelli (4 bit). Si
sarebbe anche potuto resettare il contare stesso
una volta raggiunto il valore 16, in questo caso
anche il livello sarebbe stato espresso come un
numero compreso tra 0 e 15.

GENERAZIONE PWM HARDWARE


Nonostante i vantaggi di utilizzare le interruzio-
ni, la generazione del segnale PWM via softwa-
re costituisce sempre un compito piuttosto gra-
95
voso per il PIC, soprattutto quando deve svolge-

Teoria
re anche diversi altri compiti o la frequenza scel-
ta è piuttosto alta. Una soluzione molto efficien-
te è costituita dall'impiego di periferiche har-
Figura 3 Schema per controllo luminosità LED
dware apposite presenti su molti PIC. In questo
caso è sufficiente programmare il modulo appo-

Compilatore MikroC

Un potente
compilatore C
per PICmicro

 Code Editor
 Code Explorer
 Debugger
FARE ELETTRONICA - OTTOBRE 2006

 Statistiche

Tutto in un ambiente
Windows facile ed intuitivo

Un set di strumenti veramente indispensabili


per sviluppare applicazioni con i PICmicro
Codice MIP 256095

Ordinalo subito su www.ieshop.it oppure telefona allo 02.66504755


DISTEFANO - MIKROC 4pt C 13-09-2006 9:45 Pagina 96

TEORIA MHZ RISORSE SPECIALE PRATICA

Quarta parte MikroC by Example: Generazione di segnali PWM

sito ed il segnale verrà generato in maniera del


tutto indipendente dal software. Questo // Loop infinito
approccio come vedremo è reso più semplice while(1) {
dal fatto che il MikroC mette a disposizione Pwm_Change_Duty(level);
alcune funzioni di libreria per utilizzare queste
periferiche. Nell'esempio seguente viene utiliz- if (PORTB) {
zato il modulo CCP (Capture / Cpmpare / delta=-delta;
PWM) di un PIC16F876 per controllare l'accen- run=1;
sione e lo spegnimento "soft" di una lampadina while(PORTB){};
a 12V (Figura 4). Va notato che quando si utiliz- }
za il generatore hardware l'uscita del segnale
può essere prelevata soltanto dal piedino RC2 if (run) level=level+delta;
(non è selezionabile arbitrariamente). if ((level==0)||(level==255))
run=0;
void main() {
unsigned char level; Delay_ms(20);
96 }
char delta, run;
Teoria

}
// Iniz. porte
TRISB = 0x01; La funzione Pwm_Init imposta la frequenza del-
PORTB = 0; l'onda PWM, la funzione Pwm_Start avvia la
PORTC = 0; generazione del segnale (esiste anche
TRISC = 0; Pwm_Stop per fermarlo), mentre
Pwm_Change_Duty serve per cambiare il valore
// Init. modulo PWM di duty cycle generato. Come si vede, utilizzan-
Pwm_Init(1000); // Freq. PWM 1000Hz do queste funzioni il codice risulta particolar-
Pwm_Start(); // Start PWM mente semplice. Ad ogni pressione del pulsante
viene attivato l'aggiornamento del livello (run=1)
delta=1; ed invertita la direzione d'incremento (il modo
run=0; da ottenere una rampa in salita ed in discesa in
level=255; maniera alternata). Una volta raggiunto uno dei
FARE ELETTRONICA - OTTOBRE 2006

Figura 4 Schema per accensione e spegnimento soft di una lampada


DISTEFANO - MIKROC 4pt C 12-09-2006 19:25 Pagina 97

LA SOLUZIONE
per il disegno tecnico

Sprint-Layout
sPrint-Layout è il compagno ideale di sPlan anche se può lavorare in modo del tutto indipendente. Il programma è studiato
appositamente per la realizzazione di circuiti stampati, il quale, oltre le funzioni standard necessarie alla creazione di
circuiti stampati, offre anche funzioni professionali quali: esportazione in formato GERBER ed EXCELLON.
Tra le numerose ed innovative caratteristiche ne troviamo una studiata appositamente per gli hobbisti, infatti, è possibile
scannerizzare un circuito stampato da una rivista o un qualsiasi supporto cartaceo ed importarlo per facilitarne il disegno
o la modifica. La funzione di “photoview” mostra il circuito stampato così come apparirebbe una volta prodotto (funzione
che utilizziamo per la rivista Fare Elettronica). Un libreria di componenti, facilmente creabili o modificabili, completa la
ricca dotazione del programma.
I disegni di Fare Elettronica sono realizzati con Sprint-Layout, da oggi potrai scaricarli dal sito e modificarli direttamente.
€ 39,00 (+Iva)

sPlan
Splan è un cad appositamente realizzato per la stesura di schemi elettrici che implementa tutti gli strumenti necessari allo
svolgimento del lavoro in modo semplice ed efficace. sPlan è dotato di molte funzioni tra le quali: numerazione automatica
di componenti, scaling e preview dei componenti, gestione delle librerie di simboli, modifica di ogni singolo elemento sullo
schermo, griglia di posizionamento, connessioni automatiche, e molto altro ancora. La libreria di simboli contiene moltissime
parti pronte all’uso, inoltre le funzioni di editing dei simboli consentono la creazione di nuovi molto velocemente. SPlan
Codice MIP 256097

implementa anche un potente motore di stampa e la possibilità di esportare i disegni in diversi formati, anche grafici.
€ 39,00 (+Iva)

Front Designer
I tempi sono cambiati ed il vostro progetto elettronico non ha più motivo di essere incluso in un vecchio pacchetto di sigari.
Oggi molti rivenditori offrono contenitori adatti a tutti i tipi di circuiti immaginabili, ma spesso i dispositivi autocostruiti
restano incompleti per la mancanza delle indicazioni sul pannello frontale.
Front Designer offre la possibilità di creare pannelli frontali veramente professionali.
€ 39,00 (+Iva)

Puoi ordinare Sprint-Layout , sPlan e Front Designer


sul sito Internet www.ieshop.it/abacom oppure telefonando allo 02.66504755
DISTEFANO - MIKROC 4pt C 13-09-2006 9:45 Pagina 98

TEORIA MHZ RISORSE SPECIALE PRATICA

Quarta parte MikroC by Example: Generazione di segnali PWM

due estremi (0 o 255) l'aggiornamento viene {


arrestato (run=0). Il ritardo di 20ms serve per Pwm_Change_Duty(onda[j&0x0F]);
evitare fenomeni di rimbalzo e per rendere più j++;
visibile la variazione di luminosità.
TMR0 = 192; // reset timer
GENERAZIONE DI SEGNALI INTCON = 0x20; // reset flag IRQ
ANALOGICI }
La tecnica PWM può essere utilizzata per genera-
re segnali analogici. Per fare questo è sufficiente I 16 campioni della sinusoide sono memorizzati
aggiornare il duty cycle dell'onda PWM a inter- in un array, che ad ogni interruzione viene letto
valli regolari, con i campioni del segnale analogi- per assegnare al duty cycle il valore del campio-
co da riprodurre. La frequenza di riproduzione ne corrente (puntato dall'indice j). La frequenza
dei campioni può essere al massimo pari a quella dell'onda generata sarà data da quella di over-
dell'onda PWM, e quindi la massima frequenza flow del TMR0 (Fclk/4 diviso 256-192=64), divi-
analogica riproducibile risulta metà di questa fre- so 16 campioni per periodo. Partendo da un
quenza. In realtà per evitare distorsioni è bene clock di 8MHz si ottiene una sinusoide della fre-
98
fare in modo che ogni campione della forma quenza di circa 300Hz. Per aumentare la fre-
Teoria

d'onda analogica sia mantenuto per diversi cicli quenza della sinusoide occorre caricare in TMR0
dell'onda PWM. Il codice seguente mostra la un valore maggiore, in modo da aumentare la
generazione di una sinusoide utilizzando la tecni- frequenza con cui vengono generate le interru-
ca descritta, ed il circuito di Figura 5. zioni, ma occorre mantenersi sotto la frequenza
dell'onda PWM, che è stata scelta pari a
unsigned char j; 31248Hz. Per ottenere un segnale analogico
const unsigned char pulito (poco distorto) si dovrebbe fare in modo
onda[16]={128,176,218,246, da mantenere la frequenza del segnale genera-
to abbastanza più bassa di quella di campiona-
255,246,218,176, mento, o in caso contrario utilizzare un filtro
128, 79, passa basso abbastanza ripido la cui frequenza
37, 10, di taglio può essere più vicina a quella di cam-
0, 10, 37, pionamento. Nel circuito di Figura 5 è stato uti-
79}; lizzato un semplice circuito RC come filtro
passa-basso, la cui frequenza di taglio è circa
void main() { 1500Hz. Va ricordato che in questo caso il cari-
co deve avere un'impedenza sufficientemente
// Inizializz. porte e regs alta (almeno qualche Kohm), in modo da non
PORTC = 0; variare la costante di tempo de circuito.
TRISC = 0;
OPTION_REG = 0x80; GENERAZIONE DI SUONI ED
TMR0 = 192; EFFETTI
INTCON = 0xA0; Apparentemente l'argomento potrebbe sembra-
FARE ELETTRONICA - OTTOBRE 2006

re poco correlato con la generazione di segnali


// Init. modulo PWM PWM, invece proprio i componenti del PIC che
Pwm_Init(31248); // Freq. PWM = 31KHz generano i segnali PWM possono essere utilizzati
Pwm_Start(); // Start PWM anche per generare suoni, note musicali ed effet-
ti sonori. Agendo sui rispettivi registri infatti è
// Loop infinito possibile modulare sia l'ampiezza (attraverso il
while(1) {} duty cycle) che la frequenza del segnale genera-
} to. Una volta inizializzato ed avviato il modulo
void interrupt() PWM è possibile variare il duty cycle agendo sul
registro CCPR1L, e la frequenza, agendo sul regi-
DISTEFANO - MIKROC 4pt C 12-09-2006 19:25 Pagina 99

stro PR2. Il primo determina il valore del contato- terno di una routine d'interruzione, in modo da
re TMR2 superato il quale l'uscita si porta a livel- lasciare spazio ad altre routine all'interno del
lo basso, mentre il secondo determina il valore loop principale.
per cui il timer viene resettato (più piccolo sarà
questo valore, più frequente sarà l'overflow). void main() {
Se vogliamo usare il modulo PWM per generare unsigned char j;
suoni dobbiamo soltanto tenere presente due
cose: 1) il prescaler di TMR2 (bit 0 e 1 del regi- // Init. porte
stro T2CON) deve essere impostato in modo che PORTC = 0;
la frequenza ottenuta risulti udibile; 2) Il valore di TRISC = 0;
duty cycle deve essere inferiore al limite di con- Pwm_Init(1000);
teggio, altrimenti non ci saranno variazioni nel Pwm_Change_Duty(128);
segnale d'uscita. Maggiori dettagli possono esse- Pwm_Start();
re trovati nel datasheet del PIC. T2CON=0x07; // Init. prescaler
Nell'esempio seguente è stato utilizzato un
PIC16F876 con clock ad 8MHz (stesso schema di // Loop infinito
99
Figura 5), il prescaler è stato impostato a 1:16, while(1) {

Teoria
ottenendo frequenze che coprono un range che for(j=70; j<255; j+=5) {
va da circa 100Hz a circa 1KHz. Il programma PR2=j;
assegna dei valori crescenti e poi decrescenti al Delay_ms(100);
registro PR2, ad intervalli di 100ms, ottenendo }
un effetto sonoro tipo scala ascendente e discen- for(j=255; j>70; j-=5) {
dente (o "sirena", se riprodotto più velocemente). PR2=j;
E' possibile anche calcolare i valori per PR2 in Delay_ms(100);
modo da ottenere le note musicali, che suona- }
te in sequenza possono produrre una melodia }
vera e propria. Invece assegnando a PR2 una }
sequenza veloce di valori "arbitrari" si possono
ottenere degli interessanti effetti sonori (molto CONCLUSIONI
simili a quelli dei vecchi videogame!). Com'è Gli esempi visti in queste quattro puntate
facile immaginare anche in questo caso l'ag- dovrebbero avere fornito un'idea relativamente
giornamento dei valori può essere fatto all'in- chiara di come si possano scrivere dei semplici
programmi in C per i microcon-
trollori PIC. Nella prossima pun-
tata verrà utilizzato quanto visto
fino ad ora per realizzare un'ap-
plicazione completa e legger-
mente più complessa di quelle
viste fino ad ora: un utilissimo
timer digitale programmabile.
FARE ELETTRONICA - OTTOBRE 2006

Questa applicazione ci darà l'op-


portunità si studiare come strut-
turare in C un programma più
complesso e come fare interagire
le sue parti nel modo più corret-
to. Verranno presentati come al
solito gli schemi ed il codice
completo.
Figura 5 Schema per la generazione di un segnale analogico
Codice MIP256092
di stefano - TEORIA - mikroC 5 17-10-2006 15:25 Pagina 86

TEORIA MHZ RISORSE SPECIALE PRATICA

Quarta parte
n° 256 - Ottobre 2006
Generazione di segnali PWM

Quinta parte
n° 257 - Novembre 2006
Realizzazione di un timer
MikroC
by Example:
digitale programmabile

Sesta parte
n° 258 - Dicembre 2006
Uso delle interfacce seriali

I n questa puntata verrà


utilizzato quanto visto negli
scorsi numeri per realizzare
possibile impostare: giorno della settimana, ora
di inizio, e durata. Se non si specifica un giorno
preciso, l’evento verrà attuato con frequenza
giornaliera. I dati e le impostazioni sono visualiz-
zate su un display LCD 16x2, e la programmazio-
un'applicazione completa: un
ne avviene utilizzando 4 pulsanti: “Adjust”,
timer settimanale programmabile. “Select”, “-” e “+”. In base alla modalità selezio-
Verranno illustrati i dettagli del nata i tasti possono avere diverse funzionalità. In
86
genere Il primo serve per entrare nella modalità
codice C, le tecniche utilizzate e
Teoria

di modifica dei dati, il secondo per cambiare


lo schema elettrico. modalità (visualizzazione ora / visualizzazione
eventi) o per spostare il cursore in campi succes-
sivi. Gli ultimi due servono invece per impostare
i valori voluti o per scorrere i dati. Nella modalità
SPECIFICHE DEL TIMER di visualizzazione dell’ora viene appunto mostra-
In questa puntata considereremo un progetto ta l’ora attuale (ore, minuti, secondi), il giorno
relativamente più complesso di quelli visti fino ad della settimana e lo stato dell’uscita. La pressione
ora, un esempio di applicazione completa scritta di “Adjust” permette di modificare i valori corren-
interamente in C. Questo permetterà di analizza- ti, la pressione di “Select” permette invece di
re le tecniche e le soluzioni adottate e la struttu- visualizzare gli eventi. Premendo “+” e “-” si può
ra del codice di un’applicazione reale, tra l’altro attivare o disattivare manualmente l’uscita. Nella
abbastanza utile in pratica. Nonostante l’applica- modalità di visualizzazione degli eventi è possibi-
zione non sia eccessivamente complessa, essa le visualizzare, attivare ed impostare i valori di cia-
presenta alcuni aspetti non proprio intuitivi, che scuno di essi, in modo simile a quanto visto
devono essere risolti in modo adeguato (ad prima. Dettagli più precisi possono essere dedot-
esempio la gestione dell’interfaccia utente). Per ti dal codice stesso e dal diagramma di stato
questioni di spazio non è possibile riportare l’in- riportato di seguito. Lo schema elettrico del cir-
tero codice qui (è lungo più di 660 linee!), ne cuito che implementa il timer è riportato in
verrà però descritta la struttura, il funzionamen- Figura 1, e come si può vedere è relativamente
to, e mostrate le routine principali. Il codice com- semplice. E’ stato utilizzato un PIC16F876 funzio-
pleto può comunque essere scaricato dal sito di nante a 8MHz, un display LCD “intelligente”
Fare Elettronica. 16x2 e pochi altri componenti passivi. Si sarebbe
FARE ELETTRONICA - NOVEMBRE 2006

Prima di passare alla descrizione del codice e potuto utilizzare un micro più piccolo, ma si è
delle tecniche implementative utilizzate però è il preferito puntare sul 16F876 perché avendo a
caso di descrivere in dettaglio le specifiche del- disposizione diverse porte libere, permette di
l’applicazione che ci proponiamo di realizzare. aggiungere e sperimentare funzioni aggiuntive
Il timer che vogliamo implementare deve essere (si veda in seguito).
capace di memorizzare l’ora corrente ed il giorno
della settimana, e di gestire un certo numero di STRUTTURA DEL CODICE
eventi che controllano quando l’uscita (ossia un Il programma è costituito da un unico modulo
relè a cui può essere collegato un carico arbitra- (Timer.c) e dal suo header (Timer.h). Nel primo è
rio) deve essere attivata. Per ciascun evento è contenuto il corpo di tutte le funzioni (compreso
di stefano - TEORIA - mikroC 5 17-10-2006 15:25 Pagina 87

Realizzazione di
un timer digitale
programmabile di Antonio Di Stefano

il main), mentre nel secondo sono presenti le Routine d’interruzione


dichiarazioni di macro, costanti, tipi, variabili glo- Le interruzioni sono generate dal timer TMR0 del
bali ed i prototipi delle funzioni. Questa suddivi- PIC, e servono per cadenzare il funzionamento del-
sione, come sarà più chiaro in seguito, è utile per l’intero circuito. Il codice della routine d’interruzione,
consentire di modificare alcuni parametri senza contenuta nel modulo Timer.c è riportato nel lista-
bisogno di modificare il codice. Nel listato 1 è to 2. L’aggiornamento dell’ora avviene incremen-
riportato il contenuto in Timer.h ed è utile per ini- tando i campi dell’apposita struttura, e consideran-
ziare a comprendere la struttura dell’intero pro- do i rispettivi valori di overflow (60 per secondi e
87
gramma. In generale le funzioni del timer sono minuti, 24 per le ore, 7 per i giorni). Dal momento

Teoria
gestire da due entità: la macchina a stati princi- che non è possibile dividere la frequenza del timer
pale, che si occupa in pratica di gestire l’interfac- TMR0 del PIC per un fattore tale da avere a disposi-
cia utente, e la routine d’interruzione, che invece zione un interrupt ogni secondo (o sue frazioni deci-
provvede ad aggiornare l’ora corrente e ad ese- mali), è necessario utilizzare un contatore (il campo
guire la lettura dello stato dei pulsanti con la fun- tck) per ottenere l’incremento dei secondo con la
zione anti-rimbalzo. Questa suddivisione è stata giusta cadenza. In particolare, dal momento che
scelta dal momento che l’aggiornamento dell’ora l’interruzione avviene ogni 1/125 secondi (si vedano
e lo stato dei pulsanti sono prioritari, e in qualche le impostazioni del TMR0 nella routine Init), i
caso determinano la visualizzazione dei dati ed il secondi verranno incrementati quando il contatore
comportamento del circuito. raggiunge 125 (valore di TCK_SEC).

FARE ELETTRONICA - NOVEMBRE 2006

Figura 1 Schema del timer


di stefano - TEORIA - mikroC 5 18-10-2006 15:34 Pagina 88

TEORIA MHZ RISORSE SPECIALE PRATICA

Quinta parte MikroC by Example: Realizzazione di un timer digitale programmabile

[Listato 1]
char *onoff[2]={“Off”, “On “};
char *blank=””;
/*** Timer.h ***/

// - Variabili di stato e temporanee -


// *** Costanti ***
enum stati {S_VIS_ORA, S_SET_ORA,
// Tick per secondo
S_VIS_EVENTI, S_SET_EVENTI} stato;
#define TCK_SEC 125
char thr[5], tmin[5], tsec[4], tdhr[5],
// Valore di reset del TMR0
tdmin[4], tnev[4];
#define TMR0_RESET 131
unsigned char Pcnt[4], secondo,
// Valore di reset anti-rimbalzo
start_stop, uscita;
#define P_RESET 15
unsigned char pos, nevento;
// Soglia anti-rimbalzo
#define P_SOGLIA 20
tempo ora; // Ora e giorno corrente
// Soglia ripetizione
#define P_RIPETIZ 0
evento eventi[N_EVENTI]; // Array eventi
88 // N. eventi gestiti
#define N_EVENTI 8
Teoria

// Valore posrta stato ON


// *** Prototipi ***
#define USCITA_ON 0x80
// Valore posrta stato OFF
// Inizializza registri e
#define USCITA_OFF 0x00
// variabili all’avvio
void Init(void);
// *** Definizione tipi ***

// Test pressione pulsanti


// - Tipo tempo -
// Restituisce:
typedef struct {
// 0=pulsnate non premuto,
unsigned char hr; // Ora
// 1=pulsante premuto
unsigned char min; // Minuti
// (il contatore viene resettato)
unsigned char sec; // Secondi
char PTest(char i);
unsigned char grn; // Giorno
unsigned int tck; // Clock Tick
// Stato visualizzazione ora
} tempo;
void Vis_Ora(void);

// - Tipo evento -
// Stato modifica ora
typedef struct {
void Set_Ora(void);
unsigned char hr; // Ora
unsigned char min; // Minuti
// Stato visualizza eventi
unsigned char sec; // Secondi
void Vis_Evento(void);
unsigned char grn; // Gionro
FARE ELETTRONICA - NOVEMBRE 2006

unsigned char d_hr; // Durata ore


// Stato modifica eventi
unsigned char d_min; // Durata minuti
void Set_Evento(void);
unsigned char attivo; // On/Off
} evento;
// Testa gli eventi e aggiorna l’uscita
void Test_Eventi(void);
// *** Variabili globali ***
// Funzioni di visualizzazione
void Display_Ora(char edit);
// - Stringhe-
void Display_Eventi(char edit);
char *nomegrn[8]={“Lun”, “Mar”, “Mer”,
void Cursore(char n);
“Gio”, “Ven”, “Sab”, “Dom”, “—-”};
di stefano - TEORIA - mikroC 5 17-10-2006 15:25 Pagina 89

Gestione dei pulsanti tasti possano essere percepiti come pressioni. La


I pulsanti sono gestiti con la tecnica descritta nel funzione che verifica se la soglia di un dato pul-
numero 255 di FE: ad ogni pulsante è associato sante è stata superata è PTest:
un contatore (Pcnt[…]) che viene incrementato
ad ogni interruzione se il pulsante risulta premu- char PTest(char i)
to, o viene resettato se questo viene rilasciato. {
Se il contatore raggiunge il valore scelto come if (Pcnt[i]==P_SOGLIA) {
soglia significa che è stato premuto per un inter- Pcnt[i]=P_RIPETIZ;
vallo sufficientemente lungo (precisamente (20- return 1;
15)/125s = 4ms). Questo evita che i rimbalzi dei } else {
return 0;
[Listato 2] }
void interrupt() }
{
if (start_stop)
Se è stato raggiunto il valore di soglia la funzione
ora.tck++;
restituisce 1, e resetta il contatore, in modo da
TMR0 = TMR0_RESET; // reset timer 89
consentire la successiva rilevazione. Va notato che

Teoria
il valore di reset è diverso rispetto al coso di rila-
// Aggiornamento ora
scio del pulsante (in questo caso la soglia è rag-
if (ora.tck==TCK_SEC) {
giunta in 20/250s = 160ms). Questo permette di
ora.tck=0;
differenziare tra ritardo anti-rimbalzo o ritardo di
ora.sec++;
ripetizione se il tasto è continuamente premuto.
secondo=1;
if (ora.sec==60) {
Funzione main
ora.sec=0;
La funzione main, come si può vedere dal codi-
ora.min++;
ce, è piuttosto snella ed implementa soltanto il
if (ora.min==60) {
loop principale del programma che contiene la
ora.min=0;
macchina a stati che gestisce l’interfaccia utente.
ora.hr++;
Questa struttura è stata utilizzata dal momento
if (ora.hr==24) {
che le funzioni dei tasti ed i dati visualizzati sul
ora.hr=0;
display variano a seconda del contesto e delle
ora.grn++;
azioni che si sono compiute in precedenza.
if (ora.grn==7) ora.grn=0;
Occorre quindi considerare lo stato in cui ci si
}
trova e aggiornarlo in base alle azioni dell’utente.
}
La variabile “stato”, di tipo enumerativo, svolge
}
questo compito. Il diagramma degli stati è
}
mostrato in Figura 2, il codice che li implementa
invece è mostrato nel listato 3.
// Stato pulsanti
In pratica lo stato attuale determina quale delle 4
if ((PORTC&0x01)&&(Pcnt[0]<P_SOGLIA))
funzioni è eseguita ad ogni iterazione del loop.
FARE ELETTRONICA - NOVEMBRE 2006

Pcnt[0]++; else Pcnt[0]=P_RESET;


Ciascuna funzione conterrà il codice necessario
if ((PORTC&0x02)&&(Pcnt[1]<P_SOGLIA))
per svolgere i compiti associati, e cambiare stato
Pcnt[1]++; else Pcnt[1]=P_RESET;
in funzione dei tasti premuti. Si può notare che
if ((PORTC&0x04)&&(Pcnt[2]<P_SOGLIA))
prima di entrare nel loop principale, viene richia-
Pcnt[2]++; else Pcnt[2]=P_RESET;
mata la funzione “Init” che inizializza tutte le
if ((PORTC&0x08)&&(Pcnt[3]<P_SOGLIA))
porte, i registri e le variabili utilizzate.
Pcnt[3]++; else Pcnt[3]=P_RESET;

Funzioni di gestione degli stati


INTCON = 0x20;
Le funzioni richiamate dai vari stati sono
}
Vis_Ora, Vis_Evento, Set_Ora e Set_Evento. Le
di stefano - TEORIA - mikroC 5 17-10-2006 15:25 Pagina 90

TEORIA MHZ RISORSE SPECIALE PRATICA

Quinta parte MikroC by Example: Realizzazione di un timer digitale programmabile

[Listato 3]
Funzione di visualizzazione
void main() {
Le funzioni utilizzate per la visualizzazione sono
tre: Display_Ora(), Display_Eventi() e Cursore().
Init();
Le prime due visualizzano l’ora attuale e la lista
while(1) {
di eventi rispettivamente. Esse prendono in
switch (stato) {
ingresso un parametro che può valere 0 nel
caso della visualizzazione normale, o 1 nel caso
case S_VIS_ORA:
si voglia fare apparire il cursore sul valore da
Vis_Ora();
modificare. Il posizionamento e la visualizzazio-
break;
ne del cursore in base al parametro da modifi-
care sono gestiti dalla funzione Cursore(). A
case S_SET_ORA:
scopo di esempio, la funzione Display_Ora(), è
Set_Ora();
riportata nel listato 6. Come si può vedere sono
break;
state utilizzate le funzioni di libreria del MikroC
case S_VIS_EVENTI:
per convertire i valori numerici in stringhe e per
visualizzarli sull’LCD (funzione Lcd_Out).
90 Vis_Evento();
break;
Teoria

MODIFICHE E
case S_SET_EVENTI:
MIGLIORAMENTI
Set_Evento();
Modificando il valore delle macro presenti
break;
nel file Timer.h è possibile personalizzare le
}
caratteristiche del timer in base alle proprie
} [Listato 4]
} void Vis_Ora(void)
{ if (secondo) { Display_Ora(0);
prime due gestiscono gli stati in cui sono visua-
Test_Eventi();
lizzati l’ora corrente o gli eventi, mentre gli altri
secondo=0;
gestiscono la fase di modifica dei valori in que-
}
sti stati. Ciascuna di queste funzioni è compo-
// Pulsante +
sta da una parte che si occupa della visualizza-
if (PTest(0)) { uscita = USCITA_ON;
zione (richiamando una funzione apposita), e
}
da una serie controlli sulla pressione dei pulsan-
// Pulsante -
ti, che generano il cambiamento di stato o del
if (PTest(1)) {
valore di alcune variabili. A titolo di esempio nel
uscita = USCITA_OFF;
listato 4 è riportato il codice della funzione
}
Vis_Ora. Allo scadere di ogni secondo, la varia-
// Select
bile “secondo” viene impostata ad 1 dalla rou-
if (PTest(2)) {
tine d’interruzione. In questo caso viene richia-
stato=S_VIS_EVENTI;
mata la funzione di visualizzazione dell’ora
Display_Eventi(0);
Display_Ora (per aggiornare il display) e quella
FARE ELETTRONICA - NOVEMBRE 2006

Delay_ms(250);
che controlla la scadenza degli eventi, che
}
determina se l’uscita deve essere attivata o
// Adjust
disattivata. Il codice di questa funzione, chia-
if (PTest(3)) {
mata Test_Eventi, è riportato nel listato 5.
Display_Ora(1);
In questa routine viene verificato, per ciascun
stato=S_SET_ORA;
evento, se l’ora di attivazione coincide con l’ora
start_stop=0;
attuale, o se è trascorso l’intervallo di attivazio-
Delay_ms(250);
ne associato a ciascun evento. In base a questo
}
controllo viene aggiornata la variabile “uscita”,
}
che poi viene assegnata alla porta C del PIC.
di stefano - TEORIA - mikroC 5 17-10-2006 15:25 Pagina 91

esigenze. Ad esempio se si uti-


lizza un quarzo di frequenza
diversa è sufficiente cambiare i
valori di TCK_SEC e
TMR0_RESET per ottenere la
giusta frequenza. E’ possibile
aumentare o diminuire il
numero degli eventi gestiti
modificando il valore di
N_EVENTI, e si può anche
variare il piedino di uscita e la
sua polarità modificando USCI-
TA_ON e USCITA_OFF.
Modificando il codice possono
essere aggiunte in maniera
semplice ulteriori caratteristiche 91
interessanti, ad esempio la

Teoria
gestione di più uscite indipen-
denti o l’utilizzo di segnali
esterni per condizionare deter-
minati eventi. In particolare, si
Figura 2 Diagramma degli stati del timer
può utilizzare la porta A per

Compilatore MikroC

Un potente
compilatore C
per PICmicro

✔ Code Editor
✔ Code Explorer
✔ Debugger
FARE ELETTRONICA - NOVEMBRE 2006

✔ Statistiche

Tutto in un ambiente
Windows facile ed intuitivo

Un set di strumenti veramente indispensabili


per sviluppare applicazioni con i PICmicro
Codice MIP 257091

Ordinalo subito su www.ieshop.it oppure telefona allo 02.66504755


di stefano - TEORIA - mikroC 5 17-10-2006 15:25 Pagina 92

TEORIA MHZ RISORSE SPECIALE PRATICA

Quinta parte MikroC by Example: Realizzazione di un timer digitale programmabile

[Listato 5] [Listato 6]
void Test_Eventi(void) void Display_Ora(char edit)
{ {
char i, ora_fine, min_fine, grn_fine; Lcd_Cmd(LCD_CURSOR_OFF);
for(i=0; i<N_EVENTI; i++) { ByteToStr(ora.hr, thr);
// - Disattiva uscita - ByteToStr(ora.min, tmin);
min_fine=eventi[i].min+eventi[i].d_min; ByteToStr(ora.sec, tsec);
ora_fine=eventi[i].hr+eventi[i].d_hr; if (thr[1]==’ ‘) thr[1]=’0’;
grn_fine=eventi[i].grn; if (tmin[1]==’ ‘) tmin[1]=’0’;
if (min_fine>59) { if (tsec[1]==’ ‘) tsec[1]=’0’;
min_fine=min_fine-60; thr[3]=’:’;
ora_fine++; tmin[3]=’:’;
} Lcd_Out(1,1,nomegrn[ora.grn]);
if (ora_fine>23) { Lcd_Out(2,1,(thr+1));
ora_fine=ora_fine-24; Lcd_Out(2,4,(tmin+1));
92 if (grn_fine!=7) { Lcd_Out(2,7,(tsec+1));
if (uscita)
Teoria

grn_fine++;
if (grn_fine==7) grn_fine=0; Lcd_out(2, 14, onoff[1]);
} else
} Lcd_out(2, 14, onoff[0]);
if((ora.grn==grn_fine)|| if (edit) {
(eventi[i].grn==7)) { Cursore(pos+1);
if (ora.hr==ora_fine) { } else {
if (ora.min==min_fine) { Lcd_Cmd(LCD_CURSOR_OFF);
if (eventi[i].attivo) uscita = }
USCITA_OFF; }
}
} acquisire segnali digitali o analogici esterni da
} utilizzare come abilitazione per alcuni eventi.

// - Attiva uscita - CONCLUSIONI


if((eventi[i].grn==ora.grn)|| In conclusione ricordo che l’intero codice dell’ap-
(eventi[i].grn==7)) { plicazione è scaricabile dal sito di Fare Elettronica
if (eventi[i].hr==ora.hr) { (www.farelettronica.com). Il codice può essere
if (eventi[i].min==ora.min) { compilato senza alcuna modifica con MikroC e
if (eventi[i].sec==ora.sec) { dovrebbe funzionare immediatamente se si utiliz-
if (eventi[i].attivo) uscita = za lo schema riportato prima (che può essere facil-
USCITA_ON; mente riprodotto con la scheda EasyPIC3). Con
} piccole modifiche è possibile adattare il program-
FARE ELETTRONICA - NOVEMBRE 2006

} ma ad altri modelli di PIC. La descrizione fornita


} qui è sufficientemente dettagliata da consentirne
} a chiunque la comprensione, l’utilizzo, e la modi-
fica del programma. Prima di utilizzare il codice in
} applicazioni reali, è consigliabile accertarsi di
avere compreso bene le particolarità ed il com-
PORTC = uscita; portamento del programma in tutte le condizioni
(diverse caratteristiche minori per brevità non
} sono state descritte).
Codice MIP 257086
DiStefano- TEORIA -mikroC 6 23-11-2006 13:30 Pagina 84

TEORIA MHZ RISORSE SPECIALE PRATICA

Quinta parte
n° 257 - Novembre 2006

MikroC
Realizzazione di un timer
digitale programmabile

Sesta parte
n° 258 - Dicembre 2006
Uso delle interfacce seriali

Settima parte
n° 259 - Gennaio 2007
by Example
Interfacce SPI, I2C e 1-Wire

U no dei metodi più utilizzati e molto semplice per collegare un microcontrol-


lore ad un PC al fine di scambiare dati e coman-
versatili per collegare sistemi di. Questa possibilità è utile nei casi in cui il
embedded a computer host microcontrollore è collocato in una posizione
remota (e quindi può eseguire delle operazioni
consiste nell’utilizzo
in un luogo distante dal PC), oppure per esegui-
dell’interfaccia RS-232. L’utilizzo re parte delle elaborazioni sul PC e scambiare i
di questo collegamento seriale, risultati o dei comandi.
84
Negli esempi che verranno presentati di seguito
per scambiare dati e comandi in
Teoria

sarà utilizzato lo schema riportato in Figura 1.


maniera molto semplice e robusta, Come si può vedere è stato utilizzato un
PIC16F876 ed un MAX232, un classico compo-
sarà l’oggetto di questa puntata. nente che svolge la funzione di traslare i livelli
Verranno presentati diversi da quelli compresi tra 0 e 5V del PIC a quelli
previsti dallo standard RS-232, che sono ben più
esempi e mostrato come
alti. In alcuni degli esempi presentati i pin della
implementare un semplice porta B saranno utilizzati come semplici I/O
collegamento tra un PIC e un PC digitali, in altri sarà collegato ad essi un display
LCD intelligente 16x2 .
utilizzando le librerie del MikroC.
SEGNALAZIONE DI EVENTI
Il primo esempio mostra come instaurare una
semplice comunicazione seriale tra il PIC ed il
L’interfaccia seriale RS-232 è uno standard estre- PC. In particolare il programma mostrato nel
mamente diffuso. Nonostante le basse velocità listato 1 invia al PC lo stato della porta B (in cui
gestibili, essa infatti risulta adeguata alla mag- tutti i pin sono configurati come ingressi) ogni
gior parte delle esigenze tipiche dell’automazio- volta che rivela una variazione:
ne e del controllo industriale, o del debug o
Listato 1
programmazione dei sistemi embedded. Essa
void main() {
ha inoltre il vantaggio di essere molto semplice
char pb, mpb;
ed economica e di garantire un’ottima immuni-
tà al rumore ed ai disturbi, grazie all’impiego di
FARE ELETTRONICA - DICEMBRE 2006

PORTB=0;
tensioni di segnalazione piuttosto alte (circa
TRISB=0xFF; // PORTB[0-7]: ingressi
12V). Nella versione più semplice, l’interfaccia è
costituita da soli tre fili: TX (trasmissione), RX
// Inizializzazione
(ricezione) e massa. I dati sono trasmessi serial-
Usart_Init(2400);
mente in maniera asincrona, cioè senza l’uso di
pb=0;
un clock. Per rendere possibile questo è neces-
mpb=0;
sario che il ricevitore ed il trasmettitore utilizzi-
while(1) {
no lo stesso formato di dati (velocità di trasmis-
pb=PORTB;
sione, numero e tipologia di bit).
if (pb!=mpb)
L’interfaccia RS-232 costituisce un metodo
DiStefano- TEORIA -mikroC 6 23-11-2006 13:30 Pagina 85

Uso
dell’interfaccia
seriale di Antonio Di Stefano

85

Teoria
Figura 1 Schema utilizzato negli esempi

Usart_Write(pb); Va notato che le funzioni della libreria USART,


Delay_ms(10); come suggerisce il nome, utilizzano la periferica
mpb=pb; USART presente in alcuni modelli di PIC per
} gestire la ricezione e la trasmissione seriale full
} duplex. Questo implica che i pin destinati alla
comunicazione seriale sono quelli predisposti
Come si può vedere, grazie alle librerie del per tale funzione (RC6 ed RC7 sul PIC utilizzato),
MikroC la gestione dell’interfaccia è estrema- e che non è possibile utilizzare PIC privi di que-
mente semplice. È stata utilizzata la funzione sta periferica con queste funzioni. Questa limita-
Usart_Init() per inizializzare la porta, specifican- zione può essere superata comunque, come
FARE ELETTRONICA - DICEMBRE 2006

do la velocità desiderata in baud. In questo caso verrà mostrato di seguito.


la velocità scelta è di 2400 bit al secondo (con 8 Per ricevere e trasmettere i dati seriali sul PC è
bit di dati 1 bit di start, 1 bit di stop e nessuna necessario utilizzare un programma “terminale”
parità). Queste stesse impostazioni dovranno apposito. Tra i tanti disponibili (tra cui anche
essere utilizzate per la porta del PC. La funzione l’HyperTerminal di Windows) risulta molto
Usart_Write() è utilizzata per trasmettere un comodo L’USART Terminal incluso nell’ambiente
byte. In pratica il ciclo principale controlla se ci di sviluppo del MikroC, attivabile dal menu
sono cambiamenti dello stato della porta B, ed Tools. In Figura 2 è visibile la finestra del pro-
in tal caso invia il nuovo valore al PC sotto forma gramma con le impostazioni da utilizzare.
di byte. La frequenza del ciclo è di circa 10ms. Come si può vedere, oltre ai parametri di
DiStefano- TEORIA -mikroC 6 23-11-2006 13:30 Pagina 86

TEORIA MHZ RISORSE SPECIALE PRATICA

Sesta parte MikroC by Example: Uso dell’interfaccia seriale

connessione, nella finestra sono presenti altre e lo memorizza in un buffer interno). Quando
opzioni, tra cui quella che permette di selezionare è stato ricevuto un carattere la funzione resti-
la modalità di visualizzazione dei dati ricevuti. In tuisce un valore diverso da zero (che equivale
questo caso l’impostazione scelta è Hex, in modo ad una condizione vera). Una volta letto il
da potere leggere il valore binario inviato. Negli carattere (con la funzione Usart_Read), que-
altri esempi sarà utilizzata la modalità ASCII, dal sto viene elaborato secondo l’algoritmo
momento che sarà scambiato del testo. descritto prima (controllando che si tratti
effettivamente di una lettera dell’alfabeto
CIFRATURA REMOTA maiuscola o minuscola, grazie alle funzione
Per mostrare come la comunicazione possa isupper e islower della libreria standard
avvenire anche nell’altra direzione, e si possa istau- ctype.h), ed inviato di nuovo al PC. Il risulta-
rare un primo esempio di colloquio tra il PC ed il to sarà che se inviamo la parola “Ciao”, otter-
PIC, è riportato nel listato 2 il codice di un pro- remo come risposta “Pvnb”. Se re-inviamo
gramma che riceve un testo dal PC e lo cifra secon- quest’ultima otterremo di nuovo la versione
do il codice ROT13, utilizzato spesso su Internet in chiaro. Come si può notare già da questo
come semplice forma di cifratura del testo. Il codi- esempio, la comunicazione tramite RS-232 è
86
ce consiste in pratica nel ruotare l’alfabeto anglo- orientata e ordinata a singoli byte. Per scambia-
Teoria

sassone (26 lettere) di 13 posti, in questo modo A re messaggi più complessi è necessario ideare
diventa N, la Z la M e così via. L’algoritmo è rever- o usare dei protocolli che definiscano la strut-
sibile, e quindi quando viene applicato ad un testo tura dei messaggi, come mostrato di seguito.
già cifrato, lo riporta in chiaro.
CONTROLLO DI UN DISPLAY DA PC
Listato 2 Supponiamo di volere inviare da PC il testo da
/* ROT13 remoto */ visualizzare su un display LCD. Il testo potrebbe
essere generato da un apposito software
void main() { oppure essere utilizzato soltanto come mes-
unsigned short i; saggio remoto. L’utilizzo della porta RS-232
costituisce una buona soluzione. Utilizzando
Usart_Init(2400); lo schema di Figura 1, ed il programma
while (1) { mostrato nel listato 3 è possibile ottenere in
// Se riceve un carattere maniera molto semplice quanto detto. In pra-
if (Usart_Data_Ready()) { tica il programma consente di inviare dei
// Legge il dato ricevuto comandi e delle stringhe al display in modo
i = Usart_Read(); da controllare il testo visualizzato. Inviando la
// Rotazione maiuscole stringa “C” è possibile cancellare il testo visua-
if (isupper(i)) lizzato, inviando la stringa “T x Abcd” è possi-
i=((i-’A’)+13)%26+’A’; bile scrivere la stringa “Abcd” nella riga x (che
// Rotazione minuscole
if (islower(i))
i=((i-’a’)+13)%26+’a’;
FARE ELETTRONICA - DICEMBRE 2006

// Invio risultato
Usart_Write(i);
}
}
}

A differenza di prima è stata utilizzata la fun-


zione Usart_Data_Ready() per controllare se è
stato ricevuto un carattere (la periferica USART
Figura 2 L’USART Terminal del MikroC con i parametri di collegamento
lo riceve indipendentemente dal programma,
DiStefano- TEORIA -mikroC 6 23-11-2006 13:30 Pagina 87

può essere 1 o 2). Inviando la stringa “S x” è Lcd_Init(&PORTB);


possibile spegnere o accendere il cursore e
cambiarne la forma (0=spento, 1=linea, // Init. variabili
2=quadratino lampeggiante). Ogni stringa stringa=0;
deve terminare con un “a capo” (codici ASCII i=0;
0x0D e 0x0A). Questo serve al programma for(j=0; j<32; j++)
per individuare la fine della stringa e per ini- buf[0];
ziarne l’elaborazione (ricordarsi di abilitare le
opzioni Append CR ed LF nel Terminal). while(1) {

Listato 3 // Attesa ricezione stringa


/* Controllo display remoto if ((Usart_Data_Ready())&&(!stringa)) {
via seriale */ buf[i] = Usart_Read();
void SendString(char *); if (buf[i]==’\n’) {
stringa=1;
void main() { }
i++;
87
char buf[32], i, j, stringa;

Teoria
}
PORTB=0;
TRISB=0; // Elaborazione stringa
if (stringa) {
Usart_Init(2400); switch(buf[0]) {

Scheda easyPIC4
La rivoluzionaria scheda
di sviluppo per PICmicro
✔ Programmatore USB2.0 on-board con ICD
✔ Tastiera a 32 tasti
✔ 32 LED per il monitoraggio degli I/O
✔ 4 cifre LED a 7 segmenti
✔ Predisposizione per moduli LCD alfanumerici
✔ Predisposizione per moduli LCD grafici
✔ Predisposizione per comunicazione RS232
FARE ELETTRONICA - DICEMBRE 2006

✔ Predisposizione per tastiera PS2


✔ Predisposizione per sensore di temperatura DS1820
✔ Supporto per tutte le famiglie PIC (anche PIC10F)*
✔ Predisposizione per comunicazione USB
✔ Alimentazione esterna o via USB
✔ Fornita con 16F877
Codice MIP 258087 ✔ Disponibile con o senza display

Ordinala subito su www.farelettronica.com oppure telefona allo 02.66504755


DiStefano- TEORIA -mikroC 6 23-11-2006 13:30 Pagina 88

TEORIA MHZ RISORSE SPECIALE PRATICA

Sesta parte MikroC by Example: Uso dell’interfaccia seriale

case ‘C’: suo interno si possono distinguere due parti: una


Lcd_Cmd(LCD_CLEAR); destinata alla ricezione delle stringhe, e l’altra alla
SendString(“Ok\r\n”); loro elaborazione. La prima attende l’arrivo di
break; nuovi caratteri, li riceve e li accoda nel buffer
apposito del programma (buf[…]). Quando
case ‘T’: viene rilevato un carattere di “a capo” la stringa
buf[20]=’\0’; è completa, e per indicarlo viene portata a 1 la
buf[i-2]=’\0’; variabile “stringa”. In questa situazione verrà ese-
if (buf[2]==’1’) guita la seconda parte del programma, e non più
Lcd_Out(1, 1, (buf+4)); la prima. Qui viene controllato se il primo carat-
else tere è un comando valido (cioè una “C”, una “T”
Lcd_Out(2, 1, (buf+4)); o una “S”), ed eseguite le relative funzioni. Va
SendString(“Ok\r\n”); notato che il testo inviato si trova nell’array
break; buf[…], nell’ordine in cui è stato inviato, è possi-
bile quindi estrarne i vari campi ed utilizzarli.
case ‘S’: Ogni volte che viene eseguita una delle funzioni
88
if (buf[2]==’0’) previste viene inviato al PC un “Ok” come con-
Teoria

Lcd_Cmd(LCD_CURSOR_OFF); ferma. Se non viene riconosciuto un comando


if (buf[2]==’1’) valido viene inviato un “?!?”. Per inviare queste
Lcd_Cmd(LCD_UNDERLINE_ON); stringhe è stata scritta una funzione apposita che
if (buf[2]==’2’) non fa altro che inviare sequenzialmente tutti i
Lcd_Cmd(LCD_BLINK_CURSOR_ON); byte che compongono la stringa, con la funzione
SendString(“Ok\r\n”); Usart_Write(). Notare che la scansione della strin-
break; ga si arresta in corrispondenza del terminatore,
cioè il valore binario 0x00.
default:
SendString(“?!?\r\n”); RICEZIONE CON INTERRUZIONI
} In molti casi può essere utile rendere la ricezione
delle stringhe seriali asincrona ed indipendente
for(j=0; j<32; j++) dal flusso principale del programma. Questo
buf[0]; soprattutto perché la comunicazione via RS-
stringa=0; 232 è per sua natura asincrona ed imprevedi-
i=0; bile, ed una gestione non immediata potrebbe
} portare alla perdita di dati. Si può ottenere
} quanto detto utilizzando le funzioni di libreria
} del MikroC, con qualche piccolo accorgimento.
Nel listato 4 è mostrato come svolgere la fun-
void SendString(char *str) zione di ricezione delle stringhe nella routine
{ d’interruzione.
int n;
FARE ELETTRONICA - DICEMBRE 2006

Listato 4
n=0; /* Controllo seriale remoto
while(str[n]) { UART HW con int. */
Usart_Write(str[n]); #define MAX_LEN 32
n++;
} char stringa, bpnt;
} char buf[MAX_LEN];

Dopo una prima inizializzazione della UART, del void SendString(char *);
display e delle variabili, inizia il loop principale. Al char Hex2Dec(char *);
DiStefano- TEORIA -mikroC 6 23-11-2006 13:30 Pagina 89

void Dec2Hex(char, char *); funzioni disponibili sono quasi le stesse, anche
... il loro uso richiede una più accurata pianifica-
void main() { zione dei tempi del programma (che deve fer-
… marsi ad attendere l’arrivo dei caratteri, e non
Usart_Init(2400); può gestire la comunicazione in full duplex).
// Set RCIE Nel listato 5 è mostrato un esempio imple-
PIE1|=0x20; mentato in versione “software”:
// Set GIE e PEIE
INTCON|=0xC0; Listato 5
… /* ROT13 remoto con
while(1) { UART software */
void main() {
if (stringa) { unsigned short ro = 0, *recOK, i;

switch(buf[0]) { recOK = &ro;


case ‘W’: Soft_Uart_Init(PORTA, 0, 1, 2400, 0);
89
}

Teoria
} while(1) {
… do {
} i = Soft_Uart_Read(recOK);
} while (*recOK);
interrupt()
{ if (isupper(i))
if (!stringa) { i=((i-’A’)+13)%26+’A’;
buf[bpnt] = Usart_Read(); if (islower(i))
if ((buf[bpnt]==’\n’)|| i=((i-’a’)+13)%26+’a’;
(bpnt==MAX_LEN-1)) { Soft_Uart_Write(i);
stringa=1; }
} }
bpnt++;
} Notare che i piedini di comunicazione specifi-
} cati sono RA0 ed RA1 invece di RC6 ed RC7,
questo rende possibile riscrivere i programmi
In pratica il codice è rimasto quasi invariato, presentati prima anche per PIC privi di UART,
tranne per il fatto che sono state attivate le quale il 16F84.
interruzioni associate alla ricezione dei singoli
caratteri, e la funzione di ricezione stringhe è CONCLUSIONI
stata confinata nella routine d’interruzione. Si Va ricordato che le routine mostrate possono
può notare anche che la comunicazione tra le essere provate anche su PC privi di porta seria-
due routine avviene ancora tramite la variabile le, utilizzando un adattatore USB-RS232
FARE ELETTRONICA - DICEMBRE 2006

“stringa”, che questa volta però è stata dichia- (ricordarsi di settare il numero corretto di
rata come globale. porta COM). Nella prossima puntata si conti-
nuerà a parlare di protocolli seriali, in partico-
SOFTWARE UART lare di SPI, I2C e 1-Wire, utilizzati per comuni-
Se il PIC utilizzato non dispone di una UART care con dispositivi quali memorie EEPROM,
integrata è comunque possibile implementare timer, sensori di temperatura, e tanto altro.
una comunicazione seriale. Questo grazie alle Verrà mostrato come usare le routine di libre-
routine della libreria Software_Uart del ria e come comunicare con questi dispositivi.
MikroC, che emulano il dispositivo via softwa-
re utilizzando due qualsiasi piedini di I/O. Le Codice MIP 258084
DiStefano- TEORIA -mikroC 7 19-12-2006 15:36 Pagina 76

TEORIA MHZ RISORSE SPECIALE PRATICA

Sesta parte
n° 258 - Dicembre 2006
Uso delle interfacce seriali

Settima parte
n° 259 - Gennaio 2007
MikroC
by Example
Interfacce SPI, I2C e 1-Wire

Ottava parte
n° 260 - Febbraio 2007
Uso del convertitore A/D

di pull-up. I vari dispositivi comunicano portando

I n questa puntata scopriremo


come utilizzare le interfacce
I2C, SPI e 1-Wire con il MikroC
le linee a livello basso tramite delle porte di tipo
“open drain” o “open collector”.
Il protocollo (che è abbastanza articolato, e la cui
documentazione è disponibile sul sito di Philips
e come interfacciare al PIC una Semiconductors, ora NXP) prevede la possibilità
varietà di dispositivi esterni quali di avere uno o più dispositivi master, e uno o più
dispositivi slave. Ciascuna comunicazione è inizia-
76 memorie e sensori di temperatura. ta da parte di un master imponendo sul bus uno
Teoria

stato di Start (SDA va basso mentre SCL è alto), e


termina con uno di Stop (SDA va alto mentre SCL
INTERFACCIA I2C è alto). Ad ogni scambio di dati tra master è slave
Il bus I2C (Inter-Integrated Circuit bus) è stato il ricevente può rispondere con un bit aggiuntivo
ideato da Philips per permettere, come suggerisce di Acknowledge. Sul mercato sono disponibili
lo stesso nome, la comunicazione tra diversi cir- moltissimi dispositivi che utilizzano il protocollo
cuiti integrati in maniera semplice ed economica. I2C, tra questi: sensori, porte di espansione di I/O
Esso consiste in un protocollo di comunicazione (GPIO), convertitori A/D e D/A, memorie e diver-
seriale sincrono che utilizza soltanto due fili per la se periferiche dedicate.
comunicazione. Le due linee, chiamate SDA (linea Per collegare un dispositivo I2C ad un PIC è pos-
dati bidirezionale) ed SCL (clock, fornito dal sibile utilizzare la periferica MSSP integrata, quan-
master), sono condivise tra tutte le periferiche e do disponibile, o gestire la comunicazione via
mantenute ad una tensione positiva da resistenze software utilizzando un paio qualsiasi di piedini di
FARE ELETTRONICA - GENNAIO 2007

Figura 1 Collegamento della EEPROM al PIC


DiStefano- TEORIA -mikroC 7 19-12-2006 15:36 Pagina 77

Interfacce I2C,
SPI e 1-Wire di Antonio Di Stefano

I/O. Il MikroC mette a disposizione due librerie sul bus. Come si può vedere dal codice delle fun-
dedicate alla gestione dell’interfaccia I2C che per- zioni EEPROM_Rd e EEPROM_Wr le operazioni
mettono di sfruttare entrambe le possibilità. Le eseguite per la lettura e scrittura della memoria
funzioni delle due librerie sono quasi identiche, sono esattamente quelle riportate in Figura 2. Il
quindi risulta molto semplice passare da un’im- programma memorizza alcune configurazioni
plementazione hardware ad una software e vice- nella EEPROM in indirizzi successivi, e poi li legge
versa. Per mostrare in dettaglio l’utilizzo delle fun- sequenzialmente (e ciclicamente) riportandoli sui
zioni di libreria verrà utilizzato il circuito di Figura LED collegati alla porta B.
77
1, in cui una memoria EEPROM 24LC02 (2Kbit, Per mostrare l’impiego della libreria “Soft I2C”,

Teoria
organizzata in 256 parole da 8bit) è collegata al ricorreremo allo schema riportato in Figura 3. Il
PIC, e verrà utilizzata per memorizzare i pattern circuito utilizza questa volta un PIC 16F84 (che
da visualizzare sui LED. I dati e le temporizzazioni non dispone di una periferica MSSP) e consente di
da inviare alla memoria per effettuare le operazio- copiare il contenuto di una EEPROM in un’altra.
ni di lettura e scrittura sono riportate in Figura 2. Come si può vedere entrambe le memorie sono
Nel listato 1 è invece riportato il codice che poste sullo stesso bus, ma hanno un indirizzo
mostra come leggere e scrivere singoli byte. diverso grazie al diverso livello applicato ai piedini
A0. Il Listato 2 mostra il codice che effettua la
L’interfaccia I2C deve essere inizializzata richia- copiatura della memoria.
mando la funzione I2C_Init, in cui viene specifica- Il codice è abbastanza semplice: per ciascun indi-
ta la velocità di comunicazione (frequenza del rizzo (da 0 a 255) il dato viene letto dalla EEPROM
clock seriale). È quindi possibile utilizzare le altre 0 e scritto sulla EEPROM 1. L’indirizzo della
funzioni, che eseguono le operazioni elementari EEPROM da utilizzare è passato come parametro

FARE ELETTRONICA - GENNAIO 2007

Figura 2 Temporizzazioni relative alla scrittura ed alla lettura di un byte


DiStefano- TEORIA -mikroC 7 19-12-2006 15:36 Pagina 78

TEORIA MHZ RISORSE SPECIALE PRATICA

Settima parte MikroC by Example: Interfacce I2C, SPI e 1-Wire

[Listato 1] [Listato 2]
void EEPROM_Wr(char addr, char data); void EEPROM_Wr(char addr, char data, char d);
char EEPROM_Rd(char); char EEPROM_Rd(char addr, char d);

void main() { void main() {


char i; char i, addr, dato;

PORTB=0; PORTA=0;
TRISB=0; TRISA=0;

// Inizializzazione
I2C_Init(400000); // Inizializzazione
Soft_I2C_Config(PORTB,0,1);
// Scrittura dati
EEPROM_Wr(0, 0x00); // ———— Delay_ms(100);
EEPROM_Wr(1, 0x81); // *———*
78
EEPROM_Wr(2, 0xC3); // **——** PORTA=0x01;
Teoria

EEPROM_Wr(3, 0xE7); // ***—*** for(i=0; i<256; i++)


{
Delay_ms(500); dato=EEPROM_Rd(i, 0);
EEPROM_Rw(i, dato, 1);
i=0; }
While(1) { PORTA=0x02;
PORTB=EEPROM_Rd(i&0x03);
i++; While(1) {};
Delay_ms(500); }
}
} void EEPROM_Wr(char addr, char data, char d)
{
void EEPROM_Wr(char addr, char data) d<<=1;
{ Soft_I2C_Start();
I2C_Start(); // Invia Start Soft_I2C_Write(0xA0|d);
I2C_Wr(0xA0); // Invia commando Soft_I2C_Write(addr);
I2C_Wr(addr); // Invia indirizzo Soft_I2C_Write(data);
I2C_Wr(data); // invia dato Soft_I2C_Stop();
I2C_Stop(); // Invia Stop }
}
char EEPROM_Rd(char addr, char d)
char EEPROM_Rd(char addr) {
{ char k;
char k;
d<<=1;
FARE ELETTRONICA - GENNAIO 2007

I2C_Start(); // Start Soft_I2C_Start();


I2C_Wr(0xA0); // Invia commando Soft_I2C_Write(0xA0|d);
I2C_Wr(addr); // Invia indirizzo Soft_I2C_Write(addr);
I2C_Repeated_Start(); // Start ripetuto Soft_I2C_Start();
I2C_Wr(0xA1); // Invia commando Soft_I2C_Write(0xA1);
k = I2C_Rd(0); // Lettura (no ack) k = Soft_I2C_Read(0);
I2C_Stop(); // Stop Soft_I2C_Stop();

return k; return k;
} }
DiStefano- TEORIA -mikroC 7 19-12-2006 15:36 Pagina 79

79

Teoria
Figura 3 Schema usato per la copiatura di dati tra due EEPROM

Compilatore MikroC

Un potente
compilatore C
per PICmicro

 Code Editor
 Code Explorer
 Debugger
 Statistiche
FARE ELETTRONICA - GENNAIO 2007

Tutto in un ambiente
Windows facile ed intuitivo

Un set di strumenti veramente indispensabili


per sviluppare applicazioni con i PICmicro
Codice MIP 259079

Ordinalo subito su www.ieshop.it oppure telefona allo 02.66504755


DiStefano- TEORIA -mikroC 7 19-12-2006 15:36 Pagina 80

TEORIA MHZ RISORSE SPECIALE PRATICA

Settima parte MikroC by Example: Interfacce I2C, SPI e 1-Wire

[Listato 3] sarebbe risultato meno chiaro).


void main() { In generale, utilizzando le funzio-
char lsb, *buf[6]; ni della libreria I2C viste qui è
int temp; possibile comunicare con qualsia-
si dispositivo (anche diverso dalle
PORTA = 0xFF; EEPROM), basta utilizzare la
TRISA = 0xFF; sequenza di dati e operazioni
PORTB = 0; indicate negli specifici data sheet.
TRISB = 0;
INTERFACCIA 1-WIRE
// Inizializzaione LCD L’interfaccia 1-Wire è utilizzata da
Lcd_Init(&PORTB); diversi dispositivi (prodotti princi-
Lcd_Cmd(Lcd_CURSOR_OFF); palmente da Dallas/Maxim). Essa
Lcd_Out(1, 1, “Temperatura: “); consiste in un’interfaccia seriale
asincrona che impiega soltanto
while(1) { un filo per lo scambio di dati tra
80
Ow_Reset(&PORTA,0); // Reset un master e uno o più slave. La
Teoria

Ow_Write(&PORTA,0,0xCC); // Comando SKIP_ROM comunicazione avviene a bassa


Ow_Write(&PORTA,0,0x44); // Comando CONVERT_T velocità (alcuni Kbps) ed è half
Delay_us(120); duplex, però in compenso risulta
abbastanza robusta ed economi-
Ow_Reset(&PORTA,0); ca, soprattutto quando è utilizza-
Ow_Write(&PORTA,0,0xCC); // Comando SKIP_ROM ta per gestire molti sensori. Il
Ow_Write(&PORTA,0,0xBE); // Comando READ_SCRATCHPA master può indirizzare i vari
Delay_ms(400); dispositivi utilizzando un indirizzo
a 64bit, oppure può inviare
lsb = Ow_Read(&PORTA,0); // Lettura temp. LSB comandi a tutti i dispositivi con-
temp = Ow_Read(&PORTA,0); // Lettura temp. LSB temporaneamente. La comunica-
zione avviene a pacchetti, e viene
temp = (temp << 8) + lsb; utilizzato un CRC in coda per
temp = temp/2; controllare la correttezza dei dati
ricevuti. Anche per questo tipo di
IntToStr(temp, buf); // Converte in stringa interfaccia il MikroC mette a
Lcd_Out(2, 1, buf); // Visualizza disposizione delle routine di libre-
ria che ne facilitano molto l’utiliz-
Delay_ms(500); zo. Per vederne un’applicazione
pratica, si consideri il circuito di
} Figura 4, in cui un PIC 16F84 è
collegato ad un sensore di tem-
} peratura Dallas DS1820, che è in
grado di rilevare la temperatura
FARE ELETTRONICA - GENNAIO 2007

alle funzioni e viene inglobato nel primo coman- dell’ambiente, di convertirla in un valore digitale
do inviato. Per segnalare l’inizio e la fine delle ope- a 9 bit (risoluzione di 0.5°C), e di comunicarla su
razioni vengono utilizzati due LED collegati alla richiesta al master. Il valore letto in questo caso
porta A. Va notato che le EEPROM seriali permet- viene visualizzato su un display LCD 16x2 ad
terebbero di leggere e scrivere sequenzialmente i intervalli di circa un secondo. Il codice relativo
dati, senza la necessità di inviare nuovamente l’in- all’esempio è riportato nel Listato 3.
dirizzo. Utilizzando questa modalità di accesso si Le funzioni disponibili per gestire l’interfaccia per-
sarebbe potuto ottenere una copiatura notevol- mettono di iniziare la comunicazione (Ow_Reset),
mente più veloce ed efficiente (l’esempio però scrivere (Ow_Write) e leggere (Ow_Read) un dato
DiStefano- TEORIA -mikroC 7 19-12-2006 15:36 Pagina 81

81

Teoria
Figura 4 Schema del termometro digitale con DS1820

Scheda easyPIC4
La rivoluzionaria scheda
di sviluppo per PICmicro
 Programmatore USB2.0 on-board con ICD
 Tastiera a 32 tasti
 32 LED per il monitoraggio degli I/O
 4 cifre LED a 7 segmenti
 Predisposizione per moduli LCD alfanumerici
 Predisposizione per moduli LCD grafici
 Predisposizione per comunicazione RS232
 Predisposizione per tastiera PS2
FARE ELETTRONICA - GENNAIO 2007

 Predisposizione per sensore di temperatura DS1820


 Supporto per tutte le famiglie PIC (anche PIC10F)*
 Predisposizione per comunicazione USB
 Alimentazione esterna o via USB
 Fornita con 16F877
Codice MIP 259081  Disponibile con o senza display

Ordinala subito su www.farelettronica.com oppure telefona allo 02.66504755


DiStefano- TEORIA -mikroC 7 19-12-2006 15:36 Pagina 82

TEORIA MHZ RISORSE SPECIALE PRATICA

Settima parte MikroC by Example: Interfacce I2C, SPI e 1-Wire

dal dispositivo. Ciascuna di queste funzioni pren- ste in un bus seriale sincrono, che utilizza 4 fili (3
de in ingresso un puntatore alla porta a cui è col- in alcuni casi): uno è associato al clock seriale,
legato il dispositivo, il numero del piedino (in que- comandato dal master, due sono utilizzati rispet-
sto caso RA0), e l’eventuale dato da trasmettere. tivamente per i dati in ingresso e per quelli in usci-
Per eseguire la lettura della temperatura dal ta, ed il quarto, non sempre presente svolge la
DS1820 è necessario iniziare la comunicazione funzione di Chip Select, cioè abilita o meno un
con un reset, specificare se si vuole indirizzare un particolare slave a ricevere i comandi. L’interfaccia
particolare dispositivo o meno (codice “skip SPI è utilizzata in maniera simile (o a volte in
ROM”, 0xCC), comandare un inizio di conversio- luogo) dell’interfaccia I2C, e quindi i dispositivi
ne della temperatura (codice 0x44), e successiva- che la utilizzano sono anche in questo caso molti
mente leggere il valore (codice 0xBE). Alla richie- e molto vari. Vista la similitudine utilizzeremo lo
sta di lettura il DS1820 risponde inviando l’intero stesso esempio visti prima per l’interfaccia I2C,
contenuto della memoria locale, seguito dal CRC. utilizzando però una memoria EEPROM seriale
In questo caso vengono letti soltanto i primi due SPI, in particolare la 25LC020, che è funzional-
byte, che sono quelli relativi alla temperatura mente simile alla 24LC02 vista in precedenza. Le
(rispettivamente l’LSB e l’MSB del valore a 9 bit temporizzazioni dei segnali richiesti per la lettura
82
con segno). Per visualizzare sul display il valore è e scrittura dei dati sono mostrate in Figura 5. Lo
Teoria

stato prima di tutto ricomposto il valore intero schema utilizzato è quello visto in Figura 1, in cui
(utilizzando una variabile a 16 bit), poi è stata ese- però la EEPROM I2C è sostituita da quella SPI (i
guita una divisione per 2, in modo da eliminare il collegamenti sono mostrati in Figura 6). Il codice
bit relativo ai 0.5°C. In questo modo il valore bina- per leggere e scrivere la memoria è riportato nel
rio ottenuto corrisponde al valore della tempera- Listato 4.
tura in gradi centigradi. Questo valore binario è La struttura e le funzioni del codice sono identiche
convertito in stringa utilizzando la funzione di a quelle viste nel caso dell’interfaccia I2C, in que-
libreria IntToStr del MikroC, e visualizzato con la sto caso si può notare una certa semplificazione
consueta funzione Lcd_Out. nelle funzioni di lettura e scrittura data dal fatto
che non è necessario gestire le condizioni di start
INTERFACCIA SPI e stop. Una differenza importante rispetto a quan-
L’interfaccia SPI (Serial Peripheral Interface) consi- to visto in precedenza consiste nel fatto che in
FARE ELETTRONICA - GENNAIO 2007

Figura 5 Temporizzazioni di lettura e scrittura della EEPROM SPI


DiStefano- TEORIA -mikroC 7 19-12-2006 15:36 Pagina 83

LA SOLUZIONE
per il disegno tecnico

Sprint-Layout
sPrint-Layout è il compagno ideale di sPlan anche se può lavorare in modo del tutto indipendente. Il programma è studiato
appositamente per la realizzazione di circuiti stampati, il quale, oltre le funzioni standard necessarie alla creazione di
circuiti stampati, offre anche funzioni professionali quali: esportazione in formato GERBER ed EXCELLON.
Tra le numerose ed innovative caratteristiche ne troviamo una studiata appositamente per gli hobbisti, infatti, è possibile
scannerizzare un circuito stampato da una rivista o un qualsiasi supporto cartaceo ed importarlo per facilitarne il disegno
o la modifica. La funzione di “photoview” mostra il circuito stampato così come apparirebbe una volta prodotto (funzione
che utilizziamo per la rivista Fare Elettronica). Un libreria di componenti, facilmente creabili o modificabili, completa la
ricca dotazione del programma.
I disegni di Fare Elettronica sono realizzati con Sprint-Layout, da oggi potrai scaricarli dal sito e modificarli direttamente.
€ 39,00 (+Iva)

sPlan
Splan è un cad appositamente realizzato per la stesura di schemi elettrici che implementa tutti gli strumenti necessari allo
svolgimento del lavoro in modo semplice ed efficace. sPlan è dotato di molte funzioni tra le quali: numerazione automatica
di componenti, scaling e preview dei componenti, gestione delle librerie di simboli, modifica di ogni singolo elemento sullo
schermo, griglia di posizionamento, connessioni automatiche, e molto altro ancora. La libreria di simboli contiene moltissime
parti pronte all’uso, inoltre le funzioni di editing dei simboli consentono la creazione di nuovi molto velocemente. SPlan
Codice MIP 259083

implementa anche un potente motore di stampa e la possibilità di esportare i disegni in diversi formati, anche grafici.
€ 39,00 (+Iva)

Front Designer
I tempi sono cambiati ed il vostro progetto elettronico non ha più motivo di essere incluso in un vecchio pacchetto di sigari.
Oggi molti rivenditori offrono contenitori adatti a tutti i tipi di circuiti immaginabili, ma spesso i dispositivi autocostruiti
restano incompleti per la mancanza delle indicazioni sul pannello frontale.
Front Designer offre la possibilità di creare pannelli frontali veramente professionali.
€ 39,00 (+Iva)

Puoi ordinare Sprint-Layout , sPlan e Front Designer


sul sito Internet www.ieshop.it/abacom oppure telefonando allo 02.66504755
DiStefano- TEORIA -mikroC 7 20-12-2006 12:53 Pagina 84

TEORIA MHZ RISORSE SPECIALE PRATICA

Settima parte MikroC by Example: Interfacce I2C, SPI e 1-Wire

[Listato 4]
void SPI_EEPROM_Wr(char addr, char data);
char SPI_EEPROM_Rd(char);
void main() {
char i;

PORTB=0;
TRISB=0;
PORTC=0xFF;
TRISC=0;
// Inizializzazione
Spi_Init();

Figura 6 Collegamento della EEPROM SPI al PIC // Scrittura dati


PORTC=PORTC^1;
SPI_EEPROM_Wr(0, 0x00); // ————
questo caso la periferica non viene selezionata
84 SPI_EEPROM_Wr(1, 0x81); // *———*
attraverso un indirizzo, ma piuttosto portando SPI_EEPROM_Wr(2, 0xC3); // **——**
Teoria

basso il rispettivo segnale CS. Questo è fatto SPI_EEPROM_Wr(3, 0xE7); // ***—***


gestendo lo stato del pin RC0 (in questo caso PORTC=PORTC^1;
negando il bit in questione con un XOR). Se è Delay_ms(500);
necessario collegare più dispositivi allo stesso bus, i=0;
ciascuno deve avere una linea separata per il pro- While(1) {
prio segnale di selezione, che deve essere gestita PORTC=PORTC^1;
esplicitamente (gli altri tre segnali possono essere PORTB=SPI_EEPROM_Rd(i&0x03);
collegati in comune). Se ad esempio volessimo PORTC=PORTC^1;
i++;
leggere da una memoria il cui CS è collegato a
Delay_ms(500);
RC0 e scrivere in un’altra il cui CS è collegato a
}
RC1 dovremo scrivere:
}
void SPI_EEPROM_Wr(char addr, char data)
PORTC=PORTC^1; {
dato=SPI_EEPROM_Rd(addr); Spi_Write(2);
PORTC=PORTC^1; Spi_Write(addr);
Spi_Write(data);
PORTC=PORTC^2; }
PORTB=SPI_EEPROM_Rd(i&0x03); char SPI_EEPROM_Rd(char addr)
PORTC=PORTC^2; {
char k, b=0;
Va ricordato infine che anche per l’interfaccia Spi_Write(3);
Spi_Write(addr);
SPI il MikroC dispone di una libreria di emula-
k=Spi_Read(b);
zione software che possiede le stesse funzioni
}
FARE ELETTRONICA - GENNAIO 2007

di quella utilizzata in precedenza (rispettiva-


mente Soft_Spi_Config, Sort_Spi_Read e
Soft_Spi_Write). E’ possibile in questo modo interfacce trattate. Questo consente di espan-
gestire dispositivi SPI anche con PIC che non derne notevolmente le funzionalità dei propri
dispongono della periferica MSSP. progetti in maniera molto semplice. Nella
prossima puntata vedremo come utilizzare il
CONCLUSIONI convertitore Analogico/Digitale dei PIC e la
Una volta compreso come utilizzare queste memoria EEPROM interna.
funzioni sarà possibile sfruttarle per gestire i
tanti ed utilissimi dispositivi che si basano sulle Codice MIP259076
DiStefano- TEORIA -mikroC 8 16-01-2007 9:59 Pagina 80

TEORIA MHZ RISORSE SPECIALE PRATICA

Settima parte
n° 259 - Gennaio 2007

MikroC
Interfacce SPI, I2C e 1-Wire

Ottava parte
n° 260 - Febbraio 2007
Uso del convertitore A/D

Nona parte
n° 261 - Marzo 2007
Realizzazione di un dataloger
by Example
(Iª parte)

I n questa puntata scopriremo I/O dedicati. In particolare è possibile utilizza-


re alcuni di essi come ingressi analogici o per
come gestire il convertitore impostare il valore inferiore e superiore della
analogico/digitale dei PIC con tensione da convertire. Il fatto di disporre di
più ingressi analogici rende possibile l’acquisi-
il MikroC e come utilizzarlo per
zione di segnali analogici provenienti da diverse
realizzare delle interessanti sergenti.
applicazioni. Utilizzare il convertitore AD per acquisire dei
80
segnali analogici con il MikroC è molto sem-
Teoria

plice, è sufficiente infatti utilizzare la funzione


di libreria Adc_Read. Un primo esempio di uti-
lizzo di questa funzione è mostrato nel Listato
INTRODUZIONE 1. Il circuito utilizzato per testare il program-
In molte applicazione è necessario acquisire il ma è quello visibile in Figura 1. Il segnale ana-
valore di tensioni analogiche. Questo può logico da convertire, applicato alla porta RA2
essere fatto utilizzando il convertitore può variare tra 0V e la tensione di alimentazio-
Analogico/Digitale (A/D) integrato nella mag- ne positiva (che in questo caso è 5V) ed è pre-
gior parte dei microcontrollori PIC. Il converti- levato da un trimmer utilizzato come partito-
tore in questo caso ha una risoluzione minima re. Il codice esegue la conversione, legge il
di 10 bit ed una frequenza di campionamento valore a 10 bit fornito dal convertitore e visua-
massima di alcuni KHz. La gestione dei segna- lizza gli 8 bit più significativi sui LED collegati
li analogici avviene utilizzando dei piedini di alla porta B.
FARE ELETTRONICA - FEBBRAIO 2007

Figura 1 Circuito per il test dell’ADC


DiStefano- TEORIA -mikroC 8 16-01-2007 9:59 Pagina 81

Uso del
Convertitore A/D di Antonio Di Stefano

[Listato 1] senza segno è stata utilizzata una variabile a 16 bit


// *** VU meter semplice *** per contenerlo. Il valore numerico ottenuto sarà
unsigned int dato; proporzionale alla tensione presente all’ingresso,
in particolare si otterrà il valore massimo (1023,
void main() { cioè 210-1) quando la tensione è pari a quella di
ADCON1 = 0x80; // Conf. Ingr. Analog. alimentazione (5V), e 0 quando la tensione sarà
TRISA = 0xFF; // PORTA = input pari a 0V. Ciascuna unità quindi corrisponde ad
TRISB = 0x00; una tensione di 5/1024=4.8mV. Per conoscere i 81
PORTB = 0x00; valore della tensione a partire dal valore numerico

Teoria
letto è sufficiente quindi moltiplicarlo questa
while(1) { costante. Nell’esempio, così come in molte appli-
dato = Adc_Read(2); cazioni comuni, una risoluzione di 10 bit risulta
PORTB = dato>>2; eccessiva, per cui si è scelto di eliminare (con uno
Delay_ms(20); scorrimento a destra) i due bit meno significativi,
}; ottenendo così un valore ad 8 bit (risoluzione di
} 19mV). Il valore binario risultante è riportato sui
LED in modo da potere essere visualizzato.
La funzione Adc_Read prende come parametro il
numero di canale analogico che si intende legge- REGOLAZIONE DI LUMINOSITÀ
re (cioè quale pin della porta si intende usare Nell’esempio riportato di seguito utilizzeremo il
come ingresso), esegue la conversione, e soltanto valore analogico letto dal convertitore A/D per
quando questa è terminata restituisce il valore regolare la luminosità di una lampadina (o di un
convertito. Dal momento che il dato sarà a 10 bit LED). Per fare questo verrà generato un segnale

FARE ELETTRONICA - FEBBRAIO 2007

Figura 2 Circuito per la regolazione della luminosità


DiStefano- TEORIA -mikroC 8 16-01-2007 9:59 Pagina 82

TEORIA MHZ RISORSE SPECIALE PRATICA

Ottava parte MikroC by Example: Uso del convertitore A/D

PWM il cui duty cycle sarà determinato in base realizzare un semplice VU meter, cioè un misu-
al valore analogico letto. Il segnale analogico ratore del livello di un segnale audio. Il circuito
che verrà utilizzato per comandare la regolazio- utilizzato è uguale a quello mostrato in Figura 1,
ne di luminosità potrà essere prelevato da un con l’unica differenza che il segnale audio viene
potenziometro, come mostrato in Figura 2, o da accoppiato all’ingresso analogico del PIC utiliz-
un fotodiodo, in modo da realizzare un interrut- zando la rete mostrata in Figura 3 invece del
tore crepuscolare (che cioè aumenta la lumino- semplice trimmer. Se il segnale audio utilizzato
sità della sorgente quanto maggiore è il buio). Il ha un’impedenza sufficientemente bassa ed
codice che implementa l’applicazione è riporta- un’ampiezza di alcuni Vpp attorno a 0V, il
to nel Listato 2. segnale ottenuto all’ingresso analogico avrà
come valore medio 2.5V, e rientrerà completa-
[Listato 2] mente nella gamma dinamica del convertitore
// *** Regolazione luminosità *** (cioè tra 0 e 5V). Il programma visualizza l’am-
void main() { piezza dei campioni acquisiti accendendo un
unsigned int level; numero di LED proporzionale. In più, dal
momento che le variazioni d’ampiezza del
82
TRISC = 0; segnale sono molto veloci, è stato implementa-
Teoria

PORTC = 0; to un meccanismo per rendere più visibile il


livello più alto raggiunto. In pratica l’informa-
Pwm_Init(1000); // Init. PWM zione visualizzata non è direttamente l’ampiez-
Pwm_Start(); // Start PWM za del segnale, ma una variabile che viene
aggiornata con il massimo livello raggiunto, e
che viene decrementata gradualmente (in prati-
// Loop infinito ca è una specie di rivelatore di picco). Il pro-
while(1) { gramma è visibile nel Listato 3.
level = Adc_Read(2);
level=level >> 2; [Listato 3]
//level = 255-level; // *** Versione con memorizzazione massimo
Pwm_Change_Duty(level);
Delay_ms(20); unsigned int dato, maxi;
} char dis[9]={0,1,3,7,15,31,63,127,255};
}
void main() {
La generazione del segnale PWM è ottenuta con ADCON1 = 0x80; // Configure analog
la funzioni di libreria del MikroC già descritte nel inputs and Vref
numero 256 di Fare Elettronica, che utilizzano il TRISA = 0xFF; // PORTA is input
modulo PWM hardware del PIC. Il valore del duty TRISB = 0x00;
cycle è determinato direttamente dal valore letto PORTB = 0x00;
dall’ADC (dagli 8 bit più significativi), oppure dal
suo complemento nel caso si intenda utilizzare il while(1) {
FARE ELETTRONICA - FEBBRAIO 2007

circuito come crepuscolare (i sensori di luminosità dato = Adc_Read(2);


in genere forniscono una tensione più alta quanto if (dato>maxi) maxi=dato;
maggiore è il flusso che li investe). PORTB = dis[((maxi+16)>>7)];
if (maxi>dato) maxi—;
VU METER Delay_ms(1);
Il segnale analogico campionato negli esempi };
precedenti è praticamente una tensione conti- }
nua. E’ possibile comunque acquisire segnali
variabili nel tempo (tensioni alternate). Questa Come si può vedere, una volta acquisito il dato,
capacità è utilizzata nell’esempio seguente per viene aggiornata la variabile con il valore massi-
DiStefano- TEORIA -mikroC 8 16-01-2007 9:59 Pagina 83

mo, che è utiliz- logiche utilizzando il multiplexer analogico inter-


zata per calcola- no. Per fare questo è sufficiente selezionare in
re il valore da sequenza i differenti ingressi e campionarne il
visualizzare. Più valore. La frequenza di campionamento ottenibi-
in dettaglio, dal le su ciascun canale è circa pari a quella massima
valore ad 8 bit diviso il numero di canali campionati. Utilizzando
della variabile se la funzione Adc_Read risulta molto semplice ese-
ne ottiene uno guire la conversione su più canali, come mostrato
a 3 bit (tramite nell’esempio seguente. Il programma riportato
uno shift a nel Listato 4 campiona tutti i 5 ingressi analogici
Figura 3 Circuito di accoppiamento per il
segnale audio destra), che è presenti sul PIC 16F876 e visualizza il loro valore
utilizzato come su un display LCD. Il circuito è quello mostrato in
indice di un’array che contiene le 8 configurazio- Figura 4. La lettura dei diversi ingessi è eseguita
ni dei LED da visualizzare (LED progressivamente sequenzialmente con un ciclo for. In questo caso
accesi). Per sfruttare tutti i 9 valori, prima della il valore non è conservato, ma utilizzato subito per
divisione viene effettuato un arrotondamento del la visualizzazione. La conversione da intero a strin-
83
dato, sommando un piccolo offset. ga è eseguita con la funzione WordToStr.

Teoria
CONVERSIONE DI PIÙ CANALI USO DELLE INTERRUZIONI
Come già anticipato, nonostante il convertitore Si può notare che l’algoritmo utilizzato nel pre-
AD integrato del PIC sia uno solo, è possibile cedente esempio, sebbene adeguato all’appli-
comunque acquisire il valore di più tensioni ana- cazione, non è molto efficiente, in quanto le

Compilatore MikroC

Un potente
compilatore C
per PICmicro

✔ Code Editor
✔ Code Explorer
✔ Debugger
FARE ELETTRONICA - FEBBRAIO 2007

✔ Statistiche

Tutto in un ambiente
Windows facile ed intuitivo

Un set di strumenti veramente indispensabili


per sviluppare applicazioni con i PICmicro
Codice MIP 260083

Ordinalo subito su www.ieshop.it oppure telefona allo 02.66504755


DiStefano- TEORIA -mikroC 8 16-01-2007 9:59 Pagina 84

TEORIA MHZ RISORSE SPECIALE PRATICA

Ottava parte MikroC by Example: Uso del convertitore A/D

[Listato 4] di il controllo alla restante parte del codice. Nei


// *** Conversione AD multicanale *** casi in cui siano presenti dei requisiti di tempo o
unsigned int data; di elaborazione più stringenti, è conveniente
char i, buf[8]; sfruttare il tempo richiesto per effettuare la con-
versione per eseguire del codice. Organizzando
void main() { opportunamente le varie parti del programma è
ADCON1 = 0x80; // Config. input possibile in qualche modo parallelizzare le due
TRISA = 0xFF; // PORTA = input operazioni. Per fare questo però è necessario
TRISB = 0x00; comandare manualmente il convertitore e gesti-
PORTB = 0x00; re le interruzioni, come mostrato nell’esempio
seguente. Il Listato 5 implementa una versione
// Init. Display modificata del VU meter visto prima, che impie-
Lcd_Init(&PORTB); ga la tecnica appena descritta.
Lcd_Cmd(Lcd_CLEAR); A differenza di quanto visto nel Listato 3 in que-
Lcd_Cmd(Lcd_CURSOR_OFF); sto caso sono state abilitate le interruzioni asso-
Lcd_Out(1, 1, “CH = “); ciate al convertitore A/D (registri PIR1, PIE1 e
84
INTCON), e la funzione Adc_Read è utilizzata
Teoria

while(1) { una sola volta per avviare la prima conversione


for(i=0; i<5; i++) { e per impostare i restanti registri. Il loop princi-
data = Adc_Read(i); pale ha il solo compito di aggiornare la variabi-
WordToStr(data, buf); le che memorizza il massimo e di visualizzare il
Lcd_Chr(1, 3, ‘0’+i); risultato sui LED. Questi compiti vengono ripe-
Lcd_Out(1, 7, buf); tuti in continuazione. Contemporaneamente
Delay_ms(1000); l’ADC esegue una conversione, e al suo termine
} genera un’interruzione. La routine d’interruzio-
} ne legge il dato convertito, lo memorizza nella
} variabile “dato” cancella il flag di interruzione
del convertitore e riavvia una nuova conversio-
operazioni di conversione ed “elaborazione” dei ne. Al suo termine la routine sarà nuovamente
dati sono gestite in modo del tutto sequenziale: richiamata, e riavvierà una nuova conversione,
la funzione Adc_Read infatti attende la fine della come in un loop infinito, parallelo a quello prin-
conversione prima di restituire un valore e quin- cipale. L’effetto complessivo sarà che il codice
FARE ELETTRONICA - FEBBRAIO 2007

Figura 4 Campionamento multicanale e visualizzazione su LCD


DiStefano- TEORIA -mikroC 8 16-01-2007 9:59 Pagina 85

presente nel loop principale verrà eseguito in versione sarà completata.


continuazione e la variabile “dato” verrà aggior- La frequenza di campionamento però non è sta-
nata automaticamente ogni volta che una con- bilita in modo preciso, essa infatti, dipende dal

[Listato 5]
// *** Gestione AD con interruzioni *** while(1) {
unsigned int dato, maxi; if (dato>maxi) maxi=dato;
char dis[9]={0,1,3,7,15,31,63,127,255}; PORTB = dis[((maxi+16)>>7)];
void main() { if (maxi>dato) maxi—;
ADCON0 = 0x91; // Config. input Delay_ms(1);
ADCON1 = 0x80; // Config. input };
TRISA = 0xFF; // PORTA = input }
TRISB = 0x00; void interrupt()
PORTB = 0x00; {
// Abilitazione interruzioni dato=(ADRESH<<8)+ADRESL;
PIR1=0x00; // ADIF
PIE1=0x40; // ADIE // Reset ADIF Flag
85

Teoria
INTCON = 0xC0; // GIE + PEIE PIR1=0x00;
dato = 0;
maxi = 0; // Avvio nuova conversione
// Avvio prima conversione ADCON0=ADCON0|0x04;
ADCON0=ADCON0|0x04; }

Scheda easyPIC4
La rivoluzionaria scheda
di sviluppo per PICmicro
✔ Programmatore USB2.0 on-board con ICD
✔ Tastiera a 32 tasti
✔ 32 LED per il monitoraggio degli I/O
✔ 4 cifre LED a 7 segmenti
✔ Predisposizione per moduli LCD alfanumerici
✔ Predisposizione per moduli LCD grafici
✔ Predisposizione per comunicazione RS232
✔ Predisposizione per tastiera PS2
FARE ELETTRONICA - FEBBRAIO 2007

✔ Predisposizione per sensore di temperatura DS1820


✔ Supporto per tutte le famiglie PIC (anche PIC10F)*
✔ Predisposizione per comunicazione USB
✔ Alimentazione esterna o via USB
✔ Fornita con 16F877
Codice MIP 260085 ✔ Disponibile con o senza display

Ordinala subito su www.farelettronica.com oppure telefona allo 02.66504755


DiStefano- TEORIA -mikroC 8 16-01-2007 9:59 Pagina 86

TEORIA MHZ RISORSE SPECIALE PRATICA

Ottava parte MikroC by Example: Uso del convertitore A/D

tempo impiegato per completare la conversione Grazie all’impiego del Timer TMR0 si ottiene
e dal codice eseguito. In molti casi invece è utile una frequenza di campionamento di 1KHz esatto.
campionare dei segnali con una frequenza ben Come si può vedere dal codice sono state abili-
definita e costante. Per fare questo occorre uti- tate le interruzioni relative al timer e al conver-
lizzare il timer, come mostrato nel Listato 6. titore AD, quindi la routine d’interruzione verrà
richiamata sia all’overflow del timer, sia al com-
[Listato 6]
pletamento di ogni conversione. All’interno
// *** Campionamento a 1KHz ***
della routine pertanto è necessario distinguere
unsigned int dato, maxi;
quale delle due periferiche ha generato l’inter-
char dis[9]={0,1,3,7,15,31,63,127,255};
ruzione (controllando lo stato dei flag). Se l’in-
void main() {
terruzione è stata generata dal timer, viene
ADCON0 = 0x91; // Config. input
avviato il convertitore e resettato il timer. Se l’in-
ADCON1 = 0x80; // Config. input
terruzione è stata generata dal convertitore,
TRISA = 0xFF; // PORTA = input
viene salvato il dato convertito e resettato il flag.
TRISB = 0x00;
In questo modo la frequenza di campionamen-
PORTB = 0x00;
to è stabilita dal timer, e l’aggiornamento della
86 // Inizializz. Registri
variabile avviene solo quando il convertitore ha
OPTION_REG = 0x82; // CLK/4/8
Teoria

completato il suo lavoro, tutto questo senza


TMR0 = 5; // 250 cicli
interferire sensibilmente con l’esecuzione della
// Abilitazione interruzioni
routine contenuta nel loop principale.
PIR1=0x00; // ADIF
PIE1=0x40; // ADIE
CONCLUSIONI
INTCON = 0xE0; // GIE + PEIE + TMRIE
Prima di concludere è il caso di puntualizzare
dato = 0;
alcuni dettagli riguardanti il convertitore AD ed il
maxi = 0;
suo utilizzo. Innanzi tutto ricordo che maggiori
// Avvio prima conversione
dettagli sui registri utilizzati e sulla loro funzione
ADCON0=ADCON0|0x04;
possono essere trovati sul datasheet del PIC
while(1) {
16F876. Va notato anche che negli esempi si è
if (dato>maxi) maxi=dato;
tralasciata l’attesa del “tempo di acquisizione”
PORTB = dis[((maxi+16)>>7)];
precedente ad ogni conversione. Questo tempo
if (maxi>dato) maxi—;
è necessario per caricare le capacità interne del
Delay_ms(1);
convertitore AD. Se si suppone che l’impedenza
};
dei segnali utilizzati è sufficientemente bassa, il
}
tempo richiesto è dell’ordine di pochi cicli mac-
void interrupt()
china, e quindi può in effetti essere trascurato.
{
Infine va ricordato che per campionare un segna-
// Interrupt timer:
le analogico, e poterlo ricostruire senza perdite, è
// avvio conversione e reset rimer
necessario utilizzare una frequenza di campiona-
if (INTCON&0x04) {
mento almeno doppia rispetto alla banda del
ADCON0=ADCON0|0x04;
segnale (teorema di Nyquist). Negli esempi rela-
TMR0 = 5; // reset timer
FARE ELETTRONICA - FEBBRAIO 2007

tivi al VU meter non si è tenuto conto di questo


INTCON = 0x20; // cancella flag Timer
vincolo, perché l’obiettivo non era la ricostruzio-
}
ne del segnale. Il convertitore AD e tutte le altre
// Interrupt ADC:
periferiche utilizzate negli scorsi numeri saranno
// lettura dato, reset flag
utilizzati nelle prossime puntate per realizzare un
if (PIR1&0x40) {
progetto piuttosto complesso, ma di sicuro inte-
dato=(ADRESH<<8)+ADRESL;
resse: un datalogger che può essere utilizzato per
PIR1=0x00; // cancella flag ADIF
acquisire e memorizzare dati con continuità e tra-
}
sferirli al PC su richiesta.
}
Codice MIP 260080
DiStefano- TEORIA -mikroC 9:DiStefano- TEORIA -mikroC 8 14-02-2007 15:39 Pagina 84

TEORIA MHZ RISORSE SPECIALE PRATICA

Ottava parte
n° 260 - Febbraio 2007
Uso del convertitore A/D
Nona parte
n° 261 - Marzo 2007
Realizzazione di un dataloger
MikroC
by Example
(Iª parte)
Decima parte
n° 262 - Aprile 2007
Realizzazione di un dataloger
(IIª parte)

I n questa puntata e nella


prossima verrà mostrata la
realizzazione di un datalogger
essere impiegato in una moltitudine applicazio-
ni, quali il monitoraggio ambientale, la rileva-
zione di dati meteorologici, la misurazione del
consumo di potenza in utenze domestiche, la
registrazione di dati relativi a esperimenti o
programmabile, capace di
all’evoluzione di sistemi naturali, etc. In questa
registrare segnali analogici e puntata verranno descritti i criteri di progetto
digitali per un intervallo esteso dell’hardware e del firmware e alcune delle rou-
84
tine utilizzate. Nella successiva verrà completata
di tempo e di scaricarli su PC al
Teoria

la descrizione del firmware e verranno conside-


termine dell’acquisizione. rati alcuni miglioramenti e l’implementazione di
funzioni aggiuntive.
Verranno descritti gli schemi
elettrici ed il firmware realizzato SPECIFICHE
Il datalogger che si vuole realizzare è dotato
con il MikroC.
delle seguenti caratteristiche:
• Possibilità di campionare 4 ingressi analogici
(0-5V) con risoluzione di 8 bit, 8 ingressi digi-
tali TTL e la temperatura ambiente con la pre-
INTRODUZIONE cisione di circa 1°C.
Nelle scorse puntate è stato mostrato come • La frequenza di campionamento è seleziona-
implementare alcuni algoritmi utili e come bile tra 1 campione al secondo e 1 campione
gestire diverse periferiche interne al PIC o ester- ogni 255 minuti (circa 4 ore).
ne, utilizzando il linguaggio C ed in particolare • È possibile memorizzare 2000 campioni per
le librerie messe a disposizione dal MikroC. In ciascun segnale.
questa puntata e nella prossima verrà presenta- • È possibile impostare la registrazione continua
to un progetto completo e piuttosto complesso (i dati più vecchi vengono sovrascritti da quel-
che mette assieme la maggior parte degli ele- li nuovi, quindi nella memoria saranno presen-
menti visti in precedenza. Verrà infatti descritta ti solo i 2000 campioni più recenti).
la realizzazione di un datalogger, cioè un siste- • L’interfacciamento con un PC avviene via RS-
ma capace di campionare e di registrare con 232 per la configurazione tramite comandi
continuità su un intervallo di tempo piuttosto testuali ed il download dei dati.
lungo dei valori analogici e digitali provenienti • È possibile selezionare se campionare 4 ingres-
FARE ELETTRONICA - MARZO 2007

da sensori o apparati esterni. I datalogger sono si analogici oppure 2 analogici, uno digitale
utilizzati per tenere sotto controllo sistemi o ed uno relativo alla temperatura.
apparati di vario genere, che necessitano un • L’avvio e l’arresto del campionamento avven-
monitoraggio continuo. I dati registrati durante gono tramite un pulsante o un comando
l’intero intervallo di osservazione possono esse- seriale.
re successivamente scaricati su PC per essere • Un LED rosso lampeggiante indica che il cam-
visualizzati ed analizzati meglio con software pionamento è attivo, un LED verde invece la
dedicati. Il progetto qui presentato ha delle fine del campionamento (se nessun LED è
caratteristiche abbastanza interessanti e può acceso significa che il campionamento non è
DiStefano- TEORIA -mikroC 9:DiStefano- TEORIA -mikroC 8 14-02-2007 15:39 Pagina 85

Realizzazione di
un datalogger di Antonio Di Stefano

iniziato e quindi non ci sono dati validi in guate. Si è scelto di utilizzare una EEPROM seria-
memoria). le I2C da 64Kbit per via delle ridotte dimensio-
ni del suo package, della semplicità di utilizzo e
Da queste specifiche risulta che il datalogger è per il fatto che il suo interfacciamento richiede
in grado di campionare ininterrottamente i dati soltanto due piedini.
per un intervallo di tempo compreso tra circa Per rilevare la temperatura si è scelto di collega-
33 minuti e quasi un anno, ed è abbastanza ver- re un sensore digitale del tipo DS1820 al piedi-
satile da potere essere utilizzato in una grande no RA5. Il sensore della Dallas fornisce diretta-
85
varietà di applicazioni! mente la temperatura in formato digitale con

Teoria
una buona accuratezza, utilizzando l’interfaccia
SCHEMA ELETTRICO 1-Wire, che richiede soltanto un collegamento.
Lo schema elettrico utilizzato per il datalogger è Dal momento che il pin RA5 è anche un ingres-
riportato in Figura 1. Il cuore del circuito è un so analogico, non è comunque difficile sostitui-
comune PIC16F876, che viene fatto funzionare re questo componente con un sensore di tem-
a 8MHz. Questo PIC è dotato di un convertito- peratura analogico come l’LM35 se desiderato,
re A/D a 10 bit e di 5 ingressi analogici, e può ed utilizzare il convertitore interno per campio-
quindi soddisfare le specifiche. Gli 8 ingressi nare il dato.
digitali possono essere ricavati dalla porta B. Per La comunicazione con il PC avviene utilizzando
memorizzare i dati è necessaria una memoria la periferica UART integrata nel PIC. È necessa-
non volatile, ma non è possibile utilizzare la rio però utilizzare un MAX232 per adattare i
EEPROM del PIC per via delle sue ridotte dimen- livelli di segnalazione a quelli dello standard RS-
sioni. E’ necessario pertanto collegare una 232. I due LED che segnalano lo stato di cam-
memoria non volatile esterna di dimensioni ade- pionamento in corso/completato sono collegati

FARE ELETTRONICA - MARZO 2007

Figura 1 Schema complessivo del datalogger


DiStefano- TEORIA -mikroC 9:DiStefano- TEORIA -mikroC 8 14-02-2007 15:39 Pagina 86

TEORIA MHZ RISORSE SPECIALE PRATICA

Nona parte MikroC by Example: Reallizazione di un datalogger (Iª parte)

ai pin RC0 ed RC1, mentre il pulsante di • Misurare intervalli di tempo.


avvio/arresto è collegato alla porta RC5. In pra- • Campionare i dati.
tica sono utilizzati quasi tutti i pin del PIC! • Memorizzare i dati sulla EEPROM.
Tuttavia è ancora possibile aggiungere periferi- • Comunicare con il PC per ricevere la configu-
che, se necessario, utilizzando l’interfaccia I2C o razione e per scaricare i dati.
1-Wire.
I campioni verranno memorizzati nella EEPROM Si può notare che questi compiti non sono svol-
esterna sequenzialmente e a gruppi di 4 byte. ti continuamente, ma sono attivati da due even-
Infatti secondo le specifiche possono essere ti: l’overflow del timer e la ricezione dei caratte-
campionati e memorizzati o i 4 ingressi analogi- ri dalla seriale. Per questo motivo è conveniente
ci, o soltanto due di questi, più lo stato degli strutturare il programma in base a questa consi-
ingressi digitali (che coincide con il valore letto derazione. La struttura del software è visibile in
sulla porta B), e la temperatura. Tutte queste Figura 2. Come si può vedere sono presenti due
grandezze sono ad 8 bit e possono essere parti ben distinte: il loop principale e la routine
memorizzate singolarmente in ciascuna locazio- d’interruzione. Quest’ultima è richiamata dagli
ne della memoria (che è organizzata proprio ad eventi a cui si è fatto riferimento prima. Al veri-
86
8 bit). Sarà inoltre necessario memorizzare sem- ficarsi di questi eventi il codice presente nella
Teoria

pre nella EEPROM esterna il puntatore all’indi- routine sarà eseguito e genererà dei segnali di
rizzo dell’ultimo dato memorizzato (utile nel controllo per attivare le altre funzioni. Queste
caso di memorizzazione continua) ed il numero
complessivo di campioni. Queste informazioni
saranno utili quando occorrerà scaricare i dati su
PC. Dal momento che la EEPROM scelta può
contenere 8KB, ed in ciascuna registrazione
vengono memorizzati 4 byte di dati, sarà possi-
bile memorizzare 2048 campioni per ciascuno
dei 4 dati. Considerando che i dati da memoriz-
zare sono solo 2000, gli indirizzi più alti della
EEPROM saranno liberi per memorizzare le
informazioni di servizio descritte prima.
Come visto nelle specifiche il datalogger ha una
serie di parametri configurabili, come la veloci-
tà di campionamento, la scelta dei dati da
memorizzare e la modalità di memorizzazione
(singola o in continua). Queste informazioni
vengono impostate dal PC tramite RS-232 e
devono essere memorizzate in maniera non
volatile (in modo tale da permettere lo spegni-
mento del dispositivo dopo la configurazione).
Anche in questo caso è utilizzata la EEPROM
esterna.
FARE ELETTRONICA - MARZO 2007

STRUTTURA DEL FIRMWARE


Dal momento che il progetto che vogliamo rea-
lizzare è relativamente più complesso di quelli
visti fino ad ora, è utile discutere l’organizzazio-
ne ed il funzionamento del firmware prima di
considerare l’implementazione vera e propria (il
codice). I compiti che il datalogger deve svolge-
Figura 2 Struttura del firmware del datalogger
re sono in sintesi i seguenti:
DiStefano- TEORIA -mikroC 9:DiStefano- TEORIA -mikroC 8 14-02-2007 15:39 Pagina 87

funzioni sono gestite dal loop principale, e ven- mento eseguito), le due variabili vengono ripor-
gono eseguite solo se prima sono stati attivati i tate al valore 0, in attesa di un nuovo evento.
rispettivi segnali di controllo. Ovviamente i
segnali di cui si parla, e che consentono la Comandi, decodifica e configurazione
comunicazione tra la routine d’interruzione ed il I comandi gestiti dal datalogger sono i seguenti:
loop principale, non sono altro che delle varia- • Axxx: imposta l’intervallo di campionamento
bili globali, settate da una routine e resettate a xxx minuti. Il valore di xxx può variare da 0
dall’altra. a 255, se viene specificato 0 i dati vengono
Più in dettaglio i compiti da gestire sono due: il campionati ogni secondo.
campionamento più la memorizzazione dei dati, • Bx: imposta se campionare i 4 canali analogi-
e la decodifica dei comandi inviati dal PC, usati ci (x=0) o due analogici, 8 digitali (porta B) e
per impostare la configurazione e richiedere l’in- uno di temperatura (x=1).
vio dei dati memorizzati. Anche le variabili di • Cx: se x=0 vengono registrati soltanto 2000
segnalazione sono due, chiamate “stringa” e campioni, se x=1 la registrazione è continua, i
“trig”. La prima viene impostata ad 1 dalla routi- dati vengono sovrascritti in continuazione ed
ne d’interruzione quando è stata ricevuta e in memoria sono presenti sempre gli ultimi
87
memorizzata (in un apposito buffer) una stringa 2000.

Teoria
completa (terminata da un “a capo”). Il secondo • D: mostra la configurazione attuale.
segnale viene generato quando il timer raggiun- • L: attiva o disattiva la modalità “monitor” (i
ge l’intervallo di tempo di campionamento pre- dati campionati sono anche inviati alla porta
fissato. Una volta che i due compiti sono stati seriale, e quindi visualizzati sul PC in tempo
espletati (la stringa decodificata o il campiona- reale).

Compilatore MikroC

Un potente
compilatore C
per PICmicro

✔ Code Editor
✔ Code Explorer
✔ Debugger
✔ Statistiche
FARE ELETTRONICA - MARZO 2007

Tutto in un ambiente
Windows facile ed intuitivo

Un set di strumenti veramente indispensabili


per sviluppare applicazioni con i PICmicro
Codice MIP 261087

Ordinalo subito su www.ieshop.it oppure telefona allo 02.66504755


DiStefano- TEORIA -mikroC 9:DiStefano- TEORIA -mikroC 8 14-02-2007 15:39 Pagina 88

TEORIA MHZ RISORSE SPECIALE PRATICA

Nona parte MikroC by Example: Reallizazione di un datalogger (Iª parte)

• E: avvia o ferma il campionamento. quenza di 1KHz esatto (periodo di 1ms).


• S: scarica i dati contenuti in memoria sul PC. Questa frequenza viene utilizzata per incre-
• M: nostra il menu (i comandi). mentare una serie di variabili che contano i
millisecondi, i secondi ed i minuti. Ogni volta
Tutti i comandi inviati devono terminare con che il valore di tempo coincide con quello pro-
un carattere di “a capo”, codice ASCII 10. grammato, viene posta ad 1 la variabile “trig”,
Questo carattere infatti viene utilizzato per e quindi avviato il campionamento e la memo-
individuare il termine della stringa, e quindi rizzazione dei 4 dati.
per iniziarne la decodifica. La routine d’interru-
zione infatti riceve i singoli caratteri e li accu- IL CODICE
mula in un buffer temporaneo ricostruendo la Nel Listato 1 sono riportate alcune parti del
stringa, fino a quando il carattere di “a capo” codice, in particolare quelle relative alle
non è stato ricevuto, a quel punto la variabile dichiarazioni, alla routine d’interruzione ed
di segnalazione “stringa” è posta ad 1, e il alla gestione dei comandi. Il resto del codice,
comando può essere decodificato. La decodifi- cioè quello relativo al campionamento ed alla
ca viene fatta semplicemente leggendo il memorizzazione, sarà discusso nella prossima.
88
primo carattere e gli eventuali parametri (con- All’inizio de programma vengono definite
Teoria

vertiti da testo a intero). In base a questi dati delle costanti, alcune come macro (riferite alla
viene eseguita l’operazione corrispondente. La lunghezza del buffer dei comandi ed al nume-
maggior parte dei comandi va a modificare la ro di campioni da memorizzare), altre grazie
configurazione e/o lo stato del sistema, che è al qualificatore “const”. Queste ultime sono
rappresentato dai seguenti parametri, rag- delle stringhe che vengono utilizzate nella
gruppati nella struttura “config”: comunicazione seriale. Dichiarare le stringhe
in questo modo fa si che esse vengono memo-
struct cnf { rizzate nella flash invece che nella RAM del PIC
char periodo; (dove non potrebbero essere contenute a
char canali; causa della ridotta dimensione di questa).
char loop; Successivamente vengono dichiarate le varia-
char logging; bili globali utilizzate per condividere dati e
char monitor; scambiare informazioni tra le routine. In parti-
unsigned data_ptr; colare le variabili “stringa” e “trig” sono le
unsigned n_campioni; variabili di segnalazione descritte prima. La
} config; variabile “buf” (un array) è usata come buffer
per memorizzare le stringhe passate dalla
Oltre alle impostazioni già viste sono presenti il seriale, “msec”, “sec” e “mins” sono usate per
puntatore all’indirizzo della memoria EEPROM tenere il tempo, mentre “pulsante” è un con-
riferito al dato attuale, ed il numero di campio- tatore usato per l’antirimbalzo del pulsante di
ni memorizzati. start/stop. La struttura “config” contiene la
configurazione e lo stato attuale, come
Misura del tempo descritto prima. Tra le funzioni presenti alcune
Per misurare gli intervalli di tempo viene utiliz- sono utilizzate per visualizzare le stringhe.
FARE ELETTRONICA - MARZO 2007

zato il timer hardware del PIC (il TMR0), più In particolare risulta interessante
alcune variabili e contatori. Il TMR0 è stato SendFlashString, che trasmette alla seriale una
programmato per contare ad una frequenza delle stringhe memorizzate nella flash, usando
pari a quella dei cicli macchina diviso 16. la funzione Flash_Read del MikroC. La funzio-
Partendo dalla frequenza di 8MHz, diviso 4 ne main è molto semplice: richiama una serie
cicli di clock per ciclo macchina, e ancora divi- di funzioni di inizializzazione, e poi avvia il
so 16, si ottiene una frequenza di 125KHz. Se loop principale, che contiene la funzione
si imposta un valore di reset tale da avere un “Comandi” usata per la decodifica dei coman-
overflow dopo 125 cicli, si ottiene una fre- di inviati via RS-232. Essa non fa altro che veri-
DiStefano- TEORIA -mikroC 9:DiStefano- TEORIA -mikroC 8 15-02-2007 14:44 Pagina 89

ficare se la variabile “stringa” vale 1 (stringa CONCLUSIONI


pronta da decodificare) e distinguere il Nonostante il codice presentato non sia ancora
comando inviato in base alla prima lettera, completo, è sufficientemente intuitivo da con-
usando un costrutto swirch-case. Ciascuna sentirne la comprensione del funzionamento.
sezione non fa altro che impostare un valore Nella prossima puntata verranno analizzate
della configurazione oppure visualizzare anche le restanti routine dedicate al campiona-
determinate informazioni. La routine d’inter- mento e memorizzazione dei dati, ed al dow-
ruzione esegue i due compiti descritti prima. Il nload sul PC. Saranno anche presentati alcuni
riconoscimento della sorgente d’interruzione possibili miglioramenti sia all’hardware che al
(timer o UART) è fatta controllando i rispettivi software. Il codice completo sarà anche scarica-
flag d’interruzione. bile dal sito di Fare Elettronica.
Nella sezione che riguarda il timer, e che viene Codice MIP 261084
richiamata ogni millisecondo, vengono
aggiornate le variabili per il conteggio del
tempo, e verificato se il tempo trascorso coin-
cide con quello impostato per il campiona-
NON DIMENTICARE
Se rinnovi in anticipo il tuo 89
mento. Inoltre viene gestita anche la pressio-
abbonamento non perdi

Teoria
ne del pulsante di start/stop, con un conteg- alcun numero:
gio che serve sia come anti-rimbalzo che per il nuovo abbonamento va in
ritardare l’esecuzione del comando di circa un coda e ti garantisci la
secondo (per evitare di attivare o fermare acci- continuità e il risparmio!
dentalmente il campionamento).

Scheda easyPIC4
La rivoluzionaria scheda
di sviluppo per PICmicro
✔ Programmatore USB2.0 on-board con ICD
✔ Tastiera a 32 tasti
✔ 32 LED per il monitoraggio degli I/O
✔ 4 cifre LED a 7 segmenti
✔ Predisposizione per moduli LCD alfanumerici
✔ Predisposizione per moduli LCD grafici
✔ Predisposizione per comunicazione RS232
✔ Predisposizione per tastiera PS2
FARE ELETTRONICA - MARZO 2007

✔ Predisposizione per sensore di temperatura DS1820


✔ Supporto per tutte le famiglie PIC (anche PIC10F)*
✔ Predisposizione per comunicazione USB
✔ Alimentazione esterna o via USB
✔ Fornita con 16F877
Codice MIP 261089 ✔ Disponibile con o senza display

Ordinala subito su www.farelettronica.com oppure telefona allo 02.66504755


Di Stefano- TEORIA - MikroC 10:- PRATICA 20-03-2007 9:54 Pagina 106

Ottava parte: Febbraio 2007


Uso del convertitore A/D

Nona parte: Marzo 2007


Realizzazione di un datalogger (Iª parte) MikroC
by Example
Decima parte: Aprile 2007
Realizzazione di un datalogger (IIª parte)

Undicesima parte: Maggio 2007


Utilizzo dell’interfaccia Ethernet

Nella scorsa puntata è stato descritto possono essere notate già nel codice riportato nel
Listato 1, in cui è visibile la parte iniziale, che com-
l’hardware e la struttura del software prende le dichiarazioni (la tabella delle stringhe
del datalogger e sono state mostrate non è riportata per intero per brevità), i prototipi e
la funzione main.
alcune routine. In questa verranno
analizzate con maggiore dettaglio le Listato 1

106 restanti funzioni, e verranno indicate // *** Definizioni ***


#define MAX_BUF_LEN 32
alcune possibili ottimizzazioni.
TEORIA

#define N_SAMP 2000


#define ADDR_INFO 8100
#define ADDR_END 7999
Breve riepilogo
Il datalogger presentato nella scorsa puntata svol- // *** Tabella stringhe ***
ge la funzione di campionare e memorizzare ad const char *str_intro=”*** DataLogger v1.0
intervalli regolari e su un tempo piuttosto lungo 4 ***\r\n”;
segnali analogici, oppure 2 segnali analogici, 8 const char *str_menu1=”—- Comandi —-
segnali digitali e la temperatura ambiente. Lo stru- \r\n”;
mento è costituito da un PIC 16F876, da una …
EEPROM seriale I2C da 64Kbit, da un sensore di
temperatura One-Wire DS1820. Il sistema viene // *** Variagili globali ***
collegato ad un PC via RS-232 per essere program- struct cnf {
mato o per scaricare i dati. Una volta impostati i char periodo;
parametri (intervallo di campionamento, configu- char canali :1;
razione dei canali, etc.) il sistema può comunque char loop :1;
funzionare autonomamente, e deve essere nuova- char logging :1;
mente collegato al PC solo per scaricare i dati char monitor :1;
acquisiti. Come visto nella scorsa puntata il pro- unsigned data_ptr;
gramma è guidato da due eventi: gli intervalli di unsigned n_campioni;
campionamento e l’arrivo di una stringa di testo } config;
dal PC (che deve essere decodificata, e gestita
opportunamente). Questi due eventi sono gestiti char stringa, bpnt;
dalla routine d’interruzione, che provvede a segna- char buf[MAX_BUF_LEN];
lare alle funzioni presenti nel loop principale (tra- unsigned i, n;
FARE ELETTRONICA - APRILE 2007

mite alcune variabili globali) l’azione da compiere. unsigned msec, pulsante;


Per maggiori dettagli sul funzionamento del soft- unsigned char secs, mins, trig, dato[4];
ware e sull’hardware si rimanda al numero prece-
dente di FE, in cui sono anche state introdotte // *** Prototipi ***
alcune delle routine impiegate. void Init(void);
void Comandi(void);
Dichiarazioni e main void Campiona(void);
La versione completa del programma, rispetto a void Memorizza(void);
quella introdotta nell’articolo precedente, presenta void Config_Store(void);
alcune funzioni ed accorgimenti aggiuntivi, che void Config_Load(void);
Di Stefano- TEORIA - MikroC 10:- PRATICA 20-03-2007 9:54 Pagina 107

Realizzazione di
un datalogger
di Antonio Di Stefano

void EEPROM_Wr(unsigned addr, char data); dati relativi alla configurazione (nella memoria
char EEPROM_Rd(unsigned addr); non volatile), e l’indirizzo finale della memoria
void SendString(char *); riservata ai dati. Si può notare che il tipo struttu-
void SendFlashString(unsigned); rato che memorizza la configurazione è stato
void Show_Menu(void); definito in maniera un po’ diversa rispetto a
void Show_Config(void); quello presentato nella scorsa puntata: dal
void Download(void); momento che alcuni campi assumono soltanto
void Print_data(unsigned m, char a, char b, due valori durante il funzionamento (in partico-
char c, char d); lare 0 o 1), si è specificato “:1” accanto al loro 107
nome. Questa particolare notazione (prevista dal

TEORIA
// *** Main *** C standard), consente di specificare proprio il
void main() { numero di bit da utilizzare, limitando così le
richieste di memoria (più campi saranno “impac-
Init(); chettati” in un unico byte).
SendFlashString(str_intro);
Show_Menu(); La funzione main ed il loop principale in essa
contenuto è, come si può vedere, molto sempli-
while(1) { ce. Dopo avere richiamato le funzioni di inizializ-
zazione, nel loop vengono eseguite soltanto le
if (trig) { funzioni di campionamento, memorizzazione e
Campiona(); decodifica dei comandi. Le prime sono eseguite
Memorizza(); solo quando la variabile “trig” è posta ad 1 dalla
if (config.monitor) routine d’interruzione. La seconda invece con-
Print_data(secs, dato[0], dato[1], trolla, come spiegato nello scorso articolo, che la
dato[2], dato[3]); variabile “stringa” valga 1 (stringa completa rice-
trig=0; vuta dal PC) prima di eseguire la decodifica.
} else {
if (config.n_campioni==N_SAMP) { Descrizione delle funzioni
PORTC|=0x02; Alcune funzioni, come quella d’interruzione o
Delay_ms(20); quella di decodifica dei comandi, sono state pre-
PORTC&=0xFD; sentate già nella precedente puntata, le altre ver-
Delay_ms(900); ranno descritte di seguito. Queste funzioni si
} occupano essenzialmente del campionamento
} dei dati, della scrittura e lettura dei dati e della con-
figurazione nella EEPROM, e l’invio dei dati al PC.
FARE ELETTRONICA - APRILE 2007

Comandi();
} Funzione di campionamento
} La funzione che acquisisce i dati è riportata nel
Listato 2. Il funzionamento è molto semplice: a
Si può notare che sono state definite alcune seconda della configurazione scelta per i canali ven-
costanti come macro. Queste facilitano la perso- gono letti i valori analogici relativi ai primi 4 canali,
nalizzazione del programma se si intende utiliz- oppure soltanto due di questi, il valore della porta
zare memorie di dimensioni diverse. Le costanti B (che comprende lo stato digitale di ciascuna delle
specificano in particolare il numero massimo di sue 8 linee), ed il valore della temperatura, letto dal
campioni da memorizzare, l’indirizzo di inizio dei sensore DS1820. Per eseguire tutte queste opera-
Di Stefano- TEORIA - MikroC 10:- PRATICA 20-03-2007 9:54 Pagina 108

zioni vengono utilizzate le funzioni Adc_Read(), e numero 259. In questo caso però la EEPROM
quelle della libreria One-Wire del MikroC, già impiegata, una 24LC64, richiede due byte per
discusse ed utilizzate nelle scorse puntate. La ruoti- l’indirizzamento, ed è quindi necessario ricavare
ne di lettura della temperatura è esattamente ugua- la parte alta (MSB) e quella bassa (LSB) dell’indi-
le a quella utilizzata nel numero 259 di FE. rizzo. Queste due funzioni sono richiamate da
tutte le altre che necessitano di accedere alla
Listato 2 EEPROM. Questo significa che modificandole è
possibile impiegare tipi di memoria diversi (ad
void Campiona(void) esempio la EEPROM interna al PIC, o una flash
{ seriale esterna).
int tt;
Listato 3
if (!config.canali) {
// Modalità AAAA void EEPROM_Wr(unsigned addr, char data)
dato[0] = (char)(Adc_Read(0)>>2); {
dato[1] = (char)(Adc_Read(1)>>2); // Issue I2C start signal
dato[2] = (char)(Adc_Read(2)>>2); I2C_Start();
dato[3] = (char)(Adc_Read(3)>>2); // Send byte via I2C (command to 24LC64)
} else { I2C_Wr(0xA0);
// Modalità AABT // Send byte (MSB address)
108 dato[0] = (char)(Adc_Read(0)>>2); I2C_Wr((addr>>8)&0xFF);
dato[1] = (char)(Adc_Read(1)>>2); // Send byte (LSB address)
TEORIA

dato[2] = PORTB; I2C_Wr(addr&0xFF);


// Send data (data to be written)
// Configura porta A come I/O digitale I2C_Wr(data);
ADCON1 = 0xFF; I2C_Stop();
PORTA = 0xFF; }
TRISA = 0xFF;
char EEPROM_Rd(unsigned addr)
Delay_us(120); {
Ow_Reset(&PORTA,5); char k;
// Comando SKIP_ROM
Ow_Write(&PORTA,5,0xCC); // Issue I2C start signal
// Comando CONVERT_T I2C_Start();
Ow_Write(&PORTA,5,0x44); // Send byte via I2C (device address + W)
Delay_us(120); I2C_Wr(0xA0);
Ow_Reset(&PORTA,5); // Send byte (MSB address)
// Comando SKIP_ROM I2C_Wr((addr>>8)&0xFF);
Ow_Write(&PORTA,5,0xCC); // Send byte (LSB address)
// Comando READ_SCRATCHPAD I2C_Wr(addr&0xFF);
Ow_Write(&PORTA,5,0xBE); // Issue I2C start signal
Delay_ms(1); I2C_Repeated_Start();
// Lettura LSB // Send byte (device address + R)
tt = Ow_Read(&PORTA,5); I2C_Wr(0xA1);
// Lettura MSB // Read the data (NO acknowledge)
tt = tt+(Ow_Read(&PORTA,5)<<8); k = I2C_Rd(0);
dato[3] = (char) tt/2; I2C_Stop();
FARE ELETTRONICA - APRILE 2007

ADCON1=0x82; return k;
PORTA=0; }
TRISA=0xFF;
} Memorizzazione dei dati
} La funzione di memorizzazione, scrive i 4 dati
acquisiti sulla memoria (sequenzialmente), prov-
Scrittura e lettura dalla EEPROM vedendo anche ad aggiornare il puntatore all’ul-
Anche in questo caso le routine utilizzate (visibili timo dato scritto, ed il numero complessivo dei
nel Listato 3) sono simili a quella descritte nel campioni (questa caratteristica è utile per recu-
Di Stefano- TEORIA - MikroC 10:- PRATICA 20-03-2007 9:54 Pagina 109

perare correttamente i dati, anche se il datalog- EEPROM_Wr(ADDR_INFO+4,


ger viene spento). Essa è responsabile inoltre del- (config.data_ptr)&0xFF);
l’arresto del campionamento, quando viene rag- EEPROM_Wr(ADDR_INFO+5,
giunto il numero di campioni prestabilito, o l’in- (config.data_ptr>>8)&0xFF);
dirizzo finale della zona riservata ai dati. Il codice
della funzione è riportato nel Listato 4. // Controllo fine memoria
if ((config.n_campioni==N_SAMP)||
Listato 4 (config.data_ptr>ADDR_END)) {
void Memorizza(void) if (config.loop) {
{ config.data_ptr=0;
// Memorizza campioni } else {
EEPROM_Wr(config.data_ptr, dato[0]); config.logging=0;
config.data_ptr++; }
EEPROM_Wr(config.data_ptr, dato[1]); }
config.data_ptr++;
EEPROM_Wr(config.data_ptr, dato[2]); }
config.data_ptr++;
EEPROM_Wr(config.data_ptr, dato[3]); Memorizzazione configurazione
config.data_ptr++; Anche i parametri relativi alla configurazione
config.n_campioni++; attuale sono memorizzati nella EEPROM per con-
sentire di separare la fase di programmazione 109
// Memorizza puntatore e num. campioni (che necessita di un PC), dall’effettivo impiego

TEORIA
EEPROM_Wr(ADDR_INFO+2, per il campionamento, che può avvenire anche in
(config.n_campioni)&0xFF); maniera autonoma. I parametri della configura-
EEPROM_Wr(ADDR_INFO+3, zione sono memorizzati in due byte: il primo con-
(config.n_campioni>>8)&0xFF); tiene il periodo di campionamento, mentre il

Compilatore MikroC

Un potente
compilatore C
per PICmicro

✔ Code Editor
✔ Code Explorer
✔ Debugger
✔ Statistiche
FARE ELETTRONICA - APRILE 2007

Tutto in un ambiente
Windows facile ed intuitivo

Un set di strumenti veramente indispensabili


per sviluppare applicazioni con i PICmicro
Codice MIP 262109

Ordinalo subito su www.ieshop.it oppure telefona allo 02.66504755


Di Stefano- TEORIA - MikroC 10:- PRATICA 20-03-2007 9:54 Pagina 110

secondo gli altri parametri ad 1 bit accorpati Listato 6


assieme. La funzione di lettura (Config_Load)
legge ed aggiorna anche il valore del puntatore e void Download(void)
del numero di campioni memorizzati nella {
EEPROM (che occupano i 4 byte successivi alle static unsigned addr, k;
altre informazioni). Il codice delle funzioni è static char a, b, c, d;
mostrato nel Listato 5.
addr=0;
Listato 5 k=config.n_campioni;
if (config.loop) addr=config.data_ptr;
void Config_Store(void) if (config.n_campioni>N_SAMP) k=N_SAMP;
{
unsigned char x; for(i=0; i<k; i++) {
a=EEPROM_Rd(addr);
EEPROM_Wr(ADDR_INFO, config.periodo); addr++;
b=EEPROM_Rd(addr);
x=config.canali<<2; addr++;
x|=config.loop<<1; c=EEPROM_Rd(addr);
x|=config.monitor; addr++;
EEPROM_Wr(ADDR_INFO+1, x); d=EEPROM_Rd(addr);
110 } addr++;
if (addr>ADDR_END) addr=0;
TEORIA

Print_data(i, a, b, c, d);
void Config_Load(void) }
{
unsigned char x; Config_Store();
}
config.periodo=EEPROM_Rd(ADDR_INFO);
x=EEPROM_Rd(ADDR_INFO+1); Conclusioni
Per questioni di spazio non è possibile riportare il
config.canali=(x>>2)&0x01; codice completo, che può comunque essere sca-
config.loop=(x>>1)&0x01; ricato dal sito di Fare Elettronica (www.farelettro-
config.monitor=x&0x01; nica.com) nella sezione “rivista”. I frammenti di
codice riportati dovrebbero essere comunque
config.n_campioni=EEPROM_Rd(ADDR_INFO+2)+ sufficientemente rappresentativi dell’intero fir-
(EEPROM_Rd(ADDR_INFO+3)<<8); mware e delle tecniche adottate per il suo svilup-
config.data_ptr=EEPROM_Rd(ADDR_INFO+4)+ po. Una volta compreso il suo funzionamento è
(EEPROM_Rd(ADDR_INFO+5)<<8); possibile apportare diverse modifiche per miglio-
} rare l’efficienza e la flessibilità del programma.
Una delle più interessanti consiste ad esempio
Download dei dati nell’impiegare un Real Time Clock I2C esterno
Una volta completata l’acquisizione è possibile (come il MAX1307) per fornire sia un riferimen-
scaricare i dati memorizzati inviando il comando to temporale più preciso, sia per attivare periodi-
“S” via seriale. Questo comando richiama la fun- camente (ad esempio ogni secondo) il PIC, per-
zione riportata nel Listato 6, che legge sequen- mettendogli di entrare nello stato di sleep duran-
zialmente i dati dalla EEPROM e li trasmette al te i periodi di inattività. Questo permette di
PC. Se era stata imposta una registrazione singo- ridurre significativamente i consumi del sistema,
FARE ELETTRONICA - APRILE 2007

la, i dati vengono letti a partire dall’inizio della e quindi di assicurarne una maggiore autonomia
memoria. Se è stata effettuata una registrazione se si usa un’alimentazione a batteria.
continua, viene controllato il numero dei dati
registrati, se questo è inferiore alla capacità di
memorizzazione, i dati vengono letti a partire
dall’inizio e fino al numero registrato. Se è supe-
riore, significa che i dati più vecchi presenti in
memoria inizieranno dalla locazione successiva a Codice MIP 262106
all’ultima impiegata, e dovranno essere letti in www.farelettronica.com/mip
maniera circolare.
Di Stefano- TEORIA - MikroC 11:- PRATICA 17-04-2007 16:43 Pagina 102

Ottava parte: Febbraio 2007


Uso del convertitore A/D

Nona parte: Marzo 2007


Realizzazione di un datalogger (Iª parte) MikroC
by Example
Decima parte: Aprile 2007
Realizzazione di un datalogger (IIª parte)

Undicesima parte: Maggio 2007


Utilizzo dell’interfaccia Ethernet

In questa puntata conclusiva della serie Con pochissimi componenti è quindi possibile
interfacciare un PIC a Ethernet. Occorre però
dedicata al MikroC verrà presentata implementare il firmare la gestione dei protocolli:
una delle più interessanti e promettenti questa operazione può essere più o meno com-
plessa a seconda delle funzionalità che si deside-
possibilità offerte da questo compilatore ra ottenere.
e dagli strumenti di sviluppo forniti dalla Come verrà mostrato nei prossimi paragrafi,
anche in questo caso il MikroC permette di sem-
102 Mikroelektronika: l’interfacciamento di plificare molto il lavoro necessario per realizzare
un PIC con una rete Ethernet. un’applicazione dotata di connettività di rete,
TEORIA

grazie alla disponibilità di funzioni di libreria


apposite. Per semplificare ulteriormente la proto-
tipizzazione e la sperimentazione, la
Mikroelektronika ha realizzato una piccola sche-
L’enorme diffusione che si è avuta negli ultimi da, dotata del 28J60, pensata per essere connes-
anni delle reti locali e di Internet, rende possibile sa direttamente alle schede di sviluppo
realizzare applicazioni capaci di scambiare dati e EasyPIC3/4 (Figura 1).
comandi con dispositivi dislocati praticamente in
qualsiasi parte del pianeta. Questo è reso più La scheda deve essere connessa al connettore
semplice dal fatto anche gli standard ed i proto- della EasyPIC che fa riferimento alla porta C dei
colli utilizzati sono sostanzialmente uniformi. In PIC (sulla quale si trova la periferica MSSP, che
particolare uno dei livelli fisici più diffusi ed effi- gestisce l’interfaccia SPI), come mostrato in
cienti nel campo delle reti locali è Ethernet (IEEE Figura 2. La scheda viene alimentata direttamen-
802.3). Ethernet rappresenta quindi un punto te dal connettore, e non richiede quindi ulteriori
d’accesso, non solo per comunicare con i disposi- collegamenti per funzionare, tranne quello ad un
tivi collegati nella stessa rete locale, ma anche per cavo di rete Ethernet, che avviene attraverso il
comunicare con dispositivi remoti appartenenti
ad altre reti, ma collegate ad Internet (in pratica
le diverse reti locali possono comunicare tra loro
utilizzando Internet). Per fare questo, oltre all’in-
terfacciamento “fisico” è necessario utilizzare
anche protocolli appositi, quali il TCP/IP o l’UDP.
I protocolli stabiliscono l’organizzazione dei dati
nei pacchetti, e le regole di comunicazione tra i
dispositivi.
FARE ELETTRONICA - MAGGIO 2007

Fino a qualche tempo fa l’interfacciamento fisico


di un microcontrollore a reti Ethernet risultava
piuttosto complesso o laborioso, e richiedeva in
molti casi microcontrollori dedicati.
Recentemente, con l’introduzione da parte della
Microchip dell’integrato chiamato ENC28J60,
questa operazione risulta enormemente più sem-
plice. Il 28J60 infatti è una vera e propria “scheda
di rete” Ethernet, che comunica con il microcon- Figura 1
trollore attraverso una semplice interfaccia SPI. L’Ethernet board di Mikroelektronika
Di Stefano- TEORIA - MikroC 11:- PRATICA 17-04-2007 16:43 Pagina 103

Utilizzo
dell’interfaccia
Ethernet di Antonio Di Stefano

con eventuali byte di riempimento per raggiun-


gere la lunghezza minima di 64 byte totali.
È possibile reperire maggiori informazioni sui pro-
tocolli e sul significato dei vari campi su
Internet (ad esempio su
Wikipedia). Come verrà
mostrato di seguito
comunque non è necessa-
rio conoscerli in dettaglio, 103
dal momento che molte fun-

TEORIA
zioni vengono svolte dalle
librerie.

Figura 2
Collegamento tra la scheda Ethernet e la EasyPIC3
La libreria SPI-Ethernet
La libreria che il MikroC mette a
disposizione per la gestione del 28J60 si chiama
tipico connettore RJ-45. Una volta inizializzato il SPI-Ethernet. Essa comprende diverse funzioni, tra
28J60, il componente gestisce completamente le
funzionalità del livello fisico e MAC (filtraggio dei N. BYTE CONTENUTO
pacchetti in base al loro indirizzo, ritrasmissione 6 Indirizzo MAC desinazione
in caso di errore, controllo della correttezza dei
6 Indirizzo MAC sorgente
pacchetti ricevuti, etc), mostrando al PIC soltanto
2 Tipo Ethernet (0x8000)
i registri di configurazione ed un buffer da 8KB
1 Versione e lunghezza (0x45)
che contiene sia i pacchetti ricevuti, che quelli
che si intende trasmettere. La scrittura e la lettu- 1 Tipo di Servizio (0x00)
ra dei pacchetti dal buffer avviene in modo simi- 2 Lunghezza totale
le a quanto visto a proposito delle EEPROM seria- 2 Identificativo pacchetto
li (si veda FE n.259). Una tipica applicazione quin- 2 Flag e offset frammento
di non deve fare altro che leggere i pacchetti che 1 Time to live (0xFF)
vengono ricevuti, decodificarli e generare gli
1 Protocollo (0x06=TCP, 0x11=UDP)
eventuali pacchetti di risposta. Le ultime due ope-
2 Checksum
razioni dipendono dal protocollo utilizzato. La
struttura di un pacchetto Ethernet che impiega il 4 Indirizzo IP sorgente

protocollo UDP/IP (il più diffuso su Internet per 4 Indirizzo IP destinazione


FARE ELETTRONICA - MAGGIO 2007

applicazioni proprietarie, e per lo streaming mul- 2 Porta sorgente


timediale), è mostrata in Tabella 1. Come si può 2 Porta destinazione
vedere, ciascun livello aggiunge un suo header al 2 Lunghezza
pacchetto, così si ha un header MAC (in verde), 2 Checksum
che contiene per intero il pacchetto e che com-
n Dati
prende un campo alla fine per il controllo di erro-
? Eventuali byte di riempimento
re, un header IP (in rosa), che comprende gli indi-
rizzi IP delle unità che si scambiano il pacchetto 4 CRC32

ed informazioni sul protocollo usato all’interno, Tabella 1


un header UDP (in celeste), ed i dati veri e propri, Struttura di un pacchetto UDP/IP su Ethernet
Di Stefano- TEORIA - MikroC 11:- PRATICA 17-04-2007 16:43 Pagina 104

cui quelle più utili sono le seguenti: lizzate, e la lunghezza dei dati. Le funzioni devono
restituire la lunghezza dell’eventuale risposta gene-
void SPI_Ethernet_Init( rata, oppure 0.
unsigned char *resetPort, Per leggere e scrivere i dati dal buffer del 28J60 si
unsigned char resetBit, usano le due funzioni seguenti:
unsigned char *CSportPtr,
unsigned char CSbit, void SPI_Ethernet_putByte(unsigned char v);
unsigned char *mac, unsigned char SPI_Ethernet_getByte();
unsigned char *ip,
unsigned char fullDuplex); I dati vengono letti o scritti sequenzialmente nel
buffer. L’indirizzo di inizio è quello relativo all’inizio
Il suo scopo è quello di inizializzare il 28J60 e le del campo dati del pacchetto (sia in lettura che
porte del PIC, la funzione andrà quindi invocata scrittura). Nel caso della scrittura del pacchetto, le
prima di utilizzare le altre. I parametri sono rispet- routine provvederanno ad aggiungere gli header
tivamente: la porta e il bit usato per resettare il ed a trasmettere il tutto.
componente, la porta ed il bit usati come CS Nella libreria sono disponibili anche altre funzioni
(segnale di selezione per i dispositivi SPI), i punta- utili, che però non sono pubbliche. Queste posso-
tori agli array da 6 e 4 byte relativi all’indirizzo no comunque essere richiamate includendo il file
MAC ed IP da assegnare al dispositivo, e un flag “enc28j60_libprivate.h”, che contiene le intesta-
che indica se la linea che si sta utilizzando è full- zioni. Tra queste funzioni sono presenti quelle per
104 duplex o half-duplex. La funzione deve essere leggere e scrivere i registri del 28J60 o copiare
richiamata dopo avere richiamato la funzione blocchi di memoria.
TEORIA

SPI_Init, che inizializza l’interfaccia SPI del PIC.


Esempi
void SPI_Ethernet_doPacket(); Risposta al ping
Il codice minimo che è possibile realizzare usando
Questa funzione deve essere richiamata ciclica- le funzioni di libreria è quello che si limita a rispon-
mente (anche con una certa priorità) nel loop prin- dere al ping (pacchetti ICMP Echo Request), e alle
cipale del programma. La sua funzione è quella di richieste di ARP (un protocollo che serve per richie-
controllare se sono stati ricevuti dei pacchetti, dere l’indirizzo MAC di un’unità conoscendo il suo
decodificarli, e trasmettere quelli accodati. Alcuni indirizzo IP). Queste due funzioni sono svolte auto-
tipi di pacchetti sono gestiti automaticamente maticamente dalla funzione SPI_Ethernet_doPacket,
dalla funzione, che provvede a generare e trasmet- e consentono di far riconoscere alla rete la presenza
tere le risposte. Tra questi vanno citati le richieste del dispositivo. Il codice è mostrato nel Listato 1. Il
di ARP e di eco ICMP (ping). Se sono stati ricevuti programma può essere eseguito su qualsiasi PIC
pacchetti UDP o TCP viene automaticamente dotato di interfaccia SPI e almeno 4K di memoria
richiamata una delle due funzioni seguenti (che programma (ad esempio 16F876 o 16F877).
devono essere sempre presenti nel programma,
eventualmente lasciate vuote): #define SPI_Eth_HALFDUPLEX 0
#define SPI_Eth_FULLDUPLEX 1
unsigned int SPI_Ethernet_UserTCP(
unsigned char *remoteHost, // Indirizzo MAC
unsigned int remotePort, unsigned char myMacAddr[6] =
unsigned int localPort, {0x00, 0x14, 0xA5, 0x76, 0x19, 0x3f};
unsigned int reqLength); // Indirizzo IP
unsigned char myIpAddr[4] =
unsigned int SPI_Ethernet_UserUDP( {10, 0, 0, 10};
FARE ELETTRONICA - MAGGIO 2007

unsigned char *remoteHost,


unsigned int remotePort, unsigned int SPI_Ethernet_UserTCP(
unsigned int destPort, unsigned char *remoteHost,
unsigned int reqLength); unsigned int remotePort,
unsigned int localPort,
Queste funzioni non devono essere richiamate dal unsigned int reqLength)
programma, ma vengono richiamate automatica- {
mente dalla funzione vista prima, e con i parame- return 0;
tri già impostati. In questo modo è possibile cono- }
scere subito gli indirizzi del mittente o le porte uti-
Di Stefano- TEORIA - MikroC 11:- PRATICA 17-04-2007 16:43 Pagina 105

unsigned int SPI_Ethernet_UserUDP( Uscita su Display LCD


unsigned char *remoteHost, Per mostrare come può essere gestito un pacchetto
unsigned int remotePort, UDP che trasporta dati utili, è possibile modificare il
unsigned int destPort, contenuto della funzione SPI_Ethernet_UserUDP nel
unsigned int reqLength) codice visto prima, inserendo le righe riportate nel
{ Listato 2. In questo caso i dati trasportati da ogni
return 0; pacchetto UDP vengono estratti e visualizzati su un
} display LCD. Va ricordato che occorrerà inserire nel
main, prima del loop principale, le seguenti righe
per inizializzare il display LCD:
// main entry
void main() Lcd_Init(&PORTB);
{ Lcd_Cmd(LCD_CLEAR);
Spi_Init(); Lcd_Cmd(LCD_CURSOR_OFF);
SPI_Ethernet_Init(
&PORTC, 0, Il display andrà collegato alla porta B del PIC come
&PORTC, 1, mostrato nei precedenti articoli di questa serie.
myMacAddr,
myIpAddr, unsigned int SPI_Ethernet_UserUDP(
SPI_Eth_FULLDUPLEX); unsigned char *remoteHost,
unsigned int remotePort, 105
while(1) { unsigned int destPort,

TEORIA
SPI_Ethernet_doPacket(); unsigned int reqLength)
} {
} unsigned char len = 0;

Come si può vedere il programma è estrema- Lcd_Cmd(LCD_CLEAR);


mente semplice, e apparentemente “vuoto”, nel while(reqLength—)
senso che non si vede esplicitamente la gestione txt[len++]=SPI_Ethernet_getByte();
dei pacchetti. In realtà questa è effettuata dalla
funzione SPI_Ethernet_doPacket, richiamata txt[len]=0;
ciclicamente nel loop principale. Si può notare LCD_Out(2, 1, txt);
che le funzioni SPI_Ethernet_UserTCP e
SPI_Ethernet_UserUDP sono presenti, ma non return(len);
contengono codice (restituiscono semplicemen- }
te 0). La loro presenza è necessaria perché in
caso di ricezione di pacchetti TCP o UDP esse Il contenuto del pacchetto UDP (il solo campo
verranno comunque richiamate dalla funzione dati) viene letto byte a byte con la funzione
SPI_Ethernet_doPacket. La periferica Ethernet SPI_Ethernet_getByte e memorizzato in un array,
seriale è inizializzata indicando l’uso della porta che viene terminato inserendo lo zero alla fine, e
C e dei pin 1 e 0 per il reset ed il CS, passando che viene inviato al display sfruttando la funzione
gli indirizzi MAC ed IP ed il parametro che indica di libreria LCD_Out. Si può notare che il valore
un funzionamento full duplex. Gli indirizzi sono restituito dalla funzione questa volta è proprio la
stati scelti in maniera arbitraria, e seguono deter- lunghezza dei dati ricevuti. Questo ha come effet-
minate convenzioni. to l’invio dello stesso pacchetto UDP (che si trova
L’indirizzo MAC può essere lasciato inalterato, o ancora nel buffer del 28J60) al PC. Questa possibi-
modificato leggermente nelle cifre finali (l’indi- lità, chiamata “echo”, può essere utile per accer-
FARE ELETTRONICA - MAGGIO 2007

rizzo dovrebbe essere unico!), l’IP è stato scelto tarsi che il pacchetto sia stato ricevuto corretta-
in uno dei range tipici usati per le reti locali, e mente dalla scheda. Dal momento che il protocol-
risulta statico (chiaramente anche in questo caso lo UDP non prevede meccanismi per garantire il
deve essere unico e nel formato utilizzato nella recapito dei pacchetti, può essere utile adottare
rete locale). Per testare il programma occorre tecniche di questo tipo.
collegare la scheda ad uno switch o router, a cui
è stato collegato anche un PC. Lanciando il Chiaramente i dati prelevati dal pacchetto possono
comando “ping 10.0.0.10” dalla linea di coman- essere utilizzati in modo arbitrario: ad esempio è
do, si dovrebbe ottenere la risposta da parte possibile includere comandi e dati per modificare
della scheda. lo stato delle porte di I/O.
Di Stefano- TEORIA - MikroC 11:- PRATICA 17-04-2007 16:43 Pagina 106

Accesso diretto ai dati


In qualche caso potrebbe essere utile accedere &PORTC, 1,

direttamente ai registri ed al buffer del 28J60. myMacAddr,

Questo permette di sfruttare caratteristiche non myIpAddr,

gestibili attraverso le librerie o di crearne di nuove 1) ;

(ad esempio in diverse occasioni potrebbe essere


utile spostare arbitrariamente il puntatore di lettu- while(1)

ra o scrittura del buffer). Per fare questo è sufficien- {

te inviare gli appropriati comandi di lettura e scrit- if (PORTB&0x01) {

tura via SPI. Un elenco completo dei comandi e dei // Sel Bank 1

registri dell’ENC28J60 può essere trovato nel data- par=Reg_Read(0x1F);

sheet. Nel Listato 3 è mostrato un esempio di que- par=(par&0xFC)|0x01;

sto approccio, che sfrutta la funzione di libreria sol- Reg_Write(0x5F, par);

tanto per l’inzializzazione del componente, ma


esegue le restanti operazioni manualmente. Il pro- // Numero di pacchetti

gramma consente di leggere il numero di pacchet- pktcnt=Reg_Read(0x19);

ti ricevuti (premendo un pulsante collegato a RB0), Dec2Hex(pktcnt, temp);

e di inviarli singolarmente via seriale al PC. SendString(temp);


SendString(“\r\n”);

// Indirizzo MAC ed IP Delay_ms(1000);

106 unsigned char myMacAddr[6]=


{0x00, 0x14, 0xA5, 0x76, 0x19, 0x3f}; };
TEORIA

unsigned char myIpAddr[4]=


{10, 0, 0, 10}; if (PORTB&0x02) {

unsigned pktcnt; pktcnt=Reg_Read(0x19);


if (pktcnt) {
PORTC=PORTC&0xFD;

void SendString(char *str); // “Read buffer”

void Dec2Hex(char n, char *str); Spi_Write(0x3A);

char Reg_Read(char addr); for(i=0; i<6; i++) {

void Reg_Write(char addr, char val); buf[i]=Spi_Read(temp);


Dec2Hex(buf[i], temp);

// *** main entry *** SendString(temp);

void main() }

{ len=buf[2];

int i; len=len|(buf[3]<<8);

unsigned len; for(i=0; i<len; i++) {

char par, buf[32], temp[5]; buf[6]=Spi_Read(temp);


Dec2Hex(buf[6], temp);

// Init. porte SendString(temp);

ADCON1 |= 0x0F; }

PORTA = 0;
TRISA = 0x00; if (len&0x01) Spi_Read(temp);
PORTC=PORTC|0x02;

PORTB = 0;
TRISB = 0x0F; // Dec Pktcnt
Reg_Write(0x9E, 0x40);
FARE ELETTRONICA - MAGGIO 2007

i=0; }

temp[2]=’ ‘; SendString(“\n\r”);

temp[3]=’ ‘; Delay_ms(1000);

temp[4]=0; };
}

Usart_Init(9600); }

Spi_Init();
SPI_Ethernet_Init( void SendString(char *str)

&PORTC, 0, {
Di Stefano- TEORIA - MikroC 11:- PRATICA 17-04-2007 16:43 Pagina 107

Figura 3
Dump di un pacchetto UDP. I colori si riferiscono alle sezioni mostrate in Tabella 1, la parte gialla è quella aggiunta dal 28J60

Il programma inizializza il 28J60 e attende la pres-


int n; sione di uno dei due pulsanti (RB0 o RB1). Nel frat-
n=0; tempo il componente riceve e memorizza nel suo
while(str[n]) { buffer tutti i pacchetti indirizzati al suo indirizzo
Usart_Write(str[n]); MAC o all’indirizzo di broadcast
n++; (FF:FF:FF:FF:FF:FF). Alla pressione del pulsante RB0
} viene letto il numero di pacchetti ricevuti che viene
} inviato alla UART. Premendo il pulsante RB1 viene
letto il contenuto del buffer dall’indirizzo relativo al
void Dec2Hex(char n, char *str) primo pacchetto ricevuto. I primi 6 byte sono inse-
{ riti dall’ENC28J60 e contengono informazioni sul
char c; pacchetto: 2 byte costituiscono il puntatore al pac-
chetto successivo, 2 contengono la sua lunghezza, 107
c=(n&0xF0)>>4; e gli altri 2 che forniscono informazioni sullo stato

TEORIA
if (c<10) e sugli eventuali errori. Letti i primi 6 byte, viene
c=c+’0’; estratta l‘informazione sulla lunghezza, ed utilizza-
else ta per leggere l’esatto numero di byte, che sono
c=c+’A’-10; inviati alla UART, dopo essere stati convertiti in esa-
str[0]=c; decimale e ASCII. Se il numero di byte è dispari
occorre fare una lettura aggiuntiva in modo che il
c=n&0x0F; puntatore si trovi ad un indirizzo pari (che sarà
if (c<10) quello di inizio del prossimo pacchetto). Il conte-
c=c+’0’; nuto di uno dei pacchetti ricevuti è visibile in
else Figura 3.
c=c+’A’-10;
str[1]=c; Conclusioni
Va ricordato che il MikroC ha un utile “UDP
} Terminal” che può essere richiamato dal menu
Tools. Questo programma permette di inviare pac-
void Reg_Write(char addr, char val) chetti UDP dal contenuto arbitrario e di riceverli,
{ può essere quindi utilizzato per provare i program-
PORTC=PORTC&0xFD; mi descritti. Occorre tenere presente inoltre che
Spi_Write(addr); potrebbe essere necessario modificare l’indirizzo IP
Spi_Write(val); usato nei programmi in base all’intervallo utilizza-
PORTC=PORTC|0x02; to nella propria rete locale. Un intervallo alternati-
} vo a quello riportato, utilizzato da molti router, è
{192, 168, 20, x}, dove x è un indirizzo unico asse-
char Reg_Read(char addr) gnato al dispositivo.
{ Si conclude con questa puntata la serie di articoli
FARE ELETTRONICA - MAGGIO 2007

char k=0; dedicati al MikroC. Mi auguro che gli argomenti


trattati abbiano suscitato la curiosità dei lettori
PORTC=PORTC&0xFD; verso questo ambiente di sviluppo e possano avere
Spi_Write(addr); fornito un utile introduzione alla programmazione
k=Spi_Read(&k); in C dei microcontrollori.
PORTC=PORTC|0x02;

return k; Codice MIP 263102


} www.farelettronica.com/mip