Dies ist eine Frage, die mein Vater mir immer gestellt hat. " Warum durchläuft es nicht einfach alle Anweisungen und hört am Ende auf? "
Schauen wir uns ein pathologisches Beispiel an. Der folgende Code wurde im C18-Compiler von Microchip für den PIC18 kompiliert:
void main(void)
{
}
Es erzeugt die folgende Assembler-Ausgabe:
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
Wie Sie sehen, wird main () aufgerufen und enthält am Ende eine return-Anweisung, die wir jedoch nicht explizit angegeben haben. Wenn main zurückkehrt, führt die CPU den nächsten Befehl aus, der einfach ein GOTO ist, um zum Anfang des Codes zurückzukehren. main () wird einfach immer wieder aufgerufen.
Nun, nachdem wir das gesagt haben, ist dies nicht die Art und Weise, wie Menschen Dinge normalerweise tun würden. Ich habe noch nie einen eingebetteten Code geschrieben, mit dem main () so beendet werden kann. Meistens würde mein Code ungefähr so aussehen:
void main(void)
{
while(1)
{
wait_timer();
do_some_task();
}
}
Daher würde ich main () normalerweise nie beenden.
"OK, ok" sagst du. All dies ist sehr interessant, da der Compiler sicherstellt, dass es nie eine letzte return-Anweisung gibt. Aber was passiert, wenn wir das Problem erzwingen? Was ist, wenn ich meinen Assembler mit der Hand codiere und keinen Sprung zurück zum Anfang mache?
Nun, offensichtlich würde die CPU einfach die nächsten Anweisungen ausführen. Diese würden ungefähr so aussehen:
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
Die nächste Speicheradresse nach der letzten Anweisung in main () ist leer. Auf einem Mikrocontroller mit FLASH-Speicher enthält eine leere Anweisung den Wert 0xFFFF. Zumindest auf einem PIC wird dieser Op-Code als 'nop' oder 'no operation' interpretiert. Es tut einfach nichts. Die CPU würde diese Nops bis zum Ende des Speichers weiter ausführen.
Was ist danach?
Bei der letzten Anweisung lautet der Anweisungszeiger der CPU 0x7FFe. Wenn die CPU 2 zu ihrem Befehlszeiger hinzufügt, erhält sie 0x8000, was als Überlauf auf einem PIC mit nur 32k FLASH angesehen wird, und springt so auf 0x0000 zurück, und die CPU setzt die Ausführung von Befehlen gerne am Anfang des Codes fort , als wäre es zurückgesetzt worden.
Sie haben auch gefragt, ob das Gerät ausgeschaltet werden muss. Grundsätzlich können Sie tun, was Sie wollen, und das hängt von Ihrer Anwendung ab.
Wenn Sie eine Anwendung hatten, die nach dem Einschalten nur eine Aktion ausführen musste, und dann nichts anderes tun, können Sie einfach eine Weile warten (1). am Ende von main (), damit die CPU nichts Auffälliges mehr tut.
Wenn die CPU aufgrund der Anwendung heruntergefahren werden musste, stehen je nach CPU möglicherweise verschiedene Energiesparmodi zur Verfügung. CPUs haben jedoch die Angewohnheit, wieder aufzuwachen, sodass Sie sicherstellen müssen, dass keine zeitliche Begrenzung für den Ruhezustand festgelegt wurde und kein Watch Dog Timer aktiv ist usw.
Sie könnten sogar einige externe Schaltkreise organisieren, die es der CPU ermöglichen würden, ihre eigene Stromversorgung vollständig zu unterbrechen, wenn sie fertig ist. Siehe diese Frage: Verwenden eines Tasters als einrastender Ein-Aus-Kippschalter .