0 引言
在本科的时候,从51开始入嵌入式开发的坑,慢慢的接触到了K60、STM32等单片机,但是在开发时并没有操作系统的概念,直接在裸机上编写程序(程序由一个死循环和若干个中断服务程序构成,平时CPU执行while循环中的代码,出现中断事件时,跳转到服务程序进行处理)。但随着所开发的应用系统变的复杂之后,裸机程序难以阅读和维护。
研究生时接触到了RTOS,在开发时可以将一个复杂的应用分割成多个小的任务,每个任务完成一部分工作,且都可以写成死循环的形式。一个处理器核心在某一时刻运行一个任务,RTOS根据任务的优先级,通过任务调度器进行任务与任务之间切换。由于每次对一个任务的执行时间很短,给人造成多个任务在一个时刻同时运行的错觉。若合理地划分任务且调度方法优良,则可使个任务看起来是并行执行的,减少了CPU的空闲时间,提高了CPU的利用率。
FreeRTOS是我接触和学习的第一种RTOS(后面项目中使用的是Rtthread,读研那会儿刚好比较火,而且可以通过IDE图形化界面管理工程、中文资料…),当时是19年研一刚入学,虽然写的程序还在,但已经没有什么印象了,只记得写过。。。最近准备毕业论文,没有像研一研二那样参与实验室项目(其实已经很久没写程序了,手痒痒~~),为了让自己过的更充实,满足自己的Soul Demand,加上明年工作需要使用FreeRTOS,所以决定学习FreeRTOS源码及其应用开发,并针对学习过程中遇到的问题进行总结和记录,方便日后的温故。。。。
都说“万事开头难”,今天就把头开了。。继续保命写毕设论文,后面慢慢整理。。
所选开发环境及硬件配置
- 德飞莱开发板。芯片为stm32f103zet6;LED2、LED3的引脚为PE5、PB5,低电平亮;WK_UP、KEY1、KEY2、KEY3的引脚为PA0、PE4、PE3、PE2,除WK_UP外其余按键按下为低电平;USART1 TX和RX引脚分别为PA9、PA10;外部晶振为8MHZ和32.768KHZ。
- 软件:STM32CUBMX、KEIL、串口调试助手(sscom33.exe)
1 FreeRTOS简介
RTOS全称是Real Time Operating System,不是指某一个确定的系统,而是指一类系统,比如UCOs、FreeRTOS、RTX、RT-Thread等。其中,FreeRTOS是一个免费的、可裁剪的小型RTOS系统,其特点包括:
- 内核支持抢占式,合作式和时间片调度;
- 提供了一个用于低功耗的Tickless模式;
- 系统的组件在创建时可以选择动态或者静态的RAM,比如任务、消息队列、信号量、软件定时器等
- FreeRTOS-MPU支持Corex-M系列中的MPU单元,如STM32F767;
- 系统简单、小巧、易用,通常情况下内核占用4k-9k字节的空间
- 高可移植性,代码主要C语言编写;
- 支持实时任务和协程(co-routines也有称为合作式、协同程序,本教程均成为协程);
- 任务与任务、任务与中断之间可以使用任务通知、消息队列、二值信号量、数值型信号量、递归互斥信号量和互斥信号量进行通信和同步,此外还有事件组(或者事件标志);
- 具有优先级继承特性的互斥信号量;
- 高效的软件定时器;
- 强大的跟踪执行功能;
- 堆栈溢出检测功能;
- 任务数量和任务优先级不限。
FreeRTOS的官网:https://www.freertos.org/
2 FreeRTOS移植(两种方式)
2.1 官网源码移植【传统方法】
-
步骤1 源码下载,并解压。
- 在FreeRTOS官网下载界面,下载最新版(其实之所以下载最新版,是因为带有example),当前时间为 2021/12/02。
- 解压下载后的文件,得到文件夹为FreeRTOSv202111.00`,重点在于上面那两个文件夹:FreeRTOS和FreeRTOS-Plus。
FreeRTOS文件夹中,Demo文件夹里面是FreeRTOS的相关例,License文件夹里面就是相关的许可信息,Source文件夹为FreeRTOS源码。其中,Source文件夹中的portable文件夹中有FreeRTOS与不同的具体硬件平台(编译环境、MCU)之间连接相关的文件,portable文件夹内容见下图。Keil文件夹中只有一个文件: See-also-the-RVDS-directory.txt,意思是参考RVDS文件夹里面的文件。 FreeRTOS-Plus文件夹中的源码其实并不是FreeRTOS系统的源码,是在这个FreeRTOS系统上另外增加的一些功能代码,比如CLI、FAT、TCP、Trace等。前期学习,没有必要看。 -
步骤2 通过STM32cubeMX新建并配置裸机工程,用于后面的移植。
-
步骤3 在前面生成的裸机工程中正式移植FreeRTOS源码。
最后,在stm32f1xx_it.h 文件中找到void SysTick_Handler(void) 函数,修改为如下。 void SysTick_Handler(void)
{
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)
{
xPortSysTickHandler();
}
HAL_IncTick();
}
此时,编译将0 Error(s), 0 Warning(s),如果还有其他问题请留言或自行百度解决。。。 -
步骤4 添加延时相关的文件。 在KEIL工程文件夹中新建SYSTEM\delay 文件夹,文件夹中添加delay.c 和delay.h 文件,然后再工程中添加头文件路径。 delay.h #ifndef __DELAY_H
#define __DELAY_H
#include "main.h"
void delay_init(void);
void delay_ms(uint32_t nms);
void delay_us(uint32_t nus);
void delay_xms(uint32_t nms);
#endif
delay.c #include "delay.h"
static uint32_t fac_us=0;
static uint16_t fac_ms=0;
void delay_init()
{
uint32_t reload;
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
fac_us=SystemCoreClock/1000000;
reload=SystemCoreClock/1000000;
reload*=1000000/configTICK_RATE_HZ;
fac_ms=1000/configTICK_RATE_HZ;
SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;
SysTick->LOAD=reload;
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;
}
void delay_us(uint32_t nus)
{
uint32_t ticks;
uint32_t told,tnow,tcnt=0;
uint32_t reload=SysTick->LOAD;
ticks=nus*fac_us;
told=SysTick->VAL;
while(1)
{
tnow=SysTick->VAL;
if(tnow!=told)
{
if(tnow<told)tcnt+=told-tnow;
else tcnt+=reload-tnow+told;
told=tnow;
if(tcnt>=ticks)break;
}
};
}
void delay_ms(uint32_t nms)
{
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)
{
if(nms>=fac_ms)
{
vTaskDelay(nms/fac_ms);
}
nms%=fac_ms;
}
delay_us((uint32_t)(nms*1000));
}
void delay_xms(uint32_t nms)
{
uint32_t i;
for(i=0;i<nms;i++) delay_us(1000);
}
-
步骤5 增加测试程序验证工程。约定: 添加的所有个人代码均在/* USER CODE xxx BEGIN xxx*/和/* USER CODE xxx END xxx*/之间。 -
main.h
#include "stdio.h"
#include "FreeRTOS.h"
#include "task.h"
#include "delay.h"
#include "gpio.h"
#include "usart.h"
-
gpio.h
#define LED2(n) (n?HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_RESET))
#define LED2_Toggle() (HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_5))
#define LED3(n) (n?HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET))
#define LED3_Toggle() (HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5))
#define KEY1() HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)
#define KEY2() HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_3)
#define KEY3() HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_2)
#define WK_UP() HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)
-
usart.h
#define EN_USART1_RX 1
#define RXBUFFERSIZE 1
extern uint8_t aRxBuffer[RXBUFFERSIZE];
-
usart.c
#if EN_USART1_RX==1
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance==USART1)
{
HAL_UART_Transmit(&huart1,(uint8_t*)aRxBuffer,1,1000);
while(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_TC)!=SET);
HAL_UART_Receive_IT(&huart1,(uint8_t *)aRxBuffer, RXBUFFERSIZE);
}
}
#endif
#if 1
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
};
FILE __stdout;
void _sys_exit(int x)
{
x = x;
}
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);
USART1->DR=(uint8_t)ch;
return ch;
}
#endif
-
main.c
#define START_TASK_PRIO 1
#define START_STK_SIZE 128
TaskHandle_t StartTask_Handler;
void start_task(void *pvParameters);
#define LED2_TASK_PRIO 2
#define LED2_STK_SIZE 50
TaskHandle_t LED2Task_Handler;
void led2_task(void *pvParameters);
#define LED3_TASK_PRIO 3
#define LED3_STK_SIZE 50
TaskHandle_t LED3Task_Handler;
void led3_task(void *pvParameters);
#define FLOAT_TASK_PRIO 4
#define FLOAT_STK_SIZE 128
TaskHandle_t FLOATTask_Handler;
void float_task(void *pvParameters);
main函数 int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
delay_init();
#if EN_USART1_RX==1
HAL_UART_Receive_IT(&huart1, (uint8_t *)aRxBuffer, RXBUFFERSIZE);
#endif
xTaskCreate((TaskFunction_t )start_task,
(const char* )"start_task",
(uint16_t )START_STK_SIZE,
(void* )NULL,
(UBaseType_t )START_TASK_PRIO,
(TaskHandle_t* )&StartTask_Handler);
vTaskStartScheduler();
}
void start_task(void *pvParameters)
{
taskENTER_CRITICAL();
xTaskCreate((TaskFunction_t )led2_task,
(const char* )"led1_task",
(uint16_t )LED2_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED2_TASK_PRIO,
(TaskHandle_t* )&LED2Task_Handler);
xTaskCreate((TaskFunction_t )led3_task,
(const char* )"led3_task",
(uint16_t )LED3_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED3_TASK_PRIO,
(TaskHandle_t* )&LED3Task_Handler);
xTaskCreate((TaskFunction_t )float_task,
(const char* )"float_task",
(uint16_t )FLOAT_STK_SIZE,
(void* )NULL,
(UBaseType_t )FLOAT_TASK_PRIO,
(TaskHandle_t* )&FLOATTask_Handler);
vTaskDelete(StartTask_Handler);
taskEXIT_CRITICAL();
}
void led2_task(void *pvParameters)
{
while(1)
{
LED2_Toggle();
delay_ms(500);
}
}
void led3_task(void *pvParameters)
{
while(1)
{
LED3(0);
delay_ms(200);
LED3(1);
delay_ms(800);
}
}
void float_task(void *pvParameters)
{
static double float_num=0.00;
while(1)
{
float_num+=0.01f;
printf("float_num的值为: %.4f\r\n",float_num);
delay_ms(1000);
}
}
本程序包括了设计四个任务:start_task、 led2_task、led3_task和float_task,这四个任务的任务功能如下:start_task用来创建其他三个任务;led2_task和led3_task分别控制LED2和LED3闪烁,闪烁时间不同;float_task使用printf定时向串口助手上传数据。此外,还通过串口中断将收到数据上传上位机。 对应工程名template 。
2.2 STM32cubMX软件直接创建带FreeRTOS的工程
-
首先,同样的配置下载方式、时钟; 因为Systick要用作FreeRTOS的时基定时,所以此处时钟源选择TIM7。 -
选择FreeRtos版本,有CMSIS_V1和CMSIS_V2两个版本,两者区别详细见官网介绍或链接。
所谓CMSIS,全称为Cortex Microcontroller Software Interface Standard,即基于CORTEX内核微处理的软件接口标准。 RTOSV1主要支持基于CortexM0/M0+/M3/M4/M7内核的芯片,而RTOSv2组件是基于RTOSv1的扩展,除了支持全系列的CortexM内核芯片外,还支持Cortex-A5/A7/A9内核的芯片,支持动态对象创建,支持多核系统配置,向下兼容RTOS v1组件。因为RTOS v2是对RTOSv1的扩展且与之兼容,所以一般选RTOSv2没问题。当然,如果你的芯片是CortexM核的芯片,选择RTOSv1组件也合适。
因为STM32F103ZET6为M3内核的单片机,所以我选用CMSIS_V1版本,其他配置默认。 -
配置LED、KEY和USART,主要用于后面测试; -
生成工程。 经过以上配置生成的工程是不会报错的。 -
与2.1 官网源码移植(从0开始) 中的步骤4相同,添加延时相关的文件。 -
继续完善工程,增加测试程序验证工程。约定: 添加的所有个人代码均在/* USER CODE xxx BEGIN xxx*/和/* USER CODE xxx END xxx*/之间。
#include "stdio.h"
#include "FreeRTOS.h"
#include "task.h"
#include "delay.h"
#include "gpio.h"
#include "usart.h"
#define LED2(n) (n?HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_RESET))
#define LED2_Toggle() (HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_5))
#define LED3(n) (n?HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET))
#define LED3_Toggle() (HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5))
#define KEY1() HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)
#define KEY2() HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_3)
#define KEY3() HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_2)
#define WK_UP() HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)
#define EN_USART1_RX 1
#define RXBUFFERSIZE 1
extern uint8_t aRxBuffer[RXBUFFERSIZE];
#if EN_USART1_RX==1
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance==USART1)
{
HAL_UART_Transmit(&huart1,(uint8_t*)aRxBuffer,1,1000);
while(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_TC)!=SET);
HAL_UART_Receive_IT(&huart1,(uint8_t *)aRxBuffer, RXBUFFERSIZE);
}
}
#endif
#if 1
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
};
FILE __stdout;
void _sys_exit(int x)
{
x = x;
}
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);
USART1->DR=(uint8_t)ch;
return ch;
}
#endif
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
delay_init();
#if EN_USART1_RX==1
HAL_UART_Receive_IT(&huart1, (uint8_t *)aRxBuffer, RXBUFFERSIZE);
#endif
MX_FREERTOS_Init();
osKernelStart();
}
#define LED2_TASK_PRIO 2
#define LED2_STK_SIZE 50
TaskHandle_t LED2Task_Handler;
void led2_task(void *pvParameters);
#define LED3_TASK_PRIO 3
#define LED3_STK_SIZE 50
TaskHandle_t LED3Task_Handler;
void led3_task(void *pvParameters);
#define FLOAT_TASK_PRIO 4
#define FLOAT_STK_SIZE 128
TaskHandle_t FLOATTask_Handler;
void float_task(void *pvParameters);
xTaskCreate((TaskFunction_t )led2_task,
(const char* )"led1_task",
(uint16_t )LED2_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED2_TASK_PRIO,
(TaskHandle_t* )&LED2Task_Handler);
xTaskCreate((TaskFunction_t )led3_task,
(const char* )"led3_task",
(uint16_t )LED3_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED3_TASK_PRIO,
(TaskHandle_t* )&LED3Task_Handler);
xTaskCreate((TaskFunction_t )float_task,
(const char* )"float_task",
(uint16_t )FLOAT_STK_SIZE,
(void* )NULL,
(UBaseType_t )FLOAT_TASK_PRIO,
(TaskHandle_t* )&FLOATTask_Handler);
void led2_task(void *pvParameters)
{
while(1)
{
LED2_Toggle();
delay_ms(500);
}
}
void led3_task(void *pvParameters)
{
while(1)
{
LED3(0);
delay_ms(200);
LED3(1);
delay_ms(800);
}
}
void float_task(void *pvParameters)
{
static double float_num=0.00;
while(1)
{
float_num+=0.01f;
printf("float_num的值为: %.4f\r\n",float_num);
delay_ms(1000);
}
}
本程序与2.1 官网源码移植(从0开始) 程序相同。 对应工程名template1 。
3 FreeRTOS系统配置
3.1 FreeRTOSConfig.h文件说明
FreeRTOS的系统配置文件为FreeRTOSConfig.h,在此配置文件中包含了许多INCLUDE_ 和config 的开头宏定义,可以完成FreeRTOS的裁剪和配置。 FreeRTOS官方对FreeRTOSConfig.h的文档的说明:http://www.freertos.org/a00110.html
3.1.1 INCLUDE_ 开始的宏
使用"INCLUDE_"开头的宏用来表示使能或除能FreeRTOS中相应的API函数,作用就是用来配置FreeRTOS中的可选API函数的。其实本质就是条件编译,不需要的功能就不用编译,这样就可以根据实际需求来减少系统占用的ROM和RAM大小,根据自己所使用的MCU来调整系统消耗,降低成本。
- INCLUDE_xSemaphoreGetMutexHolder
如果要使用函数xQueueGetMutexHolder()的话宏INCLUDE_xSemaphoreGetMutexHolder必须定义为1。 - INCLUDE_xTaskAbortDelay
如果要使用函数xTaskAbortDelay()的话将宏INCLUDE_xTaskAbortDelay定义为1。 - INCLUDE_vTaskDelay
如果要使用函数vTaskDelay()的话需要将宏 INCLUDE_vTaskDelay定义为1。 - INCLUDE_vTaskDelayUntil
如果要使用函数vTaskDelayUntil()的话需要将宏INCLUDE_vTaskDelayUntil定义为1。 - INCLUDE_vTaskDelete
如果要使用函数vTaskDelete()的话需要将宏INCLUDE_vTaskDelete定义为1。 - INCLUDE_xTaskGetCurrentTaskHandle
如果要使用函数 xTaskGetCurentTaskHandle() 的话需要将宏INCLUDE_xTaskGetCurrentTaskHandle定义为1。 - INCLUDE_XTaskGetHandle
如果要使用函数xTaskGetHandle()的话需要将宏INCLUDE_xTaskGetHandle定义为1。 - INCLUDE_xTaskGetldleTaskHandle
如果要使用函数 xTaskGetldleTaskHandle()的话需要将宏INCLUDE_xTaskGetidleTaskHandle定义为1。 - INCLUDE_xTaskGetSchedulerState
如果要使用函数xTaskGetSchedulerState()的话需要将宏INCLUDE_xTaskGetSchedulerState定义为1。 - INCLUDE_uxTaskGetStackHighWaterMark*
如果要使用函数 uxTaskGetStackHighWaterMark()的话需要将宏INCLUDE uxTaskGetStackHighWaterMark定义为1。 - INCLUDE_uxTaskPriorityGet
如果要使用函数uxTaskPriorityGet)的话需要将宏INCLUDE_uxTaskPriorityGet定义为1。 - INCLUDE_vTaskPrioritySet
如果要使用函数vTaskPriorityset0的话需要将宏INCLUDE_vTaskPrioritySet定义为1。 - INCLUDE_xTaskResumeFromlSR
如果要使用函数xTaskResumeFromlSR()的话需要将宏INCLUDE_xTaskResumeFromlSR和INCLUDE_vTaskSuspend都定义为1。 - INCLUDE_eTaskGetState
如果要使用函数eTaskGetState()的话需要将宏INCLUDE_eTaskGetState定义为1。 - INCLUDE_vTaskSuspend
如果要使用函数vTaskSuspend()、vTaskResume()、prvTaskIsTaskSuspended()、xTaskResumeFromlSRO的话宏INCLUDE_vTaskSuspend要定义为1; 如果要使用函数xTaskResumeFromISR()的话宏INCLUDE_xTaskResumeFromISR和INCLUDE_vTaskSuspend都必须定义为1。 - INCLUDE_xTimerPendFunctionCall
如果要使用函数xTimerPendFunctionCall()和xTimerPendFunctionCallFromlSR()的话宏INCLUDE_xTimerPendFunctionCall和configUSE_TIMERS都必须定义为1。
3.1.2 config 开始的宏
config 开始的宏和INCLUDE_ 开始的宏一样,都是用来完成 FreeRTOS 的配置和裁剪的。
- configAPPLICATION_ALLOCATED_HEAP
默认情况下,FreeRTOS 的堆内存是由编译器来分配的,将宏configAPPLICATION_ALLOCATED_HEAP定义为1的话堆内存可以由用户自行设置,堆内存在heap_1.c、 heap_2.c、 heap-3.c、heap-4.c和heap-5.c中有定义,具体在哪个文件取决于用户的选择哪种内存管理方式。本文选用heap 4.c,heap4.c中有下面定义。 可以看出,当宏configAPPLICATION_ALLOCATED_HEAP定义为1的话,需要用户自行堆内存ucHeap,否则的话就是编译器来分配的。 - configASSERT
断言,类似于C标准库中的assert()函数,调试代码的时候可以检查传入的参数是否合理。如果传递给 configASSERT()的参数为零,则会触发断言。使用断言的话会导致开销加大,一般在调试阶段使用。 注意: configASSERT()函数需要用户自行去定义,可以是显示到LCD上的函数,也可以是通过串口打印出来的函数。比如下面代码,当参数x错误时通过串口打印出发生错误的文件名和错误所在的行号:#define vAssertCalled(char,int) printf("Error:%s,%d\r\n",char,int)
#define configASSERT(x) if((x)==0) vAssertCalled(__FILE__,__LINE__)
由于内置宏__LINE__是整数型的而不是字符串型,采用宏STR和和宏VAL来辅助完成这个转化。宏STR用来把整形行号替换掉__LINE__,宏VAL用来把这个整形行号字符串化。忽略宏STR和VAL中的任何一个,只能得到字符串”LINE”。经过上述转化后,vAssertCalled只需要接收一个字符串形式的参数。#define STR(x) VAL(x)
#define VAL(x) #x
#define vAssertCalled(char) printf("Error:%s\r\n",char)
#define configASSERT(x) ((x)?(void) 0 :vAssertCalled(__FILE__ ":" STR(__LINE__) " " #x"\n" ))
- configCHECK_FOR_STACK_OVERFLOW
设置堆栈溢出检测,每个任务都有一个任务堆栈,如果使用函数xTaskCreate()创建一个任务时,这个任务的堆栈是自动从FreeRTOS的堆(ucHeap)中分配的,堆栈的大小是由参数usStackDepth的决定。如果使用函数xTaskCreateStatic()创建任务时,任务堆栈是由用户设置的,参数pxStackBuffer为任务堆栈,一般是一个数组。 堆栈溢出是导致应用程序不稳定的主要因素, FreeRTOS提供了两种可选的机制来帮助检测和调试堆栈溢出,两种机制都要设置宏configCHECK_FOR_STACK_OVERFLOW。如果使能了堆栈检测功能的话,即宏configCHECK_FOR_STACK_OVERFLOW不为0,那么用户必须提供一个钩子函数(回调函数),当内核检测到堆栈溢出以后就会调用这个钩子函数,此钩子函数原型如下:void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName );
参数xTask和pcTaskName为堆栈溢出任务的句柄和名字。注意,如果溢出非常严重,这两个参数信息也可能是错误的!!在这种情况下,可以直接检查pxCurrentTCb变量。有些处理器可能在堆栈溢出的时候生成一个fault中断来提示这种错误,另外,建议在调试的时候使用堆栈溢出检测,因为它会增加上下文切换的开销。 configCHECK_FOR_STACK_OVERFLOW=1,使用堆栈溢出检测方法1。
相关参考
1、基于CubeMx配置RTOS和GUI时的两个小问题 2、FreeRTOS 之三 全配置项详解、裁剪(FreeRTOSConfig.h) That’s all.Check it.
|