Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Caratteristiche
Architettura Harvard: il PIC possiede memorie separate per i dati e le istruzioni. Memoria FLASH per le istruzioni da 1 K (1024 istruzioni) programmabile con l'ausilio di un computer esterno. Questa memoria supporta fino ad un massimo di 1000 cicli di cancellazione/scrittura. Memoria RAM dei dati da 90 bytes, dei quali 22 utilizzati per i registri interni di sistema (SFR = Special Function Registers) e i restanti 68 come registri utente liberi (GPR = General Purpose Register) Frequenza di clock massima: 10MHz (20MHz per la versione 16F84A) Set istruzioni RISC: Un set di appena 35 istruzioni. Grazie al sistema di pipelining ogni istruzione viene eseguita in un singolo ciclo macchina (4 colpi di clock) eccetto le istruzioni di salto che richiedono 2 cicli macchina (8 colpi di clock). Ad esempio, con un oscillatore esterno da 4MHz richiesto 1 microsecondo per l'esecuzione di una istruzione non di salto e 2 microsecondi per quelle di salto (condizionato o non). Memoria EEPROM interna da 64 bytes. Questa memoria pu essere programmata via software dal PIC stesso e sopporta fino ad un massimo di 1 milione di cicli di cancellazione/scrittura. Tale memoria non pu essere utilizzata come RAM aggiuntiva per via della sua lentezza (20 ms in scrittura) e l'accesso seriale (non e' mappata nello spazio degli indirizzi direttamente raggiungibili dalla CPU e viene letta/scritta solo tramite l'ausiliio di alcuni dei registri speciali SFR) Stack a 8 livelli (Lo stack implementato in una memoria a parte non visibile da programma e quindi non occupa lo spazio della RAM) Timer interno a 8 bit Watch-dog interno 2 porte di I/O (porta A a 5 bit e porta B a 8 bit) per un totale di 13 piedini di ingresso/uscita. Ogni linea pu essere programmata indipendentemente dalle altre come linea di ingresso o linea di uscita. Possono sembrare poche ma va sempre ricordato che il microcontrollore alloggiato in un DIL a soli 18 piedini! 4 diverse possibilit di interrupt (esterno tramite piedino apposito, del timer, esterno dovuto al cambio di stato dei piedini di I/O, interno di completamento della fase di scrittura della EEPROM) Opzione di protezione del codice. Settando questa opzione viene impedito ogni tentativo di lettura della memoria FLASH d programma. Tensione di alimentazione da 2 a 6 volt. Tensione di programmazione da 12 a 14 volt. Nella sigla la f sta per flash (memoria programma flash)
Memoria 2M x 2N bit
Normalmente non si accede al singolo bit (cella) di memoria ma viene letta/scritta lintera riga selezionata dallindirizzo di riga.
Piedinatura
Come detto prima il PIC possiede 18 piedini. 13 di questi sono adibiti all'I/O in logica TTL: questo significa che i valori booleani sono rappresentati dalle tensioni 0V e +5V rispettivamente per lo 0 e 1 logico. I rimanenti piedini vengono impiegati per il reset, l'alimentazione e per l'oscillatore esterno che fornisce il clock al PIC stesso. Come si vede in figura alcuni piedini hanno una duplice funzione (ad esempio il piedino numero 3 porta la dicitura RA4 e T0CKl) anche se di default vale la prima (il piedino numero 3 e' di default RA4). Le funzioni secondarie verranno spiegate piu' avanti.
Ecco l'elenco dei piedini e delle loro funzioni: RA0, RA1, RA2, RA3, RA4 - Sono i 5 piedini della porta A (tale porta e' a 5 bit). I piedini possono essere configurati indipendentemente come linee di ingresso o di uscita. Quando un piedino e' impostato come linea di uscita, presentera' una tensione di 0V o 5V a seconda che il PIC abbia associato uno 0 o 1 logico al piedino stesso, tramite apposite istruzioni. Quando e' impostato come ingresso, il PIC potra' ricevere un valore booleano leggendo il livello di tensione fornito al piedino in questione. Se il livello e' maggiore di 2V verra' letto un 1 logico, se inferiore a 0.8V verra' letto lo 0 logico. Applicando una tensione compresa tra 0.8 e 2V il risultato e' impredicibile. RB0, RB1, RB2, RB3, RB4, RB5, RB6, RB7 - Sono gli 8 piedini della porta B. Valgono le considerazioni viste per quelli della porta A. GND - Riferimento di massa VDD - Tensione di alimentazione. Deve essere compresa tra 2 e 6V. Di norma e' 5V. OSC1 e OSC2 - A questi piedini deve essere collegato l'oscillatore che fornisce il clock al sistema. Per alcuni tipi di oscillatori sono necessari tutti e due i piedini, per altri serve solo OSC1 e quindi OSC2 puo' essere lasciato scollegato. #MCLR - Piedino di reset, detto anche MasterCLeaR. L'attivazione del piedino produce un reset del PIC, cioe' il sistema riparte da capo con l'esecuzione del programma interno, come quando viene appena acceso. Questo piedino e' l'unico ad essere attivo basso, cio' significa che solo portandolo a massa si provoca il reset del PIC; durante il funzionamento normale il piedino deve essere mantenuto a livello logico alto tramite apposita resistenza limitatrice di corrente.
contempla anche memorie di dimensioni maggiori: i progettisti tendono a ridurre le differenze tra le varie versioni dei PIC per minimizzare i costi di produzione. L'istruzione viene poi inserita nell'Instruction register (di 14 bit) e poi decodificata ed eseguita. Il Program Counter viene incrementato di uno (oppure di una altra quantita' se e' previsto un salto). Il microcontrollore possiede un solo registro esterno alla memoria RAM, il registro W (Work). Questo registro entra in gioco durante le operazioni booleane, aritmetiche o di movimentazione dei dati. Ad esempio, per copiare un byte da una posizione di memoria ad un altra occorrono due istruzioni macchina: la prima legge la posizione di memoria desiderata e ne trasferisce il contenuto (il byte) nel registro W, la seconda trasferisce il contenuto di W nella posizione di memoria di destinazione. Le operazioni booleane o aritmetiche prevedono sempre e solo due operandi, il primo e' W, il secondo e' un locazione di memoria RAM (cioe' un registro del File Register); si puo' scegliere se piazzare il risultato dell'operazione in W (e quindi sovrascrivere il primo operando), o nella stessa locazione di memoria (sovrascrivendo il secondo operando). Le porte di I/O (porta A e porta B) sono mappate in RAM, cio' significa che si puo' leggere e scrivere nelle porte semplicemente leggendo e scrivendo nelle relative locazioni in RAM. Ad esempio, una scrittura di un byte nella locazione di memoria collegata alla porta B causa la configurazione opportuna dei segnali di tensione nei piedini della porta B (RB0,RB1,RB2 ... RB7), viceversa una lettura traduce in un byte la configurazione attuale delle tensioni applicate sui piedini di tale porta. Ovviamente un piedino di I/O non puo' essere simultaneamente di input e di output: occorre decidere all'inizio la "direzione" dei piedini per tutte e 13 le linee e cio' si risolve ancora una volta scrivendo in determinate posizioni di memoria RAM. Piu' avanti si chiariranno tutti questi meccanismi con esempi in assembler.
La memoria RAM e' suddivisa in due banchi distinti (banco 0 e banco 1). Per poter accedere ad un registro e' quindi necessario specificare sia l'indirizzo di memoria che il banco. Ad esempio, all'indirizzo 01h e banco 0 abbiamo TMR0, mentre all'indirizzo 06h e banco 1 abbiamo TRIS B. Alcuni registri sono "copiati" in entrambi i banchi; cio' significa che per accede a quel registro e' sufficiente specificarne l'indirizzo di memoria, non importa quale banco sia abilitato in quel momento. Ad esempio, per accedere al registro PCL possiamo portarci all'indirizzo 02h e banco 0, ma anche all'indirizzo 02h e banco 1. I registri di questo tipo sono: Indirect addr., PCL, STATUS, FSR, PCLATH, INTCON e tutti e 68 i registri GPR.
I registri SFR
Come appare poco evidente dalla composizione della memoria RAM, i registri SFR sono solo 16 (sembrerebbero 22 scartando le 2 sole aree in azzurro, ma ovviamente quelli mappati in ambedue i banchi contano sempre per uno solo, quindi sono effettivamente 16!): Registri SFR di controllo del PIC
STATUS FSR PCLATH INTCON Registri SFR per le porte di I/O PORT_A TRIS_A PORT_B TRIS_B Registri SFR per l'utilizzo della memoria EEPROM interna EEDATA EECON1 EEADR EECON2
Una volta acceso (o resettato), il microcontrollore inizia ad eseguire il programma a partire dalla locazione 0000h, detta anche "vettore di reset", per poi proseguire con le istruzioni successive. Esiste pero' una locazione speciale, cioe' l'indirizzo 0004h, che assolve ad un compito speciale; ogni qualvolta si verifica un interrupt (secondo le modalita' descritte che si vedranno in seguito) il PIC salta immediatamente a questa locazione. In questo modo, se si desidera creare una routine di gestione delle interruzioni occorre farla cominciare da questa precisa locazione (0004h) in modo tale che essa verra' eseguita non appena si verifichera' un interrupt. Ovviamente prima della locazione 0004h occorrera' inserire un salto incondizionato per poter "oltrepassare" la routine di gestione delle interruzioni: di solito il salto e' inserito proprio in prima posizione, cioe' in 0000h, cosiche' il microcontrollore, una volta accesso o resettato, passera' immediatamente ad eseguire il salto e quindi si posizionera' alla locazione opportuna scelta dal programmatore. In figura si mostra graficamente il concetto di salto al vettore di interrupt:
Senza approfondire troppo, l'ultima istruzione della routine di gestione delle interruzioni dovra' essere un RETFIE per poter ritornare ad eseguire il programma da dove era stato interrotto, ma tutto questo verra' approfondito alla sezione relativa agli interrupt. Ovviamente se non e' prevista alcuna gestione di interrupt e quindi non c'e' alcuna routine di servizio delle interruzioni tutto il discorso puo' essere anche trascurato.
Il program counter
Il microcontrollore tiene traccia dello stato di avanzamento del programma grazie ad uno specifico puntatore, il program counter. Nel caso del 16F84, il program counter e' un puntatore a 13 bit che indica l'istruzione corrente da eseguire (solo i primi 10 bit dell program counter vengono utilizzati, per un totale di 2^10 = 1024 locazioni indirizzabili). Al reset del pic il program counter vale 0 (prima istruzione in cima alla memoria di programma), successivamente, per ogni istruzione eseguita, esso viene incrementato di uno. Le istruzioni di salto (condizionato o no) modificano esplicitamente il program counter quando sono eseguite, imponendo un salto opportuno nel normale flusso di esecuzione del programma. Gli 8 bit meno significativi del program counter sono accessibili in lettura e scrittura dal registro PCL, quindi da tale registro e' possibile sia modificare gli 8 bit meno significativi del program counter che leggerne lo stato. Il registro PCL viene costantemente aggiornato e rispecchia sempre lo stato dei 8 bit meno significativi del program counter. Per i restanti 2 bit del program counter il discorso e' differente: tali bit sono nascosti e non sono visibili all'utente, ma possono essere modificati andando a cambiare i due bit meno significativi del registro PCLATH. PCLATH non viene mai aggiornato e quindi il suo stato non corrisponde a quello dei 2 bit piu' significativi del program counter. La figura mostra come sono relazionati i registri PCL e PCLATH con il program counter:
In definitiva, durante il funzionamento normale del microcontrollore (esecuzione delle istruzioni, salti condizionati, salti incondizionati, chiamate a funzione etc..) il program counter viene aggiornato correttamente e non occorre preoccuparsi del suo stato. Occorre invece fare attenzione solo nel caso in cui una istruzione di programma modifichi esplicitamente il registro PCL: ogni volta che una qualche istruzione effettua una scrittura nel registro PCL, il contenuto di tale registro viene posto nei 8 bit meno significativi del program counter come detto in precedenza, ma anche i due bit meno significativi del registro PCLATH vengono copiati nei due bit piu' significativi del program counter (bit 8 e 9). Cio' significa che se una istruzione tenta di incrementare forzatamente il registro PCL di 10 unita' (al fine
di effettuare un salto in avanti nel programma di 10 istruzioni senza usare apposite istruzioni di salto) puo' farlo, ma occorre ricordarsi che i due bit meno significativi del registro PCLATH vengono automaticamente copiati nei due bit piu' significativi del program counter. Quindi, ogni volta che ci si appresta a scrivere in PCL, occorre far in modo che lo stato dei bit 0 e 1 di PCLATH corrisponda a quello dei bit 8 e 9 del program counter, pena un salto ad una locazione non desiderata.
La EEPROM
La EEPROM interna e' una memoria secondaria ad accesso relativamente lento (20ms per un cliclo di lettura o scrittura) di 64 bytes. La EEPROM puo' essere scritta e letta da programma attraverso gli appositi registri SFR ed il suo contenuto permane anche a microcontrollore spento; in effetti la durata massima di ritenzione dei dati puo' arrivare fino a 40 anni. La EEPROM verra' approfondita alla sezione "EEPROM interna".
Il registro STATUS
I bit 6 e 7 non sono utilizzati, il bit 5 usato per selezionare il blocco di memoria
Descrizione Microchip Add Literal and W Add W and f Clear f Clear W Register Bit clear f
Operazione equivalente W=W+k d = W + f (dove d pu essere W o f) f=0 W=0 Fb = 0 (resetto il b-esimo bit di f)
14 bit opcode
Operazioni di base
BSF F B BCF F B
Esempio: Settare ad uno il sesto bit meno significativo del registro alla locazione 0x13 BSF 0x13,5 NOTA: Il sesto bit e' il numero cinque, perche' il primo bit e' il numero zero! Esempio: Portare a zero il primo e l'ultimo bit del registro FSR BCF 0x04,0 BCF 0x04,7 oppure, utilizzando le etichette: FSR EQU 0x04 BCF FSR,0 BCF FSR,7 Esempio: Portare a zero il contenuto del registro in posizione 0x19 BCF BCF BCF BCF BCF BCF BCF BCF 0x19,0 0x19,1 0x19,2 0x19,3 0x19,4 0x19,5 0x19,6 0x19,7
NOTA: ovviamente l'azzeramento di un registro si puo' fare in modi piu' efficienti e veloci.
Esempio:
selezionare il banco 1 della RAM BSF 0x03,5 Esempio: selezionare il banco 0 della RAM BCF 0x03,5 Esempio: settare ad uno il terzo bit del registro TRIS_A supponendo che attualmente sia selezionato il banco 0. STATUS EQU 0x03 TRIS_A EQU 0x04 BSF STATUS,5 BSF TRIS_A,3 NOTA: Chiaramente l'accesso al registro STATUS (0x03) non richiede la selezione preventiva del banco in quanto e' mappato in entrambi.
Esempio: Caricare il registro W con il valore letterale 0x34 (52 in decimale) MOVLW 0x34 Esempio: Muovere il contenuto del registro W alla locazione di memoria 0x41 (in questo caso un registro GPR) MOVWF 0x41 Esempio: Muovere il contenuto del registro in posizione 0x32 nel registro W MOVF 0x32,0 Esempio: Copiare il contenuto della locazione di memoria 0x0F nella locazione 0x10. MOVF 0x0F,0 MOVWF 0x10
NOTA: In questi casi, siccome le locazioni 0x34,0x32,0x0F e 0x10 sono relative a registri GPR non e' mai necessario specificare il banco RAM (0 o 1). Esempio: Copiare il contenuto del registro TRIS_A nel registro W BSF 0x03,5 ;selezione del banco RAM 1 MOVF 0x05,0 NOTA: In questo caso e' necessario prima specificare il banco RAM per accedere a TRIS_A Esempio: Copiare il contenuto del registro OPTION_REG in PORT_A BSF 0x03,5 MOVF 0x01,0 BCF 0x03,5 MOVWF 0x05 ;selezione del banco RAM 1 ;selezione del banco RAM 0
Azzerare i registri (CLRF e CLRW) Vengono Sintassi utilizzate per azzerare delle i contenuti dei registri. istruzioni:
CLRF
Azzera il contenuto del registro puntato da F. Viene automaticamente settata a 1 la ZERO_FLAG del registro di stato.
CLRW
Azzera il contentuo del registro di lavoro W. Viene automaticamente settata a 1 la ZERO_FLAG del registro di stato. Esempio: Azzerare il contenuto di PORT_A CLRF 0x05 Esempio: Azzerare il contenuto di TRIS_B BSF 0x03,5 CLRF 0x05 ;selezione del banco RAM 1
No operation (NOP) Questa istruzione non produce alcun risultato. La sua "esecuzione" comporta solamente l'incremento del program counter alla prossima istruzione. L'istruzione di NOP viene utilizzata per effettuare dei ritardi nell'esecuzione dei programmi. Supponendo che l'oscillatore esterno sia di 4MHz, tutte le istruzioni non di salto (come NOP) verranno eseguite in 1us: se quindi e' necessario ritardare di 10us l'esecuzione in un punto di un programma, sara' sufficiente inserire in quel punto 10 NOP consecutivi. Ovviamente la risoluzione del tempo di ritardo sara' pari ad 1us. Ritardi piu' grandi possono essere effettuati tramite NOP
all'interno di cicli come si vedra' piu' avanti. L'istruzione NOP si rivela utilissima nei casi in cui si deve generare un segnale con tempistiche ben precise, ad esempio una comunicazione seriale RS232, oppure frequenze sonore. Sintassi della istruzione:
NOP
Esempio: Effettuare un ritardo di 3us supponendo un oscillatore da 8MHz. NOP NOP NOP NOP NOP NOP NOTA: Con un oscillatore da 8MHz ogni istruzione viene eseguta in 0,5us. Quindi per un ritardo di 3us occorrono 6 NOP.
Salto incondizionato (GOTO) Questa istruzione permette di saltare incondizionatamente alla linea di programma desiderata, qualunque essa sia. L'istruzione viene eseguita in due cicli macchina (8 colpi di clock). Sintassi della istruzione:
GOTO
addr
Salta ad eseguire l'istruzione posizionata all'indirizzo "addr". "addr" e' l'indirizzo di memoria di programma (memoria FLASH) alla quale si vuole saltare; percio' "addr" deve essere un valore compreso tra 0 e 1023. Spesso e' scomodo scrivere direttamente la locazione di memoria in forma numerica, percio' molti ambienti di sviluppo, come MPLAB, permettono di specificare delle label (da NON confondere con le etichette della direttiva EQU vista in precedenza!). La label e' semplicemente una stringa di testo arbitraria preceduta da ":" e la si posiziona all'interno del codice, tra una istruzione ed un altra. Quando si vuole saltare all'istruzione immediatamente successiva alla label, la si richiama nel goto scrivendo al posto di "addr" il nome dato alla label. Gli esempi successivi chiariscono ulteriormente il concetto: Esempio: Ripetere il caricamento del registro W con il valore 0x30 all'infinito. :loop ;label di nome "loop" MOVLW 0x30 ;caricamento di W con il valore 0x30 GOTO loop ;salto alla istruzione dopo "loop" Esempio: Saltare tre istruzioni BSF 0x03,5 MOVF 0x01,0 GOTO salto BCF 0x03,5 CLRW MOVLW 0x10 :salto BCF 0x03,5
;salto
MOVWF 0x05
Salti condizionati (BTFSC, BTFSS, DECFSZ e INCFSZ) Le quattro istruzioni di salto del microcontrollore sono BTFSC,BTFSS, DECFSZ e INCFSZ. Anche se i nomi di queste istruzioni possono apparire complicati, il loro funzionamento e' estremamente semplice. Quello che differenzia tra loro le istruzioni e' la politica di salto ed alcuni altri effetti collaterali specifici, ma in linea di massima si comportano in questo modo: se la condizione di salto e' FALSA si esegue l'istruzione successiva, altrimenti, se la condizione di salto e' VERA, si esegue quella immediatamente dopo a quella successiva. La figura spiega meglio il concetto:
In questo caso e' stata scelta BTFSC, ma poteva tranquillamente essere un'altra delle quattro. Se la condizione di salto (specifica per ognuna delle istruzioni) e' FALSA, allora si prosegue normalmente con l'esecuzione del programma, cioe' eseguendo l'istruzione 1, poi l'istruzione 2 e cosi' via come mostrato nella figura. Se la condizione e' VERA allora NON si esegue l'istruzione 1 ma si continua con l'esecuzione del programma dall'istruzione 2 in poi. Molto spesso l'istruzione 1 e' un salto incondizionato (GOTO); in questo modo e' possibile redirigere l'esecuzione del programma ad un altro punto. Le istruzioni di salto richiedono 2 cicli macchina quando la condizione e' VERA ed 1 solo ciclo macchina quando questa e' FALSA. Sintassi delle istruzioni:
BTFSC
F,B
Se il B-esimo bit del registro F e' settato a uno (condizione FALSA) viene eseguita l'istruzione immediatamente successiva; se vale zero (condizione VERA) l'istruzione immediatamente successiva viene saltata e si passa a quella dopo.
BTFSS
F,B
Se il B-esimo bit del registro F e' settato a zero (condizione FALSA) viene eseguita l'istruzione immediatamente successiva; se vale uno (condizione VERA) l'istruzione immediatamente successiva viene
saltata e si passa a quella dopo. Questa istruzione di salto e' simile alla precedente ma il controllo sul bit B-esimo e' invertito.
DECFSZ
F,D
Viene decrementato di una unita' il contenuto del registro F. Il risultato viene posto in W se D=0 (lasciando inalterato F), viene posto in F se D=1. Se inoltre tale risultato e' zero si salta l'istruzione successiva (condizione VERA), altrimenti (condizione FALSA) si prosegue normalmente. NOTA: Il decremento e' effettuato PRIMA del controllo della condizione!
INCFSZ
F,D
Viene incrementato di una unita' il contenuto del registro F. Il risultato viene posto in W se D=0 (lasciando inalterato F), viene posto in F se D=1. Se inoltre tale risultato e' zero (condzione VERA) si salta l'istruzione successiva, altrimenti (condizione FALSA) si prosegue normalmente. NOTA: L'incremento e' effettuato PRIMA del controllo! Esempio: Se il primo bit del registro 0x20 e' 1 caricare W con il valore 0x00, altrimenti caricare W con il valore 0xFF. REG EQU 0x20 BTFSC REG,0 GOTO yes MOVLW 0xFF GOTO end :yes MOVLW 0x00 :end ;controlla se il primo bit del registro 0x20 e' 1 ;se vale 1 vai a "yes" ;se vale 0 carica W con 0xFF e vai a "end" ;setta W con 0x00
Esempio: Scrivere nel registro 0x30 tutti i valori in successione da 0 a 255. Infine azzerare W. MOVLW 0x00 MOVWF 0x2F :loop MOVLW 0x2F,0 MOVWF 0x30 INCFSZ 0x2F,1 GOTO loop CLRW ;carico W con 0x00 ;scrivo W nel registro di appoggio 0x2F ;carico W con il contenuto di 0x2F ;scrivo il contenuto di W nel registro 0x30 ;incremento il registro 0x2F e controllo se vale zero ;non e' zero, vai a "loop" ;si e' zero, resetta W. Programma terminato.
Cicli di delay Abbiamo gia' visto come effettuare un ritardo tramite l'uso ripetuto dell'istruzione NOP. La tecnica non permette di generare grandi ritardi perche' occorrebbe inserire un gran numero di NOP in sequenza, occupando tutta la memoria di programma. Conviene invece scrivere un ciclo che itera per un determinato numero di volte, decrementando un registro GPR adibito a contatore; l'istruzione di decremento e' incorporata in quella di salto, quindi il sistema e' estremamente semplice da implementare. Piu' difficile e' stabilire il valore iniziale del contatore per ottere uno specifico tempo di ritardo: occorre infatti calcolare accuratamente tutte le durate di esecuzione delle istruzioni. Un tipico ciclo di delay e' quello riportato qui sotto: Ciclo di delay: Considerando un oscillatore a 4MHz si crea un ritardo di 500us (mezzo millisecondo)
COUNT EQU 0x30 :delay MOVLW 0xA7 MOVWF COUNT :loop DECFSZ COUNT,1 goto loop ;carico W con il valore 167 ;lo memorizzo nel registro COUNT ;decremento di uno, se non e' zero vai a "loop"
Il loop principale prevede una istruzione DECFSZ ed un GOTO. DECFSZ richiede 1us quando NON salta (cioe' nel caso in cui il registro non sia zero) e 2us quando salta (e quindi termina la routine di delay). Considerando N la nostra incognita (il valore di inzializzazione per count), DECFSZ richiede un tempo pari a: tempo_DEFSZ (N-1) volte L'istruzione In tempo_tot semplificando: tempo_tot semplificando tempo_tot risolvendo N rispetto = = a N 3N-1 si = 3(N-1)+2 us ancora: us ottiene: (tempo_tot+1)/3 richiede 1us GOTO viene = (quando non salta) e eseguita N-1 volte definitiva = (N-1)+2 all'ultimo giro richiede e quindi richiede si (N-1)+2+2(N-1) us 2us (salta). 2(N-1) us. ha: us
se "tempo_tot" e' appunto 500 come da richiesta, si ottiene N = 167 (0xA7 in esadecimale) Per avere ritardi maggiori occorre annidare piu' cicli uno dentro l'altro utilizzando quindi tanti registri contatori quanti sono i cicli annidati, ma il calcolo dei valori di inizializzazione si complica ancora di piu' ed e' necessario risolvere sistemi non lineari molto piu' complessi. Inoltre, spesso e volentieri le soluzioni prevedono valori non interi per cui o si sceglie di approssimare oppure si inseriscono NOP finali esterni ai cicli per compensare gli errori di approssimazione.