FreeRTOS任务栈#
1.任务栈的生长方向#
FreeRTOS中用portSTACK_GROWTH来区分这两种生长方式,portSTACK_GROWTH大于0为向上生长,小于零为向下生长。
-
向上生长:入栈时栈顶指针增加,出栈时栈顶指针减小
-
向下生长:入栈时栈顶指针减小,出栈时栈顶指针增加
2.任务栈的大小#
任务栈默认会被初始化为0xA5,这个被FreeRTOS用来检测任务栈的剩余大小,可以利用这个获取任务所需要的栈的尺寸。
- 任务栈内容初始化
#define tskSTACK_FILL_BYTE ( 0xa5U )
xTaskCreateStatic() --> prvInitialiseNewTask() -->
/* Fill the stack with a known value to assist debugging. */
( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) );
- 获取任务栈剩余大小
uxTaskGetStackHighWaterMark(TaskHandle_t xTask);
eg:
uint32_t fcnt=0;
fcnt=uxTaskGetStackHighWaterMark((TaskHandle_t)defaultTaskHandle);
假如你原先设置的任务栈是256 word,返回值fcnt是217 word,也就是说任务栈中有217 word都没有用到,只用到39 word,显然任务栈设置大了。
这个函数的检测原理是:从任务栈的起始地址开始,遍历寻找0xA5,找到一个0xA5就计一个数(ulCount++),0xA5的数量就是任务栈的剩余空间,具体可以阅读该函数的源码。
- 利用调试器(JLink或者STLink)和IDE来确定任务栈的大小
本文以STM32CubeIDE为例。
CMSIS-RTOS2封装
osThreadId_t defaultTaskHandle;
uint32_t defaultTaskBuffer[ 256 ];
osStaticThreadDef_t defaultTaskControlBlock;
const osThreadAttr_t defaultTask_attributes = {
.name = "defaultTask",
.stack_mem = &defaultTaskBuffer[0], // 栈内存
.stack_size = sizeof(defaultTaskBuffer),
.cb_mem = &defaultTaskControlBlock, // 任务控制块
.cb_size = sizeof(defaultTaskControlBlock),
.priority = (osPriority_t) osPriorityNormal,
};
上面是CMSIS-RTOS2封装的任务结构体,其中cb_mem对应的就是任务控制块(tskTCB),这两个结构体是一一对应的。
在调试模式下,打开实时变量观察器,可以看到任务栈顶指针的变化(理论上应该能够观察到栈顶指针的变化,但是实际测试发现IDE监测变化效果并不好,看不到栈顶指针的变化)。