🎀 文章作者:二土电子
🐸 期待大家一起学习交流!
一、任务挂起和恢复API函数
在项目中有时我们会遇到某些任务需要暂停一段时间,过一段时间在重新运行的情况。任务挂起和恢复就满足了这种需求。当某个任务需要暂停一段时间时,可以将其挂起。当需要重新运行时,再恢复就可以了。。FreeRTOS 的任务挂起和恢复API函数如下
1.1 vTaskSuspend()函数
该函数的功能是于将某个任务设置为挂起态。任务进入挂起态后,就永远都不会进入运行态。除非调用任务恢复函数 vTaskResume()或 xTaskResumeFromISR()。任务挂起函数有以下输入参数
- xTaskToSuspend
要挂起的任务的任务句柄。如果需要挂起任务自身,该参数写NULL。
任务挂起函数无返回值。
1.2 vTaskResume()函数
该函数的功能是将一个任务从挂起态恢复到就绪态。任务恢复函数有以下输入参数
- xTaskToResume
需要恢复的任务的任务句柄。
任务恢复函数也没有返回值。
1.3 xTaskResumeFromISR()函数
该函数的功能是在中断服务函数中恢复一个任务。该函数有以下输入参数
- xTaskToResume
要恢复的任务的任务句柄。
该函数有以下返回值
- pdTRUE
恢复运行的任务的任务优先级等于或者高于正在运行的任务(被中断打断的任务),这意味着在退出中断服务函数以后必须进行一次上下文切换。 - pdFALSE
恢复运行的任务的任务优先级低于当前正在运行的任务(被中断打断的任务),这意味着在退出中断服务函数的以后不需要进行上下文切换。二、任务挂起和恢复
2.1 任务1挂起解挂任务2
任务创建可以见上一篇,任务创建与删除。这里直接修改任务1。任务1修改为,运行10次任务1之后,将任务2挂起。再运行10次后,解挂任务2。任务1函数如下
void taks1_task (void *pxCreatedTask)
{
u8 task1Cunt = 0; // 任务1运行次数计数变量
while (1)
{
task1Cunt = task1Cunt + 1; // task1运行次数加1
Med_Led_StateReverse(LED0); // LED0状态取反
vTaskDelay(500); // 延时500ms
// 运行5次后
if (task1Cunt == 10)
{
// 挂起任务2
vTaskSuspend(TASK2Task_Handler);
}
if (task1Cunt == 20)
{
// 解挂任务2
vTaskResume(TASK2Task_Handler);
}
}
}
2.2 中断中解挂任务1
最开始尝试在外部中断中挂起和解挂任务1,发现挂起任务1时,程序异常。查询后发现,在中断中不能使用挂起函数。
因此调整了一下,任务2运行5次后,挂起任务1。通过外部中断,解挂任务1。
在中断中解挂任务时注意,需要判断一下函数xTaskResumeFromISR()的返回值。如果函数xTaskResumeFromISR()返回值为pdTRUE,那么说明要恢复的这个任务的任务优先级等于或者高于正在运行的任务(被中断打断的任务),所以在退出中断的时候一定要进行上下文切换
。
外部中断程序配置如下
/*
*==============================================================================
*函数名称:Exit_Init
*函数功能:初始化外部中断
*输入参数:无
*返回值:无
*备 注:无
*==============================================================================
*/
void Exit_Init (void)
{
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); // 开启AFIO时钟
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); //选择GPIO管脚用作外部中断线路
//EXTI0 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //EXTI0中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=6; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
EXTI_InitStructure.EXTI_Line=EXTI_Line0; // EXIT0
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt; // 中断
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising; // 上升沿触发
EXTI_InitStructure.EXTI_LineCmd=ENABLE; // 使能
EXTI_Init(&EXTI_InitStructure);
}
外部中断的中断服务函数如下
/*
*==============================================================================
*函数名称:EXTI0_IRQHandler
*函数功能:外部中断0中断服务函数
*输入参数:无
*返回值:无
*备 注:无
*==============================================================================
*/
// 任务1的任务句柄
extern TaskHandle_t TASK1Task_Handler;
void EXTI0_IRQHandler(void)
{
BaseType_t YieldRequired;
// 如果EXIT0中断标志位被置1
if(EXTI_GetITStatus (EXTI_Line0)==1)
{
YieldRequired = xTaskResumeFromISR(TASK1Task_Handler); // 恢复任务1
if(YieldRequired == pdTRUE)
{
// 执行一次任务调度
portYIELD_FROM_ISR(YieldRequired);
}
}
EXTI_ClearITPendingBit (EXTI_Line0); // 清除中断标志位
}
三、补充内容
3.1 FreeRTOS数据类型
在portmacro.h头文件,里面定义了2个数据类型
- TickType_t
FreeRTOS中断计数值类型,可以是16位也可以是32位,对于32位CPU来说TickType_t最好为32位。 - BaseType_t
能够让CPU运行效率最高的数据类型。对于32位CPU,BaseType_t就是uint32_t 。16位CPU就是uint16_t ,8位CPU就是uint8_t 。3.2 中断优先级分组
优先级分组:中断控制器(NVIC)允许定义每个中断优先级的比特被分割成定义中断的优先级比特和定义中断的次优先级比特。为简单起见,必须将所有位定义为抢占优先位。如果不是这样(如果某些位表示次优先级),下面的断言将失败。
简单来说,就是在设置优先级分组时,需要将全部的位都设置为抢占优先级。否则会导致程序异常。
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); // 设置系统中断优先级分组4
3.3 错误问题
在编译时发现有下面的错误提示
该错误是因为“task.h”必须出现在“FreeRTOS.h”下面。