What Happens Before the Main Loop in Microcontrollers

For those new to microcontrollers, it is common to start writing code from the main() function — as if the microcontroller will obediently execute instructions as soon as main() runs. However, before the main loop (the loop logic within the main() function) starts, the microcontroller must first complete a series of “preparatory tasks”, similar to how we need to power on a computer and wait for the system to load. These low-level operations are the foundation for its normal operation. Today, we will break down the “pre-launch process” of a microcontroller in simple terms.

1

Step: Power-On Reset, from “Blank” to “Awake”When the microcontroller is powered on, it is like a person just waking up — the brain (processor core) is blank, the data in registers and memory is random, and it doesn’t even know “where to start working”. At this point, the reset circuit will first give it a “push”:After power is connected, the reset pin will hold a low level for a short period (for example, several milliseconds), which is equivalent to sending a signal to the microcontroller: “Don’t panic, wait for the power to stabilize!” Once the power voltage reaches the operational requirements of the microcontroller (for example, a stable output of 5V or 3.3V), the reset circuit will release the pin, allowing the microcontroller to switch from the “reset state” to the “ready to run state”.The core function of this step is to prevent hardware damage: if the power is not stable and the microcontroller is forced to run, it may lead to calculation errors in the processor or even damage peripherals. Just like we wouldn’t fill a glass with water while the faucet is still running erratically — we wait for the water to stabilize before proceeding with further actions.

2

Step: Find the “Startup Entry Point” and Jump to Startup CodeOnce the power is stable and the reset is complete, the first question for the microcontroller is: “Which segment of code should I execute first?”The solution is hidden in the vector table — this is a list of addresses written in the microcontroller’s “factory-locked memory” (such as ROM), similar to a “table of contents” for an operation manual, where each item corresponds to the handling address of a “special event”. The “power-on startup” corresponds to the reset vector (the first “directory item” in the vector table).As soon as the reset ends, the microcontroller will automatically read the address from the reset vector and then “jump” to this address — the address points to what we commonly refer to as the “startup code” (for example, the startup.s assembly file). This step is akin to turning on a computer, where the system first looks for the “bootloader” instead of directly opening software — the startup code acts as the microcontroller’s “bootloader”.

3

Step: Startup Code at Work: Building the “Framework” for HardwareThe startup code is a low-level program written in assembly language, and its task is to build the “operational framework” for the microcontroller, allowing subsequent C language code (such as main()) to run correctly. It primarily accomplishes three key tasks:1. Initialize the Stack: Find a Home for “Temporary Data”When we write code, the local variables we define (for example, int a = 0) and the parameters passed during function calls require temporary storage space — this is the role of the “stack”, akin to a “temporary workspace” for the microcontroller.The startup code will first designate an area for the stack: for example, it specifies a block of space in memory (RAM) and tells the microcontroller, “This area is specifically for storing temporary data, starting from here to there.” If the stack is not initialized, function calls will “not find a place to store data”, leading to program crashes.2. Initialize Global/Static Variables: Set Initial Values for “Long-Term Data”Global variables we define (for example, int g_count = 5) and static variables (for example, static int s_flag = 0) need to be assigned initial values before main() runs. However, when the microcontroller powers on, the data in RAM is random, so the startup code must do two things:This step is like organizing commonly used files (global variables) into designated folders and clearing out blank documents (variables initialized to 0) before using a notebook — ensuring that the data is “clean and correct” when used later.3. Initialize Key Peripherals: Get the “Tools” ReadyIn embedded systems, microcontrollers often need to control peripherals (such as LEDs, serial ports, sensors), and the “initial state” of these peripherals also needs to be configured before main(). For example:These operations are similar to connecting a printer and setting up printing parameters before use — if the peripherals are not initialized, attempts to control the LED or send serial data in main() will result in “no response”.

4

Step: Call Initialization Functions to “Fill in the Gaps” for C++ CodeIf the microcontroller is running C++ code (rather than C code), the startup code must perform an additional step: calling the constructors of global C++ objects.Global objects in C++ (for example, class LED g_led;) need to execute their constructors (for example, initializing LED pins) before the program runs. However, C language does not have the concept of “constructors”, so the startup code will specifically scan the “global object list” and call their constructors one by one, ensuring these objects are “ready” before main() runs.This step is a unique “filling operation” for C++ — if skipped, global C++ objects will remain in an “uninitialized state”, leading to issues when calling their member functions later.

5

Final Step: Jump to main(), Enter “Formal Work”Once the startup code completes all the above operations — stabilizing power, finding the entry point, initializing the stack/variables/peripherals, and (in a C++ environment) calling constructors — it will execute the final instruction: jump to the address of the main() function.From this point on, the microcontroller truly begins executing the code we have written, entering the loop logic in main() (main loop) — for example, the cycle of “reading sensor data → checking if it exceeds the threshold → controlling the relay action” officially begins to “work”.

6

Summary: The “Preparatory Work” Before the Main Loop is the “Foundation” of the Microcontroller

Many people think the “startup process” is an “invisible low-level detail” that is not worth paying attention to, but in fact, it is the “foundation” for the stable operation of the microcontroller:

  • If the stack is not initialized, function calls will crash;
  • If global variables are not assigned values, data will become chaotic;
  • If peripherals are not configured, hardware control will fail.

Understanding these processes not only helps us troubleshoot “program crashes upon running” (such as stack overflow or incorrect initial values for variables) but also clarifies that “microcontrollers do not just obey commands upon power-up” — they need to complete all preparations before they canefficiently work inthe main loop.

Click the following business card to follow this public account:If you like it, remember to “share” and “recommend”, “like” or “leave a message”!

Leave a Comment