我们要知道,当我们对一个任务进行挂起和恢复的时候,任务的数据是不会被删除的,方便应用程序接着操作之前的数据上下文继续运行。
1.xTaskSuspend() 我们来简单看一下挂起函数的定义中的解释: 把里面的英文翻译了一下,大概就是:要使得这个函数有效,在FreeRTOSConfig.h中INCLUDE_vTaskSuspend 必须定位为1。无论一个任务是什么优先级,当被挂起的时候,处理器都不会再运行这个函数,Suspend这个函数不能连续的调用,挂起一个任务只需要调用一次vTaskSuspend()就够了,调用vTaskResume () 让一个任务进入就绪态,参数为NULL的时候将会挂起本身, 参数为句柄是将会挂起句柄所代表的函数。
/**
* task. h
* INCLUDE_vTaskSuspend 必须定位为1,当这个函数有效的时候
*当一个任务被挂起的时候,无论它是什么优先级,都不会再去占用系统处理时间
*不能连续的调用Suspend这个函数
* 挂起一个任务只需要调用一次vTaskSuspend()就够了
* 调用vTaskResume () 让一个任务进入就绪态
* 参数为NULL的时候将会挂起本身
* 参数为句柄是将会挂起句柄所代表的函数
* 示例用法:
void vAFunction( void )
{
TaskHandle_t xHandle;
// 创建任务
xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
// ...
// 挂起任务
vTaskSuspend( xHandle );
// ...
//这个被挂起的任务将不会再运行,直到另外一个调用vTaskResume(xHandle)
//...
// 挂起任务本身
vTaskSuspend( NULL );
// 程序不会运行到这里除非有一个任务调用了vTaskResume并且把当前任务的句柄当作参数传递进去
}
*/
//这里是函数的定义
void vTaskSuspend( TaskHandle_t xTaskToSuspend ) PRIVILEGED_FUNCTION;
2.xTaskResume() 再看一下resume函数的源码:
/**
* task. h
* 要使得这个函数有效在FreeRTOSConfig.h中INCLUDE_vTaskSuspend 必须定位为1
*恢复一个任务调用一次vTaskSuspend ()就行了,不用疯狂调用,试验了一下下,疯狂调用会怎么样,和想象中系统奔溃,地球毁灭的场景不一样,想反系统正常运行中
* 参数xTaskToResume 要进入准备态的任务的任务句柄
void vAFunction( void )
{
TaskHandle_t xHandle;
// 创建一个任务,存储句柄
xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
// ...
// 使用句柄去挂起任务
vTaskSuspend( xHandle );
// ...
//这个被挂起的任务将不会再运行,直到另外一个调用vTaskResume(xHandle)
//...
//自行恢复暂停的任务
vTaskResume( xHandle );
// 这个任务将会再一次得到系统处理
// 在系统的优先级里将会一致
}
//函数声明
void vTaskResume( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION;
3.xTaskResumeFromISR() 这个是从一个中断服务函数(ISR)中恢复暂停的任务,我们来看一下函数的定义,这个函数有一个返回值,pdTRUE或者pdFALSE,如果是TRUE就意味着唤醒的任务的优先级高于当前任务的优先级,此时的中断服务函数就会被打断。
/**
* 要使得这个函数有效在FreeRTOSConfig.h中INCLUDE_xTaskResumeFromISR 必须定位为1
* xTaskResumeFromISR()这个函数可以在ISR中调用.
* 一个任务被挂起通过xTaskSuspend() 函数后,通过xTaskResumeFromISR ()函数能够再次的运行。
*不应使用xtaskresumefromisr()将任务与如果中断可能在运行,此时任务被挂起会导致错过中断。使用同步机制的信号量可以避免这种可能性。
* @param xTaskToResume 需要进入就绪态的任务句柄
* @return pdTRUE 任务的结果应该是一个可以切换的上下文
* otherwise pdFALSE. ISR使用它来确定ISR之后是否需要上下文切换。
*/
函数声明
BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION;
测试代码
第一步初始化按键,中断服务函数,LED等:
uart_init(115200);
LED_Init();
KEY_Init();
EXTIX_Init();
第二步声明定义并创建任务:
//任务优先级
#define KEY0_TASK_PRIO 2
//堆栈大小
#define KEY0_STK_SIZE 50
//句柄 handler
TaskHandle_t KEY0Task_Handler;
//函数
void key0_task(void *pvParameters);
//优先级
#define KEY1_TASK_PRIO 3
//大小
#define KEY1_STK_SIZE 50
//句柄
TaskHandle_t KEY1Task_Handler;
//函数
void key1_task(void *pvParameters);
//优先级
#define FLOAT_TASK_PRIO 4
//?堆栈大小
#define FLOAT_STK_SIZE 128
//处理句柄
TaskHandle_t FLOATTask_Handler;
//函数
void float_task(void *pvParameters);
//KEY任务
xTaskCreate((TaskFunction_t )key0_task,
(const char* )"key0_task",
(uint16_t )KEY0_STK_SIZE,
(void* )NULL,
(UBaseType_t )KEY0_TASK_PRIO,
(TaskHandle_t* )&KEY0Task_Handler);
//KEY1任务
xTaskCreate((TaskFunction_t )key1_task,
(const char* )"key1_task",
(uint16_t )KEY1_STK_SIZE,
(void* )NULL,
(UBaseType_t )KEY1_TASK_PRIO,
(TaskHandle_t* )&KEY1Task_Handler);
第三步编写服务函数:
//KEY0任务函数
void key0_task(void *pvParameters)
{
u8 key;
u16 num = 0;
while(1)
{
key = KEY_Scan(0); //扫描按键
switch(key){
case KEY1_PRES:
vTaskSuspend(KEY1Task_Handler);
printf("suspend the task2");
break;
case KEY2_PRES:
vTaskResume(KEY1Task_Handler);
printf("resume the task1");
break;
}
vTaskDelay(10);
num++;
if(num>100){
printf("TASK1\n");
num = 0;
}
}
}
//KEY1任务函数
void key1_task(void *pvParameters)
{
while(1)
{
printf("TASK2\n");
vTaskDelay(1000);
}
}
第四步开启任务调度:
vTaskStartScheduler(); //开启任务调度
总结
对于任务的挂起与恢复这个不算难点,就三个函数,其中值得注意的是第三个函数xTaskResumeFromISR(),从中断中唤起任务,是从中断中唤醒的哦,其存在的一个问题就是,既然是从中断中唤醒任务,任务的优先级就是一个问题,如果任务的优先级比中断函数的要低倒是没多大是事情,唤醒你之后我继续干我自己的事情去呗,但是一旦唤醒的这个任务比自己要牛逼,这个时候自己就不得不让出地方,让这个任务运行,尴尬的就在这里,中断服务被硬生生的打断了,后续的动作无法执行了,RTOS给出了解决方案,就是切换上下文,具体是什么意思,怎么做还不清楚,我们继续往后学习~