FreeRTOS断言"freertos\portable\rvds\arm_cm3\port.c,244"的解决方法
这两天刚上手FreeRTOS,写程序的时候,中途编译烧录调试,代码很简单,我也感觉没啥问题,但是在串口中打印了断言freertos\portable\rvds\arm_cm3\port.c,244
,检查代码多次未能解决问题,网上也只有一篇文章提到这个问题,最终我在正点原子的论坛上找到了答案。
代码的内容就是:
两个定时器中断TIM3和TIM5。TIM3优先级高于configMAX_SYSCALL_INTERRUPT_PRIORITY
,即该中断不能被FreeRTOS的portDISABLE_INTERRUPTS()
函数停止;TIM5优先级则低于configMAX_SYSCALL_INTERRUPT_PRIORITY
,即它可以被停止。
每隔1s向串口打印信息,然后在任务中调用portDISABLE_INTERRUPTS()
关闭中断,那么TIM3应该能继续输出,而TIM5则不能。
在代码中,建立了两个任务,一个是开始任务startTask()
,一个是关中断的任务intrruptTask()
,在开始任务中创建关中断的任务。
重点来了,我写到一半的时候,直接写了个intrruptTask()
的空函数放那了,里面没有写死循环。这时烧录上去,串口就会打印断言freertos\portable\rvds\arm_cm3\port.c,244
,在intrruptTask()
里写一个while(1)
即可解决。
问题解决了,可是为什么会这样?
先放出我的代码:
/*********************任务设置区*********************/
#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 120 //开始任务的堆栈大小,实际大小为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){
//while(1); //若此不加while(1),则会打印上述断言
}
首先打印了一次的TIM3和TIM5,然后打印断言,然后打印TIM3。
我们再看port.c,244
的源码,是一个函数:
static void prvTaskExitError( void )
{
/* A function that implements a task must not exit or attempt to return to
its caller as there is nothing to return to. If a task wants to exit it
should instead call vTaskDelete( NULL ).
在执行任务时,不能退出或尝试返回到调用方,因为它没有需要返回的内容。
如果此时需要退出任务,可以调用vTaskDelete( NULL )。
Artificially force an assert() to be triggered if configASSERT() is
defined, then stop here so application writers can catch the error. */
configASSERT( uxCriticalNesting == ~0UL );
portDISABLE_INTERRUPTS();
for( ;; );
}
分析源码,可知
在出现问题的时候,该函数会先打印断言,然后调用portDISABLE_INTERRUPTS()
关闭中断,由于TIM5可以被关闭,所以后面不会再打印TIM5,TIM3不会被关闭,则后面会继续打印TIM3。
根据注释,可分析得,在运行intrruptTask()
的时候,由于没有死循环,运行一遍后需要返回,而正如注释所言:“在执行任务时,不能退出或尝试返回到调用方,因为它没有需要返回的内容。”所以会报错,解决方法就是加上死循环,这也就是为什么任务里都要有死循环。
当然你也可以不加死循环,直接调用vTaskDelete( NULL )
把任务删了(NULL就是删除自己),也不会报错,比如这样:
void intrruptTask(void *pvParameters){
/*你的任务代码*/
vTaskDelete(NULL);
}