一、任务挂起于恢复的API函数
API函数 | 描述 |
vTaskSuspend() | 挂起任务 |
vTaskResume() | 恢复被挂起的任务 |
xTaskResumeFromISR() | 在中断中恢复被挂起的任务 |
挂起:挂起任务类似暂停,可恢复; 删除任务,无法恢复。
恢复:恢复被挂起的任务。
“FromISR”:带FromISR后缀是在中断函数中专用的API函数。
任务挂起函数介绍:
void vTaskSuspend(TaskHandle_t xTaskToSuspend)
形参 | 描述 |
xTaskSuspend | 待挂起任务的任务句柄 |
无论优先级如何,被挂起的任务都将不再被执行,直到任务被恢复(调用恢复函数) 。
此函数用于挂起任务,使用时需将宏 INCLUDE_vTaskSuspend 配置为 1(config.h)。
注意:当传入的参数为NULL,则代表挂起任务自身(当前正在运行的任务)
任务恢复函数介绍(任务中恢复):
任务中恢复被挂起函数:void vTaskResume(TaskHandle_t xTaskToResume)
形参 | 描述 |
xTaskResume | 待恢复任务的任务句柄 |
使用该函数注意宏:INCLUDE_vTaskSuspend必须定义为 1。
注意:任务无论被 vTaskSuspend() 挂起多少次,只需在任务中调用 vTakResume() 恢复一次,就可以继续运行。且被恢复的任务会进入就绪态!
任务恢复函数介绍(中断中恢复):
中断中恢复被挂起函数: BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume)
形参 | 描述 |
xTaskToResume | 待恢复任务的任务句柄 |
函数:xTaskResumeFromISR返回值描述如下:
返回值 | 描述 |
pdTRUE | 任务恢复后需要进行任务切换 |
pdFALSE | 任务恢复后不需要进行任务切换 |
调用时注意返回值
使用该函数注意宏:INCLUDE_vTaskSuspend和INCLUDE_xTaskResumeFromISR必须定义为 1
该函数专用于中断服务函数中,用于解挂被挂起任务
注意:中断服务程序中要调用freeRTOS的API函数则中断优先级不能高于FreeRTOS所管理的最高优先级(5~15,中断服务优先级要在此范围内)
二、任务挂起与恢复实验
exit.c
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
delay_ms(20); /* лֶ */
switch(GPIO_Pin)
{
BaseType_t xYieldRequired;
case KEY2_INT_GPIO_PIN:
if (KEY2 == 0)
{
xYieldRequired = xTaskResumeFromISR(task1_handler);
printf("在中断中恢复task1\r\n");
}
if(xYieldRequired == pdTRUE)
{
portYIELD_FROM_ISR(xYieldRequired);
}
break;
default : break;
}
}
void extix_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
key_init();
gpio_init_struct.Pin = KEY2_INT_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; /* 下降沿触发 */
gpio_init_struct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(KEY2_INT_GPIO_PORT, &gpio_init_struct); /* KEY2配置为下降沿触发中断*/
HAL_NVIC_SetPriority(KEY2_INT_IRQn, 5, 0); /* 抢占5,子优先级0 */
HAL_NVIC_EnableIRQ(KEY2_INT_IRQn); /* 使能中断线2 */
}
//判断按键按下逻辑,KEY0按下,挂起task1,按下KEY1在任务中恢复task1
按下KEY2,在中断中恢复task1(外部中断线实现):
void task1( void * pvParameters )
{
uint32_t task1_num = 0;
while(1)
{
printf("task1_num:%d\r\n",++task1_num);
LED0_TOGGLE();
vTaskDelay(500);
}
}
void task2( void * pvParameters )
{
uint32_t task2_num = 0;
while(1)
{
printf("task2_num:%d\r\n",++task2_num);
LED1_TOGGLE();
vTaskDelay(500);
}
}
void task3( void * pvParameters )
{
uint8_t key = 0;
while(1)
{
key = key_scan(0);
if(key == KEY0_PRES)
{
printf("在中断中恢复task1\r\n");
vTaskSuspend(task1_handler);
}else if(key == KEY1_PRES)
{
printf("在中断中恢复task1\r\n");
vTaskResume(task1_handler);
}
vTaskDelay(10);
}
}
main.c
int main(void)
{
HAL_Init(); //初始化HAL库
sys_stm32_clock_init(360, 25, 2, 8);//初始化时钟,180Mhz
delay_init(180);//延时初始化
usart_init(115200);//串口初始化为115200
led_init();初始化LED
key_init();//初始化按键
sdram_init();//SRAM初始化
lcd_init();//初始化LCD
extix_init();//外部中断初始化
my_mem_init(SRAMIN);//初始化内部内存池
my_mem_init(SRAMEX);//初始化外部内存池
my_mem_init(SRAMCCM);//初始化CCM内存池
freertos_demo();
}
使用实时操作系统时的相关性
建议将所有优先级设置为优先级位,不留下任何优先级作为子优先级位,方便FreeRTOS管理。任何其他配置都会使configMAX_SYSCALL_INTERRUPT_PRIORITY设置与分配给各个外设中断的优先级之间的直接关系复杂化。
大多数系统默认为所需配置,但STM32驱动程序库明显例外。如果将STM32与STM32驱动程序一起使用,则在启动RTOS之前,通过调用(NVIC_PriorityGroup_4);来确保将所有优先级位分配为抢占优先级位。
以“FromlSR”结尾的FreeRTOS函数是中断安全的,但即使是这些函数也不能从逻辑优先级高于COnfGMAX SYSCALL INTERRUPT PRIORITY定义的优先级的中断中调用(confGMAX SYSCALL INTERRUPT PRIORITY在FreeRTOSConfg.h头文件中定义)。因此,任何使用 RTOS AP!函数的中断服务例程都必须将其优先级手动设置为数值上等于或大于 configMAX SYSCALL INTERRUPT PRIORITY 设置的值。这可确保中断的逻辑优先级等于或小于(5~15)configMAX SYSCALL INTERRUPT PRIORITY设置。
Cortex-M 中断默认为优先级值为零。零是可能的最高优先级值。因此,切勿将使用中断安全 RTOS API的中断的优先级保留为其默认值。
三、任务挂起和恢复API函数解析
#if ( INCLUDE_vTaskSuspend == 1 )
void vTaskSuspend( TaskHandle_t xTaskToSuspend )//挂起哪个任务,带入哪个任务的句柄,也就是1:根据任务句柄获取控制块
{
TCB_t * pxTCB;
taskENTER_CRITICAL();
{
/* If null is passed in here then it is the running task that is
* being suspended. */
pxTCB = prvGetTCBFromHandle( xTaskToSuspend );//返回控制块
traceTASK_SUSPEND( pxTCB );
/* Remove task from the ready/delayed list and place in the
* suspended list. */
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )//移除当前任务不论什么状态
{
taskRESET_READY_PRIORITY( pxTCB->uxPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* Is the task waiting on an event also? */
if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )//移除不论之前处于何种状态的任务
{
( void ) uxListRemove( &( pxTCB->xEventListItem ) );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );将移除的任务重新插入另外一个列表(尾部插入)
#if ( configUSE_TASK_NOTIFICATIONS == 1 )//消息通知
{
BaseType_t x;
for( x = 0; x < configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ )//遍历消息通知
{
if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION )
{
/* The task was blocked to wait for a notification, but is
* now suspended, so no notification was received. */
pxTCB->ucNotifyState[ x ] = taskNOT_WAITING_NOTIFICATION;
}
}
}
#endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */
}
taskEXIT_CRITICAL();
if( xSchedulerRunning != pdFALSE )//调度器是否正在运行,不为pdFALSE则正在运行
{
/* Reset the next expected unblock time in case it referred to the
* task that is now in the Suspended state. */
taskENTER_CRITICAL();
{
prvResetNextTaskUnblockTime();//更新下一个阻塞超时时间
}
taskEXIT_CRITICAL();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
if( pxTCB == pxCurrentTCB )//判断要挂起的是不是任务自己
{
if( xSchedulerRunning != pdFALSE )
{
/* The current task has just been suspended. */
configASSERT( uxSchedulerSuspended == 0 );
portYIELD_WITHIN_API();//执行任务切换
}
else
{
/* The scheduler is not running, but the task that was pointed
* to by pxCurrentTCB has just been suspended and pxCurrentTCB
* must be adjusted to point to a different task. */
if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks )//判断当前挂起任务总数是不是等于总任务数,是则代表所有任务都被挂起
/*lint !e931 Right has no side effect, just volatile. */
{
/* No other tasks are ready, so set pxCurrentTCB back to
* NULL so when the next task is created pxCurrentTCB will
* be set to point to it no matter what its relative priority
* is. */
pxCurrentTCB = NULL;
}
else
{
vTaskSwitchContext();//查找下一个最高优先级任务
}
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* INCLUDE_vTaskSuspend */
#if ( INCLUDE_vTaskSuspend == 1 )
void vTaskResume( TaskHandle_t xTaskToResume )
{
TCB_t * const pxTCB = xTaskToResume;
/* It does not make sense to resume the calling task. */
configASSERT( xTaskToResume );
/* The parameter cannot be NULL as it is impossible to resume the
* currently executing task. */
if( ( pxTCB != pxCurrentTCB ) && ( pxTCB != NULL ) )
{
taskENTER_CRITICAL();
{
if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE )//判断任务是否在挂起列表中
{
traceTASK_RESUME( pxTCB );
/* The ready list can be accessed even if the scheduler is
* suspended because this is inside a critical section. */
( void ) uxListRemove( &( pxTCB->xStateListItem ) );//移出列表
prvAddTaskToReadyList( pxTCB );//添加到就序列表
/* A higher priority task may have just been resumed. */
if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )//判断任务优先级是否大于当前正在运行任务优先级,是则执行任务切换
{
/* This yield may not cause the task just resumed to run,
* but will leave the lists in the correct state for the
* next yield. */
taskYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
taskEXIT_CRITICAL();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* INCLUDE_vTaskSuspend */
#if ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) )
BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume )
{
BaseType_t xYieldRequired = pdFALSE;
TCB_t * const pxTCB = xTaskToResume;
UBaseType_t uxSavedInterruptStatus;//保存中断状态
configASSERT( xTaskToResume );
portASSERT_IF_INTERRUPT_PRIORITY_INVALID();//用于检测:调用FreeRTOS的API函数的中断优先级是否在管理范围内以及是否全部设置为抢占优先级位
uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
{
if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE )
{
traceTASK_RESUME_FROM_ISR( pxTCB );
/* Check the ready lists can be accessed. */
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
{
/* Ready lists can be accessed so move the task from the
* suspended list to the ready list directly. */
if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
{
xYieldRequired = pdTRUE;
/* Mark that a yield is pending in case the user is not
* using the return value to initiate a context switch
* from the ISR using portYIELD_FROM_ISR. */
xYieldPending = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
( void ) uxListRemove( &( pxTCB->xStateListItem ) );
prvAddTaskToReadyList( pxTCB );
}
else
{
/* The delayed or ready lists cannot be accessed so the task
* is held in the pending ready list until the scheduler is
* unsuspended. */
vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) );
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
return xYieldRequired;
}
#endif /* ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) */
总结