ESERCITAZIONE # 5
Il Clock Interno - INTIO1 / INTIO2
La velocità di esecuzione di una istruzione dipende dal clock del
processore.
In queste esercitazioni stiamo utilizzando il generatore interno di clock,
che è una rete RC di precisione.
Attraverso il CONFIG iniziale è possibile impostare due modi base, INTIO1 e
INTIO2.
La differenza tra i due consiste esclusivamente nel fatto che RA6 diventa una
uscita del clock / 4 e nel secondo, invece, è un GPIO.
Il primo modo è essenzialmente previsto per avere una sorgente di clock per
altri componenti eterni, ma, in debug, è utilizzabile anche come monitor del
clock con un frequenzimetro o un oscilloscopio.
Il secondo rende disponibili due pin addizionali di I/O digitale.
Maggiori informazioni sulle caratteristiche dell' oscillatore
integrato qui.
Il processore, per default al POR stabilisce una configurazione dell'
oscillatore interno tale da generare una frequenza di 1 MHz, ma questo valore
può essere facilmente modificato attraverso il programma. Infatti va notato
che, se la scelta della sorgente del clock è una impostazione nel CONFIG,
quella della frequenza, invece, va fatta attraverso le istruzioni del programma,
che, se necessario, andrà a modificare il registro relativo.
Quindi, per ricapitolare:
- la scelta della sorgente del clock è obbligatoria attraverso il CONFIG
- la scelta della frequenza del clock interno va fatta attraverso istruzioni
del programma
Ciò vuol dire che la frequenza del clock principale nei modi INTIO1/INTIO2
può essere cambiata "in corso d' opera", ovvero durante il
funzionamento del processore. E il foglio dati riporta le temporizzazioni di
questo scambio, di cui fa parte anche un bit nel registro che consente di
verificare il momento in cui l' oscillatore si è stabilizzato.
In questa Esercitazione vogliamo apprendere come maneggiare questo scambio
della frequenza.
Per evitare l' impiego obbligato di uno strumento costoso come un
oscilloscopio o un frequenzimetro, la visualizzazione dell' avvenuta
commutazione del clock viene demandata al lampeggio di un paio di LED. Comunque,
utilizzando il modo INTIO1, chi ha ha disposizione questi strumenti potrà
collegarli al pin RA6 e verificare il valore esatto del clock dopo ogni
commutazione.
I PIC della famiglia 18F2321 hanno il controllo di queste funzioni in un
registro OSCON, che, nei bit 6-4 consente di scegliere 8 diverse frequenze:
OSCON bit 6-4 IRCF2:0
Selezione della frequenza dell' oscillatore interno INTOSC
111 = 8MHz
110 = 4 MHz
101 = 2 MHz
100 = 1 MHz (valore di default al RESET)
011 = 500 kHz
010 = 250 kHz
001 = 125 kHz
000 = 31 kHz
Sempre nello stesso registro, il bit 2 indica lo stato dell' oscillatore:
OSCCON bit 2
IOFS : INTOSC Frequency stable
1 = la frequenza prodotta da INTOSC è stabile
0 = la frequenza prodotta da INTOSC non è stabile
Realizziamo, allora, un programma, decisamente più complesso dei precedenti,
che ha lo scopo di far lampeggiare due LED ad una velocità determinata dal
clock, utilizzando il metodo già visto. Il programma commuta da un clock a 1MHz
(default al POR) a 2, 4 , 8 MHz.
Vedremo così variare la velocità di lampeggio da un ciclo molto lento con il
clock a 1 MHz ad uno rapidissimo con il clock a 8 MHz.Il
disegno di un flow chart diventa, in questo caso, un obbligo.
|
Dopo aver inizializzato il processore e gli I/O
necessari, facciamo lampeggiare i LED per un certo numero di volte,
utilizzando come base tempo il ciclo delle istruzioni, ovvero la
frequenza del clock.
Questa, dopo l' avvio del processore, sarà per default fissata a 1
MHz.
Successivamente commutiamo il clock su un valore
maggiore e ripetiamo il lampeggio dei LED. Dato che il clock varia
raddoppiando, per mantenere costante il tempo di lampeggio dei LED, ne
aumentiamo le ripetizioni della stessa quantità.
Il loop prosegue commutando a passi successivi il
clock e facendo lampeggiare di conseguenza i LED.
Arrivati alla frequenza massima prevista dai modi INTIO, ovvero 8 MHz, il clock viene commutato di nuovo a 1 MHz e il
ciclo riprende.
Nulla vieta, volendo, di ampliare il ciclo sfruttando
anche le frequenze inferiori.
|
Possiamo notare che l' operazione di lampeggio è sempre la stessa, ripetuta
più volte. Questa
situazione si presta ottimamente per essere implementata come subroutine.
|
Il numero di lampeggi da effettuare viene passato alla
subroutine attraverso il registro WREG; in questo modo è possibile la
scrittura di un solo algoritmo per un numero di lampeggi variabile tra 1
e 256 (dato che WREG è a 8 bit).
Il valore passato attraverso WREG viene salvato una
locazione RAM che servirà per contare il numero dei lampeggi.
Il lampeggio dei LED è del tutto analogo a quanto già visto in
esercizi precedenti e si basa su una routine di attesa waste
time, la
quale, dipendendo dal clock per la sua esecuzione, è la componente che
determina la frequenza del lampeggio.
Eseguito un lampeggio, il contatore viene decrementato
di una unità; se il suo contenuto è maggiore di zero, sono ancora da
eseguire lampeggi, per cui il ciclo prosegue.
Se il contatore è azzerato, tutti i lampeggi sono
stati eseguiti e il ciclo è esaurito: spegniamo tutti LED e ritorniamo
al programma principale con l' istruzione Return.
Come detto, essendo il loop determinato dalla
frequenza del clock, il suo tempo di esecuzione dimezzerà per ogni
raddoppio del clock e viceversa. Se, nel contempo, passiamo alla routine
un numero adeguato di ripetizioni, avremo una ciclo per ogni valore di
clock di durata praticamente uguale.
Lo scambio di clock che avviene tra un ciclo di
lampeggio e l' altro darà origine ad un effetto luminoso interessante. |
Come per gli altri esempi, 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.
Viene definito un tempo base di 500 ms per un clock di 1 MHz, che si
ridurrà come segue:
Clock
[MHz] |
Ciclo
[us] |
Loop
Attesa |
Lampeggi
al s |
1 |
4 |
500 ms |
2 |
2 |
2 |
250 ms |
4 |
4 |
1 |
125 ms |
8 |
8 |
0.5 |
62,5 ms |
16 |
Il nostro esercizio prevede di collegare un LED al bit 0 di PORTC ed uno
al bit 1 di PORTC, che lampeggeranno come indicato in tabella in cicli
successivi.
Per quanto riguarda la commutazione del clock, anche in questo caso non è
opportuno basarsi su istruzioni da ripetere più volte, ma diventa sensato
scrivere una piccola libreria che contenga dei macro comandi.
Questa soluzione ha molti vantaggi:
- in primo luogo, rende del tutto auto descrittivo il sorgente
- inoltre costituisce un oggetto che potrà essere ri utilizzato in altre
occasioni senza la necessità di riscrivere codice
- per ultimo, data la sua struttura auto esplicativa, non richiede la
continua consultazione del foglio dati: una volta determinati i bit da
programmare e/o verificare durante la scrittura della libreria, il foglio
dati non sarà più necessario
Una descrizione dettagliata della libreria in questione la trovate qui.
Per l' esecuzione pratica si dovranno semplicemente collegare i pin PC0 e PC1
a due LED della UniBoard (o del proprio hardware).
Il Pickit è inserito direttamente nella spina ICSP/ICD e permette un debug passo passo delle
istruzioni.
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)
- due cavetti 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.
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.
Dovendo utilizzare una libreria esterna, la dobbiamo includere. Trattandosi
di MACRO, vanno definite PRIMA di essere utilizzate.
;----------------------------------------------------------------------
; Include libreria per gestione clock interno
#include "C:\PIC\LIBRARY\18F\18FIntClock.asm" |
Assegniamo un paio di locazioni in RAM con la direttiva CBLOCK/ENDC.
Nel caso di questo programma, saranno necessari tre registri come supporto alle
routines di attesa, ai quali si aggiunge il registro del contatore di lampeggi.
;----------------------------------------------------------------------
;Assegna registri di memoria RAM
CBLOCK 0x00 ; blocco di RAM a partire da 0x00
d1 ; riserva
3 bytes per un contatore
d2
d3
counter
; contatore
ENDC
; fine blocco RAM
|
Nell' area degli Equates inseriamo alcune definizioni che ci serviranno
durante il programma.
Possiamo anche cerare due macro per il comando dei LED, con la finalità di
rendere più leggibile il listato.
;----------------------------------------------------------------------
; Equates
;
; Numero lampeggi a 1 MHz
nlamp equ .10
; assegnazioni per i LED
#define
LED0
LATC,0 ; LED collegato a
RC0
#define LED1 LATC,1
; LED collegato a RC1
;----------------------------------------------------------------------
; Macros
;
Led1On
MACRO
bsf
LATC,0
ENDM
Led1Off
MACRO
bcf
LATC,0
ENDM
Led2On
MACRO
bsf
LATC,1
ENDM
Led2Off
MACRO
bcf
LATC,1
ENDM
LedOff
MACRO
bcf
LATC,0
bcf
LATC,1
ENDM
|
Ecco il "programma" vero e proprio, con ampi commenti che
descrivono le funzioni svolte da ogni riga.
Da notare che sono utilizzate due macro dalla libreria 18FIntClock.asm:
m18SetIntClock x
seleziona in OSCON il clock specificato dal parametro x
m18IntOscStable attende la stabilizzazione dell'
oscillatore prima di proseguire
;=====================================================================
; Inizio programma
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 PC0 e PC1 come output
; agendo sul registro di direzione
bcf
TRISC,0
bcf
TRISC,1
; Ciclo primario
mainloop
movlw
nlamp ;
numero lampeggi
call
ciclo ;
lampeggio
m18SetIntClock 2
; clock 2 MHz
m18IntOscStable
; attendi stabilizzazione oscillatore
movlw
nlamp ; numero lampeggi
call
ciclo ;
lampeggio
m18SetIntClock 4
; clock 4 MHz
m18IntOscStable
; attendi stabilizzazione oscillatore
movlw nlamp
; numero lampeggi
call
ciclo ;
lampeggio
m18SetIntClock 8
; clock 8 MHz
m18IntOscStable
; attendi stabilizzazione oscillatore
movlw
nlamp
; numero lampeggi
call
ciclo ;
lampeggio
m18SetIntClock 1
; clock 1 MHz
m18IntOscStable
; attendi stabilizzazione oscillatore
bra
mainloop ; ripeti
indefinitamemte
|
Dato che utilizziamo la stessa sequenza di ciclo di lampeggio più volte diventa sensato
formularla come una subroutine e richiamarla con l' istruzione call o rcall.
L' uso delle Macro pre definite consente di avere un listato molto semplice e
leggibile con facilità.
;=====================================================================
; Subroutines
;
; Ciclo di lampeggio alternato dei due LED
; Il numero dei lampeggi arriva attraverso WREG
ciclo
movwf
counter
; salva W in counter
ciclo1
Led1Off
; LED1 spento
Led0On
; LED0 acceso
call
Attesa
; attesa
Led0Off
; scambia LED
Led1On
call
Attesa
decfsz
counter, f
; decrementa e testa il contatore
bra
ciclo1
; non 0 - altro ciclo
; contatore azzerato - fine ciclo di
lampeggio
LedOff
; spegne i LED
return
; e ritorna |
Analogamente per la routine di attesa, settata per 500 ms a 1 MHZ di
clock, secondo l' algoritmo di Nicolai Golovchenko.
; Attesa 0.5 s @ 1 MHz di clock
; Procedura ricavata dal Delay Generator di Golovchenko
; Sorgente modificato per l' uso con il set di istruzioni enhanced;
Delay = 0.5 seconds
; Clock frequency = 1 MHz
; Actual delay = 0.5 seconds = 125000 cycles
; Error = 0 %
Attesa
;124993 cycles
movlw 0xA6
movwf d1
movlw 0x62
movwf d2
Delay_0
decfszd1, f
goto $+6
decfszd2, f
goto Delay_0
;3 cycles
goto$+4
nop
;4 cycles (including call)
return
;********************************************************************
; Direttiva di fine sorgente
END |
La fine del sorgente è determinata come al solito dalla direttiva END.
Da notare che il programma vero e proprio, invece, non termina, dato che
permane nel loop che fa lampeggiare i LED
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.
|