FreeRTOS学习笔记(四)
FreeRTOS学习笔记四在STM32F407ZGT6上实现不同优先级下任务切换前言在上一篇内容我们已经讨论同优先级下任务如何切换这篇内容中我们通过一个实例了解在freeRTOS系统中如何实现不同优先级下的任务切换。同时提供一些优先级控制函数的解析。FreeRTOS任务优先级在哪里定义FreeRTOS任务的优先级定义涉及系统级配置和任务创建时的参数指定两个层面。系统级优先级范围由FreeRTOSConfig.h中的宏定义决定而单个任务的优先级在调用任务创建函数时通过参数指定。下面举例说明为FreeRTOSConfig.h#defineconfigMAX_PRIORITIES(5)/* 定义最大优先级数, 最大优先级configMAX_PRIORITIES-1, 需定义 */freeRTOS.c/** * brief FreeRTOS入口函数 * param 无 * retval 无 */voidfreertos_demo(void){xTaskCreate((TaskFunction_t)Task1,/* 任务函数 */(constchar*)Task1,/* 任务名称 */(uint16_t)128,/* 任务堆栈大小 */(void*)NULL,/* 传入给任务函数的参数 */(UBaseType_t)4,/* 任务优先级 */(TaskHandle_t*)Task1Handle);/* 任务句柄 */}在上面的例子中我将configMAX_PRIORITIES设置为5那么在创建任务时任务的最高优先级只能为4configMAX_PRIORITIES -1。如果设置为5那么会导致优先级被截断为系统最高值新版还会触发断言失败以警示配置错误。什么是优先级反转FreeRTOS 系统如何解决优先级反转是一种常见且严重的问题即在多任务操作系统中低优先级的任务持有锁或资源导致高优先级任务被阻塞进而使得高优先级任务无法执行。这种情况可能导致系统的实时性能下降影响到任务的响应速度。为了解决这一问题freeRTOS提供了优先级继承和优先级天花板这两个机制即当一个数组正在被一个低优先级的任务写入时如果此时有个高优先级的任务要尝试读取这个变量那么这个高优先级的任务就会被阻塞。同时这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级这个过程就是优先级继承。任务天花板是指将申请某资源的任务的优先级提升到可能访问该资源的所有任务中最高优先级任务的优先级。需要指出的是优先级继承并不能完全的消除优先级翻转的问题它只是尽可能的降低优先级翻转带来的影响。实际应用应该在设计之初就要避免优先级翻转的发生。后续讨论信号量时会继续对这两个机制进行进一步的阐述这里先了解概念即可。如何动态调整任务优先级除了在创建任务时指定任务的优先级我们还可以在任务运行过程中通过uxTaskPriorityGet()这个函数来查询任务优先级并通过vTaskPrioritySet()函数改变任务的优先级。函数 uxTaskPriorityGet()UBaseType_tuxTaskPriorityGet(constTaskHandle_t xTask);函数 uxTaskPriorityGet()的形参描述如下表所示形参描述xTask待获取优先级的任务函数 uxTaskPriorityGet()的返回值如下表所示返回值描述整数指定任务的优先级函数 vTaskPrioritySet()此函数用于设置指定任务的优先级若使用此函数需在 FreeRTOSConfig.h 文件中设置配 置项 INCLUDE_vTaskPrioritySet 为 1此函数的函数原型如下所示voidvTaskPrioritySet(TaskHandle_t xTask,UBaseType_t uxNewPriority);函数 vTaskPrioritySet()的形参描述如下表所示形参描述xTask待设置优先级的任务uxNewPriority任务优先级函数 vTaskPrioritySet()无返回值。给一个小的实例来具体了解一下不同任务优先级下任务的切换。本实例通过appStartTask()函数创建两个具有任务任务1的优先级设置为3其功能是让LED0每秒闪烁1次并通过串口输出运行次数。当检测到WAKEUP按键被按下后将任务2的优先级提升到比任务1的优先级高一级。任务2的优先级为3其功能是让LED1每秒闪烁2次并通过串口输出运行次数。当检测到key0按键被摁下时将任务2的优先级降低到比任务1的优先级低一级。具体代码如下key.c/** * brief 按键扫描函数 * note 该函数有响应优先级(同时按下多个按键): WK_UP KEY2 KEY1 KEY0!! * param void * retval 键值, 定义如下: * KEY0_PRES, 1, KEY0按下 * KEY1_PRES, 2, KEY1按下 * KEY2_PRES, 3, KEY2按下 * WKUP_PRES, 4, WKUP按下 */uint8_tkey_scan(void){staticuint8_tkey_up1;/* 按键弹起标志 */if(key_up(KEY00||KEY10||KEY20||WK_UP1))/* 按键松开标志为1, 且有任意一个按键按下了 */{HAL_Delay(10);/* 去抖动 */key_up0;if(KEY00)returnKEY0_PRES;if(KEY10)returnKEY1_PRES;if(KEY20)returnKEY2_PRES;if(WK_UP1)returnWKUP_PRES;}elseif(KEY01KEY11KEY21WK_UP0)/* 没有任何按键按下, 标记按键松开 */{key_up1;}return0;/* 无按键被按下 */}apptask.c/*FreeRTOS配置*//* Led0Task 任务 配置 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务 */staticvoidLed0Task(void*pvParameters);/* 任务函数 */staticTaskHandle_t Led0TaskHandleNULL;/* 任务句柄 */#defineLed0Task_PRIO3/* 任务优先级 */#defineLed0Task_STK_SIZE128/* 任务堆栈大小 *//* Led1Task 任务 配置 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务 */staticvoidLed1Task(void*pvParameters);/* 任务函数 */staticTaskHandle_t Led1TaskHandleNULL;/* 任务句柄 */#defineLed1Task_PRIO3/* 任务优先级 */#defineLed1Task_STK_SIZE128/* 任务堆栈大小 *//******************************************************************************************************//** * brief appStartTask * param 无 * retval 无 */voidappStartTask(void){taskENTER_CRITICAL();/*进入临界段关中断*/xTaskCreate((TaskFunction_t)Led0Task,/* 任务函数 */(constchar*)Led0Task,/* 任务名称 */(uint16_t)Led0Task_STK_SIZE,/* 任务堆栈大小 */(void*)NULL,/* 传入给任务函数的参数 */(UBaseType_t)Led0Task_PRIO,/* 任务优先级 */(TaskHandle_t*)Led0TaskHandle);/* 任务句柄 */xTaskCreate((TaskFunction_t)Led1Task,/* 任务函数 */(constchar*)Led1Task,/* 任务名称 */(uint16_t)Led1Task_STK_SIZE,/* 任务堆栈大小 */(void*)NULL,/* 传入给任务函数的参数 */(UBaseType_t)Led1Task_PRIO,/* 任务优先级 */(TaskHandle_t*)Led1TaskHandle);/* 任务句柄 */taskEXIT_CRITICAL();/*退出临界段开中断*/vTaskStartScheduler();/*开启任务调度器*/}/** * brief Led0Task * param pvParameters : 传入参数 * retval 无 */staticvoidLed0Task(void*pvParameters){uint16_tcnt0;UBaseType_t uxPriority;while(1){cnt;LED0_TOGGLE();taskENTER_CRITICAL();/*进入临界段关中断*/printf(任务1LED0闪烁任务1运行次数为:%3d\r\n,cnt);uxPriorityuxTaskPriorityGet(NULL);if(key_scan()WKUP_PRES){printf(wake up按键按下提升任务1优先级到任务2之上\r\n);vTaskPrioritySet(Led1TaskHandle,uxPriority1);/*设置任务2优先级*/}taskEXIT_CRITICAL();/*退出临界段开中断*/HAL_Delay(500);}}/** * brief Led1Task * param pvParameters : 传入参数 * retval 无 */staticvoidLed1Task(void*pvParameters){uint16_tcnt0;UBaseType_t uxPriority;while(1){cnt;LED1_TOGGLE();taskENTER_CRITICAL();/*进入临界段关中断*/printf(任务2LED1闪烁任务2运行次数为:%3d\r\n,cnt);uxPriorityuxTaskPriorityGet(NULL);if(key_scan()KEY0_PRES){printf(key0按键按下降低任务2优先级到任务1之下\r\n);vTaskPrioritySet(Led1TaskHandle,uxPriority-1);/*设置任务2优先级*/}taskEXIT_CRITICAL();/*退出临界段开中断*/HAL_Delay(250);}}实例结果由于时间片被禁止所以一开始只有后创建的任务2在运行。按下KEY0后由于任务1的优先级比任务2高任务1获得CPU的使用权。按下WAKEUP键后任务2的优先级比任务高故任务2继续运行。