本文章为本人学习FreeRTOS时的笔记,学习时使用 STM32 SPL库+Keil开发环境。
之前发过这篇文章但不知为何在CSDN上MD格式无法显示,故重新发一次。(真不是水浏览量)
文章目录
操作系统启动步骤
1.定义任务函数
模块化,写在自己的.c文件中
c
/**
* @brief LED_Task任务主体
* @param parameter
* @retval void
*/
static void LED_Task(void* parameter)
{
while (1)
{
LED1_ON;
vTaskDelay(500); /* 延时500个tick */
printf("LED_Task Running,LED1_ON\r\n");
LED1_OFF;
vTaskDelay(500); /* 延时500个tick */
printf("LED_Task Running,LED1_OFF\r\n");
}
}
2.空闲任务与定时器任务堆栈函数实现
当我们使用了静态创建任务的时候,configSUPPORT_STATIC_ALLOCATION 这个宏 定 义 必 须为 1 (在 FreeRTOS.h 文 件 中 ) , 并且 我 们需 要 实 现两 个 函数 : vApplicationGetIdleTaskMemory()与 vApplicationGetTimerTaskMemory(),这两个函数是用 户设定的空闲(Idle)任务与定时器(Timer)任务的堆栈大小,必须由用户自己分配,而 不能是动态分配
写在main.c,函数在main函数前声明
c
/* 空闲任务任务堆栈 */
static StackType_t Idle_Task_Stack[configMINIMAL_STACK_SIZE];
/* 定时器任务堆栈 */
static StackType_t Timer_Task_Stack[configTIMER_TASK_STACK_DEPTH];
/* 空闲任务控制块 */
static StaticTask_t Idle_Task_TCB;
/* 定时器任务控制块 */
static StaticTask_t Timer_Task_TCB;
/**
* @brief 获取空闲任务的任务堆栈和任务控制块内存
* @param ppxIdleTaskTCBBuffer : 任务控制块内存
* @param ppxIdleTaskStackBuffer : 任务堆栈内存
* @param pulIdleTaskStackSize : 任务堆栈大小
* @retval void
*/
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize)
{
*ppxIdleTaskTCBBuffer=&Idle_Task_TCB;/* 任务控制块内存 */
*ppxIdleTaskStackBuffer=Idle_Task_Stack;/* 任务堆栈内存 */
*pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;/* 任务堆栈大小 */
}
/**
* @brief 获取定时器任务的任务堆栈和任务控制块内存
* @param ppxTimerTaskTCBBuffer : 任务控制块内存
* @param ppxTimerTaskStackBuffer : 任务堆栈内存
* @param pulTimerTaskStackSize : 任务堆栈大小
* @retval void
*/
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer,
uint32_t *pulTimerTaskStackSize)
{
*ppxTimerTaskTCBBuffer=&Timer_Task_TCB;/* 任务控制块内存 */
*ppxTimerTaskStackBuffer=Timer_Task_Stack;/* 任务堆栈内存 */
*pulTimerTaskStackSize=configTIMER_TASK_STACK_DEPTH;/* 任务堆栈大小 */
}
3.定义任务栈、控制块、句柄
写在main.c
c
/* AppTaskCreate任务堆栈 */
static StackType_t AppTaskCreate_Stack[128];
/* LED任务堆栈 */
static StackType_t LED_Task_Stack[128];
/* AppTaskCreate 任务控制块 */
static StaticTask_t AppTaskCreate_TCB;
/* AppTaskCreate 任务控制块 */
static StaticTask_t LED_Task_TCB;
/* 创建任务句柄 */
static TaskHandle_t AppTaskCreate_Handle;
/* LED任务句柄 */
static TaskHandle_t LED_Task_Handle;
4.编写AppTaskCreate任务
这个任务是用于创建用户任务,为了方便管理,我们的所有的任务创建都统一放在这个函数中,在这个函数中创建成功的任务就可以直接参与任务调度了
写在main.c,在main函数前声明
c
static void AppTaskCreate(void)
{
taskENTER_CRITICAL(); //进入临界区
/* 创建LED_Task任务 */
LED_Task_Handle = xTaskCreateStatic((TaskFunction_t )LED_Task, //任务函数
(const char* )"LED_Task", //任务名称
(uint32_t )128, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )4, //任务优先级
(StackType_t* )LED_Task_Stack, //任务堆栈
(StaticTask_t* )&LED_Task_TCB); //任务控制块
if(NULL != LED_Task_Handle)/* 创建成功 */
printf("LED_Task任务创建成功!\n");
else
printf("LED_Task任务创建失败!\n");
vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
taskEXIT_CRITICAL(); //退出临界区
}
5.创建任务
静态创建任务
xTaskCreateStatic
在main函数内执行创建 AppTaskCreate 任务
c
/* 创建 AppTaskCreate 任务 */
AppTaskCreate_Handle = xTaskCreateStatic((TaskFunction_t )AppTaskCreate, //任务函数
(const char* )"AppTaskCreate", //任务名称
(uint32_t )128, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )3, //任务优先级
(StackType_t* )AppTaskCreate_Stack, //任务堆栈
(StaticTask_t* )&AppTaskCreate_TCB); //任务控制块
在AppTaskCreate任务中创建其余任务
c
/* 创建LED_Task任务 */
LED_Task_Handle = xTaskCreateStatic((TaskFunction_t )LED_Task, //任务函数
(const char* )"LED_Task", //任务名称
(uint32_t )128, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )4, //任务优先级
(StackType_t* )LED_Task_Stack, //任务堆栈
(StaticTask_t* )&LED_Task_TCB); //任务控制块
if(LED_Task_Handle != NULL)/* 创建成功 */
printf("LED_Task任务创建成功!\n");
else
printf("LED_Task任务创建失败!\n");
动态创建任务
xTaskCreate
程序跑起来内存会溢出,未查明原因
在AppTaskCreate任务中创建其余任务
c
/* 创建Receive_Task任务 */
xReturn = xTaskCreate((TaskFunction_t )Receive_Task, /* 任务入口函数 */
(const char* )"Receive_Task",/* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL, /* 任务入口函数参数 */
(UBaseType_t )2, /* 任务的优先级 */
(TaskHandle_t* )&Receive_Task_Handle);/* 任务控制块指针 */
6.启动任务
//这里建议先判断AppTaskCreate任务是否创建成功
跟在创建任务语句后
c
if(AppTaskCreate_Handle != NULL)/* 创建成功 */
vTaskStartScheduler(); /* 开启调度器 */
任务管理
任务状态切换
(1):创建任务→就绪态:
(2):就绪态→运行态:
(3):运行态→就绪态:
(4):运行态→阻塞态:(挂起、延时、 读信号量等待)
(5):阻塞态→就绪态:(任务恢复、延时时间超时、读 信号量超时或读到信号量等),
(6) (7) (8):就绪态、阻塞态、运行态→挂起态:调用 vTaskSuspend() 或 vTaskSuspendAll()
(9):挂起态→就绪态:调用 vTaskResume() 或 vTaskResumeFromISR()
删除任务
调用 vTaskDelete() 形参为要删除任 务创建时返回的任务句柄,如果是删除自身, 则形参为 NULL。
任务轮转
任务轮转即将运行态任务转就绪态,并运行最高优先级就绪态任务
以下是几个触发轮转的条件(满足一个即可)
- 任务由运行态转阻塞态(挂起、延时、 读信号量等待)
- 运行态→挂起态
延时函数
vTaskDelay 和 vTaskDelayUntil 函数的参数是以系统节拍为单位 的延时时间,而不是以毫秒为单位。要将毫秒转换为系统节拍,可以使用portTICK_PERIOD_MS常量,例如vTaskDelay(500 / portTICK_PERIOD_MS)表示延时500毫秒。
相对延时vTaskDelay()
指每次延时都是从任务执行函数vTaskDelay()开始,延时指定的时间结束。
绝对延时vTaskDelayUntil
指每隔指定的时间,执行一次调用vTaskDelayUntil()函数的任务。
消息队列使用步骤
要使用消息队列功能需要引入头文件
#include "queue.h"
1.创建消息队列
静态创建队列
xQueueCreateStatic()
使 用 xQueueCreateStatic()创建队列时,使用的是静态内存分配,所以要想使用该函数必须在 FreeRTOSConfig.h 中把 configSUPPORT_STATIC_ALLOCATION 定义为 1 来使能。
实际项目中常常是在AppTaskCreate执行vATask函数的内容,这里为了便于观察直接用一个函数框出
c
/* 创建一个可以最多可以存储 10 个 64 位变量的队列 */
#define QUEUE_LENGTH 10
#define ITEM_SIZE sizeof( uint64_t )
/*消息队列句柄*/
QueueHandle_t Test_QueueHandle = NULL;
/* 消息队列数据结构指针 */
static StaticQueue_t xStaticQueue_Structure;
/* 该数组作为队列的存储区域,大小至少有 uxQueueLength * uxItemSize 个字节 */
uint8_t ucQueueStorageArea[ QUEUE_LENGTH * ITEM_SIZE ];
void vATask( void *pvParameters ){
/* 创建一个队列 */
Test_QueueHandle = xQueueCreateStatic(QUEUE_LENGTH, /* 队列深度 */
ITEM_SIZE, /* 队列数据单元的单位 */
ucQueueStorageArea,/* 队列的存储区域 */
&xStaticQueue_Structure ); /* 队列的数据结构 */
/* 剩下的其他代码 */
while(1){
}
}
这里解释下 ucQueueStorageArea 和 xStaticQueue的区别:
ucQueueStorageArea:就是一个数组,存放队列的实际内容
xStaticQueue:是一个结构体变量,内涵了队列的属性、ucQueueStorageArea的首地址,后续操作队列实际上也就是操作xStaticQueue
动态创建队列
程序跑起来内存会溢出,未查明原因
xQueueCreate()
使用 xQueueCreate()创建队列时,使用的是动态内存分配,所以要想使用该函数必须在 FreeRTOSConfig.h 中把 configSUPPORT_DYNAMIC_ALLOCATION 定义为 1 来使能,这 是个用于使能动态内存分配的宏,通常情况下,在 FreeRTOS 中,凡是创建任务,队列, 信号量和互斥量等内核对象都需要使用动态内存分配,所以这个宏默认在 FreeRTOS.h 头文 件中已经使能(即定义为 1)。
在AppTaskCreate中执行
c
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
taskENTER_CRITICAL(); //进入临界区
/* 创建Test_Queue */
Test_Queue = xQueueCreate((UBaseType_t ) QUEUE_LEN,/* 消息队列的长度 */
(UBaseType_t ) QUEUE_SIZE);/* 消息的大小 */
if(NULL != Test_Queue)
printf("创建Test_Queue消息队列成功!\r\n");
taskEXIT_CRITICAL(); //退出临界区
2.读队列
使用 xQueueReceive() 或 xQueueReceiveFromISR() 函数读队列,读到一个数据后,队列中该数据会被移除。两个函数分别为:在任务中使用、在ISR中使用。
这里举出xQueueReceive函数的应用实例
c
/**
* @brief ReadQueue_Task任务主体
* @param parameter
* @retval void
*/
static void ReadQueue_Task(void* parameter){
BaseType_t xReturn = pdTRUE;// 定义一个创建信息返回值,默认为pdTRUE
u8 rQueue;//接收消息的变量
TickType_t lastTick = xTaskGetTickCount();//获取当前时间
while(1){
xReturn = xQueueReceive( Test_Queue, /* 消息队列的句柄 */
&rQueue, /* 收到的消息内容存放处 */
0); /* 等待时间 0ms */
if(xReturn == pdTRUE)
printf("成功读取消息,内容为:%d\r\n",rQueue);
else
printf("读取消息失败,错误码为:0x%lx\r\n",xReturn);
vTaskDelay(1000/portTICK_PERIOD_MS);
}
}
3.写列队
可以把数据写到队列头部,也可以写到尾部,注意,一次只能写一个数据,如果要写数组的话要循环遍历数组写入
xQueueSend 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait
xQueueSendToBack 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait
xQueueSendToBackFromISR 往队列尾部写入数据,此函数可以在中断函数中使用,不可阻塞
xQueueSendToFront 往队列头部写入数据,如果没有空间,阻塞时间为xTicksToWait
xQueueSendToFrontFromISR 往队列头部写入数据,此函数可以在中断函数中使用,不可阻塞
阻塞时间表示着等待队列空闲的最大超时时间。如果队列满并且xTicksToWait 被设置成0,函数立刻返回
xQueueSend()函数使用实例
c
/**
* @brief KeyScan_Task任务主体(简易消抖版)
* @param parameter
* @retval void
*/
static void KeyScan_Task(void* parameter){
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
u8 wQueue;//写入队列的变量
while(1){
if(Key1 == 0){
wQueue = 1;
vTaskDelay(20/portTICK_PERIOD_MS);
xReturn = xQueueSend( Test_Queue,
&wQueue,
0);
if(xReturn == pdPASS)
printf("消息1发送成功!\r\n");
else
printf("消息1发送失败,错误码为0x%lx!\r\n",xReturn);
while(Key1 == 0);
vTaskDelay(20/portTICK_PERIOD_MS);
}
}
}
4.消息队列删除函数
需要注意的是调用删除消息队列函 数前,系统应存在 xQueueCreate()或 xQueueCreateStatic()函数创建的消息队列。此外 vQueueDelete()也可用于删除信号量。如果删除消息队列时,有任务正在等待消息,则不应 该进行删除操作。
消息队列删除函数 vQueueDelete()使用实例
c
#define QUEUE_LENGTH 5
#define QUEUE_ITEM_SIZE 4
int main( void )
{
QueueHandle_t xQueue;
/* 创建消息队列 */
xQueue = xQueueCreate( QUEUE_LENGTH, QUEUE_ITEM_SIZE );
if ( xQueue == NULL ) {
/* 消息队列创建失败 */
} else {
/* 删除已创建的消息队列 */
vQueueDelete( xQueue );
}
}
5.复位
xQueueReset()
队列刚被创建时,里面没有数据;使用过程中可以调用xQueueReset()把队列恢复为初始状态
6.查询
可以查询队列中有多少个数据、有多少空余空间。
c
/*返回队列中可用数据的个数*/
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );
/*返回队列中可用空间的个数*/
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );
信号量
二值信号量
二值信号量和互斥信号量(以下使用互斥量表示互斥信号量)非常相似,但是有一些细 微差别:互斥量有优先级继承机制,二值信号量则没有这个机制。
创建二值信号量
xSemaphoreCreateBinary()
使用该函数创建的二值信号量是空的,在使用函数 xSemaphoreTake()获取之前必须先调用函数 **xSemaphoreGive()**释放后才可以获取。
互斥信号量
互斥信号量其实是特殊的二值信号量,由于其特有的优先级继承机制从而使它更适用 于简单互锁,也就是保护临界资源(临界资源是指任何时刻只能被一个任务访问的资源)
互斥信号量需要打开
#define configUSE_MUTEXES 1
才能使用
互斥信号量应用
本实验通过互斥信号量解决串口被相同优先级的任务访问导致的串口乱码
两个任务都使用printf:
二值信号量市藕帕炕袢⊥放~
成功,亮1秒灯
两个任务都使用xPrintf:
二值信号量释放~
信号量获取成功,亮1秒灯
c
#include <stdarg.h>//标准C库文件,让函数能够接收可变参数
static SemaphoreHandle_t printfSemphr_Handle = NULL;/* 串口打印互斥信号量句柄*/
/* 信号量数据结构指针 */
static StaticSemaphore_t printfSemphr_Structure;/*串口打印互斥信号量*/
int main(){
printfSemphr_Handle = xSemaphoreCreateMutexStatic(&printfSemphr_Structure);//创建互斥信号量
if(printfSemphr_Handle != NULL)
printf("串口打印互斥信号量创建成功~\r\n");
else
printf("串口打印互斥创建失败~\r\n");
while(1);
}
void xPrintf(char *format, ...)
{
char buf_str[200 + 1];
va_list v_args;
va_start(v_args, format);
(void)vsnprintf((char *)&buf_str[0],
(size_t ) sizeof(buf_str),
(char const *) format,
v_args);
va_end(v_args);
/* 互斥信号量 */
xSemaphoreTake(printfSemphr_Handle, portMAX_DELAY);
printf("%s", buf_str);
xSemaphoreGive(printfSemphr_Handle);
}
计数信号量
计数信号量需要打开
#define configUSE_COUNTING_SEMAPHORES 1
才能使用
创建计数信号量
静态创建
xSemaphoreCreateCountingStatic()
参数说明:
uxMaxCount | 可以达到的最大计数值。 当信号量达到此值时,它不能再被"给定"。 |
---|---|
uxInitialCount | 创建信号量时分配给信号量的计数值。 |
pxSemaphoreBuffer | 必须指向 StaticSemaphore_t 类型的变量, 该变量然后用于保存信号量的数据结构体。 |
代码案例
c
#define vSensorCountMax 1000
static SemaphoreHandle_t vSensorLCountHandle = NULL;//左测速传感器计数信号量句柄
static StaticSemaphore_t vSensorLCount_Structure;/*左测速传感器计数信号量*/
static void AppTaskCreate(void){
taskENTER_CRITICAL(); //进入临界区
vSensorLCountHandle = xSemaphoreCreateCountingStatic(vSensorCountMax, //最大计数值
0, //初始计数值
&vSensorLCount_Structure); //信号量的数据结构体
taskEXIT_CRITICAL(); //退出临界区
}
动态创建
xSemaphoreCreateCounting()
要想使用该函数必须在 FreeRTOSConfig.h 中把宏 configSUPPORT_DYNAMIC_ALLOCATION 定义为 1,即开启动 态内存分配。
查询计数信号量的当前计数值
uxSemaphoreGetCount()
-
参数:
xSemaphore 正在查询的信号量的句柄。
-
返回:
如果信号量是计数信号量,则返回信号量的当前计数值 。 如果信号量是二进制信号量, 则当信号量可用时,返回 1,当信号量不可用时, 返回 0。
递归信号量
删除信号量
vSemaphoreDelete()用于删除一个信号量,包括二值信号量,计数信号量,互斥量和递 归互斥量。如果有任务阻塞在该信号量上,那么不要删除该信号量。
释放信号量
任务中释放信号量
xSemaphoreGive()
该函数是一个用于释放信号量的宏,真正的实现过程是调用消息队列通用发送函数,释放的信号量对象必须是已经被创建的,可以用于二值信号量、计数信号量、互斥量的释放,但不能释放由函数 xSemaphoreCreateRecursiveMutex()创建的递归互斥量。
c
static void Send_Task(void* parameter)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
while (1)
{
/* K1 被按下 */
if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )
{
xReturn = xSemaphoreGive( BinarySem_Handle );//给出二值信号量 xSemaphoreGive 释放信号量
if( xReturn == pdTRUE )
printf("BinarySem_Handle二值信号量释放成功!\r\n");
else
printf("BinarySem_Handle二值信号量释放失败!\r\n");
}
/* K2 被按下 */
if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON )
{
xReturn = xSemaphoreGive( BinarySem_Handle );//给出二值信号量
if( xReturn == pdTRUE )
printf("BinarySem_Handle二值信号量释放成功!\r\n");
else
printf("BinarySem_Handle二值信号量释放失败!\r\n");
}
vTaskDelay(20);
}
}
中断中释放信号量
xSemaphoreGiveFromISR()
该函数用于释放一个信号量,带中断保护。被释放的信号量可以是二进制信号量和计数信号量。和普通版本的释放信号量 API 函数有些许不同,它不能释放互斥量,这是因为互斥量不可以在中断中使用,互斥量的优先级继承机制只能在任务中起作用,而在中断中毫无意义。
c
void vTestISR( void )
{
BaseType_t pxHigherPriorityTaskWoken;
uint32_t ulReturn;
/* 进入临界段,临界段可以嵌套 */
ulReturn = taskENTER_CRITICAL_FROM_ISR();
/* 判断是否产生中断 */
{
/* 如果产生中断,清除中断标志位 */
//释放二值信号量,发送接收到新数据标志,供前台程序查询
xSemaphoreGiveFromISR(BinarySem_Handle,&
pxHigherPriorityTaskWoken);
//如果需要的话进行一次任务切换,系统会判断是否需要进行切换
portYIELD_FROM_ISR(pxHigherPriorityTaskWoken);
}
/* 退出临界段 */
taskEXIT_CRITICAL_FROM_ISR( ulReturn );
}
获取信号量
任务中获取信号量
xSemaphoreTake()
该函数用于获取信号量,不带中断保护。获取的信号量对象可以是二 值信号量、计数信号量和互斥量,但是递归互斥量并不能使用这个 API 函数获取。
c
static void Receive_Task(void* parameter)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
while (1)
{
//获取二值信号量 xSemaphore,没获取到则一直等待
xReturn = xSemaphoreTake(BinarySem_Handle,/* 二值信号量句柄 xSemaphoreTake获取一个信号量,可以是二值信号量、计数信号量、互斥量。*/
portMAX_DELAY); /* 等待时间 */
if(pdTRUE == xReturn)
printf("BinarySem_Handle二值信号量获取成功!\n\n");
LED1_TOGGLE;
}
}
中断中获取信号量
xSemaphoreTakeFromISR()
此函数用于获取信号量,是 一个不带阻塞机制获取信号量的函数,获取对象必须由是已经创建的信号量,信号量类型 可以是二值信号量和计数信号量,它与 xSemaphoreTake()函数不同,它不能用于获取互斥量,因为互斥量不可以在中断中使用,并且互斥量特有的优先级继承机制只能在任务中起 作用,而在中断中毫无意义。
c
void vTestISR( void )
{
BaseType_t pxHigherPriorityTaskWoken;
uint32_t ulReturn;
/* 进入临界段,临界段可以嵌套 */
ulReturn = taskENTER_CRITICAL_FROM_ISR();
/* 判断是否产生中断 */
{
/* 如果产生中断,清除中断标志位 */
//释放二值信号量,发送接收到新数据标志,供前台程序查询
xSemaphoreGiveFromISR(BinarySem_Handle,&
pxHigherPriorityTaskWoken);
//如果需要的话进行一次任务切换,系统会判断是否需要进行切换
portYIELD_FROM_ISR(pxHigherPriorityTaskWoken);
}
/* 退出临界段 */
taskEXIT_CRITICAL_FROM_ISR( ulReturn );
}
软件定时器
中断管理
基本介绍
ARM Cortex-M 系列内核的中断是由硬件管理的,而 FreeRTOS 是软件,它并不接管由 硬件管理的相关中断,只支持简单的开 关中断等,所以 FreeRTOS 中的中断使用其实跟裸机差不多的,需要我们自己配置中断,并且使能中断,编写中断服务函数,在中断服务函数中使用内核 IPC 通信机制,一般建议 使用信号量、消息或事件标志组等标志事件的发生,将事件发布给处理任务,等退出中断 后再由相关处理任务具体处理中断。
FreeRTOS 的中断管理支持:
● 开/关中断。
●恢复中断。
●中断使能。
●中断屏蔽。
●可选择系统管理的中断优先级。
配置可管理中断
用 户 可 以 自 定 义 配 置 系 统 可 管 理 的 最 高 中 断 优 先 级 的 宏 定 义 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY , 它 是 用 于 配 置 内 核 中 的 basepri 寄存器的,当 basepri 设置为某个值的时候,NVIC 不会响应比该优先级低的中断, 而优先级比之更高的中断则不受影响。就是说当这个宏定义配置为 5 的时候,中断优先级 数值在 0、1、2、3、4 的这些中断是不受 FreeRTOS 屏蔽的,也就是说即使在系统进入临 界段的时候,这些中断也能被触发而不是等到退出临界段的时候才被触发,当然,这些中 断服务函数中也不能调用 FreeRTOS 提供的 API 函数接口,而中断优先级在 5 到 15 的这些 中断是可以被屏蔽的,也能安全调用 FreeRTOS 提供的 API 函数接口。
一些奇怪的发现
- 挂起一个任务后,再重新解挂,这个任务函数会从头执行
- stm32f103c6芯片动态创建任务或队列都会导致ZI-data区达到20000多,程序直接炸