前言
本篇文章将带大家学习FreeRTOS中的任务优先级,并且了解什么是任务优先级,
一、什么是任务优先级
在FreeRTOS中,任务优先级用于确定任务的执行顺序和调度顺序。任务优先级越高,任务被调度执行的频率越高。FreeRTOS支持使用整数值来表示任务优先级,整数值越高,优先级越高。
FreeRTOS中任务优先级的范围通常是从0到configMAX_PRIORITIES-1,其中configMAX_PRIORITIES是一个配置常量,表示系统中支持的最大任务优先级数量。
在FreeRTOSconfig.h中可以查看到任务优先级的配置宏:
二、FreeRTOS如何分辨出优先级最高可运行的任务
1.通用方法:
使用C函数实现,对所有的架构都是同样的代码。对configMAX_PRIORITIES的取值没有限制。但
是configMAX_PRIORITIES的取值还是尽量小,因为取值越大越浪费内存,也浪费时间。
configUSE_PORT_OPTIMISED_TASK_SELECTION被定义为0、或者未定义时,使用此方法。
2.架构相关的优化的方法:
架构相关的汇编指令,可以从一个32位的数里快速地找出为1的最高位。使用这些指令,可以快速
找出优先级最高的、可以运行的任务。
使用这种方法时,configMAX_PRIORITIES的取值不能超过32。
configUSE_PORT_OPTIMISED_TASK_SELECTION被定义为1时,使用此方法
三、FreeRTOS中的时钟节拍Tick
FreeRTOS中的时钟节拍(Tick)是一个基本的时间单位,用于管理任务调度和时间相关功能。时钟节拍的概念在实时操作系统中非常重要,因为它提供了时间的基本度量单位,允许任务和定时器按照预定的时间间隔执行。
以下是有关FreeRTOS中时钟节拍的重要信息:
1.时钟节拍的单位:时钟节拍是一个抽象的时间单位,通常表示为一个整数值。它可以映射到实际时间,但时钟节拍的精度和长度是可配置的。在大多数情况下,时钟节拍是以毫秒为单位的,但它可以配置为更短或更长的时间间隔,具体取决于应用程序的需求。
2.时钟节拍计数器:FreeRTOS维护一个时钟节拍计数器,用于跟踪已经过去的时钟节拍数。这个计数器通常是一个32位的值,可以支持大范围的时间跟踪。
3.任务调度:任务的调度和切换通常发生在时钟节拍的倍数上。当时钟节拍计数器达到任务的时间间隔(通常称为任务的延迟或周期)时,任务可能会被置于就绪状态,以便在下一个任务切换点执行。
4.定时器:时钟节拍也用于定时器功能。通过设置定时器的周期为时钟节拍的倍数,可以实现定时任务和事件的触发。
5.配置和定制:FreeRTOS允许用户配置时钟节拍的频率和精度,以适应特定应用的需求。这可以通过修改FreeRTOS配置文件中的参数来完成。例如,可以配置时钟节拍为1毫秒,10毫秒,甚至更短的时间间隔,以满足实时性要求。
6.时钟节拍在FreeRTOS中扮演了关键的角色,它不仅用于任务调度,还用于实现定时等待、超时操作和时间相关的功能。
FreeRTOS中的Tick值通常被配置为1ms:
四、什么是时间片
时间片(Time slice)是操作系统调度算法中的概念,用于分配处理器时间给多个可运行的任务。它是指操作系统将处理器的执行时间划分为固定长度的小段,每个任务在一个时间片内获得处理器的执行时间。
五、相同优先级任务怎么进行切换
在FreeRTOS默认采用了时间片轮转调度的策略,给每一个任务都分配一个固定的时间片,这个时间片的大小也就是1ms,可以通过修改configTICK_RATE_HZ这个宏来修改时间片的大小。
时间片大小(毫秒) = 1秒 / configTICK_RATE_HZ
Task1执行完一个时间片后会发生一次tick中断,然后进入tick中断处理函数中,在tick中断中选择要执行的下一个中断,当执行完tick中断后就切换Task2执行。
六、任务优先级实验
// 任务句柄 TaskHandle_t Task1Handle; TaskHandle_t Task2Handle; // 任务函数 void Task1(void* pvParameters) { while (1) { vTaskDelay(1000); printf("Task1 is running\n"); } } void Task2(void* pvParameters) { while (1) { vTaskDelay(1000); printf("Task2 is running\n"); } } int main(void) { // 创建两个任务 xTaskCreate(Task1, "Task1", configMINIMAL_STACK_SIZE, NULL, 1, &Task1Handle); xTaskCreate(Task2, "Task2", configMINIMAL_STACK_SIZE, NULL, 2, &Task2Handle); // 启动调度器 vTaskStartScheduler(); while (1) { // 主循环为空,所有的任务由 FreeRTOS 调度 } }
因为任务2的优先级比任务1的优先级高所以会先打印Task2 is running,然后再打印Task1 is running,以此往复。
七、修改任务优先级
uxTaskPriorityGet() 和 vTaskPrioritySet() 是FreeRTOS中用于获取和设置任务优先级的函数。
uxTaskPriorityGet() 函数用于获取任务的当前优先级。它接受一个参数,即任务句柄,返回一个 UBaseType_t 类型的值,表示任务的当前优先级。
vTaskPrioritySet() 函数用于设置任务的优先级。它接受两个参数,第一个参数是要设置优先级的任务句柄,第二个参数是要设置的优先级值。这个函数没有返回值。
以下是一个使用 uxTaskPriorityGet() 和 vTaskPrioritySet() 函数的示例代码:
TaskHandle_t Task1Handle; TaskHandle_t Task2Handle; void Task1(void* pvParameters) { while (1) { // 获取任务优先级 UBaseType_t priority = uxTaskPriorityGet(NULL); printf("Task1 priority: %u\n", priority); vTaskDelay(pdMS_TO_TICKS(1000)); } } void Task2(void* pvParameters) { while (1) { // 获取任务优先级 UBaseType_t priority = uxTaskPriorityGet(NULL); printf("Task2 priority: %u\n", priority); // 设置任务优先级 vTaskPrioritySet(Task1Handle, 2); vTaskDelay(pdMS_TO_TICKS(5000)); // 恢复任务优先级 vTaskPrioritySet(Task1Handle, 1); } } int main(void) { xTaskCreate(Task1, "Task1", configMINIMAL_STACK_SIZE, NULL, 1, &Task1Handle); xTaskCreate(Task2, "Task2", configMINIMAL_STACK_SIZE, NULL, 2, &Task2Handle); vTaskStartScheduler(); while (1) { // 主循环为空,所有的任务由 FreeRTOS 调度 }
在这个示例代码中,我们创建了两个任务 Task1 和 Task2,并使用 uxTaskPriorityGet() 和 vTaskPrioritySet() 进行任务优先级的获取和设置。
在 Task1 中,我们首先使用 uxTaskPriorityGet(NULL) 获取当前任务的优先级,并通过 printf 打印出来。然后使用 vTaskDelay() 延迟1秒钟。
在 Task2 中,我们首先获取当前任务的优先级,然后通过 vTaskPrioritySet(Task1Handle, 2) 将 Task1 的优先级设置为2。接着延迟5秒钟后,再通过 vTaskPrioritySet(Task1Handle, 1) 恢复 Task1 的优先级为1。
通过运行这个示例代码,将会看到在任务的执行过程中,任务的优先级获取和设置操作被执行,并通过打印语句输出任务的优先级信息。
注意,在使用 uxTaskPriorityGet() 和 vTaskPrioritySet() 函数时,需要确保任务句柄是正确的,并且函数的调用时机是合适的,以避免潜在的问题和不一致性。
总结
本篇文章就讲解到这里。