Esta es una pregunta que mi papá siempre solía hacerme. " ¿Por qué no se ejecuta todas las instrucciones y se detiene al final? "
Echemos un vistazo a un ejemplo patológico. El siguiente código fue compilado en el compilador C18 de Microchip para el PIC18:
void main(void)
{
}
Produce la siguiente salida de ensamblador:
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
Como puede ver, main () se llama, y al final contiene una declaración de devolución, aunque nosotros no lo pusimos explícitamente allí. Cuando regresa main, la CPU ejecuta la siguiente instrucción que es simplemente un GOTO para volver al principio del código. main () simplemente se llama una y otra vez.
Ahora, habiendo dicho esto, esta no es la forma en que la gente haría las cosas normalmente. Nunca he escrito ningún código incrustado que permita que main () salga así. Sobre todo, mi código se vería así:
void main(void)
{
while(1)
{
wait_timer();
do_some_task();
}
}
Por lo tanto, normalmente nunca permitiría que main () salga.
"OK ok", dices. Todo esto es muy interesante porque el compilador se asegura de que nunca haya una última declaración de retorno. ¿Pero qué pasa si forzamos el tema? ¿Qué pasa si codifiqué a mano mi ensamblador y no volví a saltar al principio?
Bueno, obviamente la CPU simplemente continuará ejecutando las siguientes instrucciones. Esos se verían así:
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
La siguiente dirección de memoria después de la última instrucción en main () está vacía. En un microcontrolador con memoria FLASH, una instrucción vacía contiene el valor 0xFFFF. Al menos en un PIC, ese código de operación se interpreta como un 'nop' o 'ninguna operación'. Simplemente no hace nada. La CPU continuaría ejecutando esos nops hasta el final de la memoria hasta el final.
¿Qué hay después de eso?
En la última instrucción, el puntero de instrucción de la CPU es 0x7FFe. Cuando la CPU agrega 2 a su puntero de instrucción, obtiene 0x8000, que se considera un desbordamiento en un PIC con solo 32k FLASH, por lo que vuelve a 0x0000, y la CPU felizmente continúa ejecutando las instrucciones al principio del código , como si se hubiera reiniciado.
También preguntaste sobre la necesidad de apagar. Básicamente, puedes hacer lo que quieras y depende de tu aplicación.
Si tuviera una aplicación que solo necesitaba hacer una cosa después del encendido, y luego no haga nada más, solo podría poner un poco de tiempo (1); al final de main () para que la CPU deje de hacer algo notable.
Si la aplicación requiere que la CPU se apague, entonces, dependiendo de la CPU, probablemente habrá varios modos de suspensión disponibles. Sin embargo, las CPU tienen el hábito de despertarse de nuevo, así que tendrías que asegurarte de que no haya límite de tiempo para el sueño, y que no esté activo el temporizador Watch Dog, etc.
Incluso podría organizar algunos circuitos externos que le permitirían a la CPU cortar completamente su propia energía cuando hubiera terminado. Vea esta pregunta: Utilizando un botón pulsador momentáneo como un interruptor de encendido y apagado .