Powrót do strony głównej

MBR 324 bajty STM32F407VE: kompaktowy bootloader

Opracowano minimalny MBR-bootloader dla STM32F407VE o rozmiarze 324 bajty. Sprawdza tablicę wektorów przed przekazaniem sterowania wtórnemu bootloaderowi pod adresem 0x08060000. Przy błędzie miga LED na PE13. Pełny kod, Makefile i etapy optymalizacji.

324-bajtowy MBR-bootloader STM32F407VE z walidacją
Advertisement 728x90

Zwarty bootloader MBR dla STM32F407VE: zaledwie 324 bajty kodu

Pierwotny bootloader (MBR) dla STM32F407VE znajduje się w pierwszym sektorze Flash pod adresem 0x08000000 i przekazuje sterowanie drugorzędnemu bootloaderowi pod adresem 0x08060000. Przed skokiem sprawdzana jest poprawność tabeli wektorów przerwań: wskaźnik stosu w zakresie SRAM (0x20000000–0x2001FFFF), adres ResetHandler w Flash (0x08000000–0x080FFFFF) z ustawionym bitem trybu Thumb.

Jeśli weryfikacja nie powiedzie się, aktywowana jest sygnalizacja błędu – miganie LED na pinie PE13 z częstotliwością 10 Hz (okres 100 ms, współczynnik wypełnienia 50%). Kod został zoptymalizowany do 324 bajtów przez usunięcie biblioteki standardowej, zewnętrznych przerwań i użycie rdzeniowego timera DWT do opóźnień.

Mapowanie pamięci NOR Flash:

Google AdInline article slot

| Sektor | Adres początkowy | Rozmiar, KB | Zawartość |

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

| 0 | 0x08000000 | 16 | Pierwotny bootloader |

Google AdInline article slot

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

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

| 7 | 0x08060000 | 128 | Drugorzędny bootloader |

Google AdInline article slot

Każdy bajt zwolniony z MBR zwiększa pojemność NVRAM.

Etapy optymalizacji rozmiaru

Optymalizacja przeprowadzana była etapowo z użyciem ARM-GCC i GNU Make:

  • Naiwna implementacja na HAL: 25192 bajtów.
  • -O0 bez debugowania: 12052 bajtów.
  • -Os -flto: 9684 bajtów.
  • DeepSeek + -Os -flto: 2124 bajty.
  • -nostdlib, usunięcie libc_init: 652 bajty.
  • Przycięta tabela wektorów: 324 bajty.

Kluczowe flagi kompilacji:

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

Implementacja w C

Główny kod w pojedynczym pliku main.c wykorzystuje bezpośredni dostęp do rejestrów bez HAL:

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

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

// Periferia
#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));
}

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

// Opóźnienie ~ms przy 168 MHz
static void delay_ms(uint32_t ms) {
    uint32_t start = DWT_CYCCNT;
    uint32_t cycles = ms * 16000;
    while ((DWT_CYCCNT - start) < cycles);
}

// Miganie 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();
}

Kompilacja projektu

Makefile z obsługą debugowania i pakowania:

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

W startup_stm32f407xx.S usunięto linię bl __libc_init_array i przycięto tabelę wektorów – pozostawiono tylko podstawowe wektory (Stack, Reset, NMI, HardFault).

Co jest ważne

  • Rozmiar 324 bajty umożliwia rozszerzenie NVRAM o ponad 15 KB w pierwszym sektorze.
  • Weryfikacja tabeli wektorów zapobiega zawieszeniu się na uszkodzonym kodzie.
  • Timer DWT zapewnia precyzyjne opóźnienia bez SysTick/PLL.
  • Bezpośredni dostęp do rejestrów minimalizuje narzuty.
  • Pełna kompilacja: ARM-GCC + niestandardowy skrypt linkera bez biblioteki standardowej.

— Editorial Team

Advertisement 728x90

Czytaj dalej