Skip to main content

STM32 Memory Structure

Introduction

Understanding the memory structure of STM32 microcontrollers is fundamental for efficient embedded programming. STM32 devices, based on ARM Cortex-M cores, feature a complex yet organized memory architecture that allows for flexible and powerful applications. This guide will walk you through the various memory regions, their purposes, and how to use them effectively in your projects.

Memory Map Overview

STM32 microcontrollers follow the ARM Cortex-M memory architecture with a 32-bit address space, allowing for up to 4GB of addressable memory. However, each STM32 variant only implements a portion of this space based on its capabilities.

Let's examine each of these regions in detail.

Flash Memory

Flash memory is non-volatile storage where your program code and constant data are stored. Key characteristics include:

  • Located at address 0x0800 0000
  • Size varies by STM32 model (from 16KB to 2MB)
  • Divided into sectors or pages (size depends on the specific STM32 family)
  • Typically requires special programming sequences to write/erase

Example: Reading from Flash Memory

c
// Reading a value from flash memory
uint32_t *flashAddress = (uint32_t *)0x08004000; // Example address in flash
uint32_t valueFromFlash = *flashAddress;

Practical Use: Storing Configuration Data in Flash

c
// Define a structure for configuration
typedef struct {
uint32_t deviceID;
uint16_t calibValues[10];
uint8_t operatingMode;
} DeviceConfig_t;

// Access configuration stored in a specific flash sector
const DeviceConfig_t *deviceConfig = (DeviceConfig_t *)0x0800F000;

// Use the configuration
void applyConfiguration(void) {
setDeviceID(deviceConfig->deviceID);
for (int i = 0; i < 10; i++) {
setCalibrationValue(i, deviceConfig->calibValues[i]);
}
setMode(deviceConfig->operatingMode);
}

SRAM (Static RAM)

SRAM is volatile memory used for:

  • Program stack
  • Heap (for dynamic memory allocation)
  • Global and static variables
  • Function local variables

Key characteristics:

  • Typically starts at address 0x2000 0000
  • Size varies by STM32 model (from 4KB to 512KB)
  • Faster access than flash memory
  • Lost when power is removed

Memory Usage in SRAM

c
// Global variable (stored in .data section)
int globalVar = 42;

// Static variable with external linkage (stored in .data section)
static int staticVar = 100;

void exampleFunction(void) {
// Local variable (stored on the stack)
int localVar = 10;

// Dynamic allocation (stored on the heap)
int *dynamicVar = (int *)malloc(sizeof(int) * 10);

// Use the variables
globalVar += localVar;

// Always free dynamic memory when done
free(dynamicVar);
}

Peripheral Memory Region

STM32 peripherals are memory-mapped, meaning they are accessed through specific memory addresses. This region is divided into:

  • APB peripherals (Advanced Peripheral Bus)
  • AHB peripherals (Advanced High-performance Bus)

Example: GPIO Register Access

c
// Direct register access for GPIO (STM32F4 example)
#define GPIOA_BASE 0x40020000
#define GPIOA_MODER (*(volatile uint32_t *)(GPIOA_BASE + 0x00))
#define GPIOA_ODR (*(volatile uint32_t *)(GPIOA_BASE + 0x14))

// Set PA5 as output
void configurePA5AsOutput(void) {
// Set mode register bits for PA5 to output (01)
GPIOA_MODER &= ~(0x3 << (5 * 2)); // Clear bits
GPIOA_MODER |= (0x1 << (5 * 2)); // Set as output
}

// Toggle PA5
void togglePA5(void) {
GPIOA_ODR ^= (1 << 5); // Toggle bit 5
}

Special Memory Regions

System Memory (Boot Loader)

  • Located at address 0x1FFF 0000 (exact address varies by family)
  • Contains the factory-programmed bootloader
  • Allows programming via UART, I2C, SPI, etc.
  • Entry determined by boot pins or option bytes

Option Bytes

  • Contains device configuration settings
  • Controls read/write protection, watchdog settings, boot options
  • Located at a fixed address (varies by STM32 family)

Backup SRAM (in some STM32 models)

  • Small section of SRAM that remains powered during sleep modes
  • Used for critical data that must survive power-saving modes
  • Usually requires special configuration to use

Memory Mapping and Remapping

STM32 can remap the starting address of the program execution through boot configuration:

Boot Configuration

Boot mode is selected through boot pins (BOOT0, BOOT1) or option bytes:

BOOT0BOOT1Boot Mode
0XMain Flash Memory
10System Memory
11Embedded SRAM

Memory Protection Units (MPU)

Higher-end STM32 devices include a Memory Protection Unit that can:

  • Prevent access to restricted memory regions
  • Set different permissions for different code sections
  • Protect critical system areas from user applications
  • Help detect memory corruption bugs

Example: Configuring MPU for a Protected Region

c
#include "stm32f4xx.h"

void configureMemoryProtection(void) {
// Disable MPU
MPU->CTRL = 0;

// Configure region 0 to protect critical system area
MPU->RNR = 0; // Select region 0
MPU->RBAR = 0x20000000; // Base address of SRAM

// Configure region attributes
// Size: 4KB, Enable: yes, Access permissions: privileged RW, user RO
MPU->RASR = (3 << 1) | // 4KB size (2^(3+1) = 16 bytes)
(1 << 0) | // Enable region
(0x3 << 24) | // AP=0x3: full access
(1 << 28); // Enable XN (eXecute Never)

// Enable MPU with default background region
MPU->CTRL = (1 << 0) | (1 << 2);

// Data Synchronization Barrier
__DSB();

// Instruction Synchronization Barrier
__ISB();
}

Common Programming Considerations

Stack Size and Configuration

The stack size must be properly configured in the linker script to prevent stack overflow:

c
// Example excerpt from a linker script (.ld file)
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}

/* Assign stack size */
_Min_Stack_Size = 0x400; /* 1KB of stack */

Memory Sections in Your Program

Understanding how program sections are arranged in memory:

SectionContainsMemory Region
.textCode, ConstantsFlash
.rodataRead-only dataFlash
.dataInitialized variablesRAM (copied from Flash)
.bssUninitialized variablesRAM (zeroed at startup)

Practical Example: Memory-Efficient Circular Buffer

Here's a complete example showing a circular buffer implementation that efficiently uses SRAM:

c
#include "stm32f4xx.h"

// Define a circular buffer in SRAM
#define BUFFER_SIZE 64
typedef struct {
uint8_t data[BUFFER_SIZE];
uint16_t head;
uint16_t tail;
uint16_t count;
} CircularBuffer_t;

// Create a buffer instance
CircularBuffer_t rxBuffer = {0};

// Initialize the buffer
void initBuffer(CircularBuffer_t *buffer) {
buffer->head = 0;
buffer->tail = 0;
buffer->count = 0;
}

// Add data to the buffer
bool bufferPush(CircularBuffer_t *buffer, uint8_t data) {
if (buffer->count >= BUFFER_SIZE) {
return false; // Buffer full
}

buffer->data[buffer->head] = data;
buffer->head = (buffer->head + 1) % BUFFER_SIZE;
buffer->count++;
return true;
}

// Get data from the buffer
bool bufferPop(CircularBuffer_t *buffer, uint8_t *data) {
if (buffer->count == 0) {
return false; // Buffer empty
}

*data = buffer->data[buffer->tail];
buffer->tail = (buffer->tail + 1) % BUFFER_SIZE;
buffer->count--;
return true;
}

// Example usage with UART interrupt
void USART2_IRQHandler(void) {
if (USART2->SR & USART_SR_RXNE) {
uint8_t data = USART2->DR;
bufferPush(&rxBuffer, data);
}
}

// Process received data
void processReceivedData(void) {
uint8_t data;
while (bufferPop(&rxBuffer, &data)) {
// Process each byte...
if (data == 'A') {
// Do something
}
}
}

Memory Usage Optimization

Tips for Reducing Memory Footprint

  1. Use appropriate data types:

    c
    // Bad - wastes memory
    int smallValue = 5; // Uses 4 bytes

    // Good - uses appropriate size
    uint8_t smallValue = 5; // Uses 1 byte
  2. Reuse variables when possible:

    c
    // Less efficient
    int temp1 = calculateValue1();
    processThing(temp1);
    int temp2 = calculateValue2();
    processThing(temp2);

    // More memory efficient
    int temp = calculateValue1();
    processThing(temp);
    temp = calculateValue2();
    processThing(temp);
  3. Consider using bit fields for flags:

    c
    // Uses 4 bytes for 4 flags
    struct {
    uint8_t flag1;
    uint8_t flag2;
    uint8_t flag3;
    uint8_t flag4;
    } flags;

    // Uses 1 byte for 8 flags
    struct {
    uint8_t flag1: 1;
    uint8_t flag2: 1;
    uint8_t flag3: 1;
    uint8_t flag4: 1;
    uint8_t flag5: 1;
    uint8_t flag6: 1;
    uint8_t flag7: 1;
    uint8_t flag8: 1;
    } flags;

Summary

Understanding the STM32 memory structure is essential for writing efficient embedded applications. Key points to remember:

  • Flash memory: Non-volatile, used for program code and constants
  • SRAM: Volatile, used for variables, stack, and heap
  • Peripheral memory: Memory-mapped hardware access
  • Special memory regions: Bootloader, option bytes, etc.
  • Memory protection: Ensures system stability and security
  • Memory optimization: Critical for resource-constrained devices

By properly leveraging these different memory regions and understanding their characteristics, you can create more robust and efficient STM32 applications.

Exercises

  1. Write a function that calculates the amount of free SRAM available at runtime.
  2. Create a simple program that stores configuration values in Flash memory.
  3. Implement a memory-safe string handling library that prevents buffer overflows.
  4. Design a system that utilizes the backup SRAM for critical data storage.
  5. Use the MPU to protect a critical section of memory from accidental writes.

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)