
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
-
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.