Questa è una domanda che mi faceva sempre mio padre. " Perché non scorre tutte le istruzioni e si ferma alla fine? "
Diamo un'occhiata a un esempio patologico. Il seguente codice è stato compilato nel compilatore C18 di Microchip per PIC18:
void main(void)
{
}
Produce il seguente output dell'assemblatore:
addr opco instruction
---- ---- -----------
0000 EF63 GOTO 0xc6
0002 F000 NOP
0004 0012 RETURN 0
.
. some instructions removed for brevity
.
00C6 EE15 LFSR 0x1, 0x500
00C8 F000 NOP
00CA EE25 LFSR 0x2, 0x500
00CC F000 NOP
.
. some instructions removed for brevity
.
00D6 EC72 CALL 0xe4, 0 // Call the initialisation code
00D8 F000 NOP //
00DA EC71 CALL 0xe2, 0 // Here we call main()
00DC F000 NOP //
00DE D7FB BRA 0xd6 // Jump back to address 00D6
.
. some instructions removed for brevity
.
00E2 0012 RETURN 0 // This is main()
00E4 0012 RETURN 0 // This is the initialisation code
Come puoi vedere, viene chiamato main () e alla fine contiene un'istruzione return, anche se non l'abbiamo esplicitamente inserita lì. Quando ritorna principale, la CPU esegue l'istruzione successiva che è semplicemente un GOTO per tornare all'inizio del codice. main () viene semplicemente chiamato più volte.
Ora, detto questo, questo non è il modo in cui le persone farebbero le cose di solito. Non ho mai scritto alcun codice incorporato che consentirebbe a main () di uscire così. Principalmente, il mio codice sarebbe simile a questo:
void main(void)
{
while(1)
{
wait_timer();
do_some_task();
}
}
Quindi normalmente non lascerei mai uscire main ().
"OK ok" stai dicendo. Tutto ciò è molto interessante che il compilatore si assicura che non ci sia mai un'ultima dichiarazione di ritorno. Ma cosa succede se forziamo il problema? Cosa succede se codifico a mano il mio assemblatore e non faccio un salto all'inizio?
Bene, ovviamente la CPU continuerebbe a eseguire le istruzioni successive. Sembrerebbero qualcosa del genere:
addr opco instruction
---- ---- -----------
00E6 FFFF NOP
00E8 FFFF NOP
00EA FFFF NOP
00EB FFFF NOP
.
. some instructions removed for brevity
.
7EE8 FFFF NOP
7FFA FFFF NOP
7FFC FFFF NOP
7FFE FFFF NOP
L'indirizzo di memoria successivo dopo l'ultima istruzione in main () è vuoto. Su un microcontrollore con memoria FLASH, un'istruzione vuota contiene il valore 0xFFFF. Almeno su un PIC, quel codice operativo viene interpretato come 'nop' o 'nessuna operazione'. Semplicemente non fa nulla. La CPU avrebbe continuato a eseguire quei nops fino in fondo alla memoria fino alla fine.
Cosa succede dopo?
All'ultima istruzione, il puntatore alle istruzioni della CPU è 0x7FFe. Quando la CPU aggiunge 2 al suo puntatore di istruzioni, ottiene 0x8000, che è considerato un overflow su un PIC con solo 32k FLASH, quindi torna indietro a 0x0000 e la CPU continua felicemente a eseguire le istruzioni all'inizio del codice , proprio come se fosse stato ripristinato.
Hai anche chiesto la necessità di spegnere. Fondamentalmente puoi fare quello che vuoi, e dipende dalla tua applicazione.
Se avessi un'applicazione che doveva solo fare una cosa dopo l'accensione, e quindi non fare nient'altro, potresti semplicemente dedicare un po '(1); alla fine di main () in modo che la CPU smetta di fare qualcosa di evidente.
Se l'applicazione richiede lo spegnimento della CPU, quindi, a seconda della CPU, saranno probabilmente disponibili varie modalità di sospensione. Tuttavia, le CPU hanno l'abitudine di svegliarsi di nuovo, quindi dovresti assicurarti che non ci siano limiti di tempo per il sonno e nessun timer Watch Dog attivo, ecc.
Potresti persino organizzare alcuni circuiti esterni che consentirebbero alla CPU di interrompere completamente la propria potenza al termine. Vedi questa domanda: Utilizzo di un pulsante momentaneo come interruttore a levetta on-off a scatto .