ESERCITAZIONE # 4
LED lampeggiante - routine di tempo
L' operazione di fare lampeggiare un LED è di per se semplice, ma richiede
l' inserimento di un un ulteriore elemento: il tempo.
Infatti cosa vuol dire lampeggiare ? Come nella barzelletta "ora va, ora
non va, ora va,...".
Ovvero un lampeggio è la sequenza di istanti in cui il LED è acceso,
intervallati da altri in cui è spento.
Si potrebbe essere tentati inizialmente di impostare il programma come segue:
|
E potrebbe parere corretto, ma non lo è, in quanto viene
trascurato l' elemento "tempo". Certamente il LED
"lampeggia", ma a noi parrebbe sempre acceso !
Perchè il LED appaia illuminato alla vista occorre che
sia acceso per un certo tempo, necessario alla retina per cogliere
l' immagine.
E per apparire spento dovrà esserlo per un tempo adeguato.
Se facciamo svolgere l' azione di spegnere e accendere
il LED in successione dalle istruzioni del processore, dobbiamo
ricordare che queste, con un clock di 1 MHz impiagano ognuna tipicamente
250 us.
Avremmo il LED acceso per qualche centinaio di uS, poi spento per
altrettanto e
così via. Il risultato per l' occhio sarebbe quello di percepire il LED
costantemente acceso ad una luminosità non proprio massima.
Dovrà quindi essere un obbligo l' inserire nel flusso
delle istruzioni una qualche procedura che temporizzi questi stati di on
e off in modo tale da far apparire allo sguardo il LED come
lampeggiante.
|
Per cui sarà necessario modificare il flow chart così:
|
Adesso la sequenza è composta dagli elementi
sufficienti alla visione del lampeggio.
Il led viene acceso, dopo di che questo stato viene
prolungato per il tempo che si ritiene adeguato.
Poi il led viene spento e anche qui si attende un
tempo adegato prima di riprendere il loop.
Dato che i blocchi di attesa sono creati con
istruzioni, posiamo dare loro la durata che più ci aggrada, ottenendo
così ogni genere di lampeggio, veloce o lento e con tempi on e off
determinabili separatamente.
Esistono vari modi per creare queste routines di
attesa, ma qui utilizziamo la più semplice, ovvero quella detta waste
time (tempo perso), dato che il processore non ha alcuna altra attività
da svolgere nel frattempo.
Una trattazione dettagliata del modo waste time la
trovate qui. |
In particolare, onde evitare per ora calcoli sui tempi di ciclo delle
istruzioni, utilizziamo il calcolatore disponibile in rete per la creazione di
ritardi di tempo, il noto Delay
Generator di Nicolai Golovchenko.
Il nostro esercizio prevede di collegare un LED al bit 0 di PORTC:
esso lampeggerà alla frequenza di 2 impulsi al secondo, determinati dai cicli
di attesa e dalla frequenza del clock del processore.
Va ricordato che stiamo utilizzando il clock interno in modalità INTIO1; il
default di questo modo imposta un oscillatore interno a 1 MHz, il che
corrisponde ad un ciclo istruzione di 4 us.
Ovviamente, dovendo visualizzare il risultato, il progetto imposta Pickit3 come
debugger e viene utilizzata la Uniboard, direttamente alimentata a 5V dal Pickit.
Si dovranno semplicemente collegare il pin PC0 ad un dei LED
della scheda.
Questo pulsante può essere benissimo quello del RESET, che non viene usato
durante l' esercizio.
Il Pickit è inserito direttamente nella spina ICSP/ICD e permette un debug passo passo delle
istruzioni.
Ovviamente sarà possibile utilizzare Pickit2 senza variazioni se non la diversa impostazione nell'
ambiente MPLAB; oppure anche REAL ICE o ICD, sempre con il semplice cambio
nel setup di MPLAB, tenendo presente che non tutti i tools sono
in grado di alimentare l' hardaware.
Inoltre è sempre possibile utilizzare SIM senza alcun hardware o
debugger collegato.
|
Una descrizione particolareggiata delle funzioni dei PORT è consultabile
qui.
Per chi non avesse chiare le connessioni, qui trova di seguito una pagina dedicata.
L' esercizio richiede le seguenti risorse:
- MPLAB IDE installato
- Pickit3 (o Pickit2)
- 28-40pin UniBoard con PIC18F2321
0 4321 (o 2221 o 4221 o altro hardware similare)
- un cavetto jumper da 14-15 cm
Il listato sorgente è una estensione di quello già visto negli altri
esercizi e che fa da base per tutti i seguenti, aggiungendo gli elementi
necessari al nuovo lavoro.
Da un punto di vista strutturale il programma agisce in questo modo:
- Azzeramento dei latch del PORTC. Questa operazione preliminare serve per pre
determinare il livello che i pin assumeranno nel momento in cui
saranno configurati come uscite. In questo caso il LED collegato a PC0 si
troverà a livello basso, quindi sarà spento.
- Imposizione della direzione uscita per i bit 0 dei PORTC.
- Accensione del LED.
- Attesa con il LED acceso
- Spegnimento del LED
- Attesa con il LED spento
- Loop infinito al punto 3.
Vediamo il listato nei dettagli.
Come inizio, è sempre presente una testata di descrizione del programma e delle sue
funzioni e la già descritta definizione del processore usato, a cui segue la
sezione di configurazione.
;*****************************************************************
; Esercitazioni PIC18 - Esercitazione # 4
;*****************************************************************
; Lampeggio di un LED - routine di tempo
; Author : afg
; Version : E18_4_00
; Date : 09/10/2010
;-----------------------------------------------------------------
; Descrizione: Il programma fa lampeggiare un LED collegato a PC0.
;-----------------------------------------------------------------
; Note : Processore PIC18F2321
; Oscillatore interno a 1 MHz (clock interno 250 us)
; senza la necessità di componenti esterni
; Debug con MPLAB IDE e PickKit3
; Previsto per funzionare con tutti gli enhanced
;*****************************************************************
LIST P=18F2321 ; Utilizziamo il PIC18F2321
radix dec
; con base decimale per le operazioni
; matematiche
#include "P18F2321.INC"
; Include l' header file
; questo header è fornito dallo stesso Assembler e non richiede
; alcun file addizionale
;---------------------------------------------------------------------
; Configurazione minima dei debug del processore
;
; Disabilita analogica dal PORTB
CONFIG PBADEN = DIG
; Background debugger enabled su RB6 e RB7 per il debug con il
; Pickit o altro, attraverso ICSP/ICD
CONFIG DEBUG = ON
; Single-Supply ICSP disabled
CONFIG LVP = OFF
; Uso dell' oscillatore interno, port su RA7 e FOSC/4 su RA6
CONFIG OSC = INTIO1
; PWRT disabled per il debug
CONFIG PWRT = OFF
; Brown-out in hardware only
CONFIG BOR = ON
; Soglia BOR 4.2V
CONFIG BORV = 1
; Funzione del pin MCLR abilitata
CONFIG MCLRE = ON
; WDT disabilitato
CONFIG WDT = OFF
|
Assegniamo un paio di locazioni in RAM con la direttiva CBLOCK/ENDC.
Nel caso di questo programma, i due registri serviranno come supporto alle
routines di attesa.
;----------------------------------------------------------------------
;Assegna registri di memoria RAM
CBLOCK 0x00 ; blocco di RAM a partire da 0x00
d1 ; riserva 2 bytes per un contatore
d2
ENDC ; fine blocco RAM
|
Nell' area degli Equates inseriamo alcune definizioni che ci serviranno
durante il programma.
Da notare che utilizziamo il registro LATC al posto di PORTC dato che è più
adeguato e che gli enhanced prevedono questa possibilità per eliminare il problema
dell' R-M-W.
Possiamo anche cerare due macro per il comando del LED, con la finalità di
rendere più leggibile il listato.
;----------------------------------------------------------------------
; Equates
;
; assegnazioni per il LED
#define
LED
LATC,0 ; LED collegato a
RC0
;----------------------------------------------------------------------
; Macros
;
LedOn
MACRO
bsf
LATC,0
ENDM
LedOff
MACRO
bcf
LATC,0
ENDM
|
Ecco il "programma" vero e proprio, con ampi commenti che
descrivono le funzioni svolte da ogni riga.
Per i salti viene usata l' istruzione bra
(BRanch Always) tipica degli enhanced.
;=====================================================================
; Inizio programma
; Il programma:
; - imposta i pin RA0, RB0, RC0 come uscite
; - porta questi pin a livello alto
; se ad essi c'è collegato un LED verso massa, esso verrà acceso
ORG 0x00
; Programma inizia a 0x00 - vettore del reset
Start nop
; dummy - linea utile solo ai fini del debug
; inizializza a 0 i latch di uscita dei PORTC
; questo serve per avere subito a livello 0 i pin che saranno
programmati come uscite
; non ha effetto sui pin programmati come ingressi.
clrf
LATC
; inizializza PORTC,0 come output
; agendo sul registro di direzione
bcf
TRISC,0
; Ciclo di lampeggio
loop LedOn
; LED acceso
call
Attesa ; Attesa .25 s
LedOff
; LED spento
call
Attesa ;
Attesa .25 s
; il programma entra in un loop infinito
sul
; test pulsante - azionamento LED
bra
loop
|
Dato che utilizziamo la stessa sequenza di attesa più volte diventa sensato
formularla come una subroutine e richiamarla con l' istruzione call o rcall.
Le subroutines sono procedure che vengono usate in un programma principale,
diciamo dei sotto-programmi, che svolgono una funzione che viene ripetuta più
volte o che viene tratta da una libreria in cui è conservata dopo essere stata
provata.
Questa struttura consente di avere listati leggibili senza essere appesantiti da
ripetizioni inutili, mentre la subroutine può essere un elemento ri
utilizzabile in altre situazioni (per questo collezionabile in una libreria o
raccolta di funzioni).
Solitamente è più pratico porre le subroutines in coda la programma; questo
perchè sono funzioni definite di per se e già verificate funzionanti e
solitamente non occorre la loro analisi durante il debug del programma
principale. Vengono utilizzate come black box dei quali interessa il dato in
ingresso e quello in uscita e non in particolare come esso venga elaborato.
Qui viene usata una procedura waste time generata dal sito di Nicolai
Golovchenko e modificata per l' uso con il set di istruzioni enhanced.
Si tratta semplicemente di caricare due registri di RAM (d1 e d2) con valori
esadecimali calcolati e decrementarli fino a portarli a zero. Siccome ogni
istruzione a 1 MHz impiega 4 us per essere eseguita, creando un loop che
"spreca" 62500 cicli si ottiene una attesa di .25 s.
=====================================================================
; Subroutines
;
; Attesa .25 s
; Procedura ricavata dal Delay Generator di Golovchenko
; Sorgente modificato per l' uso con il set di istruzioni enhanced
; Delay = 0.25 seconds
; Clock frequency = 1 MHz
; Actual delay = 0.25 seconds = 62500 cycles
; Error = 0 %
Attesa ;62493 cicli
movlw 0xD2
movwf d1
movlw 0x31
movwf d2
Delay_0
decfsz d1, f
goto $+6 (2)
decfsz d2, f
goto Delay_0
;3 cicli
goto $+2 (1)
nop
;4 cicli (includendo la call)
return
;********************************************************************
; Direttiva di fine sorgente
END |
La fine del sorgente è determinata come al solito dalla direttiva END.
Pagine di informazione sulle routine di tempo come quelle usate ora è disponibile
qui.
Da notare che il programma vero e proprio, invece, non termina, dato che
permane nel loop che fa lampeggiare il LED
Avviando lo step-by-step potremo seguire il flusso delle istruzioni ed
osservare nella finestra degli SFR lo stato e l' accensione di LED, mentre
nella finestra della RAM è possibile visualizzare lo stato dei contatori
d1 e d2.
Si ricorda che, in step by step, per saltare le routine di tempo, che sono
loop ripetitivi di decremento di due contatori, quindi ben poco significativi
per il resto del programma, il tasto
Step Over di MPLAB permette l' esecuzione
delle subroutines, ma senza visualizzazione, salvando una quota notevole di
tempo durante il debug.
Se si incontrano errori nella compilazione è opportuno verificarli con la
lista di descrizione degli errori e correggere dove si è sbagliato.
Il file compresso di questa esercitazione è scaricabile dall'
area di download.
|