目录
学习目标:
学习笔记:
一、什么是挂起
二、任务挂起
三、任务恢复
四、中断中恢复任务
1、中断任务恢复函数
2、优先级说明
实验代码:
一、任务
二、中断
学习目标:
- 掌握任务的挂起与恢复
学习笔记:
一、什么是挂起
与“阻塞”状态下的任务一样, “挂起”状态下的任务不能 被选择进入运行状态,但处于挂起状态的任务 没有超时。 相反,任务只有在分别通过 vTaskSuspend() 和 xTaskResume() API 调用明确命令时 才会进入或退出挂起状态。
相当于将任务暂停,通过 vTaskSuspend()函数挂起任务后,任务移入挂起列表,不会在进入就绪状态。当 使用xTaskResume()后恢复任务,进入就绪态。
二、任务挂起
void vTaskSuspend( TaskHandle_t xTaskToSuspend );
必须将 INCLUDE_vTaskSuspend 定义为 1 才能使用此函数。
暂停任意任务。无论任务优先级如何,任务被暂停后将永远无法获取任何微控制器处理时间。
对 vTaskSuspend 的调用不会累积次数,例如:若在同一任务上调用 vTaskSuspend () 两次,将仍然仅需调用一次 vTaskResume (),即可准备完毕暂停的任务。
参数:
xTaskToSuspend | 被挂起的任务句柄。传递空句柄将导致调用任务被暂停。 |
三、任务恢复
void vTaskResume( TaskHandle_t xTaskToResume );
必须将 INCLUDE_vTaskSuspend 定义为 1 才能使用此函数。
恢复已挂起的任务。
由一次或多次调用 vTaskSuspend () 而挂起的任务可通过单次调用 vTaskResume () 重新运行。
参数:
xTaskToResume | 要恢复的任务句柄。 |
四、中断中恢复任务
1、中断任务恢复函数
BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume );
必须将 include_vTaskSuspend 和 INCLUDE_xTaskResumeFromISR 定义为 1 才能使用此函数。
可从 ISR 内调用的恢复挂起任务的函数。
由多次调用 vTaskSuspend() 中的一次调用挂起的任务可通过单次调用 xTaskResumeFromISR() 重新运行。
xTaskResumeFromISR() 通常被视为危险函数,因为其 操作未被锁定。 因此,如果中断可能在任务被挂起之前到达, 从而中断丢失, 则绝对不应使用该函数 来同步任务与中断。 可使用信号量, 或者最好是直达任务通知,来避免这种可能性。
参数:
xTaskToResume | 要恢复的任务句柄。 |
返回:
如果恢复任务导致上下文切换,则返回 pdTRUE,否则返回 pdFALSE。 ISR 使用此信息来确定 ISR 之后是否需要上下文切换。 |
2、优先级说明
以下转载于FreeRTOS官方网站:
原文链接:https://www.freertos.org/zh-cn-cmn-s/RTOS-Cortex-M3-M4.html
建议将所有优先级位都指定为抢占优先级位, 不保留任何优先级位作为子优先级位。 任何其他配置都会 使 configMAX_SYSCALL_interrupt_PRIORITY 设置与 分配给各个外设中断之间的直接关系复杂化。
如果使用 STM32 和 STM32 驱动器库, 请通过调用 NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 ) 来确保所有优先级位都被指定为抢占优先级位,这一步需要 在启动 RTOS 前完成。
在 ARM Cortex-M 核心中,优先级的数值越小, 则中断的逻辑优先级越高。 例如,一个 数值优先级被分配为 2 的中断的逻辑优先级高于一个 数值优先级被分配为 5 的中断。 这是 ARM Cortex-M 中断优先级机制中最有悖直觉之处, 因为这与大多数未使用 ARM Cortex-M3 的微控制器架构相反。在任务优先级中数值越大,任务优先级越高。
以 "FromISR" 结尾的 FreeRTOS 函数是中断安全的,但前提是 调用这些函数的中断的逻辑优先级不高于 configMAX_SYSCALL_INTERRUPT_PRIORITY 定义的优先级(configMAX_SYSCALL_INTERRUPT_PRIORITY 在 FreeRTOSConfig.h 头文件中定义)。 因此,对于任何使用一个 RTOS API 函数的中断服务程序, 必须为其手动设置为一个数值优先级, 这个值必须等于或大于 configMAX_SYSCALL_INTERRUPT_PRIORITY 设定 的值。 这确保了中断的逻辑优先级等于或小于 configMAX_SYSCALL_INTRUPT_PRIORITY 设置。
如上所述, 使用 RTOS API 中断服务程序的逻辑优先级需要等于或低于 configMAX_SYSCALL_INTRUPT_PRIORITY 的设置(逻辑优先级越低,则优先级数值越大)。CMSIS 和各微控制器制造商提供库函数,可用于设置中断的优先级。
configMAX_SYSCALL_INTERRUPT_PRIORITY 和 configKERNEL_INTERRUPT_PRIORITY 位于 FreeRTOSConfig.h 中,需要按照 ARM Cortex-M 核心的要求 为它们指定优先级值,即将优先级位移到 最高有效位。
在正点原子的例程中,FreeRTOS管理的优先级为5-15。5是最高优先级
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15 /* 中断最低优先级 */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 /* FreeRTOS可管理的最高中断优先级 */
实验代码:
一、任务
/*FreeRTOS配置*//* START_TASK 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define START_TASK_STACK_SIZE 128
#define START_TASK_PRIO 1
TaskHandle_t start_task_handler;void start_task( void * pvParameters );#define TASK1_STACK_SIZE 128
#define TASK1_PRIO 2
TaskHandle_t task1_handler;
void task1( void * pvParameters );#define TASK2_STACK_SIZE 128
#define TASK2_PRIO 3
TaskHandle_t task2_handler;
void task2( void * pvParameters );#define TASK3_STACK_SIZE 128
#define TASK3_PRIO 4
TaskHandle_t task3_handler;
void task3( void * pvParameters );
/******************************************************************************************************//*** @brief FreeRTOS例程入口函数* @param 无* @retval 无*/
void freertos_demo(void)
{xTaskCreate( (TaskFunction_t ) start_task,(char * ) "start_task",(configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,(void * ) NULL,(UBaseType_t ) START_TASK_PRIO,(TaskHandle_t * ) &start_task_handler );vTaskStartScheduler();
}void start_task( void * pvParameters )
{taskENTER_CRITICAL();/*进入临界区*/xTaskCreate( (TaskFunction_t ) task1,(char * ) "task1",(configSTACK_DEPTH_TYPE) TASK1_STACK_SIZE,(void * ) NULL,(UBaseType_t ) TASK1_PRIO,(TaskHandle_t * ) &task1_handler );xTaskCreate( (TaskFunction_t ) task2,(char * ) "task2",(configSTACK_DEPTH_TYPE) TASK2_STACK_SIZE,(void * ) NULL,(UBaseType_t ) TASK2_PRIO,(TaskHandle_t * ) &task2_handler ); xTaskCreate( (TaskFunction_t ) task3,(char * ) "task3",(configSTACK_DEPTH_TYPE) TASK3_STACK_SIZE,(void * ) NULL,(UBaseType_t ) TASK3_PRIO,(TaskHandle_t * ) &task3_handler ); vTaskDelete(NULL);taskEXIT_CRITICAL(); /*退出临界区*/
}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){vTaskSuspend(task1_handler);}else if(key == KEY1_PRES){vTaskResume(task1_handler);printf("在任务中恢复task1\r\n");}vTaskDelay(10);}
}
二、中断
/*** @brief 外部中断初始化程序* @param 无* @retval 无*/
void extix_init(void)
{GPIO_InitTypeDef gpio_init_struct;KEY2_GPIO_CLK_ENABLE(); /* KEY2时钟使能 */WKUP_GPIO_CLK_ENABLE(); /* WKUP时钟使能 */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); HAL_NVIC_EnableIRQ(KEY2_INT_IRQn); /* 使能中断线15 */
}/*** @brief 中断服务程序中需要做的事情在HAL库中所有的外部中断服务函数都会调用此函数* @param GPIO_Pin:中断引脚号* @retval 无*/
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;}
}