四、FreeRTOS——内存
首先要复习STM32单片机中的内存五区。
1 内存五区
在嵌入式系统中,五种内存区域分别是:堆(Heap)、栈(Stack)、全局区/静态区、常量区和代码区。
-
RO (Read-Only):这部分内存用于存储只读数据,即程序代码(比如应用程序的指令)和常量数据(比如常量数组、字符串等)。这些数据在程序运行时不能被修改。
-
RW (Read-Write):这部分内存用于存储可读可写的数据。在程序运行过程中,数据可能会被修改。
-
ZI (Zero Initialized):这部分内存用于存储未初始化的数据,在程序启动时,这些数据会被自动初始化为零。
1.1 栈(Stack)
1.2 堆(Heap)
#include <stdio.h>
#include <stdlib.h>
//堆内存通常用于自主动态分配内存
void HeapExample()
{
int *array;// 定义一个指向整数的指针,用于存储动态分配的数组
int size = 10; // 需要分配的数组大小
// 从堆中分配内存,分配的总字节数为40字节,array得到的是一个指向分配内存的指针
array = (int *)malloc(size * sizeof(int));
//检查malloc是否成功分配内存。如果分配失败,malloc会返回NULL。
if (array == NULL)
{
printf("Memory allocation failed!\n");
return;
}
// 使用分配的内存
for (int i = 0; i < size; i++)
{
array[i] = i * i; // 示例:存储平方值
}
// 打印分配的内存内容
for (int i = 0; i < size; i++)
{
printf("array[%d] = %d\n", i, array[i]);
}
// 释放分配的内存
free(array);
printf("Memory freed.\n");
}
1.3 全局区/静态区
全局区/静态区用于存储全局变量和静态变量。它分为两个部分:
-
.bss段:用于存储未初始化或初始化为0的全局变量和静态变量。它不占用可执行文件的空间。
-
.data段:用于存储经过初始化且初始化值非0的全局变量和静态变量。
#include <stdio.h>
// 全局变量
int globalVar = 42;
void StaticExample()
{
// 静态变量
static int staticVar = 0;
// 每次调用函数时,静态变量的值会保留,不会像局部变量一样消失
staticVar++;
printf("Global Variable: %d\n", globalVar);
printf("Static Variable: %d\n", staticVar);
}
1.4 常量区
常量区用于存储字符串常量和其他不可修改的数据。这些数据在程序运行期间是只读的。
const char *message = "Hello, World!";
1.5 代码区
2 关于堆栈内存的设置
-
启动文件设置的堆栈是上面讲到内存五区的堆栈内存大小设置,跟FreeRTOS堆栈内存没有关系。
-
FreeRTOS的申请堆内存和任务栈内存,都是占用以下宏定义内存区域。
#define configTOTAL_HEAP_SIZE ((size_t)3072)
3 STM32 Stack_Size和Heap_Size大小设置的意义
在使用STM32编程时,一般情况下我们不会关注堆栈空间的大小,因为在STM32的启动文件中,已经帮我们预先设置好了堆栈空间的大小。如下图所示的启动代码中,Stack栈的大小为:0x400(1024Byte),Heap堆的大小为:0x200(512Byte)。
1、若工程中使用的局部变量较多,定义的数据长度较大时,若不调整栈的空间大小,则会导致程序出现栈溢出,程序运行结果与预期的不符或程序跑飞。这时我们就需要手动的调整栈的大小。
2、当工程中使用了malloc动态分配内存空间时,这时分配的空间就为堆的空间。所以若默认的堆空间大小不满足工程需求时,就需要手动调整堆空间的大小。
裸机程序里面这两个值 在程序中我要怎么计算才能知道分配多少合适?
-
Stack Size:一般小工程0X400足够,我们综合实验才设置0X1000就够用,所以默认无需设置太大。
-
Heap Size:如果没有用到标准库的malloc,就是废物,纯属浪费内存,所以直接设置为0即可。
在嵌入式使用UCOSII和LUA的时候堆栈也很重
-
Stack Size,0X2000足够,我们综合实验才设置0X1000就够用,所以默认无需设置太大。
-
Heap Size,如果没有用到标准库的malloc,就是废物,纯属浪费内存,所以直接设置为0即可。
4 使用freertos如何确定分配堆栈空间大小
运行freertos系统的大部分都是资源有限的MCU,所以对于ram我们都要考虑尽量的节省,避免资源浪费,从而也可以针对项目选择性价比更好的mcu。
首先要配置freertos的堆(heap)空间,创建任务我们还需要为每个任务分配栈(stack)空间,那么针对freertos的堆栈空间到底该如何确定?
freertos从V9版本以后同时支持静态内存和动态内存分配方式。静态内存分配在编译时候就会对freertos的内核对象分配ram空间。动态分配都是在程序运行起来以后从堆空间上分配的。这里我们也只讨论动态内存分配,动态内存分配的好处是可以在删除对象的时候释放掉内存的空间。从而保证ram的可持续利用!
先看下图弄清楚freertos的heap空间和任务栈空间的不同与联系。
FreeRTOS创建任务时默认的任务栈大小为128字,在32位系统中即为128*4=512Byte,再加上TCB块占用84Byte,一共596Byte。而大小为3072Byte的堆允许创建3个这样的任务,占用约1800Byte。堆中剩余的部分则存放了系统内核、信号量、队列、任务通知等数据。