Guida introduttiva alla programmazione assembler e C di PIC in ambiente GNU/Linux
Copyright (c) 2007 MASSIMO LUNARDON & WALTER LAIN
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License,
Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts,
and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
Le porzioni di codice sono rilasciate con licenza GNU
GPL, tranne il firmware dell'ICD2, che e' di proprieta' esclusva di Microchip Technology Incorporated.
Tutti i marchi sono registrati dai rispettivi proprietari.
Indice
Materiale
Introduzione
Questo documento e' stato prodotto allo scopo di fornire il riassunto di alcune serate relative alla programmazione di
microcontrollori della famiglia PIC16 ottenuta con l'utilizzo di strumenti open source (OS).
Queste serate sono state organizzate per conto del LUG di Vicenza,
e si sono svolte presso il Victoria Station di Vicenza, nei giorni 4, 11, 18 Dicembre 2007.
Si inizia fornendo una descrizione di cosa e' un microcontrollore, le caratteristiche principali delle famiglie di Pic, per
passare poi ad illustrare quelle dello specifico Pic adottato.
Si vedranno poi gli strumenti abitualmente utilizzati in ambito professionale in ambiente Windows; si confronteranno con gli
strumenti disponibili in ambito OS per mostrare il livello di maturita' raggiunto da questi ultimi.
Infine saranno illustrati degli esempi pratici di programmazione sia in linguaggio Assembly (basso livello) che in C
(alto livello). Se ne confronteranno le caratteristiche, le prestazioni e le difficolta'.
Torna all'indice
Microcontrollore
Un microcontrollore e' un dispositivo elettronico con un certo numero di ingressi ed uscite, che puo' essere programmato per
svolgere delle funzioni che modificano lo stato delle uscite in base a delle logiche determinate dalle istruzioni impostate.
Tali logiche possono dipendere dai livelli presenti agli eventuali ingressi piuttosto che da un clock che talvolta puo' essere
interno.
Struttura generica di un MCU Microchip
In genere un microcontrollore dispone di una certa quantita' di memoria, che puo' essere utilizzabile per contenere le
istruzioni che compongono il programma e dati che possono essere temporanei o meno.
Questa memoria sara' divisa almeno in due parti, una parte volatile (RAM) e una parte non volatile (OTP, EPROM, EEPROM,
FLASH).
Torna all'indice
PIC
I microcontrollori Pic sono prodotti da Microchip.
Esistono diversi tipi di Pic che si differenziano per il tipo di core e per il tipo di moduli presenti al loro intero. Le due famiglie
principali sono quelle con word a 16 o 14 bit.
Tutte le famiglie hanno delle caratteristiche in comune: il set base di istruzioni, l'architettura, la modalita' di
programmazione.
I vari moduli disponibili possono essere di interfaccia (I2C, USART, USB), ingressi analogici (comparatori,
convertitori A/D), ingressi digitali (contatori, timer, clock).
Torna all'indice
Caratteristiche
I Pic dispongono di una CPU RISC, che esegue un set base di 35 istruzioni (le famiglie piu' potenti hanno
delle istruzioni aggiuntive). Ogni istruzione e' codificata in una sola word ed e' eseguita in un solo ciclo di clock
, eccetto le istruzioni di branch che ne impiegano due.
L'esecuzione di una istruzione per ciclo di clock e' resa possibile da un meccanismo di pipeline.
I Pic dispongono di memoria volatile (RAM) e non volatile (FLASH, EEPROM): la RAM e' tipicamente utilizzata
per l'esecuzione dei programmi, mentre la FLASH per memorizzare il codice o i dati.
Il Pic ha una architettura di tipo Harvard: bus dati e bus istruzioni sono distinti. Cio' permette di accedere
alle istruzioni ed ai dati allo stesso istante, senza che si verifichino problemi di contesa del bus, rendendo il
controllore piu' veloce nelle applicazioni che elaborano dati residenti in memoria.
Le due principali famiglie si differenziano per la dimensione della word adottata: 16 bit in un caso, 14 nell'altro. Il bus dati e'
sempre a 8 bit. La famiglia a 16 bit e' identificata dalla sigla Pic18Xxxx, mentre la famiglia a 14 bit con la sigla Pic16Xxxx.
Esistono anche altre famiglie, come Pic12Xxxx, dsPic30, ecc...
La struttura di base e' comune a tutta la famiglia, i singoli modelli si distinguono per il numero di PORT e la presenza o
meno di altri moduli di periferiche.
Il Pic e' in grado di gestire degli interrupt, e' dotato di un watchdog con un proprio timer e le sue uscite
sono in grado di supportare una corrente sufficiente per pilotare dei led. Generalmente i Pic contengo al loro interno una
moltitudine di moduli, tanto che i pin del package non sono sufficienti per esportarne tutti i contatti elettrici; per questo motivo
le uscite sono multiplexate, e devono essere opportunamente configurate per utilizzare un modulo piuttosto
che un altro.
Un'altra caratteristica peculiare e' la capacita' di In-Circuit Serial Programing (ICSP) che permette di programmare un
Pic gia' saldato nel circuito finale. Vedremo che questa caratteristica e' molto utile.
Torna all'indice
Il PIC 16F628A
Nei nostri esempi utilizzeremo il PIC16F628A.
Il PIC16F628A e' uno dei MCU prodotti da Microchip. Appartiene alla famiglia 16F, quindi e' un MCU a 8 bit,
ed ha una memoria di tipo FLASH. Questa ultima caratteristica lo differenzia dalla famiglia 16C, che e' composta da
dispositivi OTP la cui memoria non e' riscrivibile (One Time Programming - programmabili un'unica volta).
Il bus dati e' ad 8 bit, il bus che accede alla memoria programma e' a 14 bit.
Il modello PIC16F628A e' uno dei modelli "piccoli" della sua famiglia. Al suo interno contiene:
- una CPU RISC;
- 2 porte di I/O (A e B) ad 8 bit per un totale di 16 bit indirizzabili;
- 2 comparatori analogici;
- 3 timer (due a 8 bit ed uno a 16bit);
- un modulo capture/compare (16bit)-PWM(10bit);
- un modulo USART/SCI;
- un oscillatore interno di precisione.
Struttura del PIC 16F628A
La memoria disponibile e' divisa in 3 parti: 2048 words di programma (FLASH), 224 bytes di RAM e 128 bytes di EEPROM (di
fatto si tratta di una porzione di memoria FLASH, ma dal punto di vista del software cio' e' irrilevante). Oltre a questi spazi di
memoria sono presenti un registro accumulatore W e diversi registri di configurazione. In questo tipo di Pic una word e'
di 14 bit; un qualsiasi comando assembler, una volta codificato, occupa 1 word che contiene sia il codice dell'istruzione che il dato.
Struttura della memoria programma del PIC 16F628A
La memoria RAM e' suddivisa, in modo non simmetrico, in 4 banchi. Una peculiarita' e' che gli indirizzi da 70h a 7Fh
sono comuni a tutti i banchi (cioe', qualsiasi banco si selezioni si punta sempre alle stesse locazioni), e questo puo' diventare
utile in molti casi.
Anche la struttura della memoria e' comune a tutta la famiglia; la RAM e' sempre suddivisa in 4 banchi, di cui gli ultimi 16 bytes comuni
(cambia la quantita' indirizzabile nei vari banchi), e la memoria programma e' composta di banchi da 2Kwords ciascuno. Il PIC16F628A
dispone di un solo banco utilizzabile, mentre altri modelli della stessa famiglia arrivano fino a 4.
Struttura della memoria RAM del PIC 16F628A
Sono presenti 6 tipi differenti di interrupt:
- INT: per uso generico, disponibile sul bit0 della porta B;
- TMR: viene utilizzato per la gestione dei timers (ovviamente, essendoci 3 timers ci sono anche 3 interrupt di tipo TMR);
- CM: legato al funzionamento dei comparatori (anche in questo caso, ci sono 2 comparatori e quindi 2 interrupt di tipo CM),
puo' essere attivato sul fronte di discesa o di salita dell'uscita del comparatore, e segnala un cambio stato dello stesso;
- CCP: relativo al modulo capture/compare;
- EE: che si attiva quando e' terminata una scrittura su EEPROM;
- RC/TX: utilizzati per le comunicazioni USART. In particolare, RC segnala buffer di ricezione pieno, mentre TX segnala
buffer di trasmissione vuoto.
Gli interrupt hanno due flag generali di attivazione, un master generale (GIE) ed un master degli interrupt periferici
(PEIE), entrambi presenti sul registro INTCON. L'unico interrupt non periferico e' INT, quindi negli altri casi e' sempre
necessario settare entrambi i flags per attivare gli interrupt. Quando avviene un interrupt, il programma attiva il flag
corrispondente e salta alla locazione 4h, dove il programmatore puo' eseguire le routines del caso. Dopo ogni evento
l'interrupt che l'ha generato deve essere riattivato manualmente.
Il codice che viene eseguito in seguito ad un interrupt viene anche detto vettore di interrupt.
I 3 timer sono fondamentalmente dei contatori, che incrementano il loro valore ad ogni ciclo e generano un interrupt al
passaggio per lo zero, che avviene ogni volta che raggiungono il valore massimo e provocano un over-flow passando da 0xFF a 0x00. Ne
consegue che per ottenere una corretta temporizzazione, occorrera' inserire nel contatore non il valore desiderato, ma
il resto della sottrazione tra questo valore e 0xFF, oppure 0xFFFF nel caso del timer a 16 bit.
Sono presenti dei prescaler e dei postscaler per adattare meglio i timer alle esigenze dell'applicazione.
Sono presenti anche un timer di accensione, un watchdog e un sistema di rilevamento del brown-out.
L'oscillatore interno puo' lavorare alle frequenze di 4MHz (con precisione dell'1%) oppure 48KHz, e
sostituisce egregiamente un oscillatore esterno (quarzo, RC o altro) in buona parte delle applicazioni realizzabili,
rendendo al contempo disponibili 2 pin di I/O altrimenti occupati. Unica accortezza e' disabilitare il pin di reset (MCLR)
nella configurazione, altrimenti l'oscillatore non funzionera'.
Un'altra cosa da tenere presente e' che i 2 pin di programmazione PGD e PGC (bit 6 e 7 della porta B: RB6 e RB7) non
dovrebbero mai essere utilizzati come uscite, pena l'impossibilita' di riprogrammare il dispositivo dopo la prima programmazione.
Il linguaggio assembler del PIC e' composto da 35 istruzioni, comprendenti spostamento di registri e varie operazioni su
interi a 8 bit, tra cui addizione, sottrazione, complemento a 2, AND logico, ecc... . Non sono presenti la moltiplicazione e la
divisione, per cui andranno eventualmente scritte delle routines apposite, cosi' come per operazioni su dati a piu' di 8 bit o
in virgola mobile (sulle application notes fornite da Microchip sono presenti tutte le funzioni necessarie).
Istruzioni assembler definite sul PIC 16F628A
Per ulteriori informazioni fare riferimento al Datasheet del MCU.
Torna all'indice
I tools
Per lo sviluppo degli esempi utilizzeremo del software Open Source.
I tools che utilizzeremo sono:
- gputils: una raccolta di tools di sviluppo per per microcontrollori Pic di Microchip;
- gpsim: un simulatore di Pic, emula il funzionamento del microcontrollore permettendo di visualizzare i registri;
- sdcc: un compilatore C per vari microcontrollori, in particolare per Pic a 14 e a 16 bit: necessita di gpasm (contenuto in
gputils) per codificare l'assembler prodotto dalla compilazione del codice C;
- Piklab: un ambiente IDE che permette di scegliere il tipo di Pic e la tool chain da utilizzare, compilare e programmare il
microcontrollore.
E bene che nel processo di installazione si segua l'ordine dell'elenco, in modo da soddifsfare le eventuali dipendenze.
Torna all'indice
Piklab
Piklab e' un IDE in grado di collegarsi a vari programmatori paralleli e seriali ed anche all'ICD2 Microchip.
Puo' utilizzare gpasm per la parte assembler, e diversi compilatori C (liberi e commerciali) per la
programmazione ad alto livello.
L'interfaccia presenta l'elenco dei files di progetto sulla sinistra, i files nella parte destra e i messaggi in basso.
Piklab, ambiente IDE
L'editor e' basato su Kate, e l'evidenziatura del codice puo' essere impostata secondo vari schemi.
Il programma e' in continuo sviluppo. Attualmente e' possibile utilizzare diversi programmatori e sembra sia presente un inizio di
supporto al debug in-circuit (utilizzando un debugger tipo ICD2, e' possibile eseguire il software step-by-step sull'hardware,
eseguendo cosi' un debug sia software che hardware
Il debug puo' essere eseguito anche utilizzando l'emulatore gpsim, e permette la visualizzazione dei vari registri di
sistema, oltre alle variabili create in RAM; e' anche possibile forzare lo stato dei registri e modificare il contenuto delle
variabili. Gpsim attualmente non supporta i dispositivi di tipo "A" (cioe' con moduli analogici), perlomeno della
serie Pic16
Piklab, ambiente di debug con GpSim
Per chi avesse gia' dimestichezza con l'ambiente MPLAB Microchip, Piklab risulta abbastanza semplice da utilizzare, tenendo conto
comunque che alcune macro e scorciatoie presenti in Mplab non sono riconosciute da Piklab.
Alcuni accorgimenti di cui tenere conto, pena l'impossibilita' di portare a termine la compilazione:
- i files inclusi nel progetto, tranne quello principale, devono avere estensione diversa da ".asm", ad esempio ".inc";
- occorre aggiungere una riga vuota alla fine di ogni file;
- Si e' notato che, utilizzando una macro che esegua operazioni con puntatori per eseguire operazioni tramite INDF, possono
verificarsi dei problemi. Ad esempio, la macro qui riportata (somma tra word):
ADDW MACRO Wo_A,Wo_B
MOVF Wo_B+1
ADDWF Wo_A+1,F
SKPNC
INCF Wo_A,F
MOVFW Wo_B
ADDWF Wo_A,F
ENDM
non funziona se utilizzata cosi': ADDW INDF+x,FILE oppure ADDW FILE, INDF+x
mentre funziona invece perfettamente se utilizzata cosi': ADDW INDF,FILE oppure ADDW FILE,INDF
Questi problemi sono da imputare certamente alla giovinezza dei vari progetti, ma occorre tenerne conto per poter utilizzare
l'ambiente OS senza problemi.
Torna all'indice
ICD2 Clone
Per una descrizione di questo circuito fare riferimento alla sezione specifica.
Per poter utilizzare il programmatore, si deve configurare il sistema affinche' riconosca il device USB e crei un device
ttyUSBx con i permessi opportini affinche' il dispositivo possa essere utilizzato da un account normale (non root!).
Per i sistemi che adottano il meccanismo di udev, si deve creare un file di rules ad-hoc.
Si dovra' copiare il seguente file (50-usb-ftdi.rules) in /etc/ude/rules.d/:
# udev rules file for FDTI USB-RS232 adapter
# $Id: max.rules,v 1.3 2007/02/15 22:12:10 gaufille Exp $
#
BUS!="usb", ACTION!="add", GOTO="myusb_rules_end"
# All devices
# Comment out if you are looking for product id rules
# and uncomment the next rule
SYSFS{idVendor}=="0403", MODE="666", GROUP="users"
# Ftdi device with product ID #6001 will be mounted
# on a node with "rwrwrw" rights for "users" group
SYSFS{idVendor}=="0403", SYSFS{idProduct}=="6001", MODE="666", GROUP="users"
LABEL="myusb_rules_end"
In questo modo ogni volta che sara' collegato il programmatore alla porta USB, il meccanismo di udev creera' un device /dev/ttyUSBx
con permessi di lettura e scrittura per tutti e appartenente all'utente root e al gruppo users.
Torna all'indice
Il circuito dimostrativo
Al fine di rendere immediatamente visibile il funzionamento del PIC, e' stato realizzato un semplice circuito dimostrativo.
Questo circuito e' composto da:
- 1 PIC 16F628A
- 8 led verdi
- 1 led rosso
- 2 pulsanti NO
- 9 resistenze da 1K
- 2 resistenze da 100K
Sono inoltre presenti una resistenza da 100R, una da 10K, un condensatore da 100nF ed un connettore pin strip, utilizzati per la
programmazione.
Il circuito dimostrativo (in questa versione e' presente anche una sezione di alimentazione)
Il funzionamento del circuito e' molto semplice:
- Il sistema si attiva e resta in attesa. Il led rosso e' acceso.
- Premendo il pulsante "start" viene attivata l'esecuzione dei programmi di gestione led. Il led rosso lampeggia.
- Premendo il pulsante "selezione" si cambia ciclicamente tra i vari programmi presenti.
- Premendo nuovamente il pulsante "start" viene disattivata l'esecuzione dei programmi, mantenendo in memoria l'ultimo stato.
- Premendo ancora il pulsante "start" l'esecuzione viene ripresa dal punto in cui era stata precedentemente interrotta.
Per la selezione dei pin occorre tenere conto di alcuni fattori:
- non tutti i pin sono impostabili indifferentemente come input od output;
- non tutti i pin sono utilizzabili, a causa di necessita' di programmazione;
- alcuni pin hanno un'uscita di tipo standard CMOS, altri invece sono di tipo Open Drain.
Di conseguenza, sono stati scelti per i led i pin da RB0 a RB5 e i pin RA6 e RA7.
Torna all'indice
Il software dimostrativo
Il software creato per la dimostrazione e' da considerarsi a scopo didattico e decisamente poco ottimizzato,
specialmente per quanto riguarda le dimensioni in memoria (per contro, risulta certamente piu' comprensibile di un analogo software
ottimizzato, il che e' lo scopo finale di questo lavoro).
E' composto da diverse parti:
- sezione main; comprende dichiarazioni, include, vettore Interrupt e ciclo principale.
- gestione programmi; gestisce l'esecuzione dei differenti programmi di gestione led.
- programmi di gestione led; le differenti routines che "muovono" i led.
- lettura pulsanti; legge i pulsanti e controlla i flags relativi.
- timer; inizializza i timer.
Va considerato che questo metodo di programmazione, suddividendo per argomento e funzione il programma in vari files, e'
da preferirsi ad una gestione a file unico, in quanto risulta piu' facile da gestire (obiettivamente, riducendo il numero di righe
presenti si facilita l'individuazione del codice cercato) sia durante la programmazione che durante il debug. E' anche
possibile, in questo modo, salvare e recuperare con piu' facilita' le differenti release del software, e recuperare con
maggiore facilita' porzioni di codice per riutilizzarle in altri progetti.
Sono state utilizzate diverse macro per facilitare la programmazione, ma il codice dovrebbe risultare comunque perfettamente
comprensibile, data soprattutto l'abbondanza di commenti presenti. Quando si utilizzano delle macro, occorre tenere
presente che il codice della macro viene effettivamente sostituito dal compilatore, quindi una macro di 10 istruzioni
ripetuta 10 volte occupa 100 word di programma. Anche i salti con offset ($+x, $-x) devono essere calcolati seguendo
questa considerazione.
Si procedera' ora ad una breve analisi della varie sezioni.
I listati del software, in assembler e in C.
Sezione main:
Questa sezione si occupa di avviare il sistema e di restare in attesa fino all'attivazione dell'interrupt del timer1. Per
prima cosa viene dichiarata la configurazione del MCU,tramite la dichiarazione __CONFIG 0x2150.
In questo caso, sono state disattivate le protezioni del codice e della memoria, ed il watchdog, ed e' stato
abilitato l'oscillatore interno.
Vengono quindi dichiarati alcuni dei files da includere nel progetto: variabili, costanti, headers e macro.
All'indirizzo 4h viene creato il vettore interrupt.
La prima operazione che viene eseguita e' il PUSH, ovvero il salvataggio dei registri W, program counter,STATUS e FSR.
L'istruzione PUSH e' una macro che salva questi registri su altrettante copie in RAM. Questo e' uno dei casi in cui puo'
tornare utile il fatto che gli ultimi 16 byte di RAM siano indipendenti dal banco. In caso contrario sarebbe decisamente
piu' difficile salvare lo stato dei registri senza modificarne il contenuto.
Dopo aver eseguito il PUSH, viene controllato il flag relativo al timer1. Dato che nel programma e' stato utilizzato solo
questo interrupt, sarebbe possibile evitare il controllo. E' comunque consigliabile eseguirlo, dato che il programma
potrebbe essere finito qui per gravi errori (del programmatore, il micro esegue solo quello che gli viene detto).
Se il flag di interrupt del timer1 e' attivo, viene riavviato il timer. In questo caso, non essendoci bisogno di
temporizzazioni stringenti, e' sufficiente ricaricare i valori nei registri del timer, TMR1H e TMR1L (se fosse necessario
ottenere tempi piu' precisi, sarebbe possibile sommare il contenuto dei registri del timer al valore iniziale, tenendo
cosi' conto della decina di clock persi per la gestione dell'interrupt).
Si reimposta quindi l'interrupt del timer, cancellando il flag di evento e settando il flag di abilitazione, e quindi si
attiva il flag di stato del timer.
Per motivi di comodita', al posto di utilizzare un altro timer effettivo, si e' preferito utilizzare come timer di
movimento dei led un divisore del timer principale. Questo divisore, che e' stato chiamato timer2, viene decrementato ad ogni
ciclo del timer1, finche' non avviene un passaggio per lo zero. A questo punto, viene reimpostato il divisore e attivato il flag di
stato corrispondente.
Infine si esce dal vettore interrupt eseguendo l'operazione inversa del PUSH, il POP.
Dopo il vettore interrupt, e' presente il codice che il micro esegue effettivamente una volta acceso. Per prima cosa viene
quindi eseguita l'inizializzazione. I pin necessari all'applicazione vengono impostati come ingressi o uscite (all'avvio
del sistema, tutti i pin sono settati per default come ingressi, in modo da evitare situazioni non desiderate), agendo sui
registri di controllo dei port, TRISA e TRISB (notare che questi registri si trovano nel banco 1 e non nel banco 0).
Vengono quindi disattivati tutti i led (si lavora in logica negativa per motivi elettrici) e si inizializzano il
timer1 e il timer2.
A questo punto possono essere abilitati gli interrupt ed il programma rimane in loop in attesa del timer1.
Quando il timer1 e' attivo, vengono eseguiti i controlli sulla tastiera e, se e' abilitata l'esecuzione (pulsante "start")
sul PWM del programma5 e sul timer2.
Se il timer2 e' attivo, si esegue la sub di gestione dei programmi, che abilita il programma da eseguire controllando lo
stato del pulsante "selezione", e si esegue il programma dei led abilitato.
Infine si torna a inizio ciclo, nuovamente in attesa del timer1.
Prima della fine del programma, decretata dall'istruzione END, si dichiarano gli altri files da includere nel
progetto, quelli contenenti le sub delle varie sezioni.
Gestione programmi
Questa sezione e' composta da una routine, che si occupa di controllare l'esecuzione dei programmi di gestione led, e da una piccola
sezione che gestisce il tasto "start".
Per prima cosa, viene verificato se e' avvenuto un cambio di stato del pulsante "selezione". In caso negativo, il programma abilitera'
semplicemente l'esecuzione del programma attivo, settando il relativo flag del byte di controllo.
In caso affermativo, viene invece attivato il programma successivo a quello attivo, e sono inizializzate le variabili relative.
Programmi di gestione led
Tutti questi programmi si occupano della movimentazione dei led. A tal fine utilizzano un byte su cui effettuano gli spostamenti,
demandando alla sub di scrittura sul port il compito di modificare le uscite corrette.
- Prog1: questa sub esegue lo shift dei led, spostandosi dal bit0 verso il bit7;
- Prog2: questa sub esegue lo shift dei led, spostandosi dal bit7 verso il bit0;
- Prog3: questa sub esegue lo shift dei led, spostandosi alternativamente dal bit0 verso il bit7 o dal bit7 verso il bit0, creando
l'effetto "rimbalzo". Per tenere in memoria la direzione di spostamento, viene utilizzato un bit del byte di controllo. Il codice e'
condiviso con il prog5, con la differenza che, se deve essere eseguito il prog5, il prog3 non chiama la sub di scrittura sul port;
- Prog4: questa sub esegue un doppio shift dei led, spostandosi sia dal bit0 verso il bit7 che dal bit7 verso il bit0. A tal fine,
vengono utilizzate due variabili temporanee in cui sono effettuati singolarmente i movimenti, e queste vengono poi messe insieme
creando l'effetto "incrocio";
- Prog5: questa sub esegue il prog3, aggiungendo una "scia" sui 4 led attorno al led attivo. In pratica il codice, dopo aver
chiamato il prog3, controlla quale led e' attivo, e provvede a impostare il PWM sui led adeguati. Il PWM e' composto da 2 contatori
a valore differente (luminosita' diversa per la scia), che vengono incrementati fino al raggiungimento della soglia desiderata. A
questo punto, viene attivato per un ciclo di timer1 il led corrispondente, in modo da ottenere un duty-cycle variabile.
- Port_write: questa sub scrive sui pin di uscita il byte dato.
Lettura pulsanti
La sub di lettura dei pulsanti utilizza 2 byte come buffer antirimbalzo. Il buffer del tasto viene incrementato o
decrementato a seconda che il pulsante sia premuto o meno. Quando il buffer arriva al valore di soglia il pulsante viene considerato
premuto, mentre quando arriva a zero viene considerato non premuto. Di conseguenza vengono impostati i flag di stato dei pulsanti.
Il buffer e' dimensionato a 64 cicli del timer1. Se il pulsante rimane premuto piu' a lungo, viene considerato come se non fosse
ancora stato rilasciato. Di conseguenza, per ottenere ad esempio il cambio dal programma 1 al programma 4, occorre premere e
rilasciare (per almeno 64 cicli ogni volta) il pulsante per 3 volte.
Timer
Questa sezione si occupa dell'inizializzazione dei timer. Timer1 viene abilitato con prescaler a 1, e caricato con il valore desiderato.
Viene quindi attivato l'interrupt relativo e azzerato il divisore timer2. Si ricorda che per caricare un valore nel timer1, occorre
caricare prima il MSB e poi l'LSB, poiche' il contatore carica la word in seguito al caricamento del LSB (se si tenta di
caricare i dati al contrario, si carichera' nel timer1 un valore corrispondente al LSB desiderato, lasciando l'MSB nello stato
precedente - ignoto).
Per motivi progettuali, legati soprattutto al PWM, timer1 e' stato impostato a 1ms, mentre timer2 e' pari a 100ms. In queste condizioni
l'effetto scia e' ben visibile ed il movimento dei led abbastanza fluido.
Torna all'indice
Impaginazione by KCS'81