FreeRTOS中断管理
宏configMAX_SYSCALL_INTERRUPT_PRIORITY
上次在讲临界区的时候提到了这个宏,下面来解释一下。
低于此优先级的中断可以安全调用FreeRTOS的API函数,高于此优先级的中断是FreeRTOS不能禁止的,中断服务函数也不能调用FreeRTOS的API函数!
如以STM32为例,设置NVIC优先级分组为4,即有16个抢占优先级,0最高,15最低,设置
configMAX_SYSCALL_INTERRUPT_PRIORITY
为5,则:
优先级0-4不会被FreeRTOS禁止,中断不可以调用FreeRTOS的API函数。
优先级5-15可以调用以FROM_ISR结尾的API函数,并且它们可以中断嵌套。
下面通过实验来验证一下。
FreeRTOS中断测试实验
实验目的
验证上述的
configMAX_SYSCALL_INTERRUPT_PRIORITY
作用。使用两个定时器,一个优先级为4,一个优先级为5,两个定时器每隔1s通过串口输入一段字符串。然后在某个任务中关闭中断一段时间,查看两个定时器的输出情况。
实验设计
设计两个任务
startTask()
和
interruptTask()
,功能如下:
startTask()
:创建另外一个任务。
interruptTask()
:中断测试任务,任务中会调用关闭中断函数
portDISABLE_INTERRUPTS()
来关闭中断一段时间。
TIM3和TIM5配置不再赘述,注意抢占优先级的设置,TIM3为4,TIM5为5(因为我们设置的configMAX_SYSCALL_INTERRUPT_PRIORITY为5)。
main函数代码如下:
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "timer.h"
#include "FreeRTOS.h"
#include "task.h"
/*********************任务设置区*********************/
#define startTask_STK_SIZE 120 //开始任务的堆栈大小,实际大小为120*4
#define startTask_PRIO 1 //空闲任务的优先级为0,一般不用0,数字越大,优先级越高
void startTask(void *pvParameters); //任务函数
TaskHandle_t startTask_Handler; //任务句柄,别的任务通过句柄调用该任务
#define intrruptTask_STK_SIZE 256 //开始任务的堆栈大小,实际大小为120*4
#define intrruptTask_PRIO 2 //空闲任务的优先级为0,一般不用0,数字越大,优先级越高
void intrruptTask(void *pvParameters); //任务函数
TaskHandle_t intrruptTask_Handler; //任务句柄,别的任务通过句柄调用该任务
/***************************************************/
int main(void){
/*变量声明区*/
u16 arr = 9999; //计数值10000,为1s
u16 psc = 7199; //72MHz时钟,分频系数7200,计数频率为10kHz
/*资源初始化*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
delay_init();
uart_init(115200);
LED_Init();
TIM3_Int_Init(arr,psc); //设置计时1s
TIM5_Int_Init(arr,psc);
/*任务调度区*/
xTaskCreate(
(TaskFunction_t ) startTask,
(const char * ) "startTask", //任务名
(uint16_t ) startTask_STK_SIZE, //任务堆栈大小
(void * ) NULL, //任务输入参数
(UBaseType_t ) startTask_PRIO, //任务优先级
(TaskHandle_t * ) &startTask_Handler //任务句柄
);
vTaskStartScheduler(); //启用任务调度
}
/*startTask*/
void startTask(void *pvParameters){
taskENTER_CRITICAL();
xTaskCreate(
(TaskFunction_t ) intrruptTask,
(const char * ) "intrruptTask", //任务名
(uint16_t ) intrruptTask_STK_SIZE, //任务堆栈大小
(void * ) NULL, //任务输入参数
(UBaseType_t ) intrruptTask_PRIO, //任务优先级
(TaskHandle_t * ) &intrruptTask_Handler //任务句柄
);
vTaskDelete(startTask_Handler);
taskEXIT_CRITICAL();
}
/*intrruptTask*/
void intrruptTask(void *pvParameters){
static u32 cnt = 0;
while(1){
cnt += 1;
if(cnt == 5){
printf("关闭中断\r\n");
portDISABLE_INTERRUPTS();
delay_xms(5000); //注意这里!不能调用vTaskDelay()!!!!
printf("开启中断\r\n");
portENABLE_INTERRUPTS();
}
vTaskDelay(1000);
}
}
值得注意的是:在
intrruptTask()
中,关闭中断和开启中断之间的延时不能调用vTaskDelay()!
关闭中断和开启中断之间的延时不能调用vTaskDelay()!
关闭中断和开启中断之间的延时不能调用vTaskDelay()!
关闭中断和开启中断之间的延时不能调用vTaskDelay()!
因为
vTaskDelay()
会引起任务调度,可能会调度到含有开启中断的任务,所以这里不能使用FreeRTOS自带的
vTaskDelay()
,应使用内核的延时函数。