0
0
Power-electronicsHow-ToIntermediate · 4 min read

How to Write a Bootloader in Embedded C: Simple Guide

To write a bootloader in embedded C, create a small program that runs on device startup to load and verify the main application code from memory. Use startup code to set the processor state, then implement memory read/write functions and jump to the main app after validation.
📐

Syntax

A bootloader in embedded C typically includes these parts:

  • Startup code: Initializes hardware and processor state.
  • Memory access: Functions to read/write flash or external memory.
  • Validation: Checks application integrity (e.g., checksum).
  • Jump to application: Transfers control to main program.
c
void bootloader_start(void) {
    // Initialize hardware
    init_hardware();

    // Check if application is valid
    if (is_app_valid()) {
        jump_to_application();
    } else {
        // Stay in bootloader or handle error
        while(1);
    }
}
💻

Example

This example shows a simple bootloader that checks a magic number in flash memory and jumps to the application if valid.

c
#include <stdint.h>

#define APP_START_ADDRESS 0x08004000
#define MAGIC_NUMBER      0xDEADBEEF

// Function pointer to application reset handler
typedef void (*app_entry_t)(void);

// Simulated flash memory (for example only)
uint32_t flash_memory[] = {
    0x20002000,       // Initial stack pointer (ignored here)
    MAGIC_NUMBER,     // Magic number at app start
    // ... application code ...
};

int is_app_valid(void) {
    uint32_t *app_magic = (uint32_t *)(APP_START_ADDRESS + 4);
    // In real hardware, read from flash memory address
    // Here we simulate by checking flash_memory array
    return (*app_magic == MAGIC_NUMBER);
}

void jump_to_application(void) {
    app_entry_t app_entry = (app_entry_t)(APP_START_ADDRESS + 4);
    app_entry(); // Jump to app reset handler
}

void bootloader_start(void) {
    if (is_app_valid()) {
        jump_to_application();
    } else {
        // Stay here or handle error
        while(1);
    }
}

int main(void) {
    bootloader_start();
    return 0;
}
⚠️

Common Pitfalls

Common mistakes when writing bootloaders include:

  • Not properly initializing hardware before jumping to the application.
  • Failing to validate the application code, causing jumps to invalid memory.
  • Incorrectly setting the stack pointer or program counter when jumping.
  • Overwriting bootloader memory during application updates.

Always test bootloader behavior on real hardware carefully.

c
/* Wrong: Jumping without validation */
void bootloader_start_wrong(void) {
    jump_to_application(); // May jump to invalid app
}

/* Right: Validate before jump */
void bootloader_start_right(void) {
    if (is_app_valid()) {
        jump_to_application();
    } else {
        while(1); // Stay safe
    }
}
📊

Quick Reference

  • Initialize hardware before any memory access.
  • Validate application with checksum or magic number.
  • Use function pointer to jump to app reset handler.
  • Protect bootloader memory from overwrites.
  • Test on real device to ensure correct startup.

Key Takeaways

Always validate the application code before jumping to it in the bootloader.
Initialize hardware and processor state properly in the bootloader startup.
Use a function pointer to jump cleanly to the application reset handler.
Protect bootloader memory from being overwritten during application updates.
Test bootloader thoroughly on real hardware to avoid startup failures.