Tutorials - PIC

 

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.

 



 

Copyright © afg. Tutti i diritti riservati.
Aggiornato il 24/03/11.