一、任务管理介绍
1.任务状态
1)调度器切换任务调度
2)任务是一个死循环,如果想要停止这个任务则会调用在函数最后写的delete函数进行自杀
1.就绪态
1)已经具备执行的能力,只等待调度器进行调度。
2)新创建的任务会默认设置为就绪态【表示此时就差CPU资源就可以运行】
2.运行态
1)表示此时任务正在运行,占用了CPU资源
2)是就绪任务优先级最高的运行
3.阻塞态
1)等待某一个时序或者中断的时候【此时是任务被动进入等待状态】
2)等待延时,中断
3)已经从就绪列表中被删除
4.挂起态
1)任务被暂时停止,通过调用挂起函数(vTaskSuspend())可以把指定任务挂起,任务挂起后暂时不会运行,只有调用恢复函数(xTaskResume())才可以退出挂起状态;
2)处于挂起态的任务对调度器而言是不可见的
5.挂起 VS 阻塞
“阻塞(pend)”与“挂起(suspend)”的区别?_线程挂起和阻塞的区别-CSDN博客
1)挂起态与阻塞态的区别,当任务有较长的时间不允许运行的时候,我们可以挂起任务,这样子调度器就不会管这个任务的任何信息,直到调用恢复任务的 接口;而任务处于阻塞态的时候,系统还需要判断阻塞态的任务是否超时,是否可以解除阻塞。
2)挂起是一种主动行为,因此恢复也应该要主动完成,而阻塞则是一种被动行为,是在等待事件或资源时任务的表现,你不知道他什么时候被阻塞(pend),也就不能确切的知道他什么时候恢复阻塞。【挂起:要人进行suspend和resume task;阻塞:被动进入等待】
6.各种状态
1)在任务1中经过10tick,将任务3先进行挂起,然后经过10tick在取消挂起
2)在任务2中进行10ms的延时(阻塞)
void Task1Function(void * param)
{
TickType_t tStart = xTaskGetTickCount();
//不断的获取时间
TickType_t t ;
int flag = 0;
while(1)
{
task1flagrun = 1;
task2flagrun = 0;
task3flagrun = 0;
printf("1");
if((t > tStart + 10) && !flag)
{
//将task3进入阻塞状态
vTaskSuspend(xHandlerTask3);
flag = 1;
}
if(t> tStart + 20)
{
//释放Task3
vTaskResume(xHandlerTask3);
}
}
}
void Task2Function(void * param)
{
while(1)
{
task1flagrun = 0;
task2flagrun = 1;
task3flagrun = 0;
printf("2");
vTaskDelay(10);
}
}
void Task3Function(void * param)
{
while(1)
{
task1flagrun = 0;
task2flagrun = 0;
task3flagrun = 1;
printf("3");
}
}
2.任务状态转换
1.运行<----->就绪
- 从运行态到就绪态的转换:
当一个任务正在运行时(即处于运行态),如果发生了某个事件(如定时器超时、消息队列接收、信号量释放等),可能会导致该任务释放其占用的资源(如 CPU)并返回到就绪态。这种转换通常是由任务自身触发的,例如,当任务完成其当前工作后,它可能会释放它之前占用的资源并返回到就绪态,等待下一次调度器选择它运行。此外,当一个更高优先级的任务准备就绪时,当前运行的任务可能会被抢占,从而导致当前任务从运行态转换到就绪态。这是基于优先级的抢占式调度的核心特性。
2.从就绪态到运行态的转换:
当任务处于就绪态时,它已经准备好运行,但尚未获得 CPU 的使用权。从就绪态到运行态的转换是由调度器触发的。当调度器发现当前运行的任务已经完成其工作并释放了 CPU,或者有一个更高优先级的任务准备就绪时,调度器会选择一个优先级最高的就绪任务,并将其从就绪态转换到运行态。这个过程涉及上下文切换,即保存当前运行任务的上下文(如寄存器状态、堆栈指针等),并恢复新任务的上下文。然后,新任务开始执行,从它上次停止的地方继续执行。
2.运行-->阻塞
当一个任务处于运行态时,它可能会因为等待某个资源或事件(如信号量、互斥量、消息队列中的消息等)而主动进入阻塞态。这通常发生在任务调用某些可能导致阻塞的API时,如
xSemaphoreTake()
、xQueueReceive()
等。例如,如果一个任务尝试从一个空的消息队列中接收消息,并且该任务被配置为等待消息可用(阻塞接收),那么该任务将从运行态转换到阻塞态,并且会被加入到相应资源(在这个例子中是消息队列)的等待列表中。在阻塞态,任务会释放CPU,使得其他任务可以运行。
3.阻塞--->就绪
当任务所等待的资源或事件变得可用时,任务会从阻塞态转换到就绪态。这通常是由其他任务或中断服务例程(ISR)触发的。
例如,如果一个任务因为等待一个信号量而阻塞,当另一个任务释放该信号量时(通过调用
xSemaphoreGive()
),处于阻塞态的任务会被唤醒,并从信号量的等待列表中移除,转换到就绪态。此时,任务已经准备好再次运行,但是否能够立即运行取决于其优先级和其他任务的状态
4.就绪,运行,阻塞--->挂起
从运行态到挂起态
当任务处于运行态时,可以通过调用
vTaskSuspend()
函数来将其挂起。这将导致当前运行的任务立即释放CPU,并且其状态将从运行态转换到挂起态。挂起任务不会立即停止执行,而是在下一次上下文切换时停止。从就绪态到挂起态
如果任务处于就绪态,同样可以通过调用
vTaskSuspend()
函数来将其挂起。一旦任务被挂起,它将不再参与调度器的调度,即使它的优先级比其他就绪任务高。从阻塞态到挂起态
当任务处于阻塞态(等待某个资源或事件),并且该资源或事件尚未变得可用时,任务也可以被挂起。这通常是通过调用
vTaskSuspend()
函数实现的。在这种情况下,任务将保持其阻塞状态,但同时也会被挂起。一旦任务被挂起,即使资源或事件变得可用,任务也不会被唤醒。从挂起态的恢复
要从挂起态恢复任务,可以使用
vTaskResume()
函数。这将把任务从挂起态转换回就绪态(如果任务原本处于就绪态或运行态被挂起),或者从阻塞态转换回阻塞态(如果任务原本因为等待资源或事件而被挂起)。当任务从挂起态恢复时,它将重新参与调度器的调度,并有可能在下一次上下文切换时获得CPU使用权。
二、常用API函数
1.任务挂起函数
1.vTaskSuspend
1)任务被挂起就不会获得CPU的使用权
2)无论任务当前处于什么状态,只要调用挂起函数就都可以进入挂起状态
3)如果这个传入的参数是NULL就表示挂起自己
4)需要将宏INCLUDE_vTaskSuspend配置为1
2.vTaskSuspendAll
1)这意味着一旦调用此函数,除了正在运行的任务(调用
vTaskSuspendAll()
的任务)之外,其他所有任务都将被暂停,不会获得 CPU 的使用权。2)中断服务例程(ISR)不受
vTaskSuspendAll()
的影响。即使所有任务都被挂起,中断仍然可以正常响应和处理。【与临界区不同的是,挂起任务调度器时未关闭中断;这种方式仅仅防止了任务之间的资源争夺,中断仍然可以直接响应;挂起调度器的方法适用于临界区位于壬务与任务之间的情况;这样既不需要延迟中断,同时又能确保临界区的安全性。】3)使用
xTaskResumeAll()
来恢复所有被vTaskSuspendAll()
暂停的任务的调度。4)如果系统配置了时间片轮转(task time slicing),
vTaskSuspendAll()
也会暂停时间片的切换。5)这个函数可以嵌套使用,但是如果调用多少次
vTaskSuspendAll()就要调用多少次
xTaskResumeAll()来恢复【
如果你调用了vTaskSuspendAll()
n 次,你就需要调用xTaskResumeAll()
n 次来恢复所有任务的调度。】
2.任务挂起恢复
1.vTaskResume
1)任务的恢复就让挂起的任务重新回到就绪状态
2)此函数不能用于中断函数中恢复任务挂起
3)不论任务被使用vTaskSuspend()挂起多少次,只需要调用vTaskResum()一次,即可使其继续执行。
2.xTaskResumeFromISR
1)在中断里面对任务挂起的恢复操作
2)INCLUDE_vTaskSuspend和INCLUDE_xTaskResumeFromISR必须定义为1
3)在中断服务程序中调用FreeRTOS的API函数的时候,中断的优先级不能高于FreeRTOS所管理的最高任务优先级。
3.任务删除
1.vTaskDelete
1)如果要删除自己,就传入“NULL”
2)如果要删除其他用户则传入对应的句柄即可
3)要使用这个函数,则要先将这个“INCLUDE_vTaskDelete”置为1
4)我们要在删除任务之前,先将这个任务拥有的这个内存先删除,在删除这个任务。要不然这个任务所占的空间不会自动删除,容易造成内存泄漏【空闲任务负责删除的内存是由系统分配的】
5)如果删除任务自身,需要先添加到等待删除列表,内存释放在空闲任务执行;如果删除其他任务,释放内存,任务数量--
/*
KEY1按下,挂起Task1,按下KEY2,恢复Task1
*/
void Task3(void * pvPara)
{
uint8_t key=0;
while (1)
{
/* code */
key=Key_Delete();
if(key ==KEY1_PRESS )
{
// 按下KEY1,挂起Task1
vTaskSuspend(TASK1_task_handler);
}
else if(key ==KEY2_PRESS)
{
// 按下key2,恢复task1
vTaskResume(TASK1_task_handler);
}
vTaskDelay(500);//此时Task2在执行
}
}
4.任务延时函数
1.vTaskDelay
2.vTaskDelayUntil
3. vTaskDelay 和 vTaskDelayUntil
延时方式:
vTaskDelay
是一个相对延时函数,它使任务进入挂起态,直到至少等待指定的Tick Interrupt(滴答中断)次数。换句话说,它是从调用vTaskDelay
函数开始,延时指定的时间后任务才会重新进入就绪态。vTaskDelayUntil
则是一个绝对延时函数,它会使任务等待到指定的绝对时刻后才会变为就绪态。这意味着任务会等待到指定的时间点,而不是等待某个数量的Tick Interrupt。精度和任务执行:
vTaskDelayUntil
的精度通常比vTaskDelay
高,因为它基于绝对时间进行延时。- 在某些情况下,使用
vTaskDelayUntil
的任务不会丢失执行,而vTaskDelay
可能会导致任务丢失执行。例如,当高优先级的任务长时间占用CPU时,使用vTaskDelay
的任务可能会错过其预定的执行时间。使用场景:
vTaskDelay
适用于需要相对延时的场景,例如,当你想要任务在执行某个操作后等待一段时间再继续执行时。vTaskDelayUntil
则适用于需要任务在特定时间点执行的场景,例如,定时任务或需要在特定时间间隔内执行的任务。
1.vTaskDelay
使用一个随机数组,来导致延时的时间长短不一样
static int rands[]={3,5,2,1};
void Task1Function(void * param)
{
TickType_t tStart = xTaskGetTickCount();
int i =0;
int j = 0;
while(1)
{
task1flagrun = 1;
task2flagrun = 0;
task3flagrun = 0;
for(i = 0;i<rands[j];i++)
{
printf("1");
}
j++;
if(j == 5)
{
j = 0;
}
vTaskDelay(20);
}
}
2.vTaskDelayUtil
三、任务的设计要点
2.普通任务
1)因为任务是死循环的,所以这里一定要有阻塞的机会,才可以跳转到其他任务
2)将紧急任务的优先级设计较高
3.空闲任务
空闲任务的钩子函数(vApplicationIdleHook):只能处于运行状态或者就绪状态,不能处于阻塞(如果这样没有其他任务将空闲任务唤醒)
1)任务创建的时候自动创建空闲任务
2)空闲任务中也不要编写会阻塞运行的API函数
3)主要用于释放资源
在Task1中创建Task2,并且在Task1中删除Task2【不断的创建和删除任务会导致栈空间被消耗完(因为删除任务的操作是在空闲任务中进行的)】
void Task2Function(void * param)
{
while(1)
{
printf("2");
vTaskDelay(2);
}
}
static int rands[]={3,5,2,1};
void Task1Function(void * param)
{
TaskHandle_t xHandlerTask2;
BaseType_t xReturn;
while(1)
{
printf("1");
//优先级搞一点
xTaskCreate(Task2Function,"Task",100,NULL,2,&xHandlerTask2);
if(xReturn != pdPASS)
{
printf("erroe\r\n");
}
//将task2的任务删除
vTaskDelete(xHandlerTask2);
}
}
上面代码并没有出现我们想要的结果【就是栈溢出的后果--->出现这个效果就是打印error这句话】
使用方式
static int task1flagrun = 0;
static int task2flagrun = 0;
static int taskidleflagrun = 0;
void Task2Function(void * param)
{
while (1)
{
task1flagrun = 0;
task2flagrun = 1;
taskidleflagrun = 0;
printf("2");
//vTaskDelay(2);
vTaskDelete(NULL);
}
}
void Task1Function(void * param)
{
TaskHandle_t xHandleTask2;
BaseType_t xReturn;
while (1)
{
task1flagrun = 1;
task2flagrun = 0;
taskidleflagrun = 0;
printf("1");
xReturn = xTaskCreate(Task2Function, "Task2", 1024, NULL, 2, &xHandleTask2);
if (xReturn != pdPASS)
printf("xTaskCreate err\r\n");
//vTaskDelete(xHandleTask2);
}
}
//空闲任务的钩子函数
//优先级:0,如果我们想要空闲任务有机会执行,则要让其中一个任务优先级也为0
//这样就可以跟空闲任务交替执行
void vApplicationIdleHook( void )
{
task1flagrun = 0;
task2flagrun = 0;
taskidleflagrun = 1;
printf("0");
}
4.任务执行时间
1)时间---->任务执行的周期时间,任务需要的时间长短
2)一般来说:处理时间更短的任务优先级要设置的越高(因为这样对运行时间长的任务来说根本不值得一提)
四、例子
#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define LED1_TASK_PRIO 2
//任务堆栈大小
#define LED1_STK_SIZE 50
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);
//任务优先级
#define LED2_TASK_PRIO 3
//任务堆栈大小
#define LED2_STK_SIZE 50
//任务句柄
TaskHandle_t LED2Task_Handler;
//任务函数
void led2_task(void *pvParameters);
//任务优先级
#define KEY_TASK_PRIO 4
//任务堆栈大小
#define KEY_STK_SIZE 50
//任务句柄
TaskHandle_t KEYTask_Handler;
//任务函数
void key_task(void *pvParameters);
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
int main()
{
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
LED_Init();
KEY_Init();
USART1_Init(115200);
//创建开始任务
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(); //进入临界区
//创建LED1任务
xTaskCreate((TaskFunction_t )led1_task,
(const char* )"led1_task",
(uint16_t )LED1_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED1_TASK_PRIO,
(TaskHandle_t* )&LED1Task_Handler);
//创建LED2任务
xTaskCreate((TaskFunction_t )led2_task,
(const char* )"led2_task",
(uint16_t )LED2_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED2_TASK_PRIO,
(TaskHandle_t* )&LED2Task_Handler);
//创建KEY任务
xTaskCreate((TaskFunction_t )key_task,
(const char* )"key_task",
(uint16_t )KEY_STK_SIZE,
(void* )NULL,
(UBaseType_t )KEY_TASK_PRIO,
(TaskHandle_t* )&KEYTask_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//LED1任务函数
void led1_task(void *pvParameters)
{
while(1)
{
LED1=0;
vTaskDelay(200);
LED1=1;
vTaskDelay(800);
}
}
//LED2任务函数
void led2_task(void *pvParameters)
{
while(1)
{
LED2=0;
vTaskDelay(800);
LED2=1;
vTaskDelay(200);
}
}
//KEY任务函数
void key_task(void *pvParameters)
{
u8 key=0;
while(1)
{
key=KEY_Scan(0);
if(key==KEY_UP_PRESS)
{
printf("挂起LED任务!\n");
vTaskSuspend(LED2Task_Handler);/* 挂起LED任务 */
printf("挂起LED任务成功!\n");
}
else if(key==KEY1_PRESS)
{
printf("恢复LED任务!\n");
vTaskResume(LED2Task_Handler);/* 恢复LED任务!*/
printf("恢复LED任务成功!\n");
}
//将本任务进行挂起
vTaskDelay(20);
}
}
五、中断
参考博客:
https://www.cnblogs.com/yangguang-it/p/7152549.html
1.中断服务函数
1)在中断函数中不能执行挂起任务
2)不允许执行任何会阻塞运行的API接口函数
3)保持精简短小
4)只能做标记,标记触发其他函数
5)中断过长,会影响其他函数的运行
6)STM32中的中断(0-15),数值越小,优先级越大;
FreeRTOS的任务调度:数值越小,优先级越小。
7)FreeRTOS中,将PendSV和SysTikc设置最低中断优先级(数值最大,15),保证系统任务切换不会阻塞系统其他中断的响应。
8)FreeRTOS 利用 BASEPRI寄存器实现中断管理,屏蔽优先级低于某一个值的中断比如:BASEPRI设置为0x50(只看高四位,也就是5),代表中断优先级在5~15【包含5】内的均被屏蔽,0~4的中断优先级正常执行。【表示当中断发生时,0-4并不会响应,5-15会被响应】
2.中断例子
1.CubeMX配置
记得打开定时器中断
2.基本的代码修改
因为我们使用了CubeMX重新生成代码,所以我们前面配置的代码可能会被冲掉。包括it.c文件中的PendSV_Handler和SVC_Handler应该重新被注释才可以。
3.编写定时器中断回调函数,开启中断定时器
4.打开相关的宏定义
5.编写任务函数
/*
LED1每500ms翻转一次
*/
void Task1(void * pvPara)
{
uint32_t delay=0;
while (1)
{
delay = 500000;
/* 关闭中断,观察tim的打印情况 */
printf("关闭中断\r\n");
portDISABLE_INTERRUPTS();
// 加个延时,方便观察
// vTaskDelay();//由于我们这里就写一个任务如果此时这个任务结束,会跑去执行空闲任务
while (delay--);
// 开中断,观察tim的打断情况
printf("打开中断\r\n");
portENABLE_INTERRUPTS();
}
}
3.SVC_Handler 和 PendSV_Handler
参考博客:
浅谈FreeRTOS任务启动与切换流程_svc_handler-CSDN博客
1.函数作用
SVC_Handler是用于启动第一个任务的中断;
PendSV_Handler是用于每次任务切换中断;
SysTick_Handler是一个定时器,比如一个任务运行3s,这3s就是用这个定时器来计时得到的。
2.为什么将操作放入中断中??
这是因为CM3内核有两种模式:用户和特权,模式不同,权限不同,有些操作需要在特权模式下进行。
4.FreeRTOS中断优先级配置
1.通过寄存器的高四位进行控制
对于 M3 和 M4 内核的 MCU,每个中断的优先级都是用寄存器中的 8 位来设置的。 8 位的话就可以设置 2^8 = 256 级中断,实际中用不了这么多,所以芯片厂商根据自己生产的芯片做出了调整。比如 ST的 STM32F1xx 和 F4xx 只使用了这个 8 位中的高四位[7:4],低四位取零,这样 2^4=16,只能表示 16级中断嵌套。
2.初始化:默认优先级分组为0
STM32 支持 5 种优先级分组,系统上电复位后,默认使用的是优先级分组0,也就是没有抢占式优先级,只有子优先级.
3.抢占优先级的注意点
具有高抢占式优先级的中断可以在具有低抢占式优先级的中断服务程序执行过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以抢占低抢占式优先级的中断的执行。
在抢占式优先级相同的情况下,有几个子优先级不同的中断同时到来,那么高子优先级的中断优先被响应。
在抢占式优先级相同的情况下,如果有低子优先级中断正在执行,高子优先级的中断要等待已被响应的低子优先级中断执行结束后才能得到响应,即子优先级不支持中断嵌套。
Reset、 NMI、 Hard Fault 优先级为负数,高于普通中断优先级,且优先级不可配置。
对于初学者还有一个比较纠结的问题就是系统中断(比如:PendSV,SVC,SysTick)是不是一定比外部中断(比如 SPI,USART)要高,答案:不是的,它们是在同一个 NVIC 下面设置的。强烈推荐用户将 Cortex-M3 内核的 STM32F103 和 Cortex-M4 内核的 STM32F407 以及
STM32F429 的 NVIC 优先级分组设置为 4,即:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);这
样中断优先级的管理将非常方便。 这个也是官方强烈建议的。(注意:一旦初始化好 NVIC 的优先级分组后,切不可以在应用中再次更改。)
设置 NVIC 的优先级分组为 4 表示支持 0-15 级抢占优先级 (注意, 0-15 级是 16 个级别,包含 0 级), 不支持子优先级。
在这里继续强调下这一点,在 NVIC 分组为 4 的情况下,抢占优先级可配置范围是 0-15,那么数值越小,抢占优先级的级别越高,即 0 代表最高优先级,15 代表最低优先级。
5.FreeRTOS 配置选项中 NVIC 相关配置
/*
* Cortex-M内核使用8bit来配置优先级,但是STM32只使用了高4bit,数值越小,优先级越高。
* 在往寄存器里面写数值配置的时候,是按照8bit来写的,所以真正写的时候需要经过转换,公式为:
* ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff),其中的priority就是我们配置的真正的优先级
*/
#ifdef __NVIC_PRIO_BITS /* __NVIC_PRIO_BITS 已经在stm32f4xx.h里面定义为4 */
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4
#endif
/*============================================== SysTick中断优先级配置 ============================================*/
/*
* 在往寄存器里面写数值配置的时候,是按照8bit来写的,所以真正写的时候需要经过转换,公式为:
* ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff),其中的priority就是我们配置的真正的优先级。经过这个公式之后得到的是
* 下面的这个宏:configKERNEL_INTERRUPT_PRIORITY
* SysTick的优先级我们一般配置为最低,即0xf 。这样可以提高系统的实时响应能力,即其他的外部中断可以及时的得到响应。
*/
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/*===========================================可屏蔽的中断优先级配置====================================================*/
/*
* 用于配置STM32的特殊寄存器basepri寄存器的值,用于屏蔽中断,当大于basepri值的优先级的中断将被全部屏蔽。basepri只有4bit有效,
* 默认只为0,即全部中断都没有被屏蔽。configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY配置为:5,意思就是中断优先级大于5的中断都被屏蔽。
* 当把配置好的优先级写到寄存器的时候,是按照8bit来写的,所以真正写的时候需要经过转换,公式为:
* ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff),其中的priority就是我们配置的真正的优先级。经过这个公式之后得到的是下面的这个宏:
* configMAX_SYSCALL_INTERRUPT_PRIORITY
*
* 在FreeRTOS中,关中断是通过配置basepri寄存器来实现的,关掉的中断由配置的basepri的值决定,小于basepri值的
* 中断FreeRTOS是关不掉的,这样做的好处是可以系统设计者可以人为的控制那些非常重要的中断不能被关闭,在紧要的关头必须被响应。
* 而在UCOS中,关中断是通过控制PRIMASK来实现的,PRIMASK是一个单1的二进制位,写1则除能除了NMI和硬 fault的所有中断。当UCOS关闭
* 中断之后,即使是你在系统中设计的非常紧急的中断来了都不能马上响应,这加大了中断延迟的时间,如果是性命攸关的场合,那后果估计挺严重。
* 相比UCOS的关中断的设计,FreeRTOS的设计则显得人性化很多。
*
*/
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )