Tutorials - PIC

 

Routine di attesa o ritardo (delay)
waste time




Alcuni chiarimenti sul programma di Golovchenko

Questa utility è veramente molto pratica da usare quando si debba rapidamente realizzare un loop di tempo del tipo prima  discusso.
Chi ha provato ad utilizzarla ha notato che il semplice pannello di controllo offre alcune opzioni


1 - selezione processori PIC e SX
2 - selezione tempo o cicli istruzione
3 - selezione routine o subroutine

1 - PIC è prodotto da Microchip, SX sono distribuiti da Parallax (http://www.sxmicro.com/) e sono quasi del tutto analoghi ai PIC.

2 - Si possono generare ritardi in base al tempo richiesto, in secondi, oppure come numero di cicli. Una casella permette l' inserzione della quantità richiesta e due scelte spuntabili alternativamente rendo il listato voluto.
La scelta del tempo non richiede commenti. La scelta dei cicli può essere altrettanto utile in parecchie situazioni, visto che la relazione tra tempo è cicli è ben chiara:

tempo = numero cicli * durata ciclo

dove la durata ciclo è pari a 1/4 del clock principale, come già detto. 


Va ricordato che si tratta di una applicazione di area anglofona, per cui segue la notazione USA per i numeri:

- si deve usare il punto e non la virgola, ad esempio 0.05 o 0.25 e non 0,05 o 0,25

Se non ci si attiene a questo, il programma risponde con una segnalazione di errore

- il tempo è espresso in secondi, per cui 250 ms saranno 0.25 e 50 us saranno 0.000005

Se si indica il valore errato, si avrà in risposta una routine che rende il tempo indicato nella finestra di selezione 

Il punto 3 può richiedere una discussione più lunga.
In un programma un po' complesso, l' unica via per realizzare un sorgente efficiente è quella di fare uso di subroutine.

Una subroutine è una procedura che scrivo una sola volta nel sorgente, ma posso richiamare quando e come desidero: l' istruzione CALL provvede a passare al Program Counter l' idirizzo di dove la subroutine è stata posta in memoria ed iniziandone l' esecuzione, dopo aver abbandonato il flusso del programma. Al termine dell' esecuzione, l' istruzione RETURN riporta il Program Counter alla riga successiva a quella del CALL, riprendendo il flusso del programma dove era stato interrotto.

Con una analogia, possiamo dire che una subroutine è come un timbro; se devo ripetere molte volte una scritta, è molto comodo  averla messa su un timbro e limitarsi a premerlo sulla carta invece di riscrivere a mano ogni volta la stessa frase. Il flow chart può essere questo:


Ovviamente la call e il return sono istruzioni che hanno un loro tempo di esecuzione e questo va conteggiato per la precisione dell' operazione. Quindi, l' algoritmo che genera il listato, quando barriamo la casella Generate Routine, tiene conto delle due istruzioni. Ad esempio, ripetendo la richiesta di un tempo di 250 ms @ 4 MHz, otteniamo:

; Delay = 0.25 seconds
; Clock frequency = 4 MHz

; Actual delay = 0.25 seconds = 250000 cycles
; Error = 0 %

   cblock
     d1
     d2
   endc

Delay
                ;249993 cycles
     movlw 0x4E
     movwf d1
     movlw 0xC4
     movwf d2
Delay_0
     decfszd1, f
     goto   $+2
     decfszd2, f
     goto   Delay_0

;3 cycles
     goto   $+1
     nop

;4 cycles (including call)
    return

Rispetto alla precede lista, il ciclo è impostato per 249996 cicli, impegnando la call e il return i restanti 4 cicli.


Il GOTO $

Un altro punto di notevole importanza riguarda il fatto che questa utility genera un codice semplificato dove si fa uso del simbolo $, che indica il Program Counter.
Questa notazione risale ai primi anni della programmazione ed era stata introdotta per effettuare salti senza impostare lavbel, che sull prime macchine occupavano spazio e rallentavano l' assemblaggio. Ora, sui PC moderni, questo problema non esiste più da tempo.
ma ancora si trova chi utilizza questa scrittura.
Ad esempio un:

goto  $+1

fa passare il Program Counter alla riga successiva.

Un  goto  $+2 fa saltare il P.C. alla seconda successiva e così via. 

Ma, attenzione, che questo è valido solamente in un ambiente dove le istruzioni sono lunghe 1 byte

Se la lunghezza delle istruzioni è diversa, la cosa non funziona più. Quindi una scrittura del genere NON E' PORTABILE tra processori che hanno  codici di istruzione di lunghezza diversa.

Questo capita con i PIC mid-range, PIC16 che hanno codici di 1 byte e gli enhanced, PIC18, con codici a 2 o 4 byte.
Quindi, il salto, applicato a un PIC18 porterebbe il suo Program Counter non sull' istruzione successiva, ma a metà strada, creando serri problemi.

Volendo scrivere un listato portabile, sarà obbligo eliminare il $, che è un riferimento assoluto, e sostituirlo con label. Se proprio si vuole utilizzare il $, occorrerà:

  •  moltiplicare per 2 la dimensione del salto
  •  e moltiplicare per 4 quando quando è compresa una istruzione a 4 byte, come è GOTO.

L' esempio qui sopra, applicato al set di istruzioni enhanced, diventerà:

Delay_0
       decfsz d1, f
       goto   $+6      ; modifica per il set enhanced
       decfsz d2, f
       goto   Delay_0
                       ; 3 cycles
       goto   $+4      ; modifica per il set enhanced
       nop
                       ; 4 cycles (including call)
       return

Se per salti brevi il calcolo può essere fatto manualmente, verificando la presenza di istruzioni a 4 bytes, per salti lunghi è improponibile e certa fonte di errori.

Il rischio dell' uso del $ è quello di ritrovarsi a perdere un mare di tempo alla ricerca delle cause di malfunzionamenti del programma che diventano difficili da determinare e richiedono lunghe e noise sessioni di debug passo passo.

Quindi, non solo è meglio utilizzare esclusivamente label e lasciare all' Assembler il compito di calcolare per noi, ma è opportuno abolire completamente l' uso di $, non solo nei 18F, ma in tutti i sorgenti per qualsiasi processore, perchè può essere causa di problemi difficili da risolvere.

Delay_0
       decfsz d1, f
       goto   del_1    ; goto a label
       decfsz d2, f
del_1  goto   Delay_0
                       ; 3 cycles
       goto   del_2    ; goto a label
del_2  nop
                       ; 4 cycles (including call)
       return

Una scrittura come questa può essere trasferita tra qualsiasi PIC di qualsiasi famiglia senza modifiche: il sorgente è diventato portabile.



Conclusione


Abbiamo cercato di chiarire uno dei metodi più semplici per generare ritardi e portato a conoscenza dell' esistenza sul WEB di utilities che svolgono per noi i calcoli relativi.
Ora, se tutto è chiaro, inserire ritardi waste time in un listato Assembly dovrebbe essere alla portata di tutti.

Ovviamente esistono altri modi di generare tempi, ad esempio usando i Timer, il che permette anche una azione in interrupt che rende il programma molto più efficace e permette di fare cose che con un polling sarebbero impossibili.


 

AVVERTENZA



E' da notare che l' espressione "perdita di tempo",  "tempo perso" o simile non hanno nessun senso "morale" se applicati ad una macchina.

Semmai potrà essere introdotto il concetto di tempo impiegato per fare un determinato lavoro e tempo di attesa tra un lavoro ed il successivo. Da un punto di vista energetico sarà certamente il caso di ridurre al minimo i tempi morti e il consumo di energia della macchina durante attese in cui non viene svolto lavoro utile (idle time, in inglese).

Questo concetto non ha nulla a che fare con il "perder tempo" riferito ad una persona; in questo senso la "perdita di tempo" indica il fatto che quella persona potrebbe utilizzare meglio quel tempo che ha disponibile ed invece lo spreca in attività che non gli sono di alcun vantaggio o miglioramento.

Dato che una macchina è prevista per fare un determinato lavoro e basta, non esiste per essa un concetto morale di tempo "perso", ma al massimo quello di ottimizzazione del consumo energetico detto prima.

In particolare, il waste time visto ora FA PARTE del lavoro della macchina, nel senso che i cicli di attesa servono essi stessi a qualcosa. Abbiamo vsito che se non ci fossero,  la macchina non potrebbe fare il suo lavoro come desideriamo. Che durante questi cicli la CPU sia impegnata solo a contare non ha alcun significato oltre alla necessità di far trascorre un tempo determinato senza fare nulla, in attesa del momento opportuno per effettuare l' azione voluta.

Certamente, da un punto di vista energetico, il fatto che il micro stia a contare lunghi cicli può essere inteso come uno spreco di energia, energia che il processore assorbe per svolgere il ciclo di attesa.
Nel caso in cui si voglia sviluppare una applicazione a bassissimo consumo sarà dunque il caso di implementare un sistema di attesa che riduca al minimo il consumo del processore e questo può essere realizzato con le funzioni di risparmio energetico (XLP, Sleep, ecc ) che sono disponibili nelle più recenti famiglie di PIC.
Allora, piuttosto che il semplice waste time ora visto, si dovranno implementare sistemi con wake up da SLEP tramite interrupt da eventi o da timer.
Ma si tratta di applicazioni con una complessità ben diversa da quanto ora visto e quindi riservate, ma indispensabili, alle applicazioni che lo richiedono.(anche  se sarebbe bene pensare all' attivazione di una progranmmazione " absso consumo"  anche nelle applicazioni ordinarie).

Un altro aspetto può prendere la situazione in cui il processore debba svolgere diverse tsk: in tal caso non sarà possibile sempre bloccare il processore in un loop polling, ma occorrerà una gestione in interrupt applicabile anche alle attese.

 

 

 


 

 

 

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