嵌入式系统资源总是有限的,不能随意的挥霍浪费。甚至有些时候要按照数据内容,限定数据类型,能少用一个字节,绝对不能多占用一个。嵌入式系统中,内存管理是核心问题之一,尤其在资源受限的STM32平台上。FreeRTOS作为轻量级实时操作系统,其内存分配机制与STM32的硬件特性紧密相关。
一、数据存储区域划分
- Flash:存放程序代码和常量数据(比如const char buf[10] = {0};)
- RAM:运行时的动态内存,包括全局变量、局部变量、堆栈等;
- CCMRAM(Core Coupled Memory):部分STM32系列(如STM32F4/F7/H7)特有,具有零等待周期特性,适合高频访问数据
STM32F407vg中的数据存储区定义
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 1024K
}
二、变量类型与内存分配
-
全局变量与静态局部变量
存储在静态内存区(如.bss
或.data
段),程序启动时分配,生命周期贯穿整个程序运行过程。全局变量可被所有函数访问,需注意多任务环境下的数据竞争问题。
typedef struct {
uint8_t* buffer;
size_t size;
volatile size_t head; // 写入位置
volatile size_t tail; // 读取位置
SemaphoreHandle_t mutex;
} RingBuffer;
static RingBuffer logBuffer;
我们可以看到存储区域被分配到了RAM
typedef struct {
SubscriberCallback_t callback;
TaskHandle_t task;
UBaseType_t priority;
} SubscriberNode_t;
typedef struct {
SubscriberNode_t subscribers[MAX_SUBSCRIBERS];
uint8_t subscriberCount;
SemaphoreHandle_t mutex;
} Topic_t;
static __attribute__((section(".ccmram"))) Topic_t topics[MAX_TOPICS];
变量被分配到了CCMRAM
-
私有变量(自动变量)
即函数内定义的普通局部变量,存储在栈(Stack)中,函数调用时自动分配,函数返回时释放。其生命周期与函数执行周期一致,默认情况下为线程私有(FreeRTOS中为任务私有)。
这部分占用任务独立栈空间,这个在任务初始化的时候由编译器分配,位置在RAM中。
-
FreeRTOS任务栈
每个任务拥有独立的堆栈空间,用于存储函数调用链中的自动变量、中断上下文等。堆栈大小需在任务创建时显式定义,需预留足够空间防止溢出。通过xTaskCreate()
等API配置。
xTaskCreate(pubSubTask, "PubSub", PUBLISHER_SUBSCRIBER_TSK_STACK_SIZE, NULL,
PUBLISHER_SUBSCRIBER_TSK_PRI, NULL);
这里的PUBLISHER_SUBSCRIBER_TSK_STACK_SIZE,定义了分配给pubSubTask独自占用内存空间的大小,分配在RAM中。
-
FreeRTOS中分配堆
堆的大小一定要保证全局变量&任务栈的内存占用。
// FreeRTOSConfig.h中修改宏定义
#define configTOTAL_HEAP_SIZE ((size_t)(120 * 1024))
三、小结
当然,上述内存分配后,如果整体超出芯片内存大小,会报错啊!
内存管理,虽然说由OS替我们完成了绝大部分工作,但在嵌入式系统中,我们开发代码,必须要时刻注意内存的申请、使用、资源释放等等细节。否则,到后期,一些疑难杂症一旦出现,那都是送给自己最好的回旋镖!