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.
|
|