Sei sulla pagina 1di 80

TEORIA

Prima parte n 252 - Giugno 2006

RISORSE

SPECIALE

PRATICA

Lambiente di sviluppo
Seconda parte n 253/254 - Luglio/Agosto 2006

Controllare le uscite del PIC


Terza parte n 255 - Settembre 2006

MikroC: By Example:
Dal momento che su Fare Elettronica si gi parlato estesamente sia dei PIC che del linguaggio C, non dedicheremo molto spazio alla teoria, ma ci concentreremo soprattutto sugli aspetti pratici, sulle applicazioni e sugli esempi. Inizieremo con dei programmi molto semplici, per cui anche chi si fosse perso qualcosa, non dovrebbe avere alcun problema a seguirci (alla fine dellarticolo comunque verranno indicati alcuni riferimenti bibliografici per recuperare). IL MIKROC Il MikroC un ambiente di sviluppo prodotto dalla Mikroelektronika, societ jugoslava molto conosciuta per altri suoi prodotti di successo quali il MkroBasic o le schede di sviluppo EasyPIC. Il MikroC in particolare un compilatore che stato specificamente pensato per i microcontrollori PIC, ed quindi in grado di gestire tutte le caratteristiche peculiari dei vari modelli e produrre codice ottimizzato per questi dispositivi. In realt definirlo compilatore C piuttosto restrittivo, dal momento che il MikroC integra anche una grande quantit di strumenti ausiliari, sia per la scrittura del codice, sia per soddisfare molte delle esigenze che sorgono quando si programmano sistemi a microcontrollore. Altro aspetto molto interessante la presenza di una completa documentazione interattiva ed di una nutrita serie di funzioni di libreria pronte per essere utilizzate nelle applicazioni. Grazie a queste caratteristiche, utilizzando il MikroC possibile scrivere applicazioni complesse in pochissimo tempo, come verr mostrato in questa serie di articoli. Nel prossimi paragrafi analizzeremo in dettaglio le principali caratteristiche dellambiente di sviluppo. Il compilatore Il MikroC comprende un compilatore ANSI C che implementa praticamente tutti gli aspetti

Gestione di pulsanti, tastiere e display LCD

D
80
Teoria
FARE ELETTRONICA - GIUGNO 2006

opo il notevole successo riscosso dalla serie di articoli sul MikroBasic, vogliamo presentarvi, a partire da questa puntata, il MikroC: un ambiente di sviluppo, in ANSI C per PICmicro prodotto della MikroElektronika, simile al MikroBasic per semplicit di utilizzo ed efficienza. In questa puntata conosceremo gli strumenti messi a disposizione dal programma ed inizieremo ad usarlo con alcuni semplici esempi. Nelle prossime puntate utilizzeremo il MikroC per realizzare progetti sempre pi complessi ed interessanti.

Quello che ci proponiamo di fare a partire da questa puntata mostrare come realizzare una serie di interessanti applicazioni utilizzando i microcontrollori PIC ed il linguaggio C. Lambiente di sviluppo che utilizzeremo sar il MikroC v5.0, che inizieremo a conoscere in tutte le sue caratteristiche gi dal prossimo paragrafo. Com noto il linguaggio C, al pari di altri linguaggi ad alto livello (Basic o Pascal), permette di scrivere programmi complessi in maniera semplice e veloce, per rispetto a questi produce un codice mediamente pi compatto e veloce, simile a quello che si otterrebbe programmando in assembler. Il C quindi permette di sfruttare meglio le caratteristiche del microcontrollore, e di controllare pi accuratamente il suo funzionamento e le sue periferiche.

Lambiente di sviluppo
standard del linguaggio, e che stato appositamente pensato per i microcontrollori PIC (tutti i modelli della serie 12, 16 e 18). Il compilatore in grado di generare dal codice C direttamente il codice oggetto (cio i file .hex caricabili sul PIC) o anche codice assembler leggibile, ed eventualmente modificabile dallutente. Il compilatore supporta tutti i tipi di dati previsti dallANSI C, sia quelli interi che quelli floating point, utilizzando per questi ultimi un formato a 32 bit compatibile con lo standard IEEE-754. In questultima versione possibile richiamare le funzioni in maniera annidata e ricorsiva (nei limiti consentiti dallo stack del PIC). Per rendere pi efficiente il codice generato, il MikroC applica una serie di ottimizzazione automatiche in fase di compilazione. Oltre agli aspetti standard del linguaggio, il compilatore dotato di alcune estensioni dedicate, utili per interagire con i PIC. Ad esempio possibile gestire in maniera molto semplice le routine dinterruzione o la posizione delle variabili allinterno della memoria.

di Antonio Di Stefano

Figura 1 Lambiente di sviluppo MikroC

Lambiente di sviluppo La Figura 1 mostra unimmagine panoramica del ricco ambiente di sviluppo (IDE) offerto dal MikroC. Nonostante labbondanza di strumenti, scrivere e compilare un programma molto semplice. Larea pi grande visibile in figura quella riservata alla scrittura del codice (Code Editor). Il codice scritto viene automaticamente colorato per distinguerne i diversi elementi (istruzioni, costanti, commenti, tipi, etc.). Questo facilita molto la lettura e la comprensione del codice, e pu evidenziare la presenza di eventuali errori di scrittura. Esiste anche una funzione personalizzabile che corregge gli errori di digitazione direttamente mentre si scrive il testo (!). Altre funzioni utili sono quelle che permettono di commentare o de-commentare le sezioni di codice evidenziato, o di indentarlo. Due strumenti utili mentre si scrive il codice sono il Code Assistant, ed il Parameter Assistant: due piccole finestre pop-up che servono a ricercare o a completare automaticamente il nome delle funzioni che si vuole richiamare, o i loro parametri, evitando di consultare continuamente la guida o altri punti del codice. Sulla sinistra (Figura 2) si trova il Code Explorer, dove vengono riassunti tutti gli elementi inseriti nel codice: funzioni, etichette, costanti, e variabili con il loro valore e ambito di visibilit. Cliccando su una qualsiasi delle voci verr evidenziato nel codice la sua posizione. Questo risulta

81
Teoria
FARE ELETTRONICA - GIUGNO 2006

TEORIA
Prima parte

RISORSE

SPECIALE

PRATICA

MikroC By Example: Lambiente di sviluppo

utilissimo sia per trovare velocemente gli elementi nel codice, sia per evitare errori nelluso delle variabili globali, o con le ri-definizioni e linizializzazione delle variabili. Il tab posto accanto, denominato Qhelp, mostra un elenco di tutte le funzioni di libreria integrate nel compilatore (sia quelle standard del C che le librerie per le funzioni avanzate). Cliccando su una delle voci si aprir la guida in cui la funzione viene descritta in dettaglio, spesso anche con degli esempi di utilizzo. Il piccolo riquadro sottostante riassume le propriet del progetto corrente: il modello di PIC da utilizzare, la sua frequenza di lavoro

(usata in fase di simulazione e per determinare il valore di alcune costanti), e lelenco dei files che compongono il progetto. In basso trova posto la finestra dei messaggi, in cui vengono visualizzati i risultati di compilazione e gli eventuali messaggi di errore. Nella stessa zona presente un pratico convertitore di dati decimali / binari / esadecimali, chiamato Qconvertor. I tool aggiuntivi Come accennato prima, il MikroC offre una grande quantit di strumenti aggiuntivi oltre a quelli strettamente indispensabili per scrivere il codice e compilarlo. Questi tool possono aiutare moltissimo nella fase di creazione dei programmi e nei test, e risulta molto comodo 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 memoria (Fig. 3). E possibile tenere sotto controllo loccupazione della ROM (o Flash) e della RAM del PIC, lo spazio occupato dalle diverse funzioni del programma e la loro distribuzione in memoria. Analizzando questi dati possibile in modo molto rapido capire come ottimizzare il codice dal punto di vista delloccupazione di memoria. Gli altri strumenti, dai pi semplici a quelli pi

82
Teoria

Figura 2 Il Code Explorer

FARE ELETTRONICA - GIUGNO 2006

Figura 3 Strumenti di analisi delloccupazione di memoria

sofisticati, sono richiamabili dal menu Tools. Tra quelli pi semplici abbiamo una sempre utile tabella ASCII ed un editor per caratteri a 7 segmenti (Fig. 4), che permette di disegnare il carattere voluto, ed ottenere il valore numerico corrispondente ai segmenti accesi e spenti. Tra gli strumenti pi sofisticati spiccano lUSART Terminal (Fig. 5) che pu essere utilizzato per comunicare via seriale (RS232) con un qualsiasi dispositivo o sistema esterno. E possibile gestire la comunicazione impostando tutte le consuete opzioni, inviare i dati (digitandoli) e visualizzare in tempo reale quelli ricevuti dal dispositivo esterno. Esistono

Figura 4 Editor per display a 7 segmenti

anche altri tipi di terminali, che hanno un utilizzo simile, ma ambiti diversi: lHID Terminal, usato per comunicare con dispositivi USB HID e che ingloba anche una funzione per la creazione dei descrittori USB (la classe HID utilizzata da dispositivi come joysick, mouse o tastiere, ma utilizzabile anche per trasmettere in maniera semplice dati ad un PC via USB); lUDP Terminal permette di scambiare dati utilizzando una connessione di rete ed il protocollo UDP. LMMC Card Terminal invece utilizzato per leggere blocchi di dati da una memoria tipo Multimedia Card (MMC) o Secure Digital (SD). Come vedremo la presenza di questi strumenti dovuta al fatto che il MikroC offre delle funzioni di libreria per interfacciarsi a questi tipi di dispositivi. 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 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 documentazione del MikroC, che scaricabile dal sito del produttore. Una prima classe di funzioni sono quelle per la gestione dellhardware del PIC. In questa classe sono comprese funzioni per la gestione della UART (sia hardware che emulata via software), del generatore PWM e del convertitore Analogico/Digitale. Un'altra classe implementa algoritmi per la gestione di hardware esterno comune: tasti, display a LED, display LCD e display grafici, tastiere (a matrice o anche PS2), generazione di suoni, EEPROM seriali, Flash, etc. Unaltra classe ancora permette di gestire diversi 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). Sono anche presenti delle librerie con funzioni

83
Teoria

Figura 5 LUSART Terminal

FARE ELETTRONICA - GIUGNO 2006

Figura 6 Editor per display grafici

TEORIA
Prima parte

RISORSE

SPECIALE

PRATICA

MikroC By Example: Lambiente di sviluppo

che facilitano la conversione tra tipi diversi (es. da numero float a stringa e viceversa), ed alcune operazioni trigonometriche. Il debugger Il MikroC comprende anche un source code debugger cio un simulatore che permette di eseguire il codice creato sul PC, per capire se si comporta come voluto. Questo strumento molto importante per eseguire un primo test del programma o di alcune sue routine. Il funzionamento molto semplice: una volta compilato il programma, possibile eseguirlo (anche in modalit passo-passo), ed osservare il suo comportamento. Lesecuzione del programma pu essere seguita sia sulla finestra del codice, sia su delle finestre aggiuntive che mostrano il contenuto della RAM, quello dei registri interni e dello stack, e sulla finestra del clock, che pu essere usata per valutare il tempo di esecuzione delle routine. I valori della RAM e dei registri possono essere modificati dallutente durante lesecuzione, modificando quindi il comportamento del programma. Questo risulta indispensabile in alcuni casi, in quanto il debugger non simula in dettaglio il comportamento di alcuni dei circuiti interni del PIC, come ad esempio i timer, gli ADC, le interruzioni, etc. Per facilitare losservazione dei valori assunti in particolari punti del programma, si possono piazzare dei breakpoint, che interrompono momentaneamente lesecuzione.

84
Teoria

IL PRIMO PROGETTO Per prendere confidenza con lambiente di sviluppo iniziamo con un progetto molto semplice, il classico Hello world! del mondo dei microcontrollori: faremo lampeggiare un LED. Il programma molto semplice e si presta bene a essere usato come cavia per le prime sperimentazioni: una volta capito come fare lampeggiare un LED non difficile creare pattern via via pi complessi, iniziare a giocherellare con le altre periferiche, e creare programmi molto pi vari ed interessanti. Lo schema elettrico che utilizzeremo quello mostrato in Figura 7: il PIC collegato nel modo usuale allalimentazione, ad un quarzo da 4MHz, e sul primo piedino della porta B stato collegato un LED con una resistenza per limitare la corrente. Va notato che useremo un PIC 16F84, ma questo particolare potrebbe essere del tutto irrilevante: un programma scritto in C infatti, a differenza di uno scritto in assembler praticamente indipendente dal microcontrollore usato! Se scriviamo bene il nostro codice, con piccole modifiche esso potr funzionare non solo su diversi modelli di PIC, ma anche su diversi tipi di microcontrollori o processori (ad esempio AVR, 8051, ARM, etc.). Questo uno dei maggiori vantaggi offerti dal C. Per creare il nostro primo progetto iniziamo 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 prodotti, una breve descrizione del progetto (in generale documentare i progetti importante!). Scegliamo come dispositivo un PIC16F84 ed una frequenza di clock di 4MHz. Dalle opzioni sottostanti possibile modificare i fuses del dispositivo, in questo caso usiamo il pulsante Default per selezionare quelli predefiniti (oscillatore HS, e Watchdog disabilitato). Dopo avere dato conferma, potremmo scrivere nellapposita finestra il seguente codice:

FARE ELETTRONICA - GIUGNO 2006

Figura 7 Schema usato per fare lampeggiare il LED

TEORIA
Prima parte

RISORSE

SPECIALE

PRATICA

MikroC By Example: Lambiente di sviluppo

void main() { PORTB = 0; TRISB = 0; while(1) { PORTB = 1; Delay_ms(500); PORTB = 0; Delay_ms(500); } }

86
Teoria

Il codice non scritto in modo molto elegante, per utile per capire come funziona: la funzione main quella che verr eseguita allavvio, allinizio viene assegnato il valore 0 a PORTB (che indica il valore assunto dai piedini della porta B del PIC), e TRISB (che determina la direzione dei pin, 0 indica che sono stati configurati tutti come uscite). A questo punto inizia un ciclo infinito realizzato con listruzione while. Dentro al ciclo viene assegnato il valore 1 a PORTB, cio viene portato alto il piedino RB0. Quindi viene richiamata una routine che genera un ritardo di 500ms (mezzo secondo), viene portato basso il piedino RB0, e vengono attesi altri 500ms, quindi il loop si ripete. A questo punto utile una piccola spiegazione: per capire su quale piedino della porta B agiamo, dobbiamo considerare lespressione binaria del numero che abbiamo utilizzato, ciascun bit rappresenta ordinatamente un piedino della porta. Se avessimo voluto accendere i pin RB0 ed RB1 assieme (valore binario 00000011), avremmo dovuto usare il valore decimale 3. Per facilitare questo tipo di operazioni il MikroC riconosce anche i numeri scritti direttamente in binario,

basta farli precedere da 0b (in questo caso ad esempio potremmo scrivere 0b00000011). possibile anche usare il Qconvertor posto in basso per eseguire questo tipo di conversioni. Una piccola nota importante: tutti i programmi che scriveremo saranno sempre racchiusi da, o termineranno con, un loop infinito (eventualmente vuoto). Questo necessario perch altrimenti, una volta terminata la funzione main, il programma si trover in una condizione indefinita: potrebbe bloccare il microcontrollore, compiere delle azioni imprevedibili, o ripetersi in modo non corretto. Chi conosce il C o ha gi usato altri compilatori, avr notato che non stato necessario includere nessuna libreria: il MikroC infatti provvede automaticamente a includere le definizioni relative al PIC usato (come PORTB e TRISB), e diverse funzioni di libreria. Per compilare il nostro programma, usiamo il pulsante Build Project (quello con gli ingranaggi in alto), o lequivalente voce del menu Project. Il report di compilazione sar visibile nella finestra in basso. Se diamo unocchiata alle statistiche sulloccupazione di memoria vediamo che il nostro programmino occupa 74 word nella Flash del PIC. Proviamo a scriverlo in un modo un po pi ottimizzato:
void main() { PORTB = 0; TRISB = 0; while(1) { PORTB = PORTB^0b00000001; Delay_ms(500); } }

Figura 8 Finestra per limmissione dei dati del progetto

Per ottenere il comportamento intermittente abbiamo usato loperatore XOR del C (laccento circonflesso ^). LXOR ha leffetto di invertire i bit della variabile in corrispondenza dei bit 1 indicati nelloperando, in questo caso quindi invertiamo solo il primo bit, quello corrispondente a RB0. Alla seconda inversione, il bit torna come prima, ottenendo leffetto intermittente. Il programma quindi funzioner esattamente

FARE ELETTRONICA - GIUGNO 2006

come prima. Quanto occupa il nostro programma questa volta? Soltanto 42 word! Questo risultato dovuto sia alla maggiore compattezza del programma, sia al fatto che Delay_ms una macro, non una funzione, quindi prima veniva effettivamente copiata due volte nel programma! E se volessimo far lampeggiare due led, uno su RB0 e laltro su RB1, in modo alternato? (Schema in Figura 9). Ecco come modificare il programma:
void main() { PORTB = 0b00000010; TRISB = 0; while(1) { PORTB = PORTB^0b00000011; Delay_ms(500); } }

RIFERIMENTI Il MikroC pu essere scaricato dal sito Internet del produttore allindirizzo: www.mikroelektronika.co.yu Maggiori informazioni sui PIC possono essere trovate sul corso PIC By Example pubblicato su Fare Elettronica (numeri da 157 a 176) e sul CDROM omonimo, o sul libro della serie Conoscere ed usare dal titolo PICmicro di Maurizio Del Corso e Tiziano Galizia, edito dalla Inware. Maggiori informazioni sul linguaggio C si possono trovare nella serie di articoli Vitamina C pubblicato su Fare Elettronica (numeri da 217 a 246), o sul CD-ROM omonimo, o sul libro della serie Conoscere ed usare dal titolo Il Linguaggio ANSI C di Antonio Di Stefano, edito dalla Inware Edizioni. Alcune spiegazioni pi dettagliate sugli algoritmi e sui circuiti utilizzati in alcuni degli esempi (compresa la scheda EasyPIC) si possono trovare nel tutorial sul MikroBasic, pubblicato su Fare Elettronica (dal numero 238). CONCLUSIONI Nella prossima puntata considereremo degli esempi un po pi complessi, che partendo da quelli appena visti ne espanderanno molto le possibilit utilizzando istruzioni e costrutti pi potenti, per gestire il flusso del programma e le temporizzazioni. Non mancate! Codice MIP252080

87
Teoria

Abbiamo modificato soltanto due parametri: il valore iniziale (allinizio un LED sar gi acceso, laltro spento), ed abbiamo fatto in modo che venissero invertiti entrambi i bit meno significativi.

Se vogliamo provare i nostri programmi con il debugger, possiamo richiamare la voce Start Debugger dal menu Run, ed usare le opzioni di step (stesso men, oppure tasti F7 ed F8). Se invece vogliamo provare il codice direttamente sul circuito vero e proprio, dopo la compilazione avremo a disposizione il file .hex da caricare nel PIC. Potremo usare lo stesso MikroC se stiamo usando una delle schede EasyPIC (il download avviene via USB!), oppure potremo usare un programma esterno qualsiasi (come il noto IC-Prog) ed il nostro programmatore preferito. I LED dovrebbero iniziare a lampeggiare immediataFigura 9 Schema per laccensione di due LED alternati mente, come voluto.

FARE ELETTRONICA - GIUGNO 2006

TEORIA
Prima parte n 252 - Giugno 2006

RISORSE

SPECIALE

PRATICA

Lambiente di sviluppo
Secondo parte n 253/254 - Luglio/Agosto 2006

Controllare le uscite del PIC


Terza parte n 255 - Settembre 2006

Gestione di pulsanti, tastiere e display LCD

MikroC by Example:
siderare, dal momento che ciascuna di esse pu adattarsi meglio a specifiche applicazioni. Supponiamo ad esempio di utilizzare 8 LED, ciascuno collegato ad un piedino della porta B come mostrato in figura 2, e di volerli accendere in sequenza nelle due direzioni (il classico effetto supercar). Le alternative pi convenienti sono due: in un caso possiamo utilizzare gli operatori del C che permettono di manipolare i bit, nellaltro possiamo utilizzare una tabella di consultazione (look-up table). Consideriamo il primo caso, cio partiamo da

I
104
Teoria

n questa puntata scopriremo come scrivere in C degli algoritmi dedicati alla gestione delle uscite dei PIC, per comandare LED, display ed altri dispositivi, utilizzando le tecniche pi efficienti e gli strumenti messi a disposizione dal MikroC.

