Advanced Embedded Programming | How to Prevent RAM or FLASH from Overloading?

Advanced Embedded Programming | How to Prevent RAM or FLASH from Overloading?Advanced Embedded Programming | How to Prevent RAM or FLASH from Overloading?01Introduction:

When developing with microcontrollers, issues related to RAM or FLASH usage often arise. Below, we analyze the factors affecting RAM and FLASH usage and how to optimize them.

02Analysis of RAM Usage Factors

1. Variable Storage

Global Variables/Static Variables (Global Lifetime)

uint32_t global_counter = 0;       // Initialize global variable (occupies .data segment RAM)
uint8_t buffer[1024];              // Uninitialized global array (occupies .bss segment RAM)

Local Variables (Stack Space Allocation)

void process_data() {
    float sensor_values[256];       // Local array (stack space)
    int temp = 0;                   // Local variable (stack space)
    // Automatically released after function returns
}

Dynamic Memory Allocation (Heap Space, typical malloc/new functions)

void dynamic_alloc() {
    uint16_t* ptr = malloc(512 * sizeof(uint16_t)); // Heap allocation (512 uint16)
    free(ptr);  // Must be manually released
}

2. Data Structures

Size of Data Arrays

uint8_t buffer[1024];

Number of Members in Structs/Unions

// Example 1: 3-member struct (no padding)
struct S1 {    
    uint8_t a;  // 1 byte
    uint8_t b;  // 1 byte
    uint8_t c;  // 1 byte
};              // Total size = 3 bytes (no padding)

// Example 2: 4-member struct (with padding)
struct S2 {    
    uint32_t a; // 4 bytes (aligned to 4 bytes)
    uint8_t b;  // 1 byte → padded with 3 bytes (aligned to next 4 bytes)
    uint16_t c; // 2 bytes → padded with 2 bytes (aligned to 4 bytes)
    uint8_t d;  // 1 byte → padded with 3 bytes
};              // Total size = 4+4+4+4=16 bytes (actual data only 4+1+2+1=8 bytes)

// Example 3: Optimizing member order
struct S3 {    
    uint32_t a; // 4 bytes
    uint16_t c; // 2 bytes
    uint8_t b;  // 1 byte
    uint8_t d;  // 1 byte
};              // Total size = 4+2+1+1=8 bytes (no padding)

Number of Nodes in Queues/Linked Lists

#include <stdlib.h>

// Define queue node
typedef struct QueueNode {
    int data;
    struct QueueNode* next;
} QueueNode;

// Define queue container
typedef struct {
    QueueNode* front;    // Front pointer
    QueueNode* rear;     // Rear pointer
    int count;           // Dynamic counter (O(1) to get count)
} LinkedQueue;

// Initialize queue
void init_queue(LinkedQueue* q) {
    q->front = q->rear = NULL;
    q->count = 0;
}

// Enqueue (update counter)
void enqueue(LinkedQueue* q, int value) {
    QueueNode* node = (QueueNode*)malloc(sizeof(QueueNode));
    node->data = value;
    node->next = NULL;
    
    if (q->rear == NULL) {
        q->front = q->rear = node;
    } else {
        q->rear->next = node;
        q->rear = node;
    }
    q->count++;  // Counter +1
}

// Dequeue (update counter)
int dequeue(LinkedQueue* q) {
    if (q->front == NULL) return -1; // Queue empty
    
    QueueNode* temp = q->front;
    int value = temp->data;
    
    q->front = q->front->next;
    if (q->front == NULL) {
        q->rear = NULL;
    }
    free(temp);
    q->count--;  // Counter -1
    return value;
}

// Get queue length (O(1))
int queue_size(const LinkedQueue* q) {
    return q->count;
}

3. Algorithm Characteristics

Recursive Calls

void recursive(int depth) {
    int local_var = depth;          // Each recursion creates a new stack frame
    if (depth > 0) recursive(depth - 1);
}
// Calling recursive(10) creates 10 stack frames

Intermediate Result Caching

Scenario Characteristics Recommended Solution Example
Small and Fixed Parameter Range Static Array Caching Factorial, Power Operations
Discrete and Possibly Repeated Parameters Hash Table Caching Fibonacci Sequence
Recursive Calls with Repeated Subproblems Memoization Dynamic Programming Problems
Real-time Requirements are Extremely High Pre-computed Lookup Tables Trigonometric Functions
Locality Access in Large Data Sets Chunk Caching Strategy Matrix Multiplication, Image Convolution
Data Changes Over Time Version Control/Timestamp Control Sensor Data Aggregation

State Machine/Complex Logic Preservation

// Menu system state preservation
typedef struct {
    uint8_t current_menu_level;
    uint8_t selected_item_index;
    uint32_t menu_stack[4]; // Supports 4-level menu backtracking
} MenuState;

