在RAM中大多数的空间分配为任务栈和系统栈两部分。
任务栈:顾名思义就是用来跑任务的,当我们xTaskCreate一个任务时,但是在这块任务栈里面动态分配空间。
系统栈:任务栈是不使用这里的空间的,用到这里一般都是中断函数和中断嵌套。
大多数在移植了freertos后,没怎么用RAM,但是发现RAM内存都快没了,那是freertos中有个动态分配的任务栈空间大小的宏,configTOTAL_HEAP_SIZE,老版本的freertos中没有静态创建任务,是用动态分配一块RAM空间给任务栈。
这个你是可以自己根据单片机容量大小,已经你程序大小来进行配置的。
如果你MCU容量实在是有限,可以适当减少值。
当然,你RAM超了的话编译应该是不会通过的。
关于这个宏官方的解释是
RAM中分配给freertos堆中的空间。
要开启划分任务栈configTOTAL_HEAP_SIZE这个宏前提是开启configSUPPORT_DYNAMIC_ALLOCATION这个宏,但是这个宏也不一定存在
FreeRTOS 的运行支持以下四种状态:
当任务处于实际运行状态被称之为运行态,即 CPU 的使用权被这个任务占用。
处于就绪态的任务是指那些能够运行(没有被阻塞和挂起) ,但是当前没有运行的任务,因为同优先
级或更高优先级的任务正在运行。
由于等待信号量,消息队列,事件标志组等而处于的状态被称之为阻塞态,另外任务调用延迟函数也
会处于阻塞态。
类似阻塞态,通过调用函数 vTaskSuspend()对指定任务进行挂起,挂起后这个任务将不被执行,只
有调用函数 xTaskResume()才可以将这个任务从挂起态恢复。
使用如下函数即可启动 FreeRTOS:
函数原型:
void vTaskStartScheduler( void );
函数描述:
函数 vTaskStartScheduler 用于启动 FreeRTOS 调度器,即启动 FreeRTOS 的多任务执行。
使用这个函数要注意以下几个问题:
1、 空闲任务和可选的定时器任务是在调用这个函数后自动创建的。
2、正常情况下这个函数是不会返回的,运行到这里极有可能是用于定时器任务或者空闲任务的 heap 空间不足造成创建失败,此时需要加大 FreeRTOSConfig.h 文件中定义的 heap 大小:
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) )
例如RTL8710中编译后是会生成一个 .map文件,里面详细统计了RAM的使用
根据他的使用区来计算出他的剩余空间大小
利用他的RAM 256k减去A3区和一些固定的空间可以算出剩余的系统栈空间值,任务栈的剩余值在freertos系统中是会统计打印log出来的,总体剩余空间就可以计算出来。
经过测试,系统剩余RAM空间总值一定要在10k以上,否则会报错
什么又是栈溢出呢?简单的说就是用户分配的栈空间不
够用了,溢出了。 下面我们举一个简单的实例,栈生长方向从高地址向低地址生长(M4 和 M3 是这种方式)。
FreeRTOS 提供了两种栈溢出检测机制,这两种检测都是在任务切换时才会进行:
在任务切换时检测任务栈指针是否过界了,如果过界了,在任务切换的时候会触发栈溢出钩子函
数。
void vApplicationStackOverflowHook( TaskHandle_t xTask,signed char *pcTaskName );
用户可以在钩子函数里面做一些处理。这种方法不能保证所有的栈溢出都能检测到。比如任务在执行
的过程中出现过栈溢出。任务切换前栈指针又恢复到了正常水平,这种情况在任务切换的时候是检测
不到的。又比如任务栈溢出后,把这部分栈区的数据修改了,这部分栈区的数据不重要或者暂时没有
用到还好,但如果是重要数据被修改将直接导致系统进入硬件异常,这种情况下,栈溢出检测功能也
是检测不到的。
使用方法一需要用户在 FreeRTOSConfig.h 文件中配置如下宏定义:
#define configCHECK_FOR_STACK_OVERFLOW 1
任务创建的时候将任务栈所有数据初始化为 0xa5,任务切换时进行任务栈检测的时候会检测末
尾的 16 个字节是否都是 0xa5,通过这种方式来检测任务栈是否溢出了。相比方法一,这种方法的速
度稍慢些,但是这样就有效地避免了方法一里面的部分情况。 不过依然不能保证所有的栈溢出都能检
测到,比如任务栈末尾的 16 个字节没有用到,即没有被修改,但是任务栈已经溢出了,这种情况是
检测不到的。 另外任务栈溢出后,任务栈末尾的 16 个字节没有修改,但是溢出部分的栈区数据被修
改了,这部分栈区的数据不重要或者暂时没有用到还好,但如果是重要数据被修改将直接导致系统进
入硬件异常,这种情况下,栈溢出检测功能也是检测不到的。
使用方法二需要用户在 FreeRTOSConfig.h 文件中配置如下宏定义:
#define configCHECK_FOR_STACK_OVERFLOW 2
钩子函数的主要作用就是对原有函数的功能进行扩展,用户可以根据自己的需要往里面添加相关的测
试代码, 大家可以在 FreeRTOS 工程中检索这个钩子函数 vApplicationStackOverflowHook 所在的位
置。
除了 FreeRTOS 提供的这两种栈溢出检测机制,还有其它的栈溢出检测机制,大家可以在 Mircrium 官方发布的如下这个博文中学习:
https://www.micrium.com/detecting-stack-overflows-part-2-of-2/
对于这种溢出检测,确实不容易检测到,多半进入硬件错误中断,所以以后要是遇到程序硬件中断死循环,清注意检测堆栈大小。不确定的时候,先使用printf调试打印任务栈的剩余,选择一个合理安全的值作为堆栈设置,这确实是个难以简单计算的东西。
在FreeRTOS中,用xTaskCreate()函数创建任务,相对于xTaskCreateStatic()函数,这个应该是动态创建任务的方式:
1、如果用 xTaskCreateStatic() 函数创建任务,就不需要配置 configTOTAL_HEAP_SIZE 这个参数了
和uC/OS-II中创建任务的方式是一样的,在创建任务的时候,直接在内存中分配了静态的任务栈空间,就不使用动态内存管理的方式了。
2、在uC/OS-II中,没有类似FreeRTOS中 configTOTAL_HEAP_SIZE 这样的大的堆需要配置,而是在任务创建的时候,直接申请了静态的任务栈
所以就不存在FreeRTOS那样动态的内存管理,也就没有了malloc()和free()这样的内存管理函数,也就不存在由于频繁的内存管理产生的内存碎片
3、基于以上印象,相当觉得FreeRTOS的动态内存管理方式并不适合单片机这样内存拮据的场合使用,因为会产生大量碎片而产生内存浪费和不可预知的内存问题,相对来讲,uC/OS-II的内存管理方式简单、原始,用多少就先申请多少。