GENERAZIONE DI PATTERN E SEQUENZE Nella scorsa puntata abbiamo mostrato il codice 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() { PORTB = 0; TRISB = 0; while(1) { PORTB = PORTB^0b00000001; Delay_ms(500); }
FARE ELETTRONICA - LUGLIO/AGOSTO 2006

Figura 1 Schema usato per fare lampeggiare il LED

Il programma molto semplice: utilizza solamente unoperazione di XOR (loperatore ^ del C) per ottenere linversione 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 combinazioni pi complesse (come avviene in molte applicazioni reali) questo approccio potrebbe non essere adeguato. Esistono diverse possibilit che il caso di con-

Figura 2 Schema usato per accendere i LED in sequenza

Controllare le uscite del PIC


di Antonio Di Stefano

una configurazione delle uscite, ed otteniamo le altre operando sui bit. Il codice il seguente:
void main() { char dir; /* Direzione di scorrimento 1=sinistra, 0=destra */ dir=1; // Inizializz. porta B PORTB = 0x01; TRISB = 0; // Loop infinito while(1) { // Raggiunti bordi? if (PORTB&0x01) dir=1; if (PORTB&0x80) dir=0; // Aggiornamento direzione if (dir) { PORTB = PORTB << 1; } else { PORTB = PORTB >> 1; } Delay_ms(100); } }

porta B sono state configurate tutte come uscite (TRISB=0), e soltanto il primo piedino stato posto alto (PORTB=0x01). Ricordiamo che il MikroC permette di esprimere i valori numerici anche in binario (anteponendo 0b al numero), ma abbiamo preferito utilizzare la notazione esadecimale per rendere il codice maggiormente portabile. Per convertire questi valori in binario si pu utilizzare il convertitore Qconvertor, che si trova in basso nella finestra del messaggi del MikroC. Allinterno del loop principale viene dapprima verificato se il LED attualmente acceso il primo o lultimo, e in ciascuno di questi due casi viene cambiata la direzione dello scorrimento. Per eseguire questo controllo stata utilizzata listruzione if del C: solo se la condizione dentro le parentesi verificata lespressione che segue vene eseguita. Lespressione che viene controllata lAND binario tra i bit della porta B e 0x01 (oppure 0x08). Questa espressione dar 0x01 solo se il bit meno significativo della porta B sar alto (lo stesso per il pi significativo nel caso di 0x80), altrimenti zero. Per maggiore chiarezza avremmo potuto scrivere:
if ((PORTB&0x01)==0x01) dir=1; if ((PORTB&0x80)==0x80) dir=0;

105
Teoria
FARE ELETTRONICA - LUGLIO/AGOSTO 2006

Il funzionamento del programma dovrebbe risultare abbastanza intuitivo: inizialmente viene dichiarata una variabile (dir) per tenere traccia della direzione di scorrimento, in seguito vengono inizializzate le variabili e le porte, e poi viene eseguito il loop principale. Durante il funzionamento la variabile dir assumer soltanto due valori (scelti da noi), quindi conveniente dichiararla del tipo pi piccolo possibile gestito dal C, cio char (8 bit). Le linee della

ma ricordiamo che il C considera vera qualsiasi espressione che non valga 0, quindi anche non indicando il valore atteso otteniamo lo stesso risultato. Abbiamo usato lo stesso accorgimento per il frammento di codice successivo, che quello che si occupa dellaggiornamento della posizione. Se la variabile dir vale 1 viene eseguito uno scorrimento binario di una posizione a sinistra (a destra nellimmagine), altrimenti (else) viene eseguito uno scorrimento a destra (a sinistra nellimmagine). Gli scorrimenti (operatori << e >> del C) produrranno la sequenza di valori: 0x01, 0x02, 0x04, 0x08,

TEORIA
Seconda parte

RISORSE

SPECIALE

PRATICA

MikroC by Example: Controllare le uscite del PIC

0x10, 0x80, e poi a ritroso (quando cambier la direzione di scorrimento) fino a 0x01. Dopo ogni scorrimento attendiamo 100ms usando lapposita routine di libreria Delay_ms, messa a disposizione dal MikroC, e ripetiamo lintera routine, allinfinito. Consideriamo adesso un altro approccio per ottenere lo stesso risultato. In questo caso utilizzeremo una look-up table per memorizzare le configurazioni delle uscite che vogliamo ottenere. Il codice il seguente:
void main() { char n=0; char leds[14]={0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02}; // Inizializz. porta B TRISB = 0; // Loop infinito while(1) { PORTB=leds[n]; n++; if (n==14) n=0; Delay_ms(100); } }

106
Teoria

dichiarazione dellarray. Inoltre abbiamo dichiarato unaltra variabile (n) che verr utilizzata come contatore, cio come indice per scorrere la tabella. Nel loop principale assegniamo direttamente il valore corrente della tabella alla porta B, inoltre incrementiamo lindice n (con listruzione n++). Quando lindice raggiunger il valore 14, sar riportato a 0, e quindi il conteggio ripartir da questo valore. Notare la differenza tra == ed = nellistruzione if. Ricordiamo che il primo un operatore relazionale, che controlla se il valore dellespressione di sinistra uguale a quello dellespressione di destra (restituendo un valore di verit). Laltro operatore invece assegna un valore ad una variabile. Questa seconda versione del programma, sebbene richieda un po pi di memoria, ha un vantaggio importante: basta cambiare i valori in tabella per ottenere comportamenti diversi. Si provi ad esempio ad utilizzare questi valori:
char leds[14]={0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42};

FARE ELETTRONICA - LUGLIO/AGOSTO 2006

Per creare la tabella di consultazione abbiamo utilizzato un array (leds[]), in cui abbiamo inserito manualmente le configurazioni delle uscite volute (che sono le stesse che abbiamo ottenuto col programma precedente). Ci occorrono soltanto 14 voci per la tabella, che sono state inizializzate direttamente nella

Ci saranno due LED accesi che si muoveranno in direzione opposta (basta convertire in binario i valori per rendersene conto). Lapproccio usato nel primo programma invece risulta pi flessibile quando le configurazioni utilizzate non sono sempre le stesse, ma variano nel tempo, magari in base a condizioni esterne. PILOTARE I DISPLAY A 7 SEGMENTI I display a 7 segmenti rappresentano un economico e funzionale dispositivo di visualizzazione. 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
Seconda parte

RISORSE

SPECIALE

PRATICA

MikroC by Example: Controllare le uscite del PIC

108
Teoria

lizzarli. Un display a 7 segmenti composto da 8 LED disposti a formare 7 segmenti pi un punto decimale (non sempre presente), con cui possibile rappresentare numeri, lettere e simboli grafici. I display a 7 segmenti sono disponibili in due versioni, che differiscono per i collegamenti dei LED: in entrambi i casi i LED possono essere pilotati indipendentemente da un terminale, mentre laltro comune a tutti. Il terminale comune pu essere lanodo o il catodo dei LED (e da questo particolare deriva il nome del display). Quando il terminale comune il catodo (figura 3), questultimo sar collegato a massa, e per accendere un segmento sar sufficiente fornire unalimentazione positiva al rispettivo anodo (ad esempio utilizzando un piedino di uscita portato a livello logico alto). Nel caso di display ad anodo comune (figura 4), questo terminale verr collegato al positivo dellalimentazione, e per accendere un segmento sar necessario portare a massa il catodo del segmento che si vuole accendere (usando un piedino di uscita a livello logico basso). Per pilotare un display a 7 segmenti avremo bisogno quindi di almeno 8 uscite del PIC, e con queste potremo gestire soltanto un carattere. Per fare le prime prove colleghiamo il display al PIC come mostrato in figura 5 (stiamo quindi utilizzando un display a catodo comune): i segmenti verranno pilotati tramite i piedini della porta B. Possiamo quindi procedere come negli esempi visti fino ad ora, lunico problema che dobbiamo ricavare per ogni carattere che vogliamo visualizzare il corrispondente valore da inviare alla porta B (che esprime quali segmenti sono accesi e quali spenti). Per fare questo possiamo utilizzare lap-

posito strumento messo a disposizione dal MikroC, richiamabile selezionando la voce Seven Segment Convertor dal menu Tools (figura 6). sufficiente disegnare il carattere che vogliamo visualizzare per avere il valore numerico corrispondente ad uno dei due tipi di display. Nel nostro caso ricaveremo il valore per le cifre da 0 a 9 e per le lettere da A, b, c, d, E ed F (che ci potrebbero servire per rappresentare valori esadecimali). Un modo molto semplice per associare questi caratteri ai valori numerici consiste nellutilizzare un array. Vediamo quindi come realizzare un programma che conta continuamente da 0 a 9:
void main() { char n=0; char segs[16]={0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71}; // Inizializz. porta B TRISB = 0; // Loop infinito while(1) { PORTB=segs[n]; n++; if (n==10) n=0; Delay_ms(1000); } }

Figura 5 Collegamento al display a 7 segmenti

Come si pu notare il programma quasi identico a quello visto precedentemente, sono stati modificati soltanto i valori contenuti nellarray, che in questo caso codificano i caratteri voluti. Se si estende il conteggio fino a 16 sar possibile visualizzare anche le cifre Figura 6 Il convertitore per display esadecimali da A a 7-seg F. Non possiamo

FARE ELETTRONICA - LUGLIO/AGOSTO 2006

superare 16 perch altrimenti leggeremmo una posizione dellarray che non esiste, ottenendo un risultato imprevedibile. E se volessimo utilizzare pi cifre, e quindi pi unit a 7 segmenti? Occorrerebbe utilizzare molte pi porte di I/O, oppure ricorrere alla tecnica del multiplexing, molto utilizzata in questi casi. Occorre collegare i display come mostrato in figura 7 ed utilizzare soltanto 4 linee di I/O aggiuntive (questa volta provenienti dalla porta A) per gestire le 4 cifre. Il funzionamento semplice: le linee della porta B saranno condivise da tutti i display, che quindi riceveranno gli stessi dati, ma soltanto un display alla volta sar acceso, comandando attraverso la porta A i transistor BJT sui cui collettori si trovano i display. In questo modo riusciamo a comandare individualmente i singoli display: se vogliamo visualizzare un carattere sul display pi a sinistra, dovremo inviare il codice del carattere sulla porta B e portare alto soltanto RA0. Ovviamente soltanto un display alla volta potr essere acceso. Occorre quindi accenderli in successione ad una velocit tale da non farne percepire allocchio lo spegnimento. Per ottenere questo effetto sufficiente accendere ogni cifra almeno ogni 20ms. Dal momento che le cifre sono 4, dovremo tenerle accese singolarmente meno di 5ms. Per evitare un effetto di sfarfallamento possiamo anche usare una velocit maggiore (senza esagerare, altrimenti otterremo una bassa luminosit). Riassumendo: Sulla porta B dovranno essere inviati ciclicamente i valori corrispondenti alle 4 cifre che vogliamo visualizzare. Sulle porta A dovr essere portata alta una sola linea alla volta, quella corrispondente alla cifra da visualizzare (avremo quindi ciclicamente 0b0001, 0b0010, 0b0100, 0b1000). Il codice che implementa questo comportamento il seguente:
void main() { char n, ds; char segs[16]={0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71};

char cifra[4]={1,2,3,4}; // Inizializz. porte TRISA = 0; TRISB = 0; // Loop infinito while(1) { ds = 1; for(n=0; n<4; n++) { PORTB = segs[cifra[n]]; PORTA = ds; Delay_ms(2); ds = ds << 1; } } }

109
Teoria
FARE ELETTRONICA - LUGLIO/AGOSTO 2006

Il codice somiglia molto a quello visto in precedenza, le uniche differenze riguardano lutilizzo di un altro array (cifra[]), che contiene le cifre da visualizzare nei 4 display, e limpiego della porta A per selezionare il display. Il loop principale contiene un ciclo for, che viene ripetuto per 4 volte (una per ogni cifra), al cui interno viene impostato il valore della cifra corrente sulla porta B, ed aggiornato il contenuto della porta A. In pratica allinizio la porta A conterr il valore 0b0001, alla porta B viene assegnato il valore adatto a rappresentare la prima cifra, si attende 2ms, al valore della porta A viene applicato uno scorrimento a sinistra di un bit, ottenendo 0b0010, ed il ciclo for riprende con la seconda iterazione, in cui viene assegnato alla porta B il codice per la seconda cifra. Si prosegue cos fino alla quarta, e quindi si esce dal ciclo for, ma non dal loop principale, che si ripeter allinfinito. Sul display dovrebbero apparire le cifre 1234 indicate nellarray cifra. A questo punto potremo pensare di sfruttare il display realizzato per implementare delle funzioni utili. Se ad esempio riuscissimo a visualizzare il valore di una qualsiasi variabile, potremmo usare il display come un comodo dispositivo di output (sia nelle applicazioni, sia in fase di debug). Per fare questo possiamo utilizzare alcune funzioni messe a disposizione dalle librerie del MikroC, ed in particolare quelle che permettono di creare delle stringhe a partire dal valore numerico di una variabile. Una di queste funzioni la seguente:

TEORIA
Seconda parte

RISORSE

SPECIALE

PRATICA

MikroC by Example: Controllare le uscite del PIC

void ShortToStr(short number, char *output);

TRISA = 0; TRISB = 0; // Conversione ShortToStr(val, cifra); // Loop infinito while(1) { ds = 1; for(n=0; n<4; n++) { PORTB=segs[cifra[n]-48]; if (cifra[n]==32)PORTB=0x00; if (cifra[n]==45)PORTB=0x40; PORTA = ds; Delay_ms(2); ds = ds << 1; } } }

La funzione prende in ingresso una variabile di tipo short, ed un puntatore ad un array di 4 caratteri (come il nostro cifra[]), e restituisce nellarray di caratteri una stringa corrispondente al numero indicato come primo parametro. Ad esempio:
short t = -24; char txt[4]; ShortToStr(t, txt);

110
Teoria

In questo caso larray txt[] conterr i seguenti 4 caratteri 24 (il primo carattere uno spazio). Esistono anche le funzioni IntToStr, WordToStr, FloatToStr, che lavorano nello stesso modo, ma usano tipi di variabili diversi e restituiscono un numero maggiore di caratteri. Possiamo utilizzare questa funzione con il codice fino ad ora scritto? Si, ma dobbiamo apportare qualche piccola modifica. La funzione infatti restituisce una stringa, i cui valori sono codificati in ASCII. Se diamo unocchiata alla tabella dei codici ASCII (menu Tools/ASCII Chart) vediamo che i codici che ci vengono restituiti vanno dal 48 al 57 (decimale) per i caratteri numerici, ma comprendono anche il 32 dello spazio, ed il 45 del segno . Se escludiamo i due segni non numerici, i codici dei caratteri numerici possono essere ottenuti semplicemente sottraendo 48 al codice ASCII (o anche considerando solo i 4 bit meno significativi, cio eseguendo un AND con 0x0F). Il codice diventa quindi il seguente:
void main() { char n, ds, val; char segs[16]={0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71}; char cifra[4]; val=-47; // Inizializz. porte

FARE ELETTRONICA - LUGLIO/AGOSTO 2006

In pratica stata aggiunta soltanto la variabile var, la chiamata alla funzione ShortToStr, ed alcuni controlli nel loop che visualizza i dati. In particolare allinizio visualizzata la cifra (ottenuta dal codice ASCII meno 48), poi viene verificato se il carattere uno spazio o un segno meno, ed in questi casi viene corretto loutput sulla porta B con i caratteri corrispondenti (tutti i segmenti spenti nel caso dello spazio, o solo quello centrale acceso nel caso del meno). Poi viene assegnato il valore appropriato alla porta A, come accadeva nellesempio precedente. A questo punto possiamo visualizzare il contenuto di qualsiasi variabile del nostro programma. Unultima nota interessante: non abbiamo mai utilizzato il punto decimale presente nei display. Se lo volessimo accendere in una qualsiasi delle quattro cifre, sarebbe sufficiente attivare il bit pi significativo della porta B. Questo pu essere ottenuto eseguendo un OR (operatore | del C) tra il codice del carattere attualmente visualizzato ed il valore binario 0b10000000 (cio 0x80 esadecimale):
if (punto) PORTB=PORTB|0x80;

TIMER ED INTERRUZIONI Il precedente esempio ha mostrato come sia possibile utilizzare un display a 7 segmenti per visualizzare il contenuto di una variabile. Come abbia-

mo visto per ottenere una buona visualizzazione delle 4 cifre necessario rispettare delle precise temporizzazioni. Risulta quindi piuttosto difficile aggiungere altre funzioni al programma visto prima, perch qualsiasi altro compito inserito nel loop principale altererebbe le temporizzazioni del display, e risulterebbe a sua volta rallentato dai tempi di attesa utilizzati per il display. Per risolvere questo problema necessario separare e rendere indipendenti le temporizzazioni e le funzioni di gestione del display dalla normale esecuzione del programma: questo pu essere ottenuto utilizzando il timer del PIC e le interruzioni. Il meccanismo molto semplice: il PIC ha un timer ad 8 bit (chiamato Timer0 o TMR0) che conta da 0 ad 0xFF (in totale 256 valori), ed incrementato ad ogni ciclo macchina (4 cicli di clock) o suoi multipli. Quando il valore del timer supera 0xFF, pu essere generata uninterruzione. Utilizzando il timer quindi possibile richiamare unapposita funzione (la routine dinterruzione) a intervalli di tempo

prestabiliti. Nel nostro caso il codice allinterno della routine dovr aggiornare il display un digit alla volta. Occorrer quindi impostare il timer ed il suo prescaler in modo da generare un interrupt ogni 2ms. Per abilitare le interruzioni associate al TMR0 dobbiamo impostare ad 1 i bit GIE e T0IE del registro INTCON, ed abilitare il prescaler col fattore di divisione voluto sul registro OPTION_REG (per maggiori dettagli si veda il datasheet del PIC o il tutorial PIC By Example gi pubblicato su Fare Elettronica). Se abbiamo una frequenza di clock di 4MHz, possiamo ottenere uninterruzione ogni 2ms usando un fattore di divisione pari a 8 (infatti 4*256*8/4MHz=2ms). Dopo avere impostato il corretto valore nei registri, dobbiamo scrivere la routine di gestione delle interruzioni. Per fare questo sar sufficiente creare una funzione chiamata interrupt, il MikroC la riconoscer come routine di gestione delle interruzioni, e la richiamer automaticamente in caso di interruzioni. Il codice risultante il seguente:

111
Teoria

Software MikroC
Un potente compilatore C per PICmicro
Code Editor Code Explorer 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
FARE ELETTRONICA - LUGLIO/AGOSTO 2006

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

TEORIA
Seconda parte

RISORSE

SPECIALE

PRATICA

MikroC by Example: Controllare le uscite del PIC

char n, ds, cifra[4]; char segs[16]={0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71}; void main() { short val; // Reset del timer // Inizializz. porte TRISA = 0; TRISB = 0; } n=0; ds=1; TMR0 = 0; // Reset flag TMR0IF/TMR0IE INTCON = 0x20; } n++; ds <<= 1; if (n > 3) { n = 0; ds = 1;

112
Teoria

val=-125; ShortToStr(val, cifra); OPTION_REG TMR0 INTCON while(1) { } } = 0x82; = 0; = 0xA0;

void interrupt() { PORTA=ds; PORTB=segs[cifra[n]-48]; if (cifra[n]==32) PORTB=0x00; if (cifra[n]==45) PORTB=0x40;

Le differenze rispetto alla versione precedente sono essenzialmente 3. Innanzi tutto alcune delle variabili utilizzate prima sono state dichiarate fuori dalla funzione main, quindi risultano essere delle variabili globali: tutte le funzioni possono leggerle e modificarle. Questo risulta utile dal momento che devono essere inizializzate o modificate allinterno della funzione main, ma devono essere lette ed utilizzate dentro la routine dinterruzione, inoltre il loro valore deve essere mantenuto anche quando si esce dalle rispettive funzioni. In questo modo allinterno del loop principale si potr assegnare un valore allarray cifra, e la routine dinterruzione provveder a visualizzarlo indipendentemente. La funzione main quasi vuota: contiene solamente le istruzioni di inizializzazione dei 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 solamente il verificarsi delle interruzioni. La routine dinterruzione invece svolge le stesse funzioni viste prima, solo che opera su una cifra alla volta: aggiorna il valore di PORTA e PORTB come nel programma precedente, modifica il valore di n e ds in modo da puntare al prossimo carattere, e prima di terminare resetta il valore del timer e del suo flag dinterruzione. Non necessario utilizzare la funzione di ritardo dal momento che i valori impostati sulle porte saranno mantenuti fino alla prossima interruzione, che si verificher dopo circa 2ms. A questo punto sar possibile eseguire altre operazioni nel loop principale e modificare come si vuole larray cifra[], ed il display sar aggiornato di conseguenza ed in maniera automatica. Ad esempio si pu realizzare un conteggio inserendo nel loop il seguente codice:

while(1) { ShortToStr(val, cifra); Delay_ms(250); val++; }

Notare che in questo caso il ritardo di 250ms inserito non influisce completamente sullaggornamento del display, che continua a funzionare con le sue temporizzazioni! CONCLUSIONI Abbiamo visto alcuni esempi abbastanza utili e versatili, in questo caso applicati al pilotaggio di LED e display a 7 segmenti, ma la cui struttura pu essere applicata anche ad altri tipi di dispositivi esterni (rel, motori, buzzer). Nella prossima puntata verranno presentate le routine in C per la gestione di dispositivi di input quali pulsanti, tastiere di varo tipo, e display LCD intelligenti. Codice MIP 253104

113
Teoria

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

TEORIA
Secondo parte n 253/254 - Luglio/Agosto 2006

RISORSE

SPECIALE

PRATICA

Controllare le uscite del PIC


Terza parte n 255 - Settembre 2006

Gestione di pulsanti, tastiere e display LCD


Quarta parte n 256 - Ottobre 2006

MikroC by Example:
pull-down poste tra i pulsanti e massa servono per fissare a livello basso i rispettivi piedini di I/O quando i pulsanti non sono premuti (in caso contrario il potenziale dei pin potrebbe essere facilmente influenzato da interferenze esterne). Utilizzeremo il pulsante posto su RB1 per accendere il LED e quello posto su RB2 per spegnerlo. Il codice C che implementa questo comportamento il seguente:
void main() { // Inizaliz. porta B TRISB = 0x06; PORTB = 0;

Generazione di segnali PWM

I
88
Teoria

n questa puntata scopriremo come gestire con il MikroC alcuni semplici ma fondamentali dispositivi di input collegati al PIC, quali pulsanti e tastiere. Vedremo inoltre come utilizzare un display LDC intelligente per visualizzare le informazioni

I PULSANTI Moltissime applicazioni richiedono lutilizzo di interruttori o di pulsanti per rendere possibile linterazione tra utente (o ambiente) e programma. La gestione di questi dispositivi potrebbe sembrare a prima vista molto semplice. In realt occorre tenere in considerazione molti aspetti un po nascosti che potrebbero portare a risultati del tutto inaccettabili. Per capire quali sono questi problemi consideriamo il circuito mostrato in figura 1. Abbiamo utilizzato un PIC16F84 collegando alla porta B due pulsanti ed un LED. Le resistenze di

// Loop infinito while(1) { if (PORTB&0x02) PORTB = 1; if (PORTB&0x04) PORTB = 0; } }

Figura 1 Accensione e spegnimento di un LED

Come si pu vedere, per leggere lo stato dei due pulsanti i piedini corrispondenti della porta B sono stati configurati come ingressi, scrivendo il valore corrispondente nel registro TRISB (il valore 0x06 corrisponde al valore binario 0b00000110, in cui gli 1 rappresentano i pin configurati come ingressi). Per controllare se i pulsanti sono premuti viene testato il bit corrispondente della porta B con listruzione if, e viene assegnato il valore corrispondente alle uscite. Tutto funziona senza problemi, ma forse non come si pu immaginare. Per capire meglio quello che succede proviamo a fare in modo che alla pressione di uno dei due tasti il LED venga accesso, ed alla successiva pressione venga spento. Il codice il seguente:

FARE ELETTRONICA - SETTEMBRE 2006

Gestione di pulsanti, tastiere e display LCD


void main() { // Inizaliz. porta B TRISB = 0x06; PORTB = 0; // Loop infinito while(1) { if (PORTB&0x06) PORTB = PORTB^0x01; } }

di Antonio Di Stefano

Se carichiamo il programma sul PIC e proviamo a premere un tasto, potremo osservare un comportamento diverso da quello atteso: il LED si accender e si spegner in continuazione mentre il tasto rimane premuto, ad una velocit tale da non vederne neanche il lampeggio, e sar impossibile fermarlo in uno stato prestabilito. Qual il problema? Dobbiamo ricordarci che lesecuzione del programma sul PIC estremamente pi veloce di qualsiasi nostra azione, di conseguenza, anche premendo brevemente il pulsante, il LED verr acceso e spento migliaia di volte! Un primo accorgimento per evitare queste ripetizioni consiste nellattendere che il pulsante venga rilasciato prima di ripetere il ciclo. In questo modo dovremo effettivamente premere due volte il pulsante per accenderlo e spegnerlo. Il codice il seguente:
void main() { // Inizaliz. porta B TRISB = 0x06; PORTB = 0; // Loop infinito while(1) { if (PORTB&0x06) PORTB = PORTB^0x01; // Attesa rilascio while(PORTB&0x06) {}; } }

unistruzione while che ripete in continuazione un loop vuoto fino a quando il tasto risulta premuto, al rilascio del pulsante si uscir dallistruzione e verr ripetuto il loop principale. Ora il programma dovrebbe funzionare meglio, ma si possono notare ancora delle incertezze nella risposta. Queste imprecisioni sono dovute al rimbalzo meccanico dei pulsanti, che si verifica alla pressione del tasto e genera un segnale che pu oscillare diverse volte tra livello alto e livello basso prima di stabilizzarsi. Queste oscillazioni durano tipicamente alcuni millisecondi e possono variare a seconda dalle caratteristiche meccaniche del pulsante. Vediamo come affrontare questo problema considerando un circuito (figura 2) ed un programma un po pi completo. Nel circuito un display a 7 segmenti collegato alla porta B del PIC, mentre due pulsanti sono collegati alla porta A. Quello che vogliamo ottenere che ad ogni pressione del pulsante S1 il numero sul display venga incrementato di uno, mentre ad ogni pressione di S2 il conteggio venga decrementato. Ecco il codice:
void main() { signed char cont=0; char segs[16]={0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71}; // Inizializz. porte TRISA = 0x03; TRISB = 0x00; PORTA = 0x08; // Loop infinito while(1) { // Aggiorna display PORTB=segs[cont]; // Controlla polsanti

89
Teoria
FARE ELETTRONICA - SETTEMBRE 2006

Per attendere il rilascio del tasto stata inserita

TEORIA
Terza parte

RISORSE

SPECIALE

PRATICA

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

if (PORTA&0x02) cont++; if (PORTA&0x01) cont--; // Correzione contatore if (cont>9) cont=0; if (cont<0) cont=9; // Attende rilascio while(PORTA&0x03) {}; } }

Il problema di questo approccio (attesa rilascio + ritardo) che occorre letteralmente fermare il programma per qualche istante, ritardando sia tutti gli altri compiti, sia la risposta stessa del sistema alla pressione del tasto. Questultimo effetto pu essere particolarmente fastidioso, soprattutto nelle interfacce utente. Vedremo pi avanti una soluzione pi sofisticata ed elegante a questo problema. TASTIERE A MATRICE In alcune applicazioni pu essere richiesta luso di una testiera dotata di un discreto numero di tasti (circa 10 o 20). Ovviamente non conveniente utilizzare un pin di I/O per ciascun tasto, si pu invece adottare una tecnica simile a quella utilizzata per i display a 7 segmenti: indirizzare separatamente le righe e le colonne della tastiera. In questo modo possibile gestire 16 tasti utilizzando soltanto 8 I/O! Lo schema quello mostrato in figura 3, ed comunemente utilizzato in diversi dispositivi commerciali. I piedini da RB0 a RB3 sono configurati come uscite, e pilotano le colonne della tastiera. I pin da RB4 a RB7 sono configurati come ingressi e vengono utilizzati per leggere il valore presente sulle 4 righe. Quando i tasti vengono premuti, cortocircuitano una riga con una colonna. Dal momento che le uscite vengono attivate una alla volta (come accadeva con i display a 7 segmenti), leggendo il valore delle righe possibile sapere quale tasto premuto. Ad esempio, allinizio 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 lettura di una tastiera potrebbe sembrare unimpresa 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 serve per selezionare la porta

90
Teoria

La gestione del display a 7 segmenti stata trattata nella scorsa puntata, ed il codice utilizzato praticamente lo stesso. Le due istruzioni if verificano se uno dei due pulsanti premuto e aggiornano la variabile cont di conseguenza. Listruzione while seguente attende che i pulsanti vengano rilasciati (il controllo viene fatto su entrambi, per questo stato utilizzato il valore 0x03 nellAND). Infine viene corretto il valore del conteggio se questo supera 9 o scende sotto 0 (per questo motivo la variabile di conteggio stata dichiarata come signed char). Provando il programma si pu notare lo stesso difetto visto prima: ad ogni pressione si possono avere variazioni del conteggio maggiori di 1. Occorre infatti risolvere ancora il problema dei rimbalzi. Un metodo banale per fare questo consiste nellinserire unattesa di qualche decina di millisecondi alla fine del loop, come segue:
Delay_ms(50); } }

FARE ELETTRONICA - SETTEMBRE 2006

Figura 2 Conteggio up-down su display a 7 segmenti

del PIC che dovr gestire la tastiera, la seconda restituisce un numero da 1 a 16 se uno dei tasti stato premuto, 0 altrimenti, mentre la terza esegue la stessa funzione, ma blocca lesecuzione del programma fino a quando un tasto non stato premuto e rilasciato. Un esempio di utilizzo il seguente:
// Tastiera su porta B Keypad_Init(&PORTB); // Se premuto un tasto c=Keypad_Read(); if (c) { } // Oppure: // attende la pressione c=Keypad_Release();

TASTIERE PS/2 Se abbiamo bisogno di un maggior numero di tasti e vogliamo utilizzare un minor numero di pin di I/O possibile adottare una soluzione molto pratica: possiamo collegare al PIC una tastiera PS/2, del tipo utilizzato di solito per i PC. Sono sufficienti due linee di I/O: una per i dati seriali, laltro per il clock. Ad ogni pressione di un tasto la tastiera invier il suo scan code, sotto forma di una stringa di 11 bit, in cui saranno presenti un bit di start, 8 bit di dati, un bit di parit dispari, ed un bit di stop. Se il tasto viene tenuto premuto, la tastiera invier di nuovo il dato ogni 100ms circa. Quando il tasto viene rilasciato la tastiera invier un codice 0xF0, seguito dallo scan code del tasto. I tasti speciali (tasti F1-F12, tasti cursore, etc.) sono preceduti da un codice 0xE0. Anche in questo caso non sar necessario scrivere a mano le routine di gestione, perch il MikroC mette a disposizione delle funzioni gi pronte che rendono estremamente semplice

91
Teoria

Compilatore MikroC
Un potente compilatore C per PICmicro
Code Editor Code Explorer Debugger Statistiche
Tutto in un ambiente Windows facile ed intuitivo
Un set di strumenti veramente indispensabili per sviluppare applicazioni con i PICmicro
Codice MIP 255091
FARE ELETTRONICA - SETTEMBRE 2006

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

TEORIA
Terza parte

RISORSE

SPECIALE

PRATICA

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

luso di questi dispositivi. Le funzioni che utilizzeremo sono Ps2_Init e Ps2_Key_Read. Queste funzioni restituiscono le informazioni sui tasti premuti, e gestiscono anche la conversione da scan code ad ASCII. Per effettuare una prima prova consideriamo lo schema di figura 4, in cui un display a 7 segmenti collegato alla porta B del PIC, mentre la tastiera collegata alle linee RA0 ed RA1 della porta A. Il programma che segue legge i dati inviati dalla tastiera e visualizza sul display il carattere corrispondente se questo un numero oppure una lettera da A ad F.

92
Teoria

void main() { unsigned short key, special, down; char tasto=0; char segs[16]={0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71}; // Inizializz. TRISB = 0x00; // Inizializz. PS/2 Ps2_Init(&PORTA); // Attende inizializz. Delay_ms(100); TRISA = 0; PORTA = 15; // Loop infinito while(1) { // Legge tastiera e aggiorna carattere if (Ps2_Key_Read(&key, &special, &down)) { if (down) { if ((key>='0')&&(key<='9')) tasto=key-'0'; if ((key>='a')&&(key<='f'))

identica a quella vista in precedenza, le uniche differenze risiedono nella chiamata alle due funzioni che gestiscono la tastiera. Inizialmente stata richiamata la funzione di inizializzazione, indicando che la tastiera collocata sulla porta A. Allinterno del loop principale viene richiamata la funzione Ps2_Key_Read. Questa funzione restituisce 1 se stato premuto o rilasciato un tasto, oppure 0 in caso contrario. Nel primo caso le informazioni sul tasto saranno contenute nelle tre variabili, che vengono passate per riferimento (e che quindi vengono modificate dalla funzione). La variabile key contiene il codice ASCII del tasto premuto o rilasciato, la variabile special vale 1 se stato premuto un tasto speciale (e che quindi key non contiene un valido codice ASCII). La variabile down vale 1 se il tasto stato premuto, o 0 se stato rilasciato. Nel programma viene verificato prima di tutto se si verificata la pressione di un tasto (e non un rilascio), ed in seguito viene fatto un controllo sul valore ASCII del tasto: se questo corrisponde ad un valore numerico o ad una lettera dalla A alla F (sia minuscole che maiuscole), viene generato il carattere corrispondente da visualizzare sul display. Come si pu vedere il funzionamento di queste routine molto semplice e si presta ad essere utilizzato in molte applicazioni. DISPLAY LCD INTELLIGENTI I display LCD intelligenti sono unaltra categoria di dispositivi molto comodi e versatili. Essi permettono infatti di visualizzare testo e numeri disposti su un certo numero di righe e di colonne. La programmazione di questi dispositivi non

FARE ELETTRONICA - SETTEMBRE 2006

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

La parte del programma che gestisce il display

Figura 3 Schema di una tastiera a matrice

complicatissima, ma pu risultare sicuramente un tantino laboriosa. Anche in questo caso il MikroC ci viene incontro fornendoci delle funzioni di libreria gi pronte che permettono di utilizzare in modo molto semplice ed intuitivo questo tipo di display. Le funzioni permettono di visualizzare sul display delle stringhe di testo o dei caratteri, indicandone le coordinate iniziali se desiderato. Vediamo subito un semplicissimo esempio, che applicato allo schema di figura 5, visualizza sul display la stringa Fare Elettronica! disposta su due righe:
void main() { char *testo1 = Fare; char *testo2 = Elettronica!; TRISB = 0x00; Lcd_Init(&PORTB); Lcd_Cmd(LCD_CLEAR); Lcd_Out(1,1, testo1); Lcd_Out(2,5, testo2); while(1) {} }

2006
MILANO 20-23 SETTEMBRE

32a BIENNALE INTERNAZIONALE DELLAUTOMAZIONE, STRUMENTAZIONE, MICROELETTRONICA E ICT PER LINDUSTRIA

Registrati on-line allindirizzo www.fieremostre.it/prebs e riceverai unemail che, presentata alle reception dei Padiglioni 11-9 e 18 di Fiera Milano, ti permetter di ritirare la tessera gratuita di accesso

Dapprima viene inizializzato il display sulla porta B, in seguito viene inviato un comando per pulire lo schermo (i comandi disponibili sono parecchi, e si rimanda al manuale del MikroC per un elenco completo), ed infine vengono stampate le due stringhe, la prima nella posizione iniziale (in alto a sinistra), la seconda nella riga sottostante, spostata di 5 caratteri dal bordo sinistro. Mettendo assieme quello che abbiamo imparato fino ad ora, possiamo realizzare unapplicazione leggermente pi complessa, che permette 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 programma che ci permetta di scrivere sul display tramite la tastiera, come avviene di solito sullo schermo di un normale computer. Lo schema da considerare quello di figura 5, a cui la tastiera pu essere collegata alla porta A del PIC, come visto in figura 4.
void main() { char x, y; unsigned short key, special, down;

Siamo presenti al

padiglione 9 stand S15


e abbiamo riservato un omaggio per i nostri abbonati

ACCORRETE NUMEROSI!

TEORIA
Terza parte

RISORSE

SPECIALE

PRATICA

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

// Inizializz. TRISB = 0x00; // Inizializz. display Lcd_Init(&PORTB); // Inizializz. PS/2 Ps2_Init(&PORTA); // Attende inizializz. Delay_ms(100); Lcd_Cmd(LCD_CLEAR); Lcd_Cmd(LCD_UNDERLINE_ON); x=1; y=1; // Loop principale while(1) { // Legge tastiera e visualizza if (Ps2_Key_Read(&key, &special, &down)) { if (down) { if (!special) { // Testo if ((key>31)&&(key<128)) { Lcd_Chr(y,x,key); x++; } } else { if (key==30) x--; if (key==31) x++; if (key==32) y=1; if (key==33) y=2; x=1; y=1; Lcd_Cmd(LCD_CLEAR); } } // Aggiornamento posizione if (x>16) x=16; if (x<1) x=1;
FARE ELETTRONICA - SETTEMBRE 2006

94
Teoria

// Avanza cursore

// Cursore sx // Cursore dx // Cursore su // Cursore gi

e viene attivato il cursore tramite gli appositi comandi, poi ha inizio il loop principale, in cui si verifica se c stata attivit sulla tastiera, se stato premuto un tasto (variabile down=1), se si tratta di un tasto alfanumerico o meno (variabile special). Il carattere viene quindi inviato al display oppure gestito opportunamente se corrisponde ad un tasto speciale. Come si pu vedere le coordinate del cursore vengono gestire dal programma, questo permette di evitare che il cursore possa superare larea visibile del display. I caratteri sono stampati con la funzione Lcd_Chr, che prende in ingresso le coordinate (riga e colonna), ed il codice ASCII del carattere da stampare, che in questo caso quello restituito dalla tastiera. Tra i tasti speciali quelli riconosciuti e gestiti sono i tasti cursore, che permettono di spostarsi nelle quattro direzioni, ed il tasto Enter, che viene utilizzato per cancellare il testo visualizzato. Per evitare che il cursore esca dallarea visualizzata (larga 2 righe di 16 caratteri), sono presenti degli appositi controlli che limitano le coordinate x ed y del cursore. La funzione Lcd_Out, che di solito serve per visualizzare una stringa in una data posizione, qui usata soltanto per aggiornare la posizione del cursore (infatti visualizza una stringa vuota). ANTIRIBALZO CON INTERRUZIONI Come stato accennato prima, implementare la funzione di antirimbalzo ed antiripetizione (o ripetizione ad una velocit controllata) pu causare qualche inconveniente, se non si progetta bene il tutto. Esistono molte buone soluzioni a questo problema, e di seguito ne mostreremo una che pu essere facilmente adattata alle diverse esigenze e ad un numero di pulsanti qualsiasi. Lidea quella di utilizzare le interruzioni, seguendo la stessa filosofia che abbiamo usato per gestire i display a 7 segmenti di 4 cifre: generare uninterruzione 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 pulsanti cambiato, in caso affermativo iniziamo a contare da zero, e continuiamo fintantoch lo stato rimane uguale. In questo modo sapremo che se il contatore raggiunge una certa soglia lo stato dei pul-

if (key==13) { // Enter: cancella

// Aggiorna posiz. cursore Lcd_Out(y,x,""); } } } }

Come si pu vedere il programma un mix di quelli presentati precedentemente. Il display stato assegnato alla porta B mentre la tastiera alla porta A. Inizialmente il display viene ripulito

Figura 4 Collegamento di una tastiera PS/2 al PIC

santi si stabilizzato, e quindi possiamo considerare il loro valore valido. Una volta utilizzato il valore dei pulsanti, azzeriamo il contatore in modo da prepararci alla successiva pressione. La nostra routine in

realt fa anche di meglio: gestisce anche il ritardo con cui aggiornare il valore quando il pulsante rimane premuto! Infatti il valore caricato nel contatore differente a seconda che siano verificati dei cambiamenti 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 rimane premuto, adatta quindi ai tempi di reazione dellutente. Il codice che segue mostra la tecnica descritta applicata allo schema di figura 2, in cui i pulsanti controllano lincremento o il decremento del numero visualizzato sul display a 7 segmenti.

95
Teoria
FARE ELETTRONICA - SETTEMBRE 2006

Codice MIP 255095

TEORIA
Terza parte

RISORSE

SPECIALE

PRATICA

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

char tasti, tpress; void main() { char cont=0; char segs[16]={0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71}; // Inizializz. porte TRISA = 0x03; PORTA = 0x08; TRISB = 0x00; // Inizializz. registri OPTION_REG TMR0 INTCON while(1) { // Aggiorna display PORTB=segs[cont]; // Controlla polsanti if (tpress>200) { if (tasti&0x02) cont++; } } = 0x82; = 0; = 0xA0; } } }

if (tasti&0x04) cont--; tpress=0;

// Correzione contatore if (cont>9) cont=0; if (cont<0) cont=9;

void interrupt() { if (tasti==PORTA) { if (tpress<250) tpress++; } else { tpress=180; tasti=PORTA; // Reset timer e flag TMR0 = 0; INTCON = 0x20;

96
Teoria

// Loop principale

Figura 5 Collegamento del display LCD al PIC

Rispetto al codice mostrato prima le differenze sono minime, e consistono nellimpiego della routine dinterruzione e delle variabili tpress, che viene usata come contatore per lo stato dei pulsanti, e la variabile tasti, che in pratica una copia della porta A. Come si pu vedere quando viene rilevata una transizione (variabile tasti diversa dal valore di PORTA), il valore del contatori riparte da 180 e non 0, quindi per raggiungere la soglia (200) impiegher minor tempo rispetto a quando il valore viene utilizzato (tptress riportato a 0). Va notato che la routine pu essere facilmente

FARE ELETTRONICA - SETTEMBRE 2006

estesa a pi pulsanti su pi porte, e pu essere integrata con la routine di gestione del display vista nella scorsa puntata (o con altre che richiedano simili tempistiche). CONCLUSIONI Nella prossima puntata vedremo come generare con il PIC ed il MikroC dei segnali PWM e come utilizzarli per delle interessanti applicazioni. Vedremo anche come generare del suoni e melodie con il PIC. Non mancate! Codice MIP 255088

TEORIA
Terza parte n 255 - Settembre 2006

MHZ

RISORSE

SPECIALE

PRATICA

Gestione di pulsanti, tastiere e display LCD


Quarta parte n 256 - Ottobre 2006

Generazione di segnali PWM


Quinta parte n 257 - Novembre 2006

MikroC by Example:
questa energia verr dissipata (o utilizzata) dal dispositivo stesso. L'energia media su un periodo sar quindi funzione del rapporto tra la durata del livello alto e quella del livello basso (questo rapporto chiamato "duty cycle"). Se vogliamo ottenere un valore "medio" stabile occorrer che il dispositivo collegato sia "sufficientemente lento", in modo da eseguire proprio l'operazione di "media" nel tempo. Nel caso di un microcontrollore (o altro dispositivo digitale) potremo ottenere un valore di tensione analogico compreso tra circa 0V e 5V (o comunque la tensione del livello logico alto), semplicemente generando un'onda quadra e variando il duty cycle dallo 0% al 100%. Come gi detto necessario che il circuito collegato a valle abbia un comportamento "passa basso", in modo da rispondere con sufficiente lentezza. Se questa condizione non verificata, basta aggiungere un filtro passa basso passivo come mostrato in seguito. Ma come si genera un segnale PWM? Il metodo classico illustrato in Figura 1. utile comprendere questo metodo perch sar quello che utilizzeremo negli esempi seguenti (implementato via software o hardware). Si utilizza un contatore che viene costantemente incrementato, ed un valore di soglia: se il valore del contatore inferiore alla soglia, l'uscita sar a livello alto, se il contatore maggiore o uguale, l'uscita sar posta a livello basso. Per valori bassi della soglia si otterranno quindi duty cycle bassi, e viceversa. Nei prossimi paragrafi vedremo come implementare questo meccanismo in C, utilizzando diverse tecniche.

Realizzazione di un timer digitale programmabile

L
92
Teoria
FARE ELETTRONICA - OTTOBRE 2006

a modulazione PWM permette di generare e gestire segnali analogici utilizzando le normali uscite digitali del microcontrollore. In questo articolo verranno descritte diverse tecniche per generare segnali PWM utilizzano i PIC ed il MikroC.

INTRODUZIONE Quella di generare o controllare segnali analogici utilizzando un microcontrollore un'esigenza abbastanza frequente. In molti casi infatti un'informazione binaria tipo acceso/spento non sufficiente, ma occorrere un segnale analogico vero e proprio al cui livello possa essere associata un'informazione. Quasi tutti i microcontrollori mettono a disposizione dei convertitori Analogico/Digitali, ma quasi nessuno incorpora la funzione opposta. Una soluzione un po drastica (dal punto di vista dei costi) potrebbe essere quella di utilizzare un convertitore Digitale/Analogico esterno. Per fortuna nella maggior parte di applicazioni non occorre arrivare a tanto, ed possibile utilizzare i dispositivi messi a disposizione dal micro per ottenere questa funzione. Per fare questo si ricorrere infatti alla modulazione PWM (Pulse Width Modulation, ossia modulazione a larghezza di impulsi), che consente appunto di generare dei segnali analogici partendo direttamente da segnali digitali. L'idea la seguente: si utilizza un'onda quadra, di solito a frequenza costante, scegliendo opportunamente il tempo in cui essa si mantiene a livello alto in ciascun periodo. Questo tempo quello in cui in ogni ciclo effettivamente forniamo energia al dispositivo collegato, nel tempo rimanente

Figura 1 Generazione di un segnale PWM

Generazione di segnali PWM


di Antonio Di Stefano

PWM CON CODICE SEQUENZIALE Il codice riportato di seguito mostra com' possibile implementare in C un semplice generatore di segnale PWM. In questo caso il segnale viene costruito letteralmente come spiegato prima: viene usata una variabile per implementare il contatore, una per la soglia ed una condizione che aggiorna il livello dell'uscita. Questo approccio ha il vantaggio di essere utilizzabile su qualsiasi PIC, anche i pi semplici, in quanto non si ricorre ad alcuna periferica hardware specifica, ed possibile avere l'uscita su qualsiasi piedino di I/O. Il segnale PWM generato viene utilizzato nello schema di Figura 2 per fare lampeggiare un LED con una variazione graduale della luminosit. Ci si potrebbe chiedere come mai non sia stato usato un filtro passa basso. In effetti il LED un dispositivo abbastanza veloce, e non esegue affatto la media del segnale, ma ne segue fedelmente il valore. Tuttavia il risultato che si ottiene buono, perch la media eseguita dal nostro occhio, che ha un tempo di risposta di circa un ventesimo di secondo! Il codice riportato di seguito.
void main() { unsigned char cont, level, i; TRISB = 0; PORTB = 0; cont=0; level=0;

cont++; i++; if (i==200) { level++; i=0; } } }

93
Teoria
FARE ELETTRONICA - OTTOBRE 2006

La variabile cont usata come contatore, ed quindi incrementata ad ogni iterazione. Dal momento che ampia 8 bit, raggiunto il limite di 255 torner automaticamente a zero. La variabile level usata come soglia. Se avessimo assegnato un valore fisso, avremo potuto ottenere un valore stabile di luminosit del LED. In questo caso invece il valore della soglia variato progressivamente, in modo da avere un duty cycle che inizia dallo 0% (level=0) al 100% (level=255). La soglia incrementata ogni 200 iterazioni, in modo da rendere visibile la variazione, per fare questo stata utilizzata la variabile i, che serve proprio per contare quante volte viene eseguito il loop. Un paio di considerazioni importanti: quanti livelli di luminosit possiamo ottenere? Ben 256, cio tanti quanti sono i livelli del contatore PWM. Qual invece la frequenza della nostra onda PWM? Qui la risposta meno ovvia: per completare un

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

TEORIA
Quarta parte

MHZ

RISORSE

SPECIALE

PRATICA

MikroC by Example: Generazione di segnali PWM

94
Teoria

periodo necessario che il contatore esaurisca il conteggio, quindi serviranno ben 256 iterazioni del loop per completare un solo periodo! Supponendo di utilizzare un PIC funzionante a 4MHz, possiamo misurare quanto tempo impieghiamo per eseguire un'iterazione del loop utilizzando il Debugger del MikroC, facendolo funzionare in modalit step, e osservando il tempo di esecuzione riportato in basso. Il risultato 16us, quindi 16us*256=4096us, cio ben 4ms (244Hz)! Giusto in tempo per non percepire un fastidioso sfarfallioQuesto semplice calcolo mette in luce un problema tipico del PWM: se vogliamo ottenere una risoluzione alta, dobbiamo accontentarci di frequenze basse, a meno di non "contare" a velocit molto elevate (cosa non sempre possibile o conveniente). PWM CON INTERRUZIONI Uno dei problemi del codice presentato prima che il periodo dell'onda PWM dipende dalla quantit delle istruzioni che sono presenti nel loop principale. Ovviamente in programmi completi il loop sar affollatissimo di istruzioni e routine e quindi la frequenza del PWM risulter bassissima e probabilmente non costante. Per risolvere questo inconveniente, come abbiamo fatto in altre occasioni, possiamo utilizzare le interruzioni. Programmiamo il timer TMR0 del PIC in modo da generare un'interruzione ad intervalli regolari, ed utilizziamo la routine di servizio per incrementare il contatore PWM ed aggiornare l'uscita. In questo caso la frequenza dell'onda generata sar facile da calcolare essendo data dalla frequenza delle interruzioni diviso il 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 metodo appena descritto: il segnale PWM viene utilizzato per regolare la luminosit di un LED, il cui livello pu essere regolato manualmente agendo sui due pulsanti SW1 ed SW2, che permettono di aumentarlo o diminuirlo (lo schema riportato in Figura 3). Si pu notare come la gestione dei pulsanti sia fatta nel loop principale, dove per evitare problemi di rimbalzo stato inserito addirittura un ritardo di 100ms! Tutto questo non interferisce con la generazione del segnale PWM, che invece segue tempistiche indipendenti. Un'altra differenza rispetto al codice presentato prima risiede nel

fatto che il segnale PWM generato ha una risoluzione di 16 livelli e non 256. Questo stato ottenuto incrementando il contatore di 16 invece che di 1 (l'overflow si ha in 256/16=16 passi). Questo accorgimento, unito all'utilizzo delle interruzioni permette di ottenere una frequenza PWM molto pi alta.
unsigned char cont, level; void main() { // Inizializz. porte TRISA = 0x03; PORTA = 0x00; TRISB = 0x00; // Inizializz. Registri OPTION_REG TMR0 INTCON cont=0; level=0; // Loop infinito while(1) { if (PORTA&0x01) level+=16; if (PORTA&0x02) level-=16; Delay_ms(100); } } void interrupt() { if (cont>level) { PORTB=0; } else { PORTB=1; } cont=cont+16; TMR0 = 0; INTCON = 0x20; } // reset timer // cancella flag IRQ = 0x80; = 0; = 0xA0;

FARE ELETTRONICA - OTTOBRE 2006

I pin RA0 e RA1 della porta A sono impostati come ingressi ed utilizzati per rilevare la pressione dei pulsanti. La porta B utilizzata per la generazione dell'onda PWM (sul piedino RB0). Sono abilitate le interruzioni relative al TMR0, ed impostati il suoi parametri (clock interno,

niente prescaler, etc.). Nel loop viene soltanto rilevata la pressione di uno dei due pulsanti ed aggiornato il livello, nella routine d'interruzione invece si trova lo stresso codice che prima era nel loop principale. Come gi detto il contatore 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 interruzioni, la generazione del segnale PWM via software costituisce sempre un compito piuttosto gravoso per il PIC, soprattutto quando deve svolgere anche diversi altri compiti o la frequenza scelta piuttosto alta. Una soluzione molto efficiente costituita dall'impiego di periferiche hardware apposite presenti su molti PIC. In questo caso sufficiente programmare il modulo appo-

95
Teoria

Figura 3 Schema per controllo luminosit LED

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

TEORIA
Quarta parte

MHZ

RISORSE

SPECIALE

PRATICA

MikroC by Example: Generazione di segnali PWM

sito ed il segnale verr generato in maniera del tutto indipendente dal software. Questo approccio come vedremo reso pi semplice dal fatto che il MikroC mette a disposizione alcune funzioni di libreria per utilizzare queste periferiche. Nell'esempio seguente viene utilizzato il modulo CCP (Capture / Cpmpare / PWM) di un PIC16F876 per controllare l'accensione e lo spegnimento "soft" di una lampadina a 12V (Figura 4). Va notato che quando si utilizza il generatore hardware l'uscita del segnale pu essere prelevata soltanto dal piedino RC2 (non selezionabile arbitrariamente).
void main() {

// Loop infinito while(1) { Pwm_Change_Duty(level); if (PORTB) { delta=-delta; run=1; while(PORTB){}; } if (run) level=level+delta; if ((level==0)||(level==255)) run=0; Delay_ms(20); } }

96
Teoria

unsigned char level; char delta, run; // Iniz. porte TRISB = 0x01; PORTB = 0; PORTC = 0; TRISC = 0; // Init. modulo PWM Pwm_Init(1000); Pwm_Start(); delta=1; run=0; level=255; // Freq. PWM 1000Hz // Start PWM

La funzione Pwm_Init imposta la frequenza dell'onda PWM, la funzione Pwm_Start avvia la generazione del segnale (esiste anche Pwm_Stop per fermarlo), mentre Pwm_Change_Duty serve per cambiare il valore di duty cycle generato. Come si vede, utilizzando queste funzioni il codice risulta particolarmente semplice. Ad ogni pressione del pulsante viene attivato l'aggiornamento del livello (run=1) ed invertita la direzione d'incremento (il modo da ottenere una rampa in salita ed in discesa in maniera alternata). Una volta raggiunto uno dei

FARE ELETTRONICA - OTTOBRE 2006

Figura 4 Schema per accensione e spegnimento soft di una lampada

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 alluso, inoltre le funzioni di editing dei simboli consentono la creazione di nuovi molto velocemente. SPlan 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

Codice MIP 256097

TEORIA
Quarta parte

MHZ

RISORSE

SPECIALE

PRATICA

MikroC by Example: Generazione di segnali PWM

due estremi (0 o 255) l'aggiornamento viene arrestato (run=0). Il ritardo di 20ms serve per evitare fenomeni di rimbalzo e per rendere pi visibile la variazione di luminosit. GENERAZIONE DI SEGNALI ANALOGICI La tecnica PWM pu essere utilizzata per generare segnali analogici. Per fare questo sufficiente aggiornare il duty cycle dell'onda PWM a intervalli regolari, con i campioni del segnale analogico da riprodurre. La frequenza di riproduzione dei campioni pu essere al massimo pari a quella dell'onda PWM, e quindi la massima frequenza analogica riproducibile risulta met di questa frequenza. In realt per evitare distorsioni bene fare in modo che ogni campione della forma d'onda analogica sia mantenuto per diversi cicli dell'onda PWM. Il codice seguente mostra la generazione di una sinusoide utilizzando la tecnica descritta, ed il circuito di Figura 5.
unsigned char j; const unsigned char onda[16]={128,176,218,246, 255,246,218,176, 128, 79, 37, 10, 0, 10, 37, 79}; void main() { // Inizializz. porte e regs PORTC = 0; TRISC = 0; OPTION_REG TMR0 INTCON
FARE ELETTRONICA - OTTOBRE 2006

{ Pwm_Change_Duty(onda[j&0x0F]); j++; TMR0 = 192; // reset timer INTCON = 0x20; } // reset flag IRQ

98
Teoria

I 16 campioni della sinusoide sono memorizzati in un array, che ad ogni interruzione viene letto per assegnare al duty cycle il valore del campione corrente (puntato dall'indice j). La frequenza dell'onda generata sar data da quella di overflow del TMR0 (Fclk/4 diviso 256-192=64), diviso 16 campioni per periodo. Partendo da un clock di 8MHz si ottiene una sinusoide della frequenza di circa 300Hz. Per aumentare la frequenza della sinusoide occorre caricare in TMR0 un valore maggiore, in modo da aumentare la frequenza con cui vengono generate le interruzioni, ma occorre mantenersi sotto la frequenza dell'onda PWM, che stata scelta pari a 31248Hz. Per ottenere un segnale analogico pulito (poco distorto) si dovrebbe fare in modo da mantenere la frequenza del segnale generato abbastanza pi bassa di quella di campionamento, o in caso contrario utilizzare un filtro passa basso abbastanza ripido la cui frequenza di taglio pu essere pi vicina a quella di campionamento. Nel circuito di Figura 5 stato utilizzato un semplice circuito RC come filtro passa-basso, la cui frequenza di taglio circa 1500Hz. Va ricordato che in questo caso il carico deve avere un'impedenza sufficientemente alta (almeno qualche Kohm), in modo da non variare la costante di tempo de circuito. GENERAZIONE DI SUONI ED EFFETTI Apparentemente l'argomento potrebbe sembrare poco correlato con la generazione di segnali PWM, invece proprio i componenti del PIC che generano i segnali PWM possono essere utilizzati anche per generare suoni, note musicali ed effetti sonori. Agendo sui rispettivi registri infatti possibile modulare sia l'ampiezza (attraverso il duty cycle) che la frequenza del segnale generato. Una volta inizializzato ed avviato il modulo PWM possibile variare il duty cycle agendo sul registro CCPR1L, e la frequenza, agendo sul regi-

= 0x80; = 192; = 0xA0;

// Init. modulo PWM Pwm_Init(31248); Pwm_Start(); // Loop infinito while(1) {} } void interrupt() // Freq. PWM = 31KHz // Start PWM

stro PR2. Il primo determina il valore del contatore TMR2 superato il quale l'uscita si porta a livello basso, mentre il secondo determina il valore per cui il timer viene resettato (pi piccolo sar questo valore, pi frequente sar l'overflow). Se vogliamo usare il modulo PWM per generare suoni dobbiamo soltanto tenere presente due cose: 1) il prescaler di TMR2 (bit 0 e 1 del registro T2CON) deve essere impostato in modo che la frequenza ottenuta risulti udibile; 2) Il valore di duty cycle deve essere inferiore al limite di conteggio, altrimenti non ci saranno variazioni nel segnale d'uscita. Maggiori dettagli possono essere trovati nel datasheet del PIC. Nell'esempio seguente stato utilizzato un PIC16F876 con clock ad 8MHz (stesso schema di Figura 5), il prescaler stato impostato a 1:16, ottenendo frequenze che coprono un range che va da circa 100Hz a circa 1KHz. Il programma assegna dei valori crescenti e poi decrescenti al registro PR2, ad intervalli di 100ms, ottenendo un effetto sonoro tipo scala ascendente e discendente (o "sirena", se riprodotto pi velocemente). E' possibile anche calcolare i valori per PR2 in modo da ottenere le note musicali, che suonate 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 simili a quelli dei vecchi videogame!). Com' facile immaginare anche in questo caso l'aggiornamento dei valori pu essere fatto all'in-

terno di una routine d'interruzione, in modo da lasciare spazio ad altre routine all'interno del loop principale.
void main() { unsigned char j; // Init. porte PORTC = 0; TRISC = 0; Pwm_Init(1000); Pwm_Change_Duty(128); Pwm_Start(); T2CON=0x07; // Init. prescaler

// Loop infinito while(1) { for(j=70; j<255; j+=5) { PR2=j; Delay_ms(100); } for(j=255; j>70; j-=5) { PR2=j; Delay_ms(100); } } }

99
Teoria
FARE ELETTRONICA - OTTOBRE 2006

CONCLUSIONI Gli esempi visti in queste quattro puntate dovrebbero avere fornito un'idea relativamente chiara di come si possano scrivere dei semplici programmi in C per i microcontrollori PIC. Nella prossima puntata verr utilizzato quanto visto fino ad ora per realizzare un'applicazione completa e leggermente pi complessa di quelle viste fino ad ora: un utilissimo timer digitale programmabile. Questa applicazione ci dar l'opportunit si studiare come strutturare in C un programma pi complesso e come fare interagire le sue parti nel modo pi corretto. Verranno presentati come al solito gli schemi ed il codice completo. Codice MIP256092

Figura 5 Schema per la generazione di un segnale analogico

TEORIA
Quarta parte n 256 - Ottobre 2006

MHZ

RISORSE

SPECIALE

PRATICA

Generazione di segnali PWM


Quinta parte n 257 - Novembre 2006

Realizzazione di un timer digitale programmabile


Sesta parte n 258 - Dicembre 2006

MikroC by Example:
possibile impostare: giorno della settimana, ora di inizio, e durata. Se non si specifica un giorno preciso, levento verr attuato con frequenza giornaliera. I dati e le impostazioni sono visualizzate su un display LCD 16x2, e la programmazione avviene utilizzando 4 pulsanti: Adjust, Select, - e +. In base alla modalit selezionata i tasti possono avere diverse funzionalit. In genere Il primo serve per entrare nella modalit di modifica dei dati, il secondo per cambiare modalit (visualizzazione ora / visualizzazione eventi) o per spostare il cursore in campi successivi. Gli ultimi due servono invece per impostare i valori voluti o per scorrere i dati. Nella modalit di visualizzazione dellora viene appunto mostrata lora attuale (ore, minuti, secondi), il giorno della settimana e lo stato delluscita. La pressione di Adjust permette di modificare i valori correnti, la pressione di Select permette invece di visualizzare gli eventi. Premendo + e - si pu attivare o disattivare manualmente luscita. Nella modalit di visualizzazione degli eventi possibile visualizzare, attivare ed impostare i valori di ciascuno di essi, in modo simile a quanto visto prima. Dettagli pi precisi possono essere dedotti dal codice stesso e dal diagramma di stato riportato di seguito. Lo schema elettrico del circuito che implementa il timer riportato in Figura 1, e come si pu vedere relativamente semplice. E stato utilizzato un PIC16F876 funzionante a 8MHz, un display LCD intelligente 16x2 e pochi altri componenti passivi. Si sarebbe potuto utilizzare un micro pi piccolo, ma si preferito puntare sul 16F876 perch avendo a disposizione diverse porte libere, permette di aggiungere e sperimentare funzioni aggiuntive (si veda in seguito). STRUTTURA DEL CODICE Il programma costituito da un unico modulo (Timer.c) e dal suo header (Timer.h). Nel primo contenuto il corpo di tutte le funzioni (compreso

Uso delle interfacce seriali

I
86
Teoria
FARE ELETTRONICA - NOVEMBRE 2006

n questa puntata verr utilizzato quanto visto negli scorsi numeri per realizzare un'applicazione completa: un timer settimanale programmabile. Verranno illustrati i dettagli del codice C, le tecniche utilizzate e lo schema elettrico.

SPECIFICHE DEL TIMER In questa puntata considereremo un progetto relativamente pi complesso di quelli visti fino ad ora, un esempio di applicazione completa scritta interamente in C. Questo permetter di analizzare le tecniche e le soluzioni adottate e la struttura del codice di unapplicazione reale, tra laltro abbastanza utile in pratica. Nonostante lapplicazione non sia eccessivamente complessa, essa presenta alcuni aspetti non proprio intuitivi, che devono essere risolti in modo adeguato (ad esempio la gestione dellinterfaccia utente). Per questioni di spazio non possibile riportare lintero codice qui ( lungo pi di 660 linee!), ne verr per descritta la struttura, il funzionamento, e mostrate le routine principali. Il codice completo pu comunque essere scaricato dal sito di Fare Elettronica. Prima di passare alla descrizione del codice e delle tecniche implementative utilizzate per il caso di descrivere in dettaglio le specifiche dellapplicazione che ci proponiamo di realizzare. Il timer che vogliamo implementare deve essere capace di memorizzare lora corrente ed il giorno della settimana, e di gestire un certo numero di eventi che controllano quando luscita (ossia un rel a cui pu essere collegato un carico arbitrario) deve essere attivata. Per ciascun evento

Realizzazione di un timer digitale programmabile


di Antonio Di Stefano

il main), mentre nel secondo sono presenti le dichiarazioni di macro, costanti, tipi, variabili globali ed i prototipi delle funzioni. Questa suddivisione, come sar pi chiaro in seguito, utile per consentire di modificare alcuni parametri senza bisogno di modificare il codice. Nel listato 1 riportato il contenuto in Timer.h ed utile per iniziare a comprendere la struttura dellintero programma. In generale le funzioni del timer sono gestire da due entit: la macchina a stati principale, che si occupa in pratica di gestire linterfaccia utente, e la routine dinterruzione, che invece provvede ad aggiornare lora corrente e ad eseguire la lettura dello stato dei pulsanti con la funzione anti-rimbalzo. Questa suddivisione stata scelta dal momento che laggiornamento dellora e lo stato dei pulsanti sono prioritari, e in qualche caso determinano la visualizzazione dei dati ed il comportamento del circuito.

Routine dinterruzione Le interruzioni sono generate dal timer TMR0 del PIC, e servono per cadenzare il funzionamento dellintero circuito. Il codice della routine dinterruzione, contenuta nel modulo Timer.c riportato nel listato 2. Laggiornamento dellora avviene incrementando i campi dellapposita struttura, e considerando i rispettivi valori di overflow (60 per secondi e minuti, 24 per le ore, 7 per i giorni). Dal momento che non possibile dividere la frequenza del timer TMR0 del PIC per un fattore tale da avere a disposizione un interrupt ogni secondo (o sue frazioni decimali), necessario utilizzare un contatore (il campo tck) per ottenere lincremento dei secondo con la giusta cadenza. In particolare, dal momento che linterruzione avviene ogni 1/125 secondi (si vedano le impostazioni del TMR0 nella routine Init), i secondi verranno incrementati quando il contatore raggiunge 125 (valore di TCK_SEC).

87
Teoria
FARE ELETTRONICA - NOVEMBRE 2006

Figura 1 Schema del timer

TEORIA

MHZ

RISORSE

SPECIALE

PRATICA

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

[Listato 1] /*** Timer.h ***/

char *onoff[2]={Off, On }; char *blank=; // - Variabili di stato e temporanee -

// *** Costanti *** // Tick per secondo #define TCK_SEC 125 // Valore di reset del TMR0 #define TMR0_RESET 131 // Valore di reset anti-rimbalzo #define P_RESET 15 // Soglia anti-rimbalzo #define P_SOGLIA 20 // Soglia ripetizione #define P_RIPETIZ 0

enum stati {S_VIS_ORA, S_SET_ORA, S_VIS_EVENTI, S_SET_EVENTI} stato; char thr[5], tmin[5], tsec[4], tdhr[5], tdmin[4], tnev[4]; unsigned char Pcnt[4], secondo, start_stop, uscita; unsigned char pos, nevento; tempo ora; // Ora e giorno corrente // Array eventi

88
Teoria

evento eventi[N_EVENTI];

// N. eventi gestiti #define N_EVENTI 8 // Valore posrta stato ON #define USCITA_ON 0x80 // Inizializza registri e // variabili allavvio void Init(void); // *** Definizione tipi *** // Test pressione pulsanti // - Tipo tempo typedef struct { unsigned char hr; unsigned char min; unsigned char sec; unsigned char grn; unsigned int } tempo; // - Tipo evento typedef struct { unsigned char hr; unsigned char min; unsigned char sec; unsigned char grn; // Ora // Minuti // Secondi // Gionro // Durata ore // Durata minuti // On/Off // Testa gli eventi e aggiorna luscita void Test_Eventi(void); // *** Variabili globali *** // - Stringhechar *nomegrn[8]={Lun, Mar, Mer, Gio, Ven, Sab, Dom, -}; // Funzioni di visualizzazione void Display_Ora(char edit); void Display_Eventi(char edit); void Cursore(char n); // Stato modifica eventi void Set_Evento(void); // Stato visualizza eventi void Vis_Evento(void); tck; // Ora // Minuti // Secondi // Giorno // Clock Tick // Stato visualizzazione ora void Vis_Ora(void); // Stato modifica ora void Set_Ora(void); // Restituisce: // // // 0=pulsnate non premuto, 1=pulsante premuto (il contatore viene resettato) // Valore posrta stato OFF #define USCITA_OFF 0x00 // *** Prototipi ***

char PTest(char i);

FARE ELETTRONICA - NOVEMBRE 2006

unsigned char d_hr; unsigned char d_min; unsigned char attivo; } evento;

Gestione dei pulsanti I pulsanti sono gestiti con la tecnica descritta nel numero 255 di FE: ad ogni pulsante associato un contatore (Pcnt[]) che viene incrementato ad ogni interruzione se il pulsante risulta premuto, o viene resettato se questo viene rilasciato. Se il contatore raggiunge il valore scelto come soglia significa che stato premuto per un intervallo sufficientemente lungo (precisamente (2015)/125s = 4ms). Questo evita che i rimbalzi dei
[Listato 2] void interrupt() { if (start_stop) ora.tck++; TMR0 = TMR0_RESET; // Aggiornamento ora if (ora.tck==TCK_SEC) { ora.tck=0; ora.sec++; secondo=1; if (ora.sec==60) { ora.sec=0; ora.min++; if (ora.min==60) { ora.min=0; ora.hr++; if (ora.hr==24) { ora.hr=0; ora.grn++; if (ora.grn==7) ora.grn=0; } } } } // Stato pulsanti if ((PORTC&0x01)&&(Pcnt[0]<P_SOGLIA)) Pcnt[0]++; else Pcnt[0]=P_RESET; if ((PORTC&0x02)&&(Pcnt[1]<P_SOGLIA)) Pcnt[1]++; else Pcnt[1]=P_RESET; if ((PORTC&0x04)&&(Pcnt[2]<P_SOGLIA)) Pcnt[2]++; else Pcnt[2]=P_RESET; if ((PORTC&0x08)&&(Pcnt[3]<P_SOGLIA)) Pcnt[3]++; else Pcnt[3]=P_RESET; INTCON = 0x20; } // reset timer

tasti possano essere percepiti come pressioni. La funzione che verifica se la soglia di un dato pulsante stata superata PTest:
char PTest(char i) { if (Pcnt[i]==P_SOGLIA) { Pcnt[i]=P_RIPETIZ; return 1; } else { return 0; } }

Se stato raggiunto il valore di soglia la funzione restituisce 1, e resetta il contatore, in modo da consentire la successiva rilevazione. Va notato che il valore di reset diverso rispetto al coso di rilascio del pulsante (in questo caso la soglia raggiunta in 20/250s = 160ms). Questo permette di differenziare tra ritardo anti-rimbalzo o ritardo di ripetizione se il tasto continuamente premuto. Funzione main La funzione main, come si pu vedere dal codice, piuttosto snella ed implementa soltanto il loop principale del programma che contiene la macchina a stati che gestisce linterfaccia utente. Questa struttura stata utilizzata dal momento che le funzioni dei tasti ed i dati visualizzati sul display variano a seconda del contesto e delle azioni che si sono compiute in precedenza. Occorre quindi considerare lo stato in cui ci si trova e aggiornarlo in base alle azioni dellutente. 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. In pratica lo stato attuale determina quale delle 4 funzioni eseguita ad ogni iterazione del loop. Ciascuna funzione conterr il codice necessario per svolgere i compiti associati, e cambiare stato in funzione dei tasti premuti. Si pu notare che prima di entrare nel loop principale, viene richiamata la funzione Init che inizializza tutte le porte, i registri e le variabili utilizzate. Funzioni di gestione degli stati Le funzioni richiamate dai vari stati sono Vis_Ora, Vis_Evento, Set_Ora e Set_Evento. Le

89
Teoria
FARE ELETTRONICA - NOVEMBRE 2006

TEORIA

MHZ

RISORSE

SPECIALE

PRATICA

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

[Listato 3] void main() { Init(); while(1) { switch (stato) { case S_VIS_ORA: Vis_Ora(); break; case S_SET_ORA: Set_Ora(); break; case S_VIS_EVENTI:

90
Teoria

Vis_Evento(); break; case S_SET_EVENTI: Set_Evento(); break; } } }

Funzione di visualizzazione Le funzioni utilizzate per la visualizzazione sono tre: Display_Ora(), Display_Eventi() e Cursore(). Le prime due visualizzano lora attuale e la lista di eventi rispettivamente. Esse prendono in ingresso un parametro che pu valere 0 nel caso della visualizzazione normale, o 1 nel caso si voglia fare apparire il cursore sul valore da modificare. Il posizionamento e la visualizzazione del cursore in base al parametro da modificare sono gestiti dalla funzione Cursore(). A scopo di esempio, la funzione Display_Ora(), riportata nel listato 6. Come si pu vedere sono state utilizzate le funzioni di libreria del MikroC per convertire i valori numerici in stringhe e per visualizzarli sullLCD (funzione Lcd_Out). MODIFICHE E MIGLIORAMENTI Modificando il valore delle macro presenti 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); Test_Eventi(); secondo=0; } // Pulsante + if (PTest(0)) { uscita = USCITA_ON; } // Pulsante if (PTest(1)) { uscita = USCITA_OFF; } // Select if (PTest(2)) { stato=S_VIS_EVENTI; Display_Eventi(0); Delay_ms(250); } // Adjust if (PTest(3)) { Display_Ora(1); stato=S_SET_ORA; start_stop=0; Delay_ms(250); } }

prime due gestiscono gli stati in cui sono visualizzati lora corrente o gli eventi, mentre gli altri gestiscono la fase di modifica dei valori in questi stati. Ciascuna di queste funzioni composta da una parte che si occupa della visualizzazione (richiamando una funzione apposita), e da una serie controlli sulla pressione dei pulsanti, che generano il cambiamento di stato o del valore di alcune variabili. A titolo di esempio nel listato 4 riportato il codice della funzione Vis_Ora. Allo scadere di ogni secondo, la variabile secondo viene impostata ad 1 dalla routine dinterruzione. In questo caso viene richiamata la funzione di visualizzazione dellora Display_Ora (per aggiornare il display) e quella che controlla la scadenza degli eventi, che determina se luscita deve essere attivata o disattivata. Il codice di questa funzione, chiamata Test_Eventi, riportato nel listato 5. In questa routine viene verificato, per ciascun evento, se lora di attivazione coincide con lora attuale, o se trascorso lintervallo di attivazione associato a ciascun evento. In base a questo controllo viene aggiornata la variabile uscita, che poi viene assegnata alla porta C del PIC.

FARE ELETTRONICA - NOVEMBRE 2006

Figura 2 Diagramma degli stati del timer

esigenze. Ad esempio se si utilizza 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 USCITA_ON e USCITA_OFF. Modificando il codice possono essere aggiunte in maniera semplice ulteriori caratteristiche interessanti, ad esempio la gestione di pi uscite indipendenti o lutilizzo di segnali esterni per condizionare determinati eventi. In particolare, si pu utilizzare la porta A per

91
Teoria

Compilatore MikroC
Un potente compilatore C per PICmicro
Code Editor Code Explorer Debugger Statistiche
Tutto in un ambiente Windows facile ed intuitivo
Un set di strumenti veramente indispensabili per sviluppare applicazioni con i PICmicro
Codice MIP 257091
FARE ELETTRONICA - NOVEMBRE 2006

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

TEORIA

MHZ

RISORSE

SPECIALE

PRATICA

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

[Listato 5] void Test_Eventi(void) { char i, ora_fine, min_fine, grn_fine; for(i=0; i<N_EVENTI; i++) { // - Disattiva uscita min_fine=eventi[i].min+eventi[i].d_min; ora_fine=eventi[i].hr+eventi[i].d_hr; grn_fine=eventi[i].grn; if (min_fine>59) { min_fine=min_fine-60; ora_fine++; } if (ora_fine>23) { ora_fine=ora_fine-24; if (grn_fine!=7) { grn_fine++; if (grn_fine==7) grn_fine=0; } } if((ora.grn==grn_fine)|| (eventi[i].grn==7)) { if (ora.hr==ora_fine) { if (ora.min==min_fine) { if (eventi[i].attivo) uscita = USCITA_OFF; } } } // - Attiva uscita if((eventi[i].grn==ora.grn)|| (eventi[i].grn==7)) { if (eventi[i].hr==ora.hr) { if (eventi[i].min==ora.min) { if (eventi[i].sec==ora.sec) { if (eventi[i].attivo) uscita = USCITA_ON;
FARE ELETTRONICA - NOVEMBRE 2006

[Listato 6] void Display_Ora(char edit) { Lcd_Cmd(LCD_CURSOR_OFF); ByteToStr(ora.hr, thr); ByteToStr(ora.min, tmin); ByteToStr(ora.sec, tsec); if (thr[1]== ) thr[1]=0; if (tmin[1]== ) tmin[1]=0; if (tsec[1]== ) tsec[1]=0; thr[3]=:; tmin[3]=:; Lcd_Out(1,1,nomegrn[ora.grn]); Lcd_Out(2,1,(thr+1)); Lcd_Out(2,4,(tmin+1)); Lcd_Out(2,7,(tsec+1)); if (uscita) Lcd_out(2, 14, onoff[1]); else Lcd_out(2, 14, onoff[0]); if (edit) { Cursore(pos+1); } else { Lcd_Cmd(LCD_CURSOR_OFF); } }

92
Teoria

acquisire segnali digitali o analogici esterni da utilizzare come abilitazione per alcuni eventi. CONCLUSIONI In conclusione ricordo che lintero codice dellapplicazione scaricabile dal sito di Fare Elettronica (www.farelettronica.com). Il codice pu essere compilato senza alcuna modifica con MikroC e dovrebbe funzionare immediatamente se si utilizza lo schema riportato prima (che pu essere facilmente riprodotto con la scheda EasyPIC3). Con piccole modifiche possibile adattare il programma ad altri modelli di PIC. La descrizione fornita qui sufficientemente dettagliata da consentirne a chiunque la comprensione, lutilizzo, e la modifica del programma. Prima di utilizzare il codice in applicazioni reali, consigliabile accertarsi di avere compreso bene le particolarit ed il comportamento del programma in tutte le condizioni (diverse caratteristiche minori per brevit non sono state descritte). Codice MIP 257086

} } } } } PORTC = uscita; }

TEORIA
Quinta parte n 257 - Novembre 2006

MHZ

RISORSE

SPECIALE

PRATICA

Realizzazione di un timer digitale programmabile


Sesta parte n 258 - Dicembre 2006

Uso delle interfacce seriali


Settima parte n 259 - Gennaio 2007

MikroC by Example
molto semplice per collegare un microcontrollore ad un PC al fine di scambiare dati e comandi. Questa possibilit utile nei casi in cui il microcontrollore collocato in una posizione remota (e quindi pu eseguire delle operazioni in un luogo distante dal PC), oppure per eseguire parte delle elaborazioni sul PC e scambiare i risultati o dei comandi. Negli esempi che verranno presentati di seguito sar utilizzato lo schema riportato in Figura 1. Come si pu vedere stato utilizzato un PIC16F876 ed un MAX232, un classico componente che svolge la funzione di traslare i livelli da quelli compresi tra 0 e 5V del PIC a quelli previsti dallo standard RS-232, che sono ben pi alti. In alcuni degli esempi presentati i pin della porta B saranno utilizzati come semplici I/O digitali, in altri sar collegato ad essi un display LCD intelligente 16x2 . SEGNALAZIONE DI EVENTI Il primo esempio mostra come instaurare una semplice comunicazione seriale tra il PIC ed il PC. In particolare il programma mostrato nel listato 1 invia al PC lo stato della porta B (in cui tutti i pin sono configurati come ingressi) ogni volta che rivela una variazione:
Listato 1
void main() { char pb, mpb; PORTB=0; TRISB=0xFF; // PORTB[0-7]: ingressi

Interfacce SPI, I2C e 1-Wire

U
84
Teoria
FARE ELETTRONICA - DICEMBRE 2006

no dei metodi pi utilizzati e versatili per collegare sistemi embedded a computer host consiste nellutilizzo dellinterfaccia RS-232. Lutilizzo di questo collegamento seriale, per scambiare dati e comandi in maniera molto semplice e robusta, sar loggetto di questa puntata. Verranno presentati diversi esempi e mostrato come implementare un semplice collegamento tra un PIC e un PC utilizzando le librerie del MikroC.

Linterfaccia seriale RS-232 uno standard estremamente diffuso. Nonostante le basse velocit gestibili, essa infatti risulta adeguata alla maggior parte delle esigenze tipiche dellautomazione e del controllo industriale, o del debug o programmazione dei sistemi embedded. Essa ha inoltre il vantaggio di essere molto semplice ed economica e di garantire unottima immunit al rumore ed ai disturbi, grazie allimpiego di tensioni di segnalazione piuttosto alte (circa 12V). Nella versione pi semplice, linterfaccia costituita da soli tre fili: TX (trasmissione), RX (ricezione) e massa. I dati sono trasmessi serialmente in maniera asincrona, cio senza luso di un clock. Per rendere possibile questo necessario che il ricevitore ed il trasmettitore utilizzino lo stesso formato di dati (velocit di trasmissione, numero e tipologia di bit). Linterfaccia RS-232 costituisce un metodo

// Inizializzazione Usart_Init(2400); pb=0; mpb=0; while(1) { pb=PORTB; if (pb!=mpb)

Uso dellinterfaccia seriale

di Antonio Di Stefano

85
Teoria

Figura 1

Schema utilizzato negli esempi

Usart_Write(pb); Delay_ms(10); mpb=pb; } }

Come si pu vedere, grazie alle librerie del MikroC la gestione dellinterfaccia estremamente semplice. stata utilizzata la funzione Usart_Init() per inizializzare la porta, specificando la velocit desiderata in baud. In questo caso la velocit scelta di 2400 bit al secondo (con 8 bit di dati 1 bit di start, 1 bit di stop e nessuna parit). Queste stesse impostazioni dovranno essere utilizzate per la porta del PC. La funzione Usart_Write() utilizzata per trasmettere un byte. In pratica il ciclo principale controlla se ci sono cambiamenti dello stato della porta B, ed in tal caso invia il nuovo valore al PC sotto forma di byte. La frequenza del ciclo di circa 10ms.

Va notato che le funzioni della libreria USART, come suggerisce il nome, utilizzano la periferica 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 per tale funzione (RC6 ed RC7 sul PIC utilizzato), e che non possibile utilizzare PIC privi di questa periferica con queste funzioni. Questa limitazione pu essere superata comunque, come verr mostrato di seguito. Per ricevere e trasmettere i dati seriali sul PC necessario utilizzare un programma terminale apposito. Tra i tanti disponibili (tra cui anche lHyperTerminal di Windows) risulta molto comodo LUSART Terminal incluso nellambiente di sviluppo del MikroC, attivabile dal menu Tools. In Figura 2 visibile la finestra del programma con le impostazioni da utilizzare. Come si pu vedere, oltre ai parametri di

FARE ELETTRONICA - DICEMBRE 2006

TEORIA
Sesta parte

MHZ

RISORSE

SPECIALE

PRATICA

MikroC by Example: Uso dellinterfaccia seriale

connessione, nella finestra sono presenti altre opzioni, tra cui quella che permette di selezionare la modalit di visualizzazione dei dati ricevuti. In questo caso limpostazione scelta Hex, in modo da potere leggere il valore binario inviato. Negli altri esempi sar utilizzata la modalit ASCII, dal momento che sar scambiato del testo. CIFRATURA REMOTA Per mostrare come la comunicazione possa avvenire anche nellaltra direzione, e si possa istaurare un primo esempio di colloquio tra il PC ed il PIC, riportato nel listato 2 il codice di un programma che riceve un testo dal PC e lo cifra secondo il codice ROT13, utilizzato spesso su Internet come semplice forma di cifratura del testo. Il codice consiste in pratica nel ruotare lalfabeto anglosassone (26 lettere) di 13 posti, in questo modo A diventa N, la Z la M e cos via. Lalgoritmo reversibile, e quindi quando viene applicato ad un testo gi cifrato, lo riporta in chiaro.
Listato 2
/* ROT13 remoto */ void main() { unsigned short i; Usart_Init(2400); while (1) { // Se riceve un carattere if (Usart_Data_Ready()) { // Legge il dato ricevuto i = Usart_Read(); // Rotazione maiuscole if (isupper(i)) i=((i-A)+13)%26+A; // Rotazione minuscole if (islower(i))
FARE ELETTRONICA - DICEMBRE 2006

86
Teoria

e lo memorizza in un buffer interno). Quando stato ricevuto un carattere la funzione restituisce un valore diverso da zero (che equivale ad una condizione vera). Una volta letto il carattere (con la funzione Usart_Read), questo viene elaborato secondo lalgoritmo descritto prima (controllando che si tratti effettivamente di una lettera dellalfabeto maiuscola o minuscola, grazie alle funzione isupper e islower della libreria standard ctype.h), ed inviato di nuovo al PC. Il risultato sar che se inviamo la parola Ciao, otterremo come risposta Pvnb. Se re-inviamo questultima otterremo di nuovo la versione in chiaro. Come si pu notare gi da questo esempio, la comunicazione tramite RS-232 orientata e ordinata a singoli byte. Per scambiare messaggi pi complessi necessario ideare o usare dei protocolli che definiscano la struttura dei messaggi, come mostrato di seguito. CONTROLLO DI UN DISPLAY DA PC Supponiamo di volere inviare da PC il testo da visualizzare su un display LCD. Il testo potrebbe essere generato da un apposito software oppure essere utilizzato soltanto come messaggio remoto. Lutilizzo della porta RS-232 costituisce una buona soluzione. Utilizzando lo schema di Figura 1, ed il programma mostrato nel listato 3 possibile ottenere in maniera molto semplice quanto detto. In pratica il programma consente di inviare dei comandi e delle stringhe al display in modo da controllare il testo visualizzato. Inviando la stringa C possibile cancellare il testo visualizzato, inviando la stringa T x Abcd possibile scrivere la stringa Abcd nella riga x (che

i=((i-a)+13)%26+a; // Invio risultato Usart_Write(i); } } }

A differenza di prima stata utilizzata la funzione Usart_Data_Ready() per controllare se stato ricevuto un carattere (la periferica USART lo riceve indipendentemente dal programma,

Figura 2 LUSART Terminal del MikroC con i parametri di collegamento

pu essere 1 o 2). Inviando la stringa S x possibile spegnere o accendere il cursore e cambiarne la forma (0=spento, 1=linea, 2=quadratino lampeggiante). Ogni stringa deve terminare con un a capo (codici ASCII 0x0D e 0x0A). Questo serve al programma per individuare la fine della stringa e per iniziarne lelaborazione (ricordarsi di abilitare le opzioni Append CR ed LF nel Terminal).
Listato 3
/* Controllo display remoto via seriale */ void SendString(char *); void main() { char buf[32], i, j, stringa;

Lcd_Init(&PORTB); // Init. variabili stringa=0; i=0; for(j=0; j<32; j++) buf[0]; while(1) { // Attesa ricezione stringa if ((Usart_Data_Ready())&&(!stringa)) { buf[i] = Usart_Read(); if (buf[i]==\n) { stringa=1; } i++; }

87
Teoria

PORTB=0; TRISB=0; Usart_Init(2400); // Elaborazione stringa if (stringa) { 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

TEORIA
Sesta parte

MHZ

RISORSE

SPECIALE

PRATICA

MikroC by Example: Uso dellinterfaccia seriale

case C: Lcd_Cmd(LCD_CLEAR); SendString(Ok\r\n); break; case T: buf[20]=\0; buf[i-2]=\0; if (buf[2]==1) Lcd_Out(1, 1, (buf+4)); else Lcd_Out(2, 1, (buf+4)); SendString(Ok\r\n); break; case S: if (buf[2]==0) Lcd_Cmd(LCD_CURSOR_OFF); if (buf[2]==1) Lcd_Cmd(LCD_UNDERLINE_ON); if (buf[2]==2) Lcd_Cmd(LCD_BLINK_CURSOR_ON); SendString(Ok\r\n); break; default: SendString(?!?\r\n); } for(j=0; j<32; j++) buf[0]; stringa=0; i=0; } } } void SendString(char *str) {
FARE ELETTRONICA - DICEMBRE 2006

88
Teoria

suo interno si possono distinguere due parti: una destinata alla ricezione delle stringhe, e laltra alla loro elaborazione. La prima attende larrivo di nuovi caratteri, li riceve e li accoda nel buffer apposito del programma (buf[]). Quando viene rilevato un carattere di a capo la stringa completa, e per indicarlo viene portata a 1 la variabile stringa. In questa situazione verr eseguita la seconda parte del programma, e non pi la prima. Qui viene controllato se il primo carattere un comando valido (cio una C, una T o una S), ed eseguite le relative funzioni. Va notato che il testo inviato si trova nellarray buf[], nellordine in cui stato inviato, possibile quindi estrarne i vari campi ed utilizzarli. Ogni volte che viene eseguita una delle funzioni previste viene inviato al PC un Ok come conferma. Se non viene riconosciuto un comando valido viene inviato un ?!?. Per inviare queste stringhe stata scritta una funzione apposita che non fa altro che inviare sequenzialmente tutti i byte che compongono la stringa, con la funzione Usart_Write(). Notare che la scansione della stringa si arresta in corrispondenza del terminatore, cio il valore binario 0x00. RICEZIONE CON INTERRUZIONI In molti casi pu essere utile rendere la ricezione delle stringhe seriali asincrona ed indipendente dal flusso principale del programma. Questo soprattutto perch la comunicazione via RS232 per sua natura asincrona ed imprevedibile, 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 funzione di ricezione delle stringhe nella routine dinterruzione.
Listato 4

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

Dopo una prima inizializzazione della UART, del display e delle variabili, inizia il loop principale. Al

void Dec2Hex(char, char *); ... void main() { Usart_Init(2400); // Set RCIE PIE1|=0x20; // Set GIE e PEIE INTCON|=0xC0; while(1) { if (stringa) { switch(buf[0]) { case W: } } } interrupt() { if (!stringa) { buf[bpnt] = Usart_Read(); if ((buf[bpnt]==\n)|| (bpnt==MAX_LEN-1)) { stringa=1; } bpnt++; } }

funzioni disponibili sono quasi le stesse, anche il loro uso richiede una pi accurata pianificazione dei tempi del programma (che deve fermarsi ad attendere larrivo dei caratteri, e non pu gestire la comunicazione in full duplex). Nel listato 5 mostrato un esempio implementato in versione software:
Listato 5
/* ROT13 remoto con UART software */ void main() { unsigned short ro = 0, *recOK, i; recOK = &ro; Soft_Uart_Init(PORTA, 0, 1, 2400, 0); while(1) { do { i = Soft_Uart_Read(recOK); } while (*recOK); if (isupper(i)) i=((i-A)+13)%26+A; if (islower(i)) i=((i-a)+13)%26+a; Soft_Uart_Write(i); } }

89
Teoria

In pratica il codice rimasto quasi invariato, tranne per il fatto che sono state attivate le interruzioni associate alla ricezione dei singoli caratteri, e la funzione di ricezione stringhe stata confinata nella routine dinterruzione. Si pu notare anche che la comunicazione tra le due routine avviene ancora tramite la variabile stringa, che questa volta per stata dichiarata come globale. SOFTWARE UART Se il PIC utilizzato non dispone di una UART integrata comunque possibile implementare una comunicazione seriale. Questo grazie alle routine della libreria Software_Uart del MikroC, che emulano il dispositivo via software utilizzando due qualsiasi piedini di I/O. Le

Notare che i piedini di comunicazione specificati sono RA0 ed RA1 invece di RC6 ed RC7, questo rende possibile riscrivere i programmi presentati prima anche per PIC privi di UART, quale il 16F84. CONCLUSIONI Va ricordato che le routine mostrate possono essere provate anche su PC privi di porta seriale, utilizzando un adattatore USB-RS232 (ricordarsi di settare il numero corretto di porta COM). Nella prossima puntata si continuer a parlare di protocolli seriali, in particolare di SPI, I2C e 1-Wire, utilizzati per comunicare con dispositivi quali memorie EEPROM, timer, sensori di temperatura, e tanto altro. Verr mostrato come usare le routine di libreria e come comunicare con questi dispositivi. Codice MIP 258084

FARE ELETTRONICA - DICEMBRE 2006

TEORIA

MHZ

RISORSE

SPECIALE

PRATICA

Sesta parte n 258 - Dicembre 2006

Uso delle interfacce seriali


Settima parte n 259 - Gennaio 2007

Interfacce SPI, I2C e 1-Wire


Ottava parte n 260 - Febbraio 2007

MikroC by Example
di pull-up. I vari dispositivi comunicano portando 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 Semiconductors, ora NXP) prevede la possibilit di avere uno o pi dispositivi master, e uno o pi dispositivi slave. Ciascuna comunicazione iniziata da parte di un master imponendo sul bus uno stato di Start (SDA va basso mentre SCL alto), e termina con uno di Stop (SDA va alto mentre SCL alto). Ad ogni scambio di dati tra master slave il ricevente pu rispondere con un bit aggiuntivo di Acknowledge. Sul mercato sono disponibili moltissimi dispositivi che utilizzano il protocollo I2C, tra questi: sensori, porte di espansione di I/O (GPIO), convertitori A/D e D/A, memorie e diverse periferiche dedicate. Per collegare un dispositivo I2C ad un PIC possibile utilizzare la periferica MSSP integrata, quando disponibile, o gestire la comunicazione via software utilizzando un paio qualsiasi di piedini di

Uso del convertitore A/D

I
76
Teoria
FARE ELETTRONICA - GENNAIO 2007

n questa puntata scopriremo come utilizzare le interfacce I2C, SPI e 1-Wire con il MikroC e come interfacciare al PIC una variet di dispositivi esterni quali memorie e sensori di temperatura.
INTERFACCIA I2C Il bus I2C (Inter-Integrated Circuit bus) stato ideato da Philips per permettere, come suggerisce lo stesso nome, la comunicazione tra diversi circuiti integrati in maniera semplice ed economica. Esso consiste in un protocollo di comunicazione seriale sincrono che utilizza soltanto due fili per la comunicazione. Le due linee, chiamate SDA (linea dati bidirezionale) ed SCL (clock, fornito dal master), sono condivise tra tutte le periferiche e mantenute ad una tensione positiva da resistenze

Figura 1 Collegamento della EEPROM al PIC

Interfacce I2C, SPI e 1-Wire


I/O. Il MikroC mette a disposizione due librerie dedicate alla gestione dellinterfaccia I2C che permettono di sfruttare entrambe le possibilit. Le funzioni delle due librerie sono quasi identiche, quindi risulta molto semplice passare da unimplementazione hardware ad una software e viceversa. Per mostrare in dettaglio lutilizzo delle funzioni di libreria verr utilizzato il circuito di Figura 1, in cui una memoria EEPROM 24LC02 (2Kbit, organizzata in 256 parole da 8bit) collegata al PIC, e verr utilizzata per memorizzare i pattern da visualizzare sui LED. I dati e le temporizzazioni da inviare alla memoria per effettuare le operazioni di lettura e scrittura sono riportate in Figura 2. Nel listato 1 invece riportato il codice che mostra come leggere e scrivere singoli byte. Linterfaccia I2C deve essere inizializzata richiamando la funzione I2C_Init, in cui viene specificata la velocit di comunicazione (frequenza del clock seriale). quindi possibile utilizzare le altre funzioni, che eseguono le operazioni elementari

di Antonio Di Stefano

sul bus. Come si pu vedere dal codice delle funzioni EEPROM_Rd e EEPROM_Wr le operazioni eseguite per la lettura e scrittura della memoria sono esattamente quelle riportate in Figura 2. Il programma memorizza alcune configurazioni nella EEPROM in indirizzi successivi, e poi li legge sequenzialmente (e ciclicamente) riportandoli sui LED collegati alla porta B. Per mostrare limpiego della libreria Soft I2C, ricorreremo allo schema riportato in Figura 3. Il circuito utilizza questa volta un PIC 16F84 (che non dispone di una periferica MSSP) e consente di copiare il contenuto di una EEPROM in unaltra. Come si pu vedere entrambe le memorie sono poste sullo stesso bus, ma hanno un indirizzo diverso grazie al diverso livello applicato ai piedini A0. Il Listato 2 mostra il codice che effettua la copiatura della memoria. Il codice abbastanza semplice: per ciascun indirizzo (da 0 a 255) il dato viene letto dalla EEPROM 0 e scritto sulla EEPROM 1. Lindirizzo della EEPROM da utilizzare passato come parametro

77
Teoria
FARE ELETTRONICA - GENNAIO 2007

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

TEORIA

MHZ

RISORSE

SPECIALE

PRATICA

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

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

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

void main() { char i; PORTB=0; TRISB=0; // Inizializzazione I2C_Init(400000); // Scrittura EEPROM_Wr(0, EEPROM_Wr(1, EEPROM_Wr(2, EEPROM_Wr(3, dati 0x00); 0x81); 0xC3); 0xE7);

void main() { char i, addr, dato; PORTA=0; TRISA=0;

// Inizializzazione Soft_I2C_Config(PORTB,0,1); // // // // ** **** ****** Delay_ms(100); PORTA=0x01; for(i=0; i<256; i++) { dato=EEPROM_Rd(i, 0); EEPROM_Rw(i, dato, 1); } PORTA=0x02; While(1) {}; } void EEPROM_Wr(char addr, char data, char d) { d<<=1; Soft_I2C_Start(); Soft_I2C_Write(0xA0|d); Soft_I2C_Write(addr); Soft_I2C_Write(data); Soft_I2C_Stop(); } char EEPROM_Rd(char addr, char d) { char k; d<<=1; Soft_I2C_Start(); Soft_I2C_Write(0xA0|d); Soft_I2C_Write(addr); Soft_I2C_Start(); Soft_I2C_Write(0xA1); k = Soft_I2C_Read(0); Soft_I2C_Stop(); return k; }

78
Teoria

Delay_ms(500); i=0; While(1) { PORTB=EEPROM_Rd(i&0x03); i++; Delay_ms(500); } } void EEPROM_Wr(char addr, char data) { I2C_Start(); // Invia Start I2C_Wr(0xA0); // Invia commando I2C_Wr(addr); // Invia indirizzo I2C_Wr(data); // invia dato I2C_Stop(); // Invia Stop } char EEPROM_Rd(char addr) { char k;

FARE ELETTRONICA - GENNAIO 2007

I2C_Start(); I2C_Wr(0xA0); I2C_Wr(addr); I2C_Repeated_Start(); I2C_Wr(0xA1); k = I2C_Rd(0); I2C_Stop(); return k; }

// // // // // // //

Start Invia commando Invia indirizzo Start ripetuto Invia commando Lettura (no ack) Stop

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
FARE ELETTRONICA - GENNAIO 2007

Statistiche
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

TEORIA

MHZ

RISORSE

SPECIALE

PRATICA

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

[Listato 3]
void main() { char lsb, *buf[6]; int temp; PORTA TRISA PORTB TRISB = 0xFF; = 0xFF; = 0; = 0;

sarebbe risultato meno chiaro). In generale, utilizzando le funzioni della libreria I2C viste qui possibile comunicare con qualsiasi dispositivo (anche diverso dalle EEPROM), basta utilizzare la sequenza di dati e operazioni indicate negli specifici data sheet.

80
Teoria
FARE ELETTRONICA - GENNAIO 2007

INTERFACCIA 1-WIRE Linterfaccia 1-Wire utilizzata da // Inizializzaione LCD diversi dispositivi (prodotti princiLcd_Init(&PORTB); palmente da Dallas/Maxim). Essa Lcd_Cmd(Lcd_CURSOR_OFF); consiste in uninterfaccia seriale Lcd_Out(1, 1, Temperatura: ); asincrona che impiega soltanto un filo per lo scambio di dati tra while(1) { un master e uno o pi slave. La Ow_Reset(&PORTA,0); // Reset comunicazione avviene a bassa Ow_Write(&PORTA,0,0xCC); // Comando SKIP_ROM velocit (alcuni Kbps) ed half Ow_Write(&PORTA,0,0x44); // Comando CONVERT_T duplex, per in compenso risulta Delay_us(120); abbastanza robusta ed economica, soprattutto quando utilizzaOw_Reset(&PORTA,0); ta per gestire molti sensori. Il Ow_Write(&PORTA,0,0xCC); // Comando SKIP_ROM master pu indirizzare i vari Ow_Write(&PORTA,0,0xBE); // Comando READ_SCRATCHPA dispositivi utilizzando un indirizzo Delay_ms(400); a 64bit, oppure pu inviare comandi a tutti i dispositivi conlsb = Ow_Read(&PORTA,0); // Lettura temp. LSB temporaneamente. La comunicatemp = Ow_Read(&PORTA,0); // Lettura temp. LSB zione avviene a pacchetti, e viene utilizzato un CRC in coda per temp = (temp << 8) + lsb; controllare la correttezza dei dati temp = temp/2; ricevuti. Anche per questo tipo di interfaccia il MikroC mette a IntToStr(temp, buf); // Converte in stringa disposizione delle routine di libreLcd_Out(2, 1, buf); // Visualizza ria che ne facilitano molto lutilizzo. Per vederne unapplicazione Delay_ms(500); pratica, si consideri il circuito di Figura 4, in cui un PIC 16F84 } collegato ad un sensore di temperatura Dallas DS1820, che in } grado di rilevare la temperatura alle funzioni e viene inglobato nel primo coman- dellambiente, di convertirla in un valore digitale do inviato. Per segnalare linizio e la fine delle ope- a 9 bit (risoluzione di 0.5C), 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 lin- allesempio riportato nel Listato 3. dirizzo. Utilizzando questa modalit di accesso si Le funzioni disponibili per gestire linterfaccia persarebbe potuto ottenere una copiatura notevol- mettono di iniziare la comunicazione (Ow_Reset), mente pi veloce ed efficiente (lesempio per scrivere (Ow_Write) e leggere (Ow_Read) un dato

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 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
FARE ELETTRONICA - GENNAIO 2007

Disponibile con o senza display

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

TEORIA

MHZ

RISORSE

SPECIALE

PRATICA

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

82
Teoria

dal dispositivo. Ciascuna di queste funzioni prende in ingresso un puntatore alla porta a cui collegato il dispositivo, il numero del piedino (in questo caso RA0), e leventuale dato da trasmettere. Per eseguire la lettura della temperatura dal DS1820 necessario iniziare la comunicazione con un reset, specificare se si vuole indirizzare un particolare dispositivo o meno (codice skip ROM, 0xCC), comandare un inizio di conversione della temperatura (codice 0x44), e successivamente leggere il valore (codice 0xBE). Alla richiesta di lettura il DS1820 risponde inviando lintero contenuto della memoria locale, seguito dal CRC. In questo caso vengono letti soltanto i primi due byte, che sono quelli relativi alla temperatura (rispettivamente lLSB e lMSB del valore a 9 bit con segno). Per visualizzare sul display il valore stato prima di tutto ricomposto il valore intero (utilizzando una variabile a 16 bit), poi stata eseguita una divisione per 2, in modo da eliminare il bit relativo ai 0.5C. In questo modo il valore binario ottenuto corrisponde al valore della temperatura in gradi centigradi. Questo valore binario convertito in stringa utilizzando la funzione di libreria IntToStr del MikroC, e visualizzato con la consueta funzione Lcd_Out. INTERFACCIA SPI Linterfaccia SPI (Serial Peripheral Interface) consi-

ste in un bus seriale sincrono, che utilizza 4 fili (3 in alcuni casi): uno associato al clock seriale, comandato dal master, due sono utilizzati rispettivamente per i dati in ingresso e per quelli in uscita, ed il quarto, non sempre presente svolge la funzione di Chip Select, cio abilita o meno un particolare slave a ricevere i comandi. Linterfaccia SPI utilizzata in maniera simile (o a volte in luogo) dellinterfaccia I2C, e quindi i dispositivi che la utilizzano sono anche in questo caso molti e molto vari. Vista la similitudine utilizzeremo lo stesso esempio visti prima per linterfaccia I2C, utilizzando per una memoria EEPROM seriale SPI, in particolare la 25LC020, che funzionalmente simile alla 24LC02 vista in precedenza. Le temporizzazioni dei segnali richiesti per la lettura e scrittura dei dati sono mostrate in Figura 5. Lo schema utilizzato quello visto in Figura 1, in cui per la EEPROM I2C sostituita da quella SPI (i collegamenti sono mostrati in Figura 6). Il codice per leggere e scrivere la memoria riportato nel Listato 4. La struttura e le funzioni del codice sono identiche a quelle viste nel caso dellinterfaccia I2C, in questo caso si pu notare una certa semplificazione nelle funzioni di lettura e scrittura data dal fatto che non necessario gestire le condizioni di start e stop. Una differenza importante rispetto a quanto visto in precedenza consiste nel fatto che in

FARE ELETTRONICA - GENNAIO 2007

Figura 5 Temporizzazioni di lettura e scrittura della EEPROM SPI

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 alluso, inoltre le funzioni di editing dei simboli consentono la creazione di nuovi molto velocemente. SPlan 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

Codice MIP 259083

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

84
Teoria

questo caso la periferica non viene selezionata attraverso un indirizzo, ma piuttosto portando basso il rispettivo segnale CS. Questo fatto gestendo lo stato del pin RC0 (in questo caso negando il bit in questione con un XOR). Se necessario collegare pi dispositivi allo stesso bus, ciascuno deve avere una linea separata per il proprio segnale di selezione, che deve essere gestita esplicitamente (gli altri tre segnali possono essere collegati in comune). Se ad esempio volessimo leggere da una memoria il cui CS collegato a RC0 e scrivere in unaltra il cui CS collegato a RC1 dovremo scrivere:
PORTC=PORTC^1; dato=SPI_EEPROM_Rd(addr); PORTC=PORTC^1; PORTC=PORTC^2; PORTB=SPI_EEPROM_Rd(i&0x03); PORTC=PORTC^2;

// Scrittura dati PORTC=PORTC^1; SPI_EEPROM_Wr(0, 0x00); // SPI_EEPROM_Wr(1, 0x81); // ** SPI_EEPROM_Wr(2, 0xC3); // **** SPI_EEPROM_Wr(3, 0xE7); // ****** PORTC=PORTC^1; Delay_ms(500); i=0; While(1) { PORTC=PORTC^1; PORTB=SPI_EEPROM_Rd(i&0x03); PORTC=PORTC^1; i++; Delay_ms(500); } } void SPI_EEPROM_Wr(char addr, char data) { Spi_Write(2); Spi_Write(addr); Spi_Write(data); } char SPI_EEPROM_Rd(char addr) { char k, b=0; Spi_Write(3); Spi_Write(addr); k=Spi_Read(b); }

FARE ELETTRONICA - GENNAIO 2007

Va ricordato infine che anche per linterfaccia SPI il MikroC dispone di una libreria di emulazione software che possiede le stesse funzioni di quella utilizzata in precedenza (rispettivamente Soft_Spi_Config, Sort_Spi_Read e Soft_Spi_Write). E possibile in questo modo gestire dispositivi SPI anche con PIC che non dispongono della periferica MSSP. CONCLUSIONI Una volta compreso come utilizzare queste funzioni sar possibile sfruttarle per gestire i tanti ed utilissimi dispositivi che si basano sulle

interfacce trattate. Questo consente di espanderne notevolmente le funzionalit dei propri progetti in maniera molto semplice. Nella prossima puntata vedremo come utilizzare il convertitore Analogico/Digitale dei PIC e la memoria EEPROM interna. Codice MIP259076

TEORIA
Settima parte n 259 - Gennaio 2007

MHZ

RISORSE

SPECIALE

PRATICA

Interfacce SPI, I2C e 1-Wire


Ottava parte n 260 - Febbraio 2007

Uso del convertitore A/D


Nona parte n 261 - Marzo 2007

MikroC by Example
I/O dedicati. In particolare possibile utilizzare alcuni di essi come ingressi analogici o per impostare il valore inferiore e superiore della tensione da convertire. Il fatto di disporre di pi ingressi analogici rende possibile lacquisizione di segnali analogici provenienti da diverse sergenti. Utilizzare il convertitore AD per acquisire dei segnali analogici con il MikroC molto semplice, sufficiente infatti utilizzare la funzione di libreria Adc_Read. Un primo esempio di utilizzo di questa funzione mostrato nel Listato 1. Il circuito utilizzato per testare il programma quello visibile in Figura 1. Il segnale analogico da convertire, applicato alla porta RA2 pu variare tra 0V e la tensione di alimentazione positiva (che in questo caso 5V) ed prelevato da un trimmer utilizzato come partitore. Il codice esegue la conversione, legge il valore a 10 bit fornito dal convertitore e visualizza gli 8 bit pi significativi sui LED collegati alla porta B.

Realizzazione di un dataloger (I parte)

I
80
Teoria

n questa puntata scopriremo come gestire il convertitore analogico/digitale dei PIC con il MikroC e come utilizzarlo per realizzare delle interessanti applicazioni.

INTRODUZIONE In molte applicazione necessario acquisire il valore di tensioni analogiche. Questo pu essere fatto utilizzando il convertitore Analogico/Digitale (A/D) integrato nella maggior parte dei microcontrollori PIC. Il convertitore in questo caso ha una risoluzione minima di 10 bit ed una frequenza di campionamento massima di alcuni KHz. La gestione dei segnali analogici avviene utilizzando dei piedini di

FARE ELETTRONICA - FEBBRAIO 2007

Figura 1 Circuito per il test dellADC

Uso del Convertitore A/D


[Listato 1]
// *** VU meter semplice *** unsigned int dato; void main() { ADCON1 = 0x80; TRISA TRISB PORTB = 0xFF; = 0x00; = 0x00; // Conf. Ingr. Analog. // PORTA = input

di Antonio Di Stefano

while(1) { dato = Adc_Read(2); PORTB = dato>>2; Delay_ms(20); }; }

senza segno stata utilizzata una variabile a 16 bit per contenerlo. Il valore numerico ottenuto sar proporzionale alla tensione presente allingresso, in particolare si otterr il valore massimo (1023, cio 210-1) quando la tensione pari a quella di alimentazione (5V), e 0 quando la tensione sar pari a 0V. Ciascuna unit quindi corrisponde ad una tensione di 5/1024=4.8mV. Per conoscere i valore della tensione a partire dal valore numerico letto sufficiente quindi moltiplicarlo questa costante. Nellesempio, cos come in molte applicazioni comuni, una risoluzione di 10 bit risulta eccessiva, per cui si scelto di eliminare (con uno 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. REGOLAZIONE DI LUMINOSIT Nellesempio riportato di seguito utilizzeremo il valore analogico letto dal convertitore A/D per regolare la luminosit di una lampadina (o di un LED). Per fare questo verr generato un segnale

81
Teoria

La funzione Adc_Read prende come parametro il numero di canale analogico che si intende leggere (cio quale pin della porta si intende usare come ingresso), esegue la conversione, e soltanto quando questa terminata restituisce il valore convertito. Dal momento che il dato sar a 10 bit

FARE ELETTRONICA - FEBBRAIO 2007

Figura 2 Circuito per la regolazione della luminosit

TEORIA

MHZ

RISORSE

SPECIALE

PRATICA

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

PWM il cui duty cycle sar determinato in base al valore analogico letto. Il segnale analogico che verr utilizzato per comandare la regolazione di luminosit potr essere prelevato da un potenziometro, come mostrato in Figura 2, o da un fotodiodo, in modo da realizzare un interruttore crepuscolare (che cio aumenta la luminosit della sorgente quanto maggiore il buio). Il codice che implementa lapplicazione riportato nel Listato 2.
[Listato 2]
// *** Regolazione luminosit *** void main() { unsigned int level;

82
TRISC = 0;

PORTC = 0; Pwm_Init(1000); Pwm_Start(); // Init. PWM // Start PWM

// Loop infinito while(1) { level = Adc_Read(2); level=level >> 2; //level = 255-level; Pwm_Change_Duty(level); Delay_ms(20); } }

realizzare un semplice VU meter, cio un misuratore del livello di un segnale audio. Il circuito utilizzato uguale a quello mostrato in Figura 1, con lunica differenza che il segnale audio viene accoppiato allingresso analogico del PIC utilizzando la rete mostrata in Figura 3 invece del semplice trimmer. Se il segnale audio utilizzato ha unimpedenza sufficientemente bassa ed unampiezza di alcuni Vpp attorno a 0V, il segnale ottenuto allingresso analogico avr come valore medio 2.5V, e rientrer completamente nella gamma dinamica del convertitore (cio tra 0 e 5V). Il programma visualizza lampiezza dei campioni acquisiti accendendo un numero di LED proporzionale. In pi, dal momento che le variazioni dampiezza del segnale sono molto veloci, stato implementato un meccanismo per rendere pi visibile il livello pi alto raggiunto. In pratica linformazione visualizzata non direttamente lampiezza del segnale, ma una variabile che viene aggiornata con il massimo livello raggiunto, e che viene decrementata gradualmente (in pratica una specie di rivelatore di picco). Il programma visibile nel Listato 3.
[Listato 3]
// *** Versione con memorizzazione massimo unsigned int dato, maxi; char dis[9]={0,1,3,7,15,31,63,127,255}; void main() {

Teoria

FARE ELETTRONICA - FEBBRAIO 2007

La generazione del segnale PWM ottenuta con la funzioni di libreria del MikroC gi descritte nel numero 256 di Fare Elettronica, che utilizzano il modulo PWM hardware del PIC. Il valore del duty cycle determinato direttamente dal valore letto dallADC (dagli 8 bit pi significativi), oppure dal suo complemento nel caso si intenda utilizzare il circuito come crepuscolare (i sensori di luminosit in genere forniscono una tensione pi alta quanto maggiore il flusso che li investe). VU METER Il segnale analogico campionato negli esempi precedenti praticamente una tensione continua. E possibile comunque acquisire segnali variabili nel tempo (tensioni alternate). Questa capacit utilizzata nellesempio seguente per

ADCON1 = 0x80; inputs and Vref TRISA TRISB PORTB = 0xFF; = 0x00; = 0x00;

// Configure analog // PORTA is input

while(1) { dato = Adc_Read(2); if (dato>maxi) maxi=dato; PORTB = dis[((maxi+16)>>7)]; if (maxi>dato) maxi; Delay_ms(1); }; }

Come si pu vedere, una volta acquisito il dato, viene aggiornata la variabile con il valore massi-

mo, che utilizzata per calcolare il valore da visualizzare. Pi in dettaglio, dal valore ad 8 bit della variabile se ne ottiene uno a 3 bit (tramite uno shift a Figura 3 Circuito di accoppiamento per il destra), che segnale audio utilizzato come indice di unarray che contiene le 8 configurazioni dei LED da visualizzare (LED progressivamente accesi). Per sfruttare tutti i 9 valori, prima della divisione viene effettuato un arrotondamento del dato, sommando un piccolo offset. CONVERSIONE DI PI CANALI Come gi anticipato, nonostante il convertitore AD integrato del PIC sia uno solo, possibile comunque acquisire il valore di pi tensioni ana-

logiche utilizzando il multiplexer analogico interno. Per fare questo sufficiente selezionare in sequenza i differenti ingressi e campionarne il valore. La frequenza di campionamento ottenibile su ciascun canale circa pari a quella massima diviso il numero di canali campionati. Utilizzando la funzione Adc_Read risulta molto semplice eseguire la conversione su pi canali, come mostrato nellesempio seguente. Il programma riportato nel Listato 4 campiona tutti i 5 ingressi analogici presenti sul PIC 16F876 e visualizza il loro valore su un display LCD. Il circuito quello mostrato in Figura 4. La lettura dei diversi ingessi eseguita sequenzialmente con un ciclo for. In questo caso il valore non conservato, ma utilizzato subito per la visualizzazione. La conversione da intero a stringa eseguita con la funzione WordToStr. USO DELLE INTERRUZIONI Si pu notare che lalgoritmo utilizzato nel precedente esempio, sebbene adeguato allapplicazione, non molto efficiente, in quanto le

83
Teoria

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

TEORIA

MHZ

RISORSE

SPECIALE

PRATICA

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

[Listato 4]
// *** Conversione AD multicanale *** unsigned int data; char i, buf[8]; void main() { ADCON1 = 0x80; TRISA TRISB PORTB = 0xFF; = 0x00; = 0x00; // Config. input // PORTA = input

// Init. Display Lcd_Init(&PORTB); Lcd_Cmd(Lcd_CLEAR); Lcd_Cmd(Lcd_CURSOR_OFF); Lcd_Out(1, 1, CH while(1) { for(i=0; i<5; i++) { data = Adc_Read(i); WordToStr(data, buf); Lcd_Chr(1, 3, 0+i); Lcd_Out(1, 7, buf); Delay_ms(1000); } } } = );

84
Teoria

operazioni di conversione ed elaborazione dei dati sono gestite in modo del tutto sequenziale: la funzione Adc_Read infatti attende la fine della conversione prima di restituire un valore e quin-

di il controllo alla restante parte del codice. Nei casi in cui siano presenti dei requisiti di tempo o di elaborazione pi stringenti, conveniente sfruttare il tempo richiesto per effettuare la conversione per eseguire del codice. Organizzando opportunamente le varie parti del programma possibile in qualche modo parallelizzare le due operazioni. Per fare questo per necessario comandare manualmente il convertitore e gestire le interruzioni, come mostrato nellesempio seguente. Il Listato 5 implementa una versione modificata del VU meter visto prima, che impiega la tecnica appena descritta. A differenza di quanto visto nel Listato 3 in questo caso sono state abilitate le interruzioni associate al convertitore A/D (registri PIR1, PIE1 e INTCON), e la funzione Adc_Read utilizzata una sola volta per avviare la prima conversione e per impostare i restanti registri. Il loop principale ha il solo compito di aggiornare la variabile che memorizza il massimo e di visualizzare il risultato sui LED. Questi compiti vengono ripetuti in continuazione. Contemporaneamente lADC esegue una conversione, e al suo termine genera uninterruzione. La routine dinterruzione legge il dato convertito, lo memorizza nella variabile dato cancella il flag di interruzione del convertitore e riavvia una nuova conversione. Al suo termine la routine sar nuovamente richiamata, e riavvier una nuova conversione, come in un loop infinito, parallelo a quello principale. Leffetto complessivo sar che il codice

FARE ELETTRONICA - FEBBRAIO 2007

Figura 4 Campionamento multicanale e visualizzazione su LCD

presente nel loop principale verr eseguito in continuazione e la variabile dato verr aggiornata automaticamente ogni volta che una con[Listato 5]
// *** Gestione AD con interruzioni *** unsigned int dato, maxi; char dis[9]={0,1,3,7,15,31,63,127,255}; void main() { ADCON0 = 0x91; ADCON1 = 0x80; TRISA TRISB PORTB = 0xFF; = 0x00; = 0x00; // ADIF // ADIE // Config. input // Config. input // PORTA = input

versione sar completata. La frequenza di campionamento per non stabilita in modo preciso, essa infatti, dipende dal

while(1) { if (dato>maxi) maxi=dato; PORTB = dis[((maxi+16)>>7)]; if (maxi>dato) maxi; Delay_ms(1); }; } void interrupt() { dato=(ADRESH<<8)+ADRESL; // Reset ADIF Flag PIR1=0x00; // Avvio nuova conversione ADCON0=ADCON0|0x04; }

// Abilitazione interruzioni PIR1=0x00; PIE1=0x40; dato = 0; maxi = 0; // Avvio prima conversione ADCON0=ADCON0|0x04;

85
Teoria

INTCON = 0xC0; // GIE + PEIE

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 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
FARE ELETTRONICA - FEBBRAIO 2007

Disponibile con o senza display

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

TEORIA

MHZ

RISORSE

SPECIALE

PRATICA

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

tempo impiegato per completare la conversione e dal codice eseguito. In molti casi invece utile campionare dei segnali con una frequenza ben definita e costante. Per fare questo occorre utilizzare il timer, come mostrato nel Listato 6.
[Listato 6]
// *** Campionamento a 1KHz *** unsigned int dato, maxi; char dis[9]={0,1,3,7,15,31,63,127,255}; void main() { ADCON0 = 0x91; ADCON1 = 0x80; TRISA TRISB PORTB = 0xFF; = 0x00; = 0x00; = 0x82; // CLK/4/8 = 5; // 250 cicli // Config. input // Config. input // PORTA = input

86
Teoria

// Inizializz. Registri OPTION_REG TMR0 PIR1=0x00; PIE1=0x40; dato = 0; maxi = 0; // Avvio prima conversione ADCON0=ADCON0|0x04; while(1) { if (dato>maxi) maxi=dato; PORTB = dis[((maxi+16)>>7)]; if (maxi>dato) maxi; Delay_ms(1); }; } void interrupt() { // Interrupt timer: // avvio conversione e reset rimer if (INTCON&0x04) { ADCON0=ADCON0|0x04;

// Abilitazione interruzioni // ADIF // ADIE

Grazie allimpiego del Timer TMR0 si ottiene una frequenza di campionamento di 1KHz esatto. Come si pu vedere dal codice sono state abilitate le interruzioni relative al timer e al convertitore AD, quindi la routine dinterruzione verr richiamata sia alloverflow del timer, sia al completamento di ogni conversione. Allinterno della routine pertanto necessario distinguere quale delle due periferiche ha generato linterruzione (controllando lo stato dei flag). Se linterruzione stata generata dal timer, viene avviato il convertitore e resettato il timer. Se linterruzione stata generata dal convertitore, viene salvato il dato convertito e resettato il flag. In questo modo la frequenza di campionamento stabilita dal timer, e laggiornamento della variabile avviene solo quando il convertitore ha completato il suo lavoro, tutto questo senza interferire sensibilmente con lesecuzione della routine contenuta nel loop principale. CONCLUSIONI Prima di concludere il caso di puntualizzare alcuni dettagli riguardanti il convertitore AD ed il suo utilizzo. Innanzi tutto ricordo che maggiori dettagli sui registri utilizzati e sulla loro funzione possono essere trovati sul datasheet del PIC 16F876. Va notato anche che negli esempi si tralasciata lattesa del tempo di acquisizione precedente ad ogni conversione. Questo tempo necessario per caricare le capacit interne del convertitore AD. Se si suppone che limpedenza dei segnali utilizzati sufficientemente bassa, il tempo richiesto dellordine di pochi cicli macchina, e quindi pu in effetti essere trascurato. Infine va ricordato che per campionare un segnale analogico, e poterlo ricostruire senza perdite, necessario utilizzare una frequenza di campionamento almeno doppia rispetto alla banda del segnale (teorema di Nyquist). Negli esempi relativi al VU meter non si tenuto conto di questo vincolo, perch lobiettivo non era la ricostruzione del segnale. Il convertitore AD e tutte le altre periferiche utilizzate negli scorsi numeri saranno utilizzati nelle prossime puntate per realizzare un progetto piuttosto complesso, ma di sicuro interesse: un datalogger che pu essere utilizzato per acquisire e memorizzare dati con continuit e trasferirli al PC su richiesta. Codice MIP 260080

INTCON = 0xE0; // GIE + PEIE + TMRIE

FARE ELETTRONICA - FEBBRAIO 2007

TMR0 = 5; INTCON = 0x20; } // Interrupt ADC:

// reset timer // cancella flag Timer

// lettura dato, reset flag if (PIR1&0x40) { dato=(ADRESH<<8)+ADRESL; PIR1=0x00; } } // cancella flag ADIF

TEORIA
Ottava parte n 260 - Febbraio 2007

MHZ

RISORSE

SPECIALE

PRATICA

Uso del convertitore A/D


Nona parte n 261 - Marzo 2007

Realizzazione di un dataloger (I parte)


Decima parte n 262 - Aprile 2007

MikroC by Example
essere impiegato in una moltitudine applicazioni, quali il monitoraggio ambientale, la rilevazione di dati meteorologici, la misurazione del consumo di potenza in utenze domestiche, la registrazione di dati relativi a esperimenti o allevoluzione di sistemi naturali, etc. In questa puntata verranno descritti i criteri di progetto dellhardware e del firmware e alcune delle routine utilizzate. Nella successiva verr completata la descrizione del firmware e verranno considerati alcuni miglioramenti e limplementazione di funzioni aggiuntive. SPECIFICHE Il datalogger che si vuole realizzare dotato delle seguenti caratteristiche: Possibilit di campionare 4 ingressi analogici (0-5V) con risoluzione di 8 bit, 8 ingressi digitali TTL e la temperatura ambiente con la precisione di circa 1C. La frequenza di campionamento selezionabile tra 1 campione al secondo e 1 campione ogni 255 minuti (circa 4 ore). possibile memorizzare 2000 campioni per ciascun segnale. possibile impostare la registrazione continua (i dati pi vecchi vengono sovrascritti da quelli nuovi, quindi nella memoria saranno presenti solo i 2000 campioni pi recenti). Linterfacciamento con un PC avviene via RS232 per la configurazione tramite comandi testuali ed il download dei dati. possibile selezionare se campionare 4 ingressi analogici oppure 2 analogici, uno digitale ed uno relativo alla temperatura. Lavvio e larresto del campionamento avvengono tramite un pulsante o un comando seriale. Un LED rosso lampeggiante indica che il campionamento attivo, un LED verde invece la fine del campionamento (se nessun LED acceso significa che il campionamento non

Realizzazione di un dataloger (II parte)

I
84
Teoria
FARE ELETTRONICA - MARZO 2007

n questa puntata e nella prossima verr mostrata la realizzazione di un datalogger programmabile, capace di registrare segnali analogici e digitali per un intervallo esteso di tempo e di scaricarli su PC al termine dellacquisizione. Verranno descritti gli schemi elettrici ed il firmware realizzato con il MikroC.

INTRODUZIONE Nelle scorse puntate stato mostrato come implementare alcuni algoritmi utili e come gestire diverse periferiche interne al PIC o esterne, utilizzando il linguaggio C ed in particolare le librerie messe a disposizione dal MikroC. In questa puntata e nella prossima verr presentato un progetto completo e piuttosto complesso che mette assieme la maggior parte degli elementi visti in precedenza. Verr infatti descritta la realizzazione di un datalogger, cio un sistema capace di campionare e di registrare con continuit su un intervallo di tempo piuttosto lungo dei valori analogici e digitali provenienti da sensori o apparati esterni. I datalogger sono utilizzati per tenere sotto controllo sistemi o apparati di vario genere, che necessitano un monitoraggio continuo. I dati registrati durante lintero intervallo di osservazione possono essere successivamente scaricati su PC per essere visualizzati ed analizzati meglio con software dedicati. Il progetto qui presentato ha delle caratteristiche abbastanza interessanti e pu

Realizzazione di un datalogger
iniziato e quindi non ci sono dati validi in memoria). Da queste specifiche risulta che il datalogger in grado di campionare ininterrottamente i dati per un intervallo di tempo compreso tra circa 33 minuti e quasi un anno, ed abbastanza versatile da potere essere utilizzato in una grande variet di applicazioni! SCHEMA ELETTRICO Lo schema elettrico utilizzato per il datalogger riportato in Figura 1. Il cuore del circuito un comune PIC16F876, che viene fatto funzionare a 8MHz. Questo PIC dotato di un convertitore A/D a 10 bit e di 5 ingressi analogici, e pu quindi soddisfare le specifiche. Gli 8 ingressi digitali possono essere ricavati dalla porta B. Per memorizzare i dati necessaria una memoria non volatile, ma non possibile utilizzare la EEPROM del PIC per via delle sue ridotte dimensioni. E necessario pertanto collegare una memoria non volatile esterna di dimensioni ade-

di Antonio Di Stefano

guate. Si scelto di utilizzare una EEPROM seriale I2C da 64Kbit per via delle ridotte dimensioni del suo package, della semplicit di utilizzo e per il fatto che il suo interfacciamento richiede soltanto due piedini. Per rilevare la temperatura si scelto di collegare un sensore digitale del tipo DS1820 al piedino RA5. Il sensore della Dallas fornisce direttamente la temperatura in formato digitale con una buona accuratezza, utilizzando linterfaccia 1-Wire, che richiede soltanto un collegamento. Dal momento che il pin RA5 anche un ingresso analogico, non comunque difficile sostituire questo componente con un sensore di temperatura analogico come lLM35 se desiderato, ed utilizzare il convertitore interno per campionare il dato. La comunicazione con il PC avviene utilizzando la periferica UART integrata nel PIC. necessario per utilizzare un MAX232 per adattare i livelli di segnalazione a quelli dello standard RS232. I due LED che segnalano lo stato di campionamento in corso/completato sono collegati

85
Teoria
FARE ELETTRONICA - MARZO 2007

Figura 1 Schema complessivo del datalogger

TEORIA
Nona parte

MHZ

RISORSE

SPECIALE

PRATICA

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

86
Teoria

FARE ELETTRONICA - MARZO 2007

ai pin RC0 ed RC1, mentre il pulsante di avvio/arresto collegato alla porta RC5. In pratica sono utilizzati quasi tutti i pin del PIC! Tuttavia ancora possibile aggiungere periferiche, se necessario, utilizzando linterfaccia I2C o 1-Wire. I campioni verranno memorizzati nella EEPROM esterna sequenzialmente e a gruppi di 4 byte. Infatti secondo le specifiche possono essere campionati e memorizzati o i 4 ingressi analogici, o soltanto due di questi, pi lo stato degli ingressi digitali (che coincide con il valore letto sulla porta B), e la temperatura. Tutte queste grandezze sono ad 8 bit e possono essere memorizzate singolarmente in ciascuna locazione della memoria (che organizzata proprio ad 8 bit). Sar inoltre necessario memorizzare sempre nella EEPROM esterna il puntatore allindirizzo dellultimo dato memorizzato (utile nel 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 possibile memorizzare 2048 campioni per ciascuno dei 4 dati. Considerando che i dati da memorizzare 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 velocit 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 spegnimento del dispositivo dopo la configurazione). Anche in questo caso utilizzata la EEPROM esterna. STRUTTURA DEL FIRMWARE Dal momento che il progetto che vogliamo realizzare relativamente pi complesso di quelli visti fino ad ora, utile discutere lorganizzazione ed il funzionamento del firmware prima di considerare limplementazione vera e propria (il codice). I compiti che il datalogger deve svolgere sono in sintesi i seguenti:

Misurare intervalli di tempo. Campionare i dati. Memorizzare i dati sulla EEPROM. Comunicare con il PC per ricevere la configurazione e per scaricare i dati.

Si pu notare che questi compiti non sono svolti continuamente, ma sono attivati da due eventi: loverflow del timer e la ricezione dei caratteri dalla seriale. Per questo motivo conveniente strutturare il programma in base a questa considerazione. La struttura del software visibile in Figura 2. Come si pu vedere sono presenti due parti ben distinte: il loop principale e la routine dinterruzione. Questultima richiamata dagli eventi a cui si fatto riferimento prima. Al verificarsi di questi eventi il codice presente nella routine sar eseguito e generer dei segnali di controllo per attivare le altre funzioni. Queste

Figura 2 Struttura del firmware del datalogger

funzioni sono gestite dal loop principale, e vengono eseguite solo se prima sono stati attivati i rispettivi segnali di controllo. Ovviamente i segnali di cui si parla, e che consentono la comunicazione tra la routine dinterruzione ed il loop principale, non sono altro che delle variabili globali, settate da una routine e resettate dallaltra. Pi in dettaglio i compiti da gestire sono due: il campionamento pi la memorizzazione dei dati, e la decodifica dei comandi inviati dal PC, usati per impostare la configurazione e richiedere linvio dei dati memorizzati. Anche le variabili di segnalazione sono due, chiamate stringa e trig. La prima viene impostata ad 1 dalla routine dinterruzione quando stata ricevuta e memorizzata (in un apposito buffer) una stringa completa (terminata da un a capo). Il secondo segnale viene generato quando il timer raggiunge lintervallo di tempo di campionamento prefissato. Una volta che i due compiti sono stati espletati (la stringa decodificata o il campiona-

mento eseguito), le due variabili vengono riportate al valore 0, in attesa di un nuovo evento. Comandi, decodifica e configurazione I comandi gestiti dal datalogger sono i seguenti: Axxx: imposta lintervallo di campionamento a xxx minuti. Il valore di xxx pu variare da 0 a 255, se viene specificato 0 i dati vengono campionati ogni secondo. Bx: imposta se campionare i 4 canali analogici (x=0) o due analogici, 8 digitali (porta B) e uno di temperatura (x=1). Cx: se x=0 vengono registrati soltanto 2000 campioni, se x=1 la registrazione continua, i dati vengono sovrascritti in continuazione ed in memoria sono presenti sempre gli ultimi 2000. D: mostra la configurazione attuale. L: attiva o disattiva la modalit monitor (i dati campionati sono anche inviati alla porta seriale, e quindi visualizzati sul PC in tempo reale).

87
Teoria

Compilatore MikroC
Un potente compilatore C per PICmicro
Code Editor Code Explorer Debugger Statistiche
Tutto in un ambiente Windows facile ed intuitivo
Un set di strumenti veramente indispensabili per sviluppare applicazioni con i PICmicro
Codice MIP 261087
FARE ELETTRONICA - MARZO 2007

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

TEORIA
Nona parte

MHZ

RISORSE

SPECIALE

PRATICA

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

E: avvia o ferma il campionamento. S: scarica i dati contenuti in memoria sul PC. M: nostra il menu (i comandi).

88
Teoria

Tutti i comandi inviati devono terminare con un carattere di a capo, codice ASCII 10. Questo carattere infatti viene utilizzato per individuare il termine della stringa, e quindi per iniziarne la decodifica. La routine dinterruzione infatti riceve i singoli caratteri e li accumula in un buffer temporaneo ricostruendo la stringa, fino a quando il carattere di a capo non stato ricevuto, a quel punto la variabile di segnalazione stringa posta ad 1, e il comando pu essere decodificato. La decodifica viene fatta semplicemente leggendo il primo carattere e gli eventuali parametri (convertiti da testo a intero). In base a questi dati viene eseguita loperazione corrispondente. La maggior parte dei comandi va a modificare la configurazione e/o lo stato del sistema, che rappresentato dai seguenti parametri, raggruppati nella struttura config:
struct cnf { char periodo; char canali; char loop; char logging; char monitor; unsigned data_ptr; unsigned n_campioni; } config;

quenza di 1KHz esatto (periodo di 1ms). Questa frequenza viene utilizzata per incrementare una serie di variabili che contano i millisecondi, i secondi ed i minuti. Ogni volta che il valore di tempo coincide con quello programmato, viene posta ad 1 la variabile trig, e quindi avviato il campionamento e la memorizzazione dei 4 dati. IL CODICE Nel Listato 1 sono riportate alcune parti del codice, in particolare quelle relative alle dichiarazioni, alla routine dinterruzione ed alla gestione dei comandi. Il resto del codice, cio quello relativo al campionamento ed alla memorizzazione, sar discusso nella prossima. Allinizio de programma vengono definite delle costanti, alcune come macro (riferite alla lunghezza del buffer dei comandi ed al numero di campioni da memorizzare), altre grazie al qualificatore const. Queste ultime sono delle stringhe che vengono utilizzate nella comunicazione seriale. Dichiarare le stringhe in questo modo fa si che esse vengono memorizzate nella flash invece che nella RAM del PIC (dove non potrebbero essere contenute a causa della ridotta dimensione di questa). Successivamente vengono dichiarate le variabili globali utilizzate per condividere dati e scambiare informazioni tra le routine. In particolare le variabili stringa e trig sono le variabili di segnalazione descritte prima. La variabile buf (un array) usata come buffer per memorizzare le stringhe passate dalla seriale, msec, sec e mins sono usate per tenere il tempo, mentre pulsante un contatore usato per lantirimbalzo del pulsante di start/stop. La struttura config contiene la configurazione e lo stato attuale, come descritto prima. Tra le funzioni presenti alcune sono utilizzate per visualizzare le stringhe. In particolare risulta interessante SendFlashString, che trasmette alla seriale una delle stringhe memorizzate nella flash, usando la funzione Flash_Read del MikroC. La funzione main molto semplice: richiama una serie di funzioni di inizializzazione, e poi avvia il loop principale, che contiene la funzione Comandi usata per la decodifica dei comandi inviati via RS-232. Essa non fa altro che veri-

Oltre alle impostazioni gi viste sono presenti il puntatore allindirizzo della memoria EEPROM riferito al dato attuale, ed il numero di campioni memorizzati. Misura del tempo Per misurare gli intervalli di tempo viene utilizzato il timer hardware del PIC (il TMR0), pi alcune variabili e contatori. Il TMR0 stato programmato per contare ad una frequenza pari a quella dei cicli macchina diviso 16. Partendo dalla frequenza di 8MHz, diviso 4 cicli di clock per ciclo macchina, e ancora diviso 16, si ottiene una frequenza di 125KHz. Se si imposta un valore di reset tale da avere un overflow dopo 125 cicli, si ottiene una fre-

FARE ELETTRONICA - MARZO 2007

ficare se la variabile stringa vale 1 (stringa pronta da decodificare) e distinguere il comando inviato in base alla prima lettera, usando un costrutto swirch-case. Ciascuna sezione non fa altro che impostare un valore della configurazione oppure visualizzare determinate informazioni. La routine dinterruzione esegue i due compiti descritti prima. Il riconoscimento della sorgente dinterruzione (timer o UART) fatta controllando i rispettivi flag dinterruzione. Nella sezione che riguarda il timer, e che viene richiamata ogni millisecondo, vengono aggiornate le variabili per il conteggio del tempo, e verificato se il tempo trascorso coincide con quello impostato per il campionamento. Inoltre viene gestita anche la pressione del pulsante di start/stop, con un conteggio che serve sia come anti-rimbalzo che per ritardare lesecuzione del comando di circa un secondo (per evitare di attivare o fermare accidentalmente il campionamento).

CONCLUSIONI Nonostante il codice presentato non sia ancora completo, sufficientemente intuitivo da consentirne la comprensione del funzionamento. Nella prossima puntata verranno analizzate anche le restanti routine dedicate al campionamento e memorizzazione dei dati, ed al download sul PC. Saranno anche presentati alcuni possibili miglioramenti sia allhardware che al software. Il codice completo sar anche scaricabile dal sito di Fare Elettronica. Codice MIP 261084

NON DIMENTICARE
Se rinnovi in anticipo il tuo abbonamento non perdi alcun numero: il nuovo abbonamento va in coda e ti garantisci la continuit e il risparmio!

89
Teoria

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 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
FARE ELETTRONICA - MARZO 2007

Disponibile con o senza display

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

Ottava parte: Febbraio 2007 Uso del convertitore A/D Nona parte: Marzo 2007 Realizzazione di un datalogger (I parte) Decima parte: Aprile 2007 Realizzazione di un datalogger (II parte) Undicesima parte: Maggio 2007 Utilizzo dellinterfaccia Ethernet

MikroC by Example
possono essere notate gi nel codice riportato nel Listato 1, in cui visibile la parte iniziale, che comprende le dichiarazioni (la tabella delle stringhe non riportata per intero per brevit), i prototipi e la funzione main.
Listato 1
// *** Definizioni *** #define MAX_BUF_LEN 32 #define N_SAMP #define ADDR_INFO #define ADDR_END 2000 8100 7999

Nella scorsa puntata stato descritto lhardware e la struttura del software del datalogger e sono state mostrate alcune routine. In questa verranno analizzate con maggiore dettaglio le 106
TEORIA

restanti funzioni, e verranno indicate alcune possibili ottimizzazioni.

Breve riepilogo
Il datalogger presentato nella scorsa puntata svolge la funzione di campionare e memorizzare ad intervalli regolari e su un tempo piuttosto lungo 4 segnali analogici, oppure 2 segnali analogici, 8 segnali digitali e la temperatura ambiente. Lo strumento costituito da un PIC 16F876, da una EEPROM seriale I2C da 64Kbit, da un sensore di temperatura One-Wire DS1820. Il sistema viene collegato ad un PC via RS-232 per essere programmato o per scaricare i dati. Una volta impostati i parametri (intervallo di campionamento, configurazione dei canali, etc.) il sistema pu comunque funzionare autonomamente, e deve essere nuovamente collegato al PC solo per scaricare i dati acquisiti. Come visto nella scorsa puntata il programma guidato da due eventi: gli intervalli di campionamento e larrivo di una stringa di testo dal PC (che deve essere decodificata, e gestita opportunamente). Questi due eventi sono gestiti dalla routine dinterruzione, che provvede a segnalare alle funzioni presenti nel loop principale (tramite alcune variabili globali) lazione da compiere. Per maggiori dettagli sul funzionamento del software e sullhardware si rimanda al numero precedente di FE, in cui sono anche state introdotte alcune delle routine impiegate.
// *** Tabella stringhe *** const char *str_intro=*** DataLogger v1.0 ***\r\n; const char *str_menu1=\r\n; // *** Variagili globali *** struct cnf { char periodo; char canali char loop char logging char monitor :1; :1; :1; :1; Comandi -

unsigned data_ptr; unsigned n_campioni; } config; char stringa, bpnt; char buf[MAX_BUF_LEN]; unsigned i, n; unsigned msec, pulsante; unsigned char secs, mins, trig, dato[4]; // *** Prototipi *** void Init(void); void Comandi(void); void Campiona(void); void Memorizza(void); void Config_Store(void); void Config_Load(void);

FARE ELETTRONICA - APRILE 2007

Dichiarazioni e main
La versione completa del programma, rispetto a quella introdotta nellarticolo precedente, presenta alcune funzioni ed accorgimenti aggiuntivi, che

Realizzazione di un datalogger
di Antonio Di Stefano
void EEPROM_Wr(unsigned addr, char data); char EEPROM_Rd(unsigned addr); void SendString(char *); void SendFlashString(unsigned); void Show_Menu(void); void Show_Config(void); void Download(void); void Print_data(unsigned m, char a, char b, char c, char d); // *** Main *** void main() { Init(); SendFlashString(str_intro); Show_Menu(); while(1) { if (trig) { Campiona(); Memorizza(); if (config.monitor) Print_data(secs, dato[0], dato[1], dato[2], dato[3]); trig=0; } else { if (config.n_campioni==N_SAMP) { PORTC|=0x02; Delay_ms(20); PORTC&=0xFD; Delay_ms(900); } } Comandi(); } }

dati relativi alla configurazione (nella memoria non volatile), e lindirizzo finale della memoria riservata ai dati. Si pu notare che il tipo strutturato che memorizza la configurazione stato definito in maniera un po diversa rispetto a quello presentato nella scorsa puntata: dal momento che alcuni campi assumono soltanto due valori durante il funzionamento (in particolare 0 o 1), si specificato :1 accanto al loro nome. Questa particolare notazione (prevista dal C standard), consente di specificare proprio il numero di bit da utilizzare, limitando cos le richieste di memoria (pi campi saranno impacchettati in un unico byte). La funzione main ed il loop principale in essa contenuto , come si pu vedere, molto semplice. Dopo avere richiamato le funzioni di inizializzazione, nel loop vengono eseguite soltanto le funzioni di campionamento, memorizzazione e decodifica dei comandi. Le prime sono eseguite solo quando la variabile trig posta ad 1 dalla routine dinterruzione. La seconda invece controlla, come spiegato nello scorso articolo, che la variabile stringa valga 1 (stringa completa ricevuta dal PC) prima di eseguire la decodifica.

107
TEORIA

Descrizione delle funzioni


Alcune funzioni, come quella dinterruzione o quella di decodifica dei comandi, sono state presentate gi nella precedente puntata, le altre verranno descritte di seguito. Queste funzioni si occupano essenzialmente del campionamento dei dati, della scrittura e lettura dei dati e della configurazione nella EEPROM, e linvio dei dati al PC.
FARE ELETTRONICA - APRILE 2007

Si pu notare che sono state definite alcune costanti come macro. Queste facilitano la personalizzazione del programma se si intende utilizzare memorie di dimensioni diverse. Le costanti specificano in particolare il numero massimo di campioni da memorizzare, lindirizzo di inizio dei

Funzione di campionamento La funzione che acquisisce i dati riportata nel Listato 2. Il funzionamento molto semplice: a seconda della configurazione scelta per i canali vengono letti i valori analogici relativi ai primi 4 canali, oppure soltanto due di questi, il valore della porta B (che comprende lo stato digitale di ciascuna delle sue 8 linee), ed il valore della temperatura, letto dal sensore DS1820. Per eseguire tutte queste opera-

zioni vengono utilizzate le funzioni Adc_Read(), e quelle della libreria One-Wire del MikroC, gi discusse ed utilizzate nelle scorse puntate. La ruotine di lettura della temperatura esattamente uguale a quella utilizzata nel numero 259 di FE.
Listato 2
void Campiona(void) { int tt;

numero 259. In questo caso per la EEPROM impiegata, una 24LC64, richiede due byte per lindirizzamento, ed quindi necessario ricavare la parte alta (MSB) e quella bassa (LSB) dellindirizzo. Queste due funzioni sono richiamate da tutte le altre che necessitano di accedere alla EEPROM. Questo significa che modificandole possibile impiegare tipi di memoria diversi (ad esempio la EEPROM interna al PIC, o una flash seriale esterna).
Listato 3

if (!config.canali) { // Modalit AAAA dato[0] = (char)(Adc_Read(0)>>2); dato[1] = (char)(Adc_Read(1)>>2); dato[2] = (char)(Adc_Read(2)>>2); dato[3] = (char)(Adc_Read(3)>>2); } else { // Modalit AABT void EEPROM_Wr(unsigned addr, char data) { // Issue I2C start signal I2C_Start(); // Send byte via I2C I2C_Wr(0xA0); // Send byte (MSB address) I2C_Wr((addr>>8)&0xFF); // Send byte (LSB address) I2C_Wr(addr&0xFF); // Send data (data to be written) // Configura porta A come I/O digitale ADCON1 = 0xFF; PORTA TRISA = 0xFF; = 0xFF; char EEPROM_Rd(unsigned addr) Delay_us(120); Ow_Reset(&PORTA,5); // Comando SKIP_ROM Ow_Write(&PORTA,5,0xCC); // Comando CONVERT_T Ow_Write(&PORTA,5,0x44); Delay_us(120); Ow_Reset(&PORTA,5); // Comando SKIP_ROM Ow_Write(&PORTA,5,0xCC); // Comando READ_SCRATCHPAD Ow_Write(&PORTA,5,0xBE); Delay_ms(1); // Lettura LSB tt = Ow_Read(&PORTA,5); // Lettura MSB tt = tt+(Ow_Read(&PORTA,5)<<8); dato[3] = (char) tt/2;
FARE ELETTRONICA - APRILE 2007

(command to 24LC64)

108
TEORIA

dato[0] = (char)(Adc_Read(0)>>2); dato[1] = (char)(Adc_Read(1)>>2); dato[2] = PORTB;

I2C_Wr(data); I2C_Stop(); }

{ char k; // Issue I2C start signal I2C_Start(); // Send byte via I2C I2C_Wr(0xA0); // Send byte (MSB address) I2C_Wr((addr>>8)&0xFF); // Send byte (LSB address) I2C_Wr(addr&0xFF); // Issue I2C start signal I2C_Repeated_Start(); // Send byte (device address + R) I2C_Wr(0xA1); // Read the data (NO acknowledge) k = I2C_Rd(0); I2C_Stop(); return k; } (device address + W)

ADCON1=0x82; PORTA=0; TRISA=0xFF; } }

Scrittura e lettura dalla EEPROM Anche in questo caso le routine utilizzate (visibili nel Listato 3) sono simili a quella descritte nel

Memorizzazione dei dati La funzione di memorizzazione, scrive i 4 dati acquisiti sulla memoria (sequenzialmente), provvedendo anche ad aggiornare il puntatore allultimo dato scritto, ed il numero complessivo dei campioni (questa caratteristica utile per recu-

perare correttamente i dati, anche se il datalogger viene spento). Essa responsabile inoltre dellarresto del campionamento, quando viene raggiunto il numero di campioni prestabilito, o lindirizzo finale della zona riservata ai dati. Il codice della funzione riportato nel Listato 4.
Listato 4
void Memorizza(void) { // Memorizza campioni EEPROM_Wr(config.data_ptr, dato[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]); config.data_ptr++; config.n_campioni++; // Memorizza puntatore e num. campioni EEPROM_Wr(ADDR_INFO+2, (config.n_campioni)&0xFF); EEPROM_Wr(ADDR_INFO+3, (config.n_campioni>>8)&0xFF);

EEPROM_Wr(ADDR_INFO+4, (config.data_ptr)&0xFF); EEPROM_Wr(ADDR_INFO+5, (config.data_ptr>>8)&0xFF); // Controllo fine memoria if ((config.n_campioni==N_SAMP)|| (config.data_ptr>ADDR_END)) { if (config.loop) { config.data_ptr=0; } else { config.logging=0; } } }

Memorizzazione configurazione Anche i parametri relativi alla configurazione attuale sono memorizzati nella EEPROM per consentire di separare la fase di programmazione (che necessita di un PC), dalleffettivo impiego per il campionamento, che pu avvenire anche in maniera autonoma. I parametri della configurazione sono memorizzati in due byte: il primo contiene il periodo di campionamento, mentre il

109
TEORIA

Compilatore MikroC
Un potente compilatore C per PICmicro
Code Editor Code Explorer Debugger Statistiche
Tutto in un ambiente Windows facile ed intuitivo
Un set di strumenti veramente indispensabili per sviluppare applicazioni con i PICmicro
Codice MIP 262109
FARE ELETTRONICA - APRILE 2007

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

secondo gli altri parametri ad 1 bit accorpati assieme. La funzione di lettura (Config_Load) legge ed aggiorna anche il valore del puntatore e del numero di campioni memorizzati nella EEPROM (che occupano i 4 byte successivi alle altre informazioni). Il codice delle funzioni mostrato nel Listato 5.
Listato 5
void Config_Store(void) { unsigned char x; EEPROM_Wr(ADDR_INFO, config.periodo); x=config.canali<<2; x|=config.loop<<1; x|=config.monitor; EEPROM_Wr(ADDR_INFO+1, x);

Listato 6
void Download(void) { static unsigned addr, k; static char a, b, c, d; addr=0; k=config.n_campioni; if (config.loop) addr=config.data_ptr; if (config.n_campioni>N_SAMP) k=N_SAMP; for(i=0; i<k; i++) { a=EEPROM_Rd(addr); addr++; b=EEPROM_Rd(addr); addr++; c=EEPROM_Rd(addr); addr++; d=EEPROM_Rd(addr); addr++; if (addr>ADDR_END) addr=0; Print_data(i, a, b, c, d);

110
TEORIA

void Config_Load(void) { unsigned char x; } config.periodo=EEPROM_Rd(ADDR_INFO); x=EEPROM_Rd(ADDR_INFO+1); config.canali=(x>>2)&0x01; config.loop=(x>>1)&0x01; config.monitor=x&0x01; config.n_campioni=EEPROM_Rd(ADDR_INFO+2)+ (EEPROM_Rd(ADDR_INFO+3)<<8); config.data_ptr=EEPROM_Rd(ADDR_INFO+4)+ (EEPROM_Rd(ADDR_INFO+5)<<8); }

} Config_Store();

Conclusioni
Per questioni di spazio non possibile riportare il codice completo, che pu comunque essere scaricato dal sito di Fare Elettronica (www.farelettronica.com) nella sezione rivista. I frammenti di codice riportati dovrebbero essere comunque sufficientemente rappresentativi dellintero firmware e delle tecniche adottate per il suo sviluppo. Una volta compreso il suo funzionamento possibile apportare diverse modifiche per migliorare lefficienza e la flessibilit del programma. Una delle pi interessanti consiste ad esempio nellimpiegare un Real Time Clock I2C esterno (come il MAX1307) per fornire sia un riferimento temporale pi preciso, sia per attivare periodicamente (ad esempio ogni secondo) il PIC, permettendogli di entrare nello stato di sleep durante i periodi di inattivit. Questo permette di ridurre significativamente i consumi del sistema, e quindi di assicurarne una maggiore autonomia se si usa unalimentazione a batteria.

Download dei dati Una volta completata lacquisizione possibile scaricare i dati memorizzati inviando il comando S via seriale. Questo comando richiama la funzione riportata nel Listato 6, che legge sequenzialmente i dati dalla EEPROM e li trasmette al PC. Se era stata imposta una registrazione singola, i dati vengono letti a partire dallinizio della memoria. Se stata effettuata una registrazione continua, viene controllato il numero dei dati registrati, se questo inferiore alla capacit di memorizzazione, i dati vengono letti a partire dallinizio e fino al numero registrato. Se superiore, significa che i dati pi vecchi presenti in memoria inizieranno dalla locazione successiva a allultima impiegata, e dovranno essere letti in maniera circolare.

FARE ELETTRONICA - APRILE 2007

Codice MIP 262106 www.farelettronica.com/mip

Ottava parte: Febbraio 2007 Uso del convertitore A/D Nona parte: Marzo 2007 Realizzazione di un datalogger (I parte) Decima parte: Aprile 2007 Realizzazione di un datalogger (II parte) Undicesima parte: Maggio 2007 Utilizzo dellinterfaccia Ethernet

MikroC by Example
Con pochissimi componenti quindi possibile interfacciare un PIC a Ethernet. Occorre per implementare il firmare la gestione dei protocolli: questa operazione pu essere pi o meno complessa a seconda delle funzionalit che si desidera ottenere. Come verr mostrato nei prossimi paragrafi, anche in questo caso il MikroC permette di semplificare molto il lavoro necessario per realizzare unapplicazione dotata di connettivit di rete, grazie alla disponibilit di funzioni di libreria apposite. Per semplificare ulteriormente la prototipizzazione e la sperimentazione, la Mikroelektronika ha realizzato una piccola scheda, dotata del 28J60, pensata per essere connessa direttamente alle schede di sviluppo EasyPIC3/4 (Figura 1). La scheda deve essere connessa al connettore della EasyPIC che fa riferimento alla porta C dei PIC (sulla quale si trova la periferica MSSP, che gestisce linterfaccia SPI), come mostrato in Figura 2. La scheda viene alimentata direttamente dal connettore, e non richiede quindi ulteriori collegamenti per funzionare, tranne quello ad un cavo di rete Ethernet, che avviene attraverso il

In questa puntata conclusiva della serie dedicata al MikroC verr presentata una delle pi interessanti e promettenti possibilit offerte da questo compilatore e dagli strumenti di sviluppo forniti dalla 102
TEORIA
FARE ELETTRONICA - MAGGIO 2007

Mikroelektronika: linterfacciamento di un PIC con una rete Ethernet.

Lenorme diffusione che si avuta negli ultimi anni delle reti locali e di Internet, rende possibile realizzare applicazioni capaci di scambiare dati e comandi con dispositivi dislocati praticamente in qualsiasi parte del pianeta. Questo reso pi semplice dal fatto anche gli standard ed i protocolli utilizzati sono sostanzialmente uniformi. In particolare uno dei livelli fisici pi diffusi ed efficienti nel campo delle reti locali Ethernet (IEEE 802.3). Ethernet rappresenta quindi un punto daccesso, non solo per comunicare con i dispositivi collegati nella stessa rete locale, ma anche per 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 allinterfacciamento fisico necessario utilizzare anche protocolli appositi, quali il TCP/IP o lUDP. I protocolli stabiliscono lorganizzazione dei dati nei pacchetti, e le regole di comunicazione tra i dispositivi. Fino a qualche tempo fa linterfacciamento fisico di un microcontrollore a reti Ethernet risultava piuttosto complesso o laborioso, e richiedeva in molti casi microcontrollori dedicati. Recentemente, con lintroduzione da parte della Microchip dellintegrato chiamato ENC28J60, questa operazione risulta enormemente pi semplice. Il 28J60 infatti una vera e propria scheda di rete Ethernet, che comunica con il microcontrollore attraverso una semplice interfaccia SPI.

Figura 1 LEthernet board di Mikroelektronika

Utilizzo dellinterfaccia Ethernet

di Antonio Di Stefano

con eventuali byte di riempimento per raggiungere la lunghezza minima di 64 byte totali. possibile reperire maggiori informazioni sui protocolli e sul significato dei vari campi su Internet (ad esempio su Wikipedia). Come verr mostrato di seguito comunque non necessario conoscerli in dettaglio, dal momento che molte funzioni vengono svolte dalle librerie.
Figura 2 Collegamento tra la scheda Ethernet e la EasyPIC3

103
TEORIA

La libreria SPI-Ethernet
La libreria che il MikroC mette a disposizione per la gestione del 28J60 si chiama SPI-Ethernet. Essa comprende diverse funzioni, tra
N.
BYTE

tipico connettore RJ-45. Una volta inizializzato il 28J60, il componente gestisce completamente le funzionalit del livello fisico e MAC (filtraggio dei pacchetti in base al loro indirizzo, ritrasmissione in caso di errore, controllo della correttezza dei pacchetti ricevuti, etc), mostrando al PIC soltanto i registri di configurazione ed un buffer da 8KB che contiene sia i pacchetti ricevuti, che quelli che si intende trasmettere. La scrittura e la lettura dei pacchetti dal buffer avviene in modo simile a quanto visto a proposito delle EEPROM seriali (si veda FE n.259). Una tipica applicazione quindi non deve fare altro che leggere i pacchetti che vengono ricevuti, decodificarli e generare gli eventuali pacchetti di risposta. Le ultime due operazioni dipendono dal protocollo utilizzato. La struttura di un pacchetto Ethernet che impiega il protocollo UDP/IP (il pi diffuso su Internet per applicazioni proprietarie, e per lo streaming multimediale), mostrata in Tabella 1. Come si pu vedere, ciascun livello aggiunge un suo header al pacchetto, cos si ha un header MAC (in verde), che contiene per intero il pacchetto e che comprende un campo alla fine per il controllo di errore, un header IP (in rosa), che comprende gli indirizzi IP delle unit che si scambiano il pacchetto ed informazioni sul protocollo usato allinterno, un header UDP (in celeste), ed i dati veri e propri,

CONTENUTO
Indirizzo MAC desinazione Indirizzo MAC sorgente Tipo Ethernet (0x8000) Versione e lunghezza (0x45) Tipo di Servizio (0x00) Lunghezza totale Identificativo pacchetto Flag e offset frammento Time to live (0xFF) Protocollo (0x06=TCP, 0x11=UDP) Checksum Indirizzo IP sorgente Indirizzo IP destinazione Porta sorgente Porta destinazione Lunghezza Checksum Dati Eventuali byte di riempimento CRC32
FARE ELETTRONICA - MAGGIO 2007

6 6 2 1 1 2 2 2 1 1 2 4 4 2 2 2 2 n ? 4

Tabella 1 Struttura di un pacchetto UDP/IP su Ethernet

cui quelle pi utili sono le seguenti:


void SPI_Ethernet_Init( unsigned char *resetPort, unsigned char resetBit, unsigned char *CSportPtr, unsigned char CSbit, unsigned char *mac, unsigned char *ip, unsigned char fullDuplex);

lizzate, e la lunghezza dei dati. Le funzioni devono restituire la lunghezza delleventuale risposta generata, oppure 0. Per leggere e scrivere i dati dal buffer del 28J60 si usano le due funzioni seguenti:
void SPI_Ethernet_putByte(unsigned char v); unsigned char SPI_Ethernet_getByte();

104
TEORIA

Il suo scopo quello di inizializzare il 28J60 e le porte del PIC, la funzione andr quindi invocata prima di utilizzare le altre. I parametri sono rispettivamente: la porta e il bit usato per resettare il componente, la porta ed il bit usati come CS (segnale di selezione per i dispositivi SPI), i puntatori agli array da 6 e 4 byte relativi allindirizzo MAC ed IP da assegnare al dispositivo, e un flag che indica se la linea che si sta utilizzando fullduplex o half-duplex. La funzione deve essere richiamata dopo avere richiamato la funzione SPI_Init, che inizializza linterfaccia SPI del PIC.
void SPI_Ethernet_doPacket();

I dati vengono letti o scritti sequenzialmente nel buffer. Lindirizzo di inizio quello relativo allinizio del campo dati del pacchetto (sia in lettura che scrittura). Nel caso della scrittura del pacchetto, le routine provvederanno ad aggiungere gli header ed a trasmettere il tutto. Nella libreria sono disponibili anche altre funzioni utili, che per non sono pubbliche. Queste possono comunque essere richiamate includendo il file enc28j60_libprivate.h, che contiene le intestazioni. Tra queste funzioni sono presenti quelle per leggere e scrivere i registri del 28J60 o copiare blocchi di memoria.

Esempi
Questa funzione deve essere richiamata ciclicamente (anche con una certa priorit) nel loop principale del programma. La sua funzione quella di controllare se sono stati ricevuti dei pacchetti, decodificarli, e trasmettere quelli accodati. Alcuni tipi di pacchetti sono gestiti automaticamente dalla funzione, che provvede a generare e trasmettere le risposte. Tra questi vanno citati le richieste di ARP e di eco ICMP (ping). Se sono stati ricevuti pacchetti UDP o TCP viene automaticamente richiamata una delle due funzioni seguenti (che devono essere sempre presenti nel programma, eventualmente lasciate vuote):
unsigned int SPI_Ethernet_UserTCP( unsigned char *remoteHost, unsigned int remotePort, unsigned int localPort, unsigned int reqLength); unsigned int SPI_Ethernet_UserUDP( unsigned char *remoteHost, unsigned int remotePort, unsigned int destPort, unsigned int reqLength); unsigned int SPI_Ethernet_UserTCP( unsigned char *remoteHost, unsigned int remotePort, unsigned int localPort, unsigned int reqLength) { return 0; } // Indirizzo MAC unsigned char myMacAddr[6] = {0x00, 0x14, 0xA5, 0x76, 0x19, 0x3f}; // Indirizzo IP unsigned char myIpAddr[4] = {10, 0, 0, 10};

Risposta al ping Il codice minimo che possibile realizzare usando le funzioni di libreria quello che si limita a rispondere al ping (pacchetti ICMP Echo Request), e alle richieste di ARP (un protocollo che serve per richiedere lindirizzo MAC di ununit conoscendo il suo indirizzo IP). Queste due funzioni sono svolte automaticamente dalla funzione SPI_Ethernet_doPacket, e consentono di far riconoscere alla rete la presenza del dispositivo. Il codice mostrato nel Listato 1. Il programma pu essere eseguito su qualsiasi PIC dotato di interfaccia SPI e almeno 4K di memoria programma (ad esempio 16F876 o 16F877).
#define SPI_Eth_HALFDUPLEX 0 #define SPI_Eth_FULLDUPLEX 1

FARE ELETTRONICA - MAGGIO 2007

Queste funzioni non devono essere richiamate dal programma, ma vengono richiamate automaticamente dalla funzione vista prima, e con i parametri gi impostati. In questo modo possibile conoscere subito gli indirizzi del mittente o le porte uti-

unsigned int SPI_Ethernet_UserUDP( unsigned char *remoteHost, unsigned int remotePort, unsigned int destPort, unsigned int reqLength) { return 0; }

Uscita su Display LCD Per mostrare come pu essere gestito un pacchetto UDP che trasporta dati utili, possibile modificare il contenuto della funzione SPI_Ethernet_UserUDP nel codice visto prima, inserendo le righe riportate nel Listato 2. In questo caso i dati trasportati da ogni 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:
Lcd_Init(&PORTB); Lcd_Cmd(LCD_CLEAR); Lcd_Cmd(LCD_CURSOR_OFF);

// main entry void main() { Spi_Init(); SPI_Ethernet_Init( &PORTC, 0, &PORTC, 1, myMacAddr, myIpAddr, SPI_Eth_FULLDUPLEX); while(1) { SPI_Ethernet_doPacket(); } } { unsigned char len = 0; Lcd_Cmd(LCD_CLEAR); while(reqLength) txt[len++]=SPI_Ethernet_getByte(); txt[len]=0; LCD_Out(2, 1, txt); return(len); } unsigned int SPI_Ethernet_UserUDP( unsigned char *remoteHost, unsigned int remotePort, unsigned int destPort, unsigned int reqLength)

Il display andr collegato alla porta B del PIC come mostrato nei precedenti articoli di questa serie.

105
TEORIA

Come si pu vedere il programma estremamente semplice, e apparentemente vuoto, nel senso che non si vede esplicitamente la gestione dei pacchetti. In realt questa effettuata dalla funzione SPI_Ethernet_doPacket, richiamata ciclicamente nel loop principale. Si pu notare che le funzioni SPI_Ethernet_UserTCP e SPI_Ethernet_UserUDP sono presenti, ma non contengono codice (restituiscono semplicemente 0). La loro presenza necessaria perch in caso di ricezione di pacchetti TCP o UDP esse verranno comunque richiamate dalla funzione SPI_Ethernet_doPacket. La periferica Ethernet seriale inizializzata indicando luso della porta C e dei pin 1 e 0 per il reset ed il CS, passando gli indirizzi MAC ed IP ed il parametro che indica un funzionamento full duplex. Gli indirizzi sono stati scelti in maniera arbitraria, e seguono determinate convenzioni. Lindirizzo MAC pu essere lasciato inalterato, o modificato leggermente nelle cifre finali (lindirizzo dovrebbe essere unico!), lIP stato scelto in uno dei range tipici usati per le reti locali, e risulta statico (chiaramente anche in questo caso deve essere unico e nel formato utilizzato nella rete locale). Per testare il programma occorre collegare la scheda ad uno switch o router, a cui stato collegato anche un PC. Lanciando il comando ping 10.0.0.10 dalla linea di comando, si dovrebbe ottenere la risposta da parte della scheda.

Il contenuto del pacchetto UDP (il solo campo dati) viene letto byte a byte con la funzione SPI_Ethernet_getByte e memorizzato in un array, che viene terminato inserendo lo zero alla fine, e che viene inviato al display sfruttando la funzione di libreria LCD_Out. Si pu notare che il valore restituito dalla funzione questa volta proprio la lunghezza dei dati ricevuti. Questo ha come effetto linvio dello stesso pacchetto UDP (che si trova ancora nel buffer del 28J60) al PC. Questa possibilit, chiamata echo, pu essere utile per accertarsi che il pacchetto sia stato ricevuto correttamente dalla scheda. Dal momento che il protocollo UDP non prevede meccanismi per garantire il recapito dei pacchetti, pu essere utile adottare tecniche di questo tipo. Chiaramente i dati prelevati dal pacchetto possono essere utilizzati in modo arbitrario: ad esempio possibile includere comandi e dati per modificare lo stato delle porte di I/O.

FARE ELETTRONICA - MAGGIO 2007

Accesso diretto ai dati


In qualche caso potrebbe essere utile accedere direttamente ai registri ed al buffer del 28J60. Questo permette di sfruttare caratteristiche non gestibili attraverso le librerie o di crearne di nuove (ad esempio in diverse occasioni potrebbe essere utile spostare arbitrariamente il puntatore di lettura o scrittura del buffer). Per fare questo sufficiente inviare gli appropriati comandi di lettura e scrittura via SPI. Un elenco completo dei comandi e dei registri dellENC28J60 pu essere trovato nel datasheet. Nel Listato 3 mostrato un esempio di questo approccio, che sfrutta la funzione di libreria soltanto per linzializzazione del componente, ma esegue le restanti operazioni manualmente. Il programma consente di leggere il numero di pacchetti ricevuti (premendo un pulsante collegato a RB0), e di inviarli singolarmente via seriale al PC.
// Indirizzo MAC ed IP &PORTC, 1, myMacAddr, myIpAddr, 1) ; while(1) { if (PORTB&0x01) { // Sel Bank 1 par=Reg_Read(0x1F); par=(par&0xFC)|0x01; Reg_Write(0x5F, par); // Numero di pacchetti pktcnt=Reg_Read(0x19); Dec2Hex(pktcnt, temp); SendString(temp); SendString(\r\n); Delay_ms(1000); }; if (PORTB&0x02) { pktcnt=Reg_Read(0x19); if (pktcnt) { PORTC=PORTC&0xFD; void SendString(char *str); void Dec2Hex(char n, char *str); char Reg_Read(char addr); void Reg_Write(char addr, char val); // *** main entry *** void main() { int i; unsigned len; char par, buf[32], temp[5]; // Init. porte ADCON1 |= 0x0F; PORTA = 0; TRISA = 0x00; PORTB = 0; TRISB = 0x0F;
FARE ELETTRONICA - MAGGIO 2007

106
TEORIA

unsigned char myMacAddr[6]= {0x00, 0x14, 0xA5, 0x76, 0x19, 0x3f}; unsigned char myIpAddr[4]= {10, 0, 0, 10}; unsigned pktcnt;

// Read buffer Spi_Write(0x3A); for(i=0; i<6; i++) { buf[i]=Spi_Read(temp); Dec2Hex(buf[i], temp); SendString(temp); } len=buf[2]; len=len|(buf[3]<<8); for(i=0; i<len; i++) { buf[6]=Spi_Read(temp); Dec2Hex(buf[6], temp); SendString(temp); } if (len&0x01) Spi_Read(temp); PORTC=PORTC|0x02; // Dec Pktcnt Reg_Write(0x9E, 0x40); } SendString(\n\r); Delay_ms(1000); }; } } void SendString(char *str) {

i=0; temp[2]= ; temp[3]= ; temp[4]=0; Usart_Init(9600); Spi_Init(); SPI_Ethernet_Init( &PORTC, 0,

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

int n; n=0; while(str[n]) { Usart_Write(str[n]); n++; } } void Dec2Hex(char n, char *str) { char c; c=(n&0xF0)>>4; if (c<10) c=c+0; else c=c+A-10; str[0]=c; c=n&0x0F; if (c<10) c=c+0; else c=c+A-10; str[1]=c; } void Reg_Write(char addr, char val) { PORTC=PORTC&0xFD; Spi_Write(addr); Spi_Write(val); PORTC=PORTC|0x02; } char Reg_Read(char addr) { char k=0; PORTC=PORTC&0xFD; Spi_Write(addr); k=Spi_Read(&k); PORTC=PORTC|0x02; return k; }

Il programma inizializza il 28J60 e attende la pressione di uno dei due pulsanti (RB0 o RB1). Nel frattempo il componente riceve e memorizza nel suo buffer tutti i pacchetti indirizzati al suo indirizzo MAC o allindirizzo di broadcast (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 dallindirizzo relativo al primo pacchetto ricevuto. I primi 6 byte sono inseriti dallENC28J60 e contengono informazioni sul pacchetto: 2 byte costituiscono il puntatore al pacchetto successivo, 2 contengono la sua lunghezza, e gli altri 2 che forniscono informazioni sullo stato e sugli eventuali errori. Letti i primi 6 byte, viene estratta linformazione sulla lunghezza, ed utilizzata per leggere lesatto numero di byte, che sono inviati alla UART, dopo essere stati convertiti in esadecimale e ASCII. Se il numero di byte dispari occorre fare una lettura aggiuntiva in modo che il puntatore si trovi ad un indirizzo pari (che sar quello di inizio del prossimo pacchetto). Il contenuto di uno dei pacchetti ricevuti visibile in Figura 3.

107
TEORIA

Conclusioni
Va ricordato che il MikroC ha un utile UDP Terminal che pu essere richiamato dal menu Tools. Questo programma permette di inviare pacchetti UDP dal contenuto arbitrario e di riceverli, pu essere quindi utilizzato per provare i programmi descritti. Occorre tenere presente inoltre che potrebbe essere necessario modificare lindirizzo IP usato nei programmi in base allintervallo utilizzato nella propria rete locale. Un intervallo alternativo a quello riportato, utilizzato da molti router, {192, 168, 20, x}, dove x un indirizzo unico assegnato al dispositivo. Si conclude con questa puntata la serie di articoli dedicati al MikroC. Mi auguro che gli argomenti trattati abbiano suscitato la curiosit dei lettori verso questo ambiente di sviluppo e possano avere fornito un utile introduzione alla programmazione in C dei microcontrollori.

FARE ELETTRONICA - MAGGIO 2007

Codice MIP 263102 www.farelettronica.com/mip