void navigate_back(MenuState* state) {
    if (state->current_menu_level > 0) {
        state->current_menu_level--;
        state->selected_item_index = state->menu_stack[state->current_menu_level];
    }
}

4. Peripheral Dependencies

DMA Buffer

Communication Protocol Buffers (USB, CAN, UART, etc.)

Variables in Interrupt Service Routines (ISR)

03FLASH Usage Factors (Program Storage)

1. The Code Itself

Number and Complexity of Functions (more loops/conditional branches lead to longer machine code)

// Function code compiled into Flash
float calculate_rms(float* samples, int len) {
    float sum = 0;
    for (int i = 0; i < len; i++) {
        sum += samples[i] * samples[i];  // Loop code occupies Flash
    }
    return sqrt(sum / len);
}

Inline Functions/Template Instantiation (repeated code generation)

// Compiler may inline short functions (increasing Flash usage)
static inline void delay_us(uint32_t us) {
    uint32_t loops = us * (SystemCoreClock / 1000000);
    while (loops--);
}

// Multiple calls may lead to repeated code
delay_us(100);  // May be inlined

delay_us(200);  // Inlined again

Library Function Calls (math library sqrt(), sin())

double degrees_to_radians(double degrees) {
    return degrees * (M_PI / 180.0);
}

// Example: Calculate sine of 45°
double angle = 45.0;
double sin_45 = sin(degrees_to_radians(angle));

2. Constant Data

Lookup Tables

// CRC32 pre-computed table stored in Flash (occupies 1024 bytes Flash)
const uint32_t crc32_table[256] = {
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba...
};

String/Font Resources

char str1[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; // Explicit initialization
char str2[] = "World";  // Automatically deduced length (includes '\0', actual length 6)
char str3[10];          // Uninitialized, must be manually assigned

Configuration Parameters

typedef struct {
    int max_connections;    // Maximum number of connections
    char log_path[256];     // Log file path
    float timeout_seconds;  // Timeout duration (seconds)
    bool enable_debug;      // Debug mode switch
} AppConfig;

3. System Overhead

Interrupt Vector Table

// Interrupt service function address table (generated by compiler, occupies Flash)
void (* const isr_vector[])(void) __attribute__((section(".isr_vector"))) = {
    (void*)0x20001000,  // Initial stack pointer (hardware defined)
    Reset_Handler,      // Reset interrupt
    USART1_IRQHandler,  // Serial port interrupt
    // ...other interrupt vectors
};

Startup Code

startup.s // Initialization code

Debug Information

void parse_json(const char* json_str) {
    assert(json_str != NULL);
    assert(json_str[0] == '{' && "JSON must start with '{'"); // Format validation
    // Parsing logic...
}

4. System and Protocol Stack

RTOS Task Control Blocks

// Key configurations related to stack
#define configMINIMAL_STACK_SIZE   128     // Idle task stack size (words)
#define configCHECK_FOR_STACK_OVERFLOW 2   // Enable strict stack overflow checking
#define configUSE_TRACE_FACILITY    1      // Enable debug tracing functionality

Protocol Stack Code

// TCP state abbreviations (e.g., TCP_SYN_SENT)
typedef enum {
    TCP_CLOSED,
    TCP_SYN_SENT,
    TCP_ESTABLISHED,
    // ...other states
} tcp_state_t;

// Handle TCP state transitions (abbreviation: tcp_proc)
void tcp_proc(tcp_conn_t* conn, tcp_segment_t* seg) {
    switch (conn->state) {
        case TCP_SYN_SENT:
            if (seg->flags & TCP_FLAG_SYN_ACK) {
                conn->state = TCP_ESTABLISHED;
            }
            break;
        // ...other state handling
    }
}

04Optimization Suggestions

  1. RAM Optimization

  • Use <span><span>static const</span></span> instead of global variables
  • Reduce recursion levels, switch to iterative implementations
  • Use dynamic memory allocation cautiously (avoid fragmentation)
  • Flash Optimization

    • Enable compiler optimizations (e.g., GCC’s <span><span>-Os</span></span> for size optimization)
    • Merge duplicate code segments (through macros or function encapsulation)
    • Use <span><span>PROGMEM</span></span> (AVR) or <span><span>__attribute__((section(".flash_data"))</span></span> to force constants into Flash

    05Conclusion Engineer Liu believes that when coding, one must pay attention to RAM and FLASH usage when defining each variable and writing each function. This long-term cultivation will lead to improvement. If one does not pay attention during development and only organizes and optimizes after the code is finished, it will consume a lot of time and may lead to various inexplicable errors.

    Leave a Comment