Routine di attesa o ritardo (delay)
waste time
|
Waste time by loop
Per evitare troppe parole, partiamo da un
esempio pratico e sfruttiamo il lavoro che Nikolai Golovchenko mette a
disposizione in rete a:
http://www.golovchenko.org/cgi-bin/delay
(in copia anche a http://www.piclist.com/cgi-bin/delay.exe).
Si tratta di un generatore on-line in grado di produrre la sequenza di
istruzioni per implementare un ritardo di qualsiasi durata per qualsiasi clock,
per processori PIC e SX.
Semplicissimo da utilizzare: chiediamogli un ritardo di 50 us con clock di 4 MHz.
Ne otteniamo il seguente risultato:
; Delay =
5e-05 seconds
; Clock frequency = 4 MHz
; Actual delay = 5e-05 seconds = 50 cycles
; Error = 0 %
cblock
d1
endc
;49 cycles
movlw 0x10
movwf d1
Delay_0
decfsz d1, f
goto Delay_0
;1 cycle
nop
|
Per ottenere 50 us di ritardo con un
ciclo di istruzione di 1 us, il sistema è semplice, è quello del vampiro:
faccio un "mucchio" di semi (bit) in un registro (d1) e lo do da
contare al processore: il processore decrementa il registro e quando ha
terminato, il tempo voluto è trascorso. Il flow chart è questo:
Ci si potrà chiedere perchè, per ottenere 50
cicli si sia caricato 10h (16 decimale). Il motivo è semplice: il loop è
composto da 2 istruzioni:
Delay_0
; decrementa il registro d1, salta istruzione successiva
se il registro è = 0
; 1 ciclo se non si effettua il salto, 2 cicli se si effettua il salto
decfsz d1, f
; 1 (2) us
goto Delay_0
; + 2 us |
per cui il loop spende (3 us * 15 volte) + 2
us dell' ultimo DECFSZ = 47 us, a cui si deve aggiungere il tempo di esecuzione
delle istruzioni iniziali che caricano i "semi" nel registro:
movlw0x10
; 1 ciclo
movwfd1
; + 1 ciclo = 2 cicli
per un totale di 48 cicli. Se si somma
1 ciclo del NOP finale si arriva a 50 cicli, che a 1 us per ciclo da i richiesti
50 us.
Semplice, vero ? Ma non tanto semplice il calcolare il valore da caricare nel
contatore. La formula per questo specifico è:
tempo = 2 cicli + ( (3 *
valore caricato in d1) - 1)
Per loop complessi, come vediamo subito, il
calcolo dei valori da mettere nei registri può essere altrettanto complesso e,
se esiste una via per eliminare calcoli tediosi, vale sempre la pena di usarla.
Per questo l' utility indicata risparmia tempo ed errori.
Una nota : le righe tra CBLOCK e ENDC comprese
sono l' indicazione dei registri di memoria che andranno riservati per la
routine di tempo.
Quale è il massimo ritardo implementabile in
questo modo ?
Quella data dal massimo numero inscrivibile in d1, che è 0xFF e che origina:
che genera
;766 cycles
movlw0xFF
movwfd1
Delay_0
decfsz d1, f
gotoDelay_0
|
ovvero 766 cicli che @ 4 MHz sono 766 us e che
diventano 383 us a 8 MHz e così via.
Se la frequenza del clock è diversa, basterà moltiplicare il ciclo per il
numero delle istruzioni nel loop e si otterrà il tempo.
Dove è necessario un tempo maggiore si
utilizzerà un loop che ripete più volte il primo loop e così via fino ad
ottenere il tempo voluto. Ogni loop richiederà un registro in RAM per il suo
contatore.
Ad esempio, per ottenere 250 ms @ 4 MHz, occorrono due registri RAM, d1 e d2
(conteggio a 16 bit).
Per ottenere 250 ms @ 20 MHz ne occorrono 3 e così via.
ottenendo
; Delay = 0.25 seconds
; Clock frequency = 4 MHz
; Actual delay = 0.25 seconds = 250000 cycles
; Error = 0 %
cblock
d1
d2
endc
;249998
cycles
movlw 0x4F
movwf d1
movlw 0xC4
movwf d2
Delay_0
decfszd1, f
goto $+2
decfszd2, f
goto Delay_0
;2 cycles
goto $+1
|
L' ultimo GOTO, che porta solamente all'
istruzione successiva, serve ad aggiungere 2 cicli.
Ripetiamo che questo approccio alla gestione del tempo è il più semplice, ma
va usato con criterio, perchè durante la gestione delle routine di tempo il
processore non può fare altro. Questa situazione non è possibile in programmi
diversi da quelli ultra semplici per didattica o piccoli demo; nel caso di
applicazione anche solo un poco più complesse sarà necessario utilizzare
sistemi in interrupt, che garantiscono la stessa accuratezza consentendo l'
esecuzione in tempo reale e/o di più task contemporanee.
In altri casi, invece, il waste time è la
scelta migliore, ad esempio nello stretching (stiramento, prolungamento) di un
impulso. Vediamo un esempio pratico.
Alcuni dispositivi con comunicazione seriale
necessitano di clock che possono essere molto lunghi rispetto al ciclo di
istruzione del processore. Ad esempio, il dispositivo può richiedere che il
periodo del clock sia 1 us minimo. Questo non è un problema se il clock del PIC
è 4 MHz, in quanto corrisponde ad un ciclo di istruzione.
; impulso di clock
seriale da 1 us
; ciclo di istruzione 1 us
bsf pindelclock
; clock a 1
bcf pindelclock
; clock a 0
|
Ma se il PIC va a 20 MHz, il suo ciclo di
istruzione diventa solo 200 ns. Occorre sostenere il tempo di durata del clock
seriale con un ritardo. Trattandosi di pochi cicli, la via ottimale è quella
del waste time.
; impulso di clock
seriale da 1 us
; ciclo di istrauzione 200 ns
bsf pindelclock
; clock a 1
nop
; 200 ns
nop
; + 200 ns
nop
; + 200 ns
nop
; + 200 ns
bcf
pindelclock ; + 200 ns = 1 us e
clock a 0 |
In un caso come questo non possiamo fare
diversamente dal buttare un pochino di tempo.
|