Zpět na domů

MBR 324 bajtů STM32F407VE: kompaktní bootloader

Vyvinut minimální MBR bootloader pro STM32F407VE o velikosti 324 bajtů. Kontroluje tabulku vektorů před předáním řízení sekundárnímu bootloaderu na adrese 0x08060000. Při chybě bliká LED na PE13. Kompletní kód, Makefile a etapy optimalizace.

324-bajtový MBR bootloader STM32F407VE s validací
Advertisement 728x90

Kompaktní MBR bootloader pro STM32F407VE: 324 bajtů kódu

Primární zavaděč (MBR) pro STM32F407VE je umístěn v prvním sektoru Flash na adrese 0x08000000 a předává řízení sekundárnímu zavaděči na adrese 0x08060000. Před skokem je ověřena platnost tabulky vektorů přerušení: ukazatel zásobníku v rozsahu SRAM (0x20000000–0x2001FFFF), adresa ResetHandler ve Flash (0x08000000–0x080FFFFF) s nastaveným bitem Thumb-režimu.

Pokud ověření neprojde, aktivuje se indikace chyby – blikání LED na pinu PE13 s frekvencí 10 Hz (perioda 100 ms, střída 50 %). Kód je optimalizován na 324 bajtů odstraněním standardní knihovny, externích přerušení a použitím jádrového timeru DWT pro zpoždění.

Rozložení NOR Flash:

Google AdInline article slot

| Sektor | Počáteční adresa | Velikost, Kb | Obsah |

|--------|------------------|--------------|-------|

| 0 | 0x08000000 | 16 | Primární zavaděč |

Google AdInline article slot

| 1–3 | 0x08004000–0x0800C000 | 16×3 | NVRAM |

| 4–6 | 0x08010000–0x08040000 | 64+128+128 | Aplikace |

| 7 | 0x08060000 | 128 | Sekundární zavaděč |

Google AdInline article slot

Každý bajt uvolněný z MBR zvětšuje objem NVRAM.

Fáze optimalizace velikosti

Optimalizace byla provedena postupně s použitím ARM-GCC a GNU Make:

  • Naivní implementace na HAL: 25192 bajtů.
  • -O0 bez ladění: 12052 bajtů.
  • -Os -flto: 9684 bajtů.
  • DeepSeek + -Os -flto: 2124 bajtů.
  • -nostdlib, odstranění libc_init: 652 bajtů.
  • Ořezaná tabulka vektorů: 324 bajtů.

Klíčové příznaky kompilace:

  • -nostdlib -nostartfiles -ffreestanding
  • -ffunction-sections -fdata-sections -Wl,--gc-sections
  • -Os pro velikost, -mcpu=cortex-m4 -mthumb

Implementace v C

Hlavní kód v jediném souboru main.c používá přímý přístup k registrům bez HAL:

// main.c - Bootloader pro STM32F407VE
#include <stdint.h>

#define APP_BASE        0x08060000UL
#define SRAM_BASE       0x20000000UL
#define SRAM_END        0x2001FFFFUL
#define FLASH_BASE      0x08000000UL
#define FLASH_END       0x080FFFFFUL

// Periferie
#define RCC_BASE        0x40023800UL
#define GPIOE_BASE      0x40021000UL
#define DWT_BASE        0xE0001000UL

#define RCC_AHB1ENR     (*((volatile uint32_t*)(RCC_BASE + 0x30)))
#define GPIOx_MODER     (*((volatile uint32_t*)(GPIOE_BASE + 0x00)))
#define GPIOx_ODR       (*((volatile uint32_t*)(GPIOE_BASE + 0x14)))
#define DWT_CYCCNT      (*((volatile uint32_t*)(DWT_BASE + 0x04)))
#define DWT_CTRL        (*((volatile uint32_t*)(DWT_BASE + 0x00)))

typedef struct {
    uint32_t stack_ptr;
    uint32_t reset_handler;
} VectorTable_t;

static inline int is_valid_sram(uint32_t addr) {
    return ((SRAM_BASE <= addr) && (addr <= SRAM_END));
}

static inline int is_valid_flash(uint32_t addr) {
    return ((FLASH_BASE <= addr) && (addr <= FLASH_END));
}

// Inicializace DWT
static void dwt_init(void) {
    *(volatile uint32_t*)0xE000EDF0 |= (1UL << 24);
    DWT_CTRL |= 1;
}

// Zpoždění ~ms při 168 MHz
static void delay_ms(uint32_t ms) {
    uint32_t start = DWT_CYCCNT;
    uint32_t cycles = ms * 16000;
    while ((DWT_CYCCNT - start) < cycles);
}

// Blikání LED PE13 10 Hz
static void blink_led(void) __attribute__((noreturn));
static void blink_led(void) {
    RCC_AHB1ENR |= (1 << 4);
    GPIOx_MODER &= ~(3 << 26);
    GPIOx_MODER |= (1 << 26);
    dwt_init();
    while(1) {
        GPIOx_ODR |= (1 << 13);
        delay_ms(50);
        GPIOx_ODR &= ~(1 << 13);
        delay_ms(50);
    }
}

static int is_valid_vector_table(const VectorTable_t *app_vec) {
    if (!is_valid_sram(app_vec->stack_ptr)) return 0;
    uint32_t reset_addr = app_vec->reset_handler;
    if (!is_valid_flash(reset_addr & ~1)) return 0;
    if ((reset_addr & 1) == 0) return 0;
    return 1;
}

typedef void (*pFunction)(void);
int main(void) {
    const VectorTable_t *app_vec = (const VectorTable_t*)APP_BASE;
    if (is_valid_vector_table(app_vec)) {
        pFunction jump = (pFunction)app_vec->reset_handler;
        jump();
    }
    blink_led();
}

Sestavení projektu

Makefile s podporou ladění a balení:

CFLAGS += -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=softfp
CFLAGS += -Os -ffunction-sections -fdata-sections -nostdlib
CFLAGS += -nostartfiles -fno-builtin -ffreestanding
LDFLAGS += -T gcc_arm_mbr.ld -Wl,--gc-sections

SOURCES = main.c system_stm32f4xx.c
OBJECTS = $(SOURCES:.c=.o) startup_stm32f407xx.o

all: $(TARGET).bin

V startup_stm32f407xx.S byla odstraněna řádka bl __libc_init_array a ořezána tabulka vektorů – zůstaly pouze základní vektory (Stack, Reset, NMI, HardFault).

Co je důležité

  • Velikost 324 bajtů umožňuje rozšířit NVRAM o 15+ Kb v prvním sektoru.
  • Validace tabulky vektorů zabraňuje zaseknutí na poškozeném kódu.
  • DWT-timer zajišťuje přesná zpoždění bez SysTick/PLL.
  • Přímý přístup k registrům minimalizuje režii.
  • Kompletní sestavení: ARM-GCC + vlastní linker script bez standardní knihovny.

— Editorial Team

Advertisement 728x90

Číst dál