Handling Internal Flash Memory Operations on STM32 Devices

The internal flash memory integrated into STM32 microcontrollers provides non-volatile storage for both executable code and user data. While reading from this memory is as straightforward as dereferencing a pointer, modifying its contents requires strict adherence to hardware protection mechanisms. Write and erase cycles must be guarded by unlock and lock procedures to prevant accidental corruption of the running firmware.

Internal Flash Architecture

The flash space is typically segmented into three distinct regions:

  • Main Memory: Serves as the primary storage for application binaries. If the allocated code footprint leaves unused space at the end of this region, developers can repurpose it for persistent parameter storage. The exact boundary of the loaded program can be determined by inspecting the linker map file generated by the IDE. The final loaded base address indicates the start of available flash space.
  • System Memory: Contains factory-programmed data that is strictly read-only. This section hosts bootloaders and ISP routines required for serial firmware updates, functioning similarly to fixed memory-mapped regions in operating systems.
  • Option Bytes: Houses configuration bits that control read protection levels, watchdog source selection, and power-mode reset behaviors. Modifying these settings alters the microcontroller's operational constraints and must be handled through dedicated flash control registers.

Operational Constraints and Design Considerations

Flash memory exhibits asymmetric read/write characteristics. The fundamental unit for erasure is a full page, whereas programming can occur at word (32-bit) or half-word (16-bit) granularity. This architecture introduces several engineering challenges:

  1. Page Erasure vs. Granular Writes: Since erasing affects an entire page, modifying a single value without a backup mechanism will overwrite adjacent data. Implementing a RAM-based page buffer allows modifications to be aggregated before a single erase-and-program cycle, minimizing data corruption risks.
  2. Alignment Requirements: Hardware enforces strict address alignment. Word writes must target addresses divisible by four, while half-word writes require even addresses. Misaligned access attempts will trigger hard faults or data corruption.
  3. Cross-Page Boundaries: Data structures spanning multiple flash pages are not natively supported for atomic operations. Logical boundaries must be manually managed by the application layer.
  4. Bit Transition Physics: Flash cells transition from 1 to 0 during programming but require a full page erase to return to 1. This physics-based limitation dictates why targeted deletion is impossible without page-level operations.

Implementation Strategy

Direct register manipulation is unnecessary when leveraging the Standard Peripheral Library. The following implementation abstracts address calculations and enforces alignment rules.

#include "stm32f10x_flash.h"
#include <stdint.h>

#define FLASH_PAGE_SIZE       2048U
#define FLASH_BASE_USER_AREA  0x08008000UL

static inline uint32_t get_absolute_address(uint32_t offset) {
    return FLASH_BASE_USER_AREA + offset;
}

void persist_data(uint32_t offset, uint32_t payload) {
    uint32_t target_addr = get_absolute_address(offset);
    uint32_t page_start  = FLASH_BASE_USER_AREA + ((offset / FLASH_PAGE_SIZE) * FLASH_PAGE_SIZE);

    FLASH_Unlock();
    FLASH_ErasePage(page_start);
    FLASH_ProgramWord(target_addr, payload);
    FLASH_Lock();
}

uint32_t retrieve_data(uint32_t offset) {
    uint32_t target_addr = get_absolute_address(offset);
    volatile const uint32_t *mem_ptr = (volatile const uint32_t *)target_addr;
    return *mem_ptr;
}

Code Architecture Breakdown

  • Address Mapping: The get_absolute_address utility translates a logical offset into a physical flash address. Using offsets improves portability and reduces hardcoding errors.
  • Page Boundary Calculation: Integer division (offset / FLASH_PAGE_SIZE) * FLASH_PAGE_SIZE reliably computes the starting address of the relevant flash page without floating-point dependencies.
  • Protection Sequence: FLASH_Unlock() disables write protection, allowing modifications. FLASH_ErasePage() resets the target page to all 0xFF. FLASH_ProgramWord() then writes the 32-bit payload. The sequence concludes with FLASH_Lock() to re-enable protection.
  • Pointer Dereferencing: Reading utilizes a volatile uint32_t cast. The volatile qualifier instructs the compiler to bypass optimization caches, guaranteeing that every read operation fetches the current value directly from the physical memory bus rather than a cached register. This is critical when interacting with memory-mapped hardware regions.

Tags: STM32 Embedded C Flash Memory Standard Peripheral Library Microcontroller Firmware

Posted on Fri, 08 May 2026 16:14:18 +0000 by AsianGuyJTran