FreeRTOS任务优先级和系统心跳Tick

您所在的位置:网站首页 查看心跳 FreeRTOS任务优先级和系统心跳Tick

FreeRTOS任务优先级和系统心跳Tick

2024-07-14 16:14| 来源: 网络整理| 查看: 265

1. FreeRTOS任务优先级介绍

当我们使用xTaskCreate() API函数创建一个任务的时候,会为任务赋予一个初始的优先级,当然这个优先级可以在调度器启动后,我们可以使用vTaskPrioritySet() API函数来进行优先级修改的。

void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority );

其中xTask参数是传递进某个任务的句柄,NULL则表示修改自己的优先级。uxNewPriority参数表示新设置的优先级,取值范围0~(configMAX_PRIORITIES – 1)。

使用uxTaskPriorityGet来获得任务的优先级:

UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask );

xTask参数是某个任务句柄,NULL表示获取自己的优先级。返回值就是该任务的优先级。

优先级的取值范围是:0~(configMAX_PRIORITIES – 1),数值越大优先级越高。其中configMAX_PRIORITIES这个宏的值可以在FreeRTOSConfig.h 中设定。FreeRTOS虽然没有规定这个宏的最大值,但是实际开发中,我们应该尽量选择一个合适的取值,因为这个值越大,那么内核对内存的开销就越大。

FreeRTOS的调度器总是会确保:

在所有可以运行的任务中,选择其中具有最高优先级的任务运行;对于优先级相同的、可以运行的任务,轮流执行。 2. 系统心跳 — tick

对于相同优先级的任务,FreeRTOS要轮流执行的话,就需要有系统心跳。系统心跳来源于SysTick系统定时器产生周期性中断,比如我们设置SysTick定时器10ms产生一次中断,那么FreeRTOS的心跳就是10ms。

如下图所示: 在这里插入图片描述

其中t1、t2、t3时刻发生定时器中断两次中断的时间间隔,称为时间片(time slice、tick period)时间片的长度在在FreeRTOSConfig.h 文件中,由configTICK_RATE_HZ这个宏确定,比如设定为1000,那么时间片长度就是1/1000s,也就是1ms。

那么对于相同优先级的任务,他们是怎么轮流进行切换的呢?如下图可以概括: 在这里插入图片描述

Task2任务是后面创建的,所以会首先运行Tssk2任务;Task2任务运行完一个时间片长度后,发生SysTick系统定时器中断,进入对于的中断处理函数: 中断处理函数选择下一个可以运行的任务执行完中断处理函数后,就跳转到新的任务,即Task1任务继续运行 Task1运行t2~t3这个时间片;上图也可以看出,进行任务切换的时候,需要使用到中断处理函数,中断运行也需要时间的。所以任务的运行并不是在t1,t2,t3的这些时间点上运行的。

使用系统心跳来进行延时:

很明显系统心跳—Tick可以用来衡量时间,自然我们就可以用它来实现延时。FreeRTOS提供了专门的API延时函数给我们使用,函数原型如下:

void vTaskDelay( const TickType_t xTicksToDelay );

该函数使用时会引起使用的任务进入阻塞态。其中,参数xTicksToDelay就是我们想要延时的时间长度,延时多长除了和该参数大小有关,还与设置的系统心跳频率有关。

用法如下:

/* 假设configTICK_RATE_HZ=100, Tick周期时10ms, 那么等待2个Tick,也就是等待20ms */ vTaskDelay(2); /* 还可以使用pdMS_TO_TICKS宏把ms转换为tick */ vTaskDelay(pdMS_TO_TICKS(100)); // 等待100ms

使用vTaskDelay函数时,建议以ms为单位,使用pdMS_TO_TICKS把时间转换为Tick。这样的代码就与configTICK_RATE_HZ无关,即使配置项configTICK_RATE_HZ改变了,我们也不用去修改代码。

3. 优先级示例代码

示例代码创建2个任务。

任务1代码如下:

/* Task1,Task2都不会进入阻塞或者暂停状态,根据优先级决定谁能运行*/ void vTask1( void *pvParameters ) { UBaseType_t uxPriority; /* 得到Task1自己的优先级 */ uxPriority = uxTaskPriorityGet( NULL ); for( ; ; ) { printf( "Task 1 is running\r\n" ); printf("About to raise the Task 2 priority\r\n" ); /* 提升Task2的优先级高于Task1,Task2会即刻执行 */ vTaskPrioritySet( xTask2Handle, ( uxPriority + 1 ) ); } }

任务2代码如下:

/* Task1,Task2都不会进入阻塞或者暂停状态,根据优先级决定谁能运行*/ void vTask2( void *pvParameters ) { UBaseType_t uxPriority; /* 得到Task2自己的优先级 */ uxPriority = uxTaskPriorityGet( NULL ); for( ; ; ) { printf( "Task 2 is running\r\n" ); printf("About to lower the Task 2 priority\r\n" ); /* 降低了Task2的优先级,使得它的优先级比Task1还小,那么Task1会马上得到运行 */ vTaskPrioritySet( xTask2Handle, ( uxPriority - 2 ) ); } }

main函数

#include #include "bsp_usart.h" #include "FreeRTOS.h" #include "task.h" TaskHandle_t xTask2Handle; int main( void ) { USART_Config(); // 串口初始化 /* Task1的优先级更高, Task1先执行 */ xTaskCreate( vTask1, "Task 1", 1000, NULL, 2, NULL ); xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, &xTask2Handle ); /* 启动调度器 */ vTaskStartScheduler(); /* 如果程序运行到了这里就表示出错了, 一般是内存不足 */ return 0; }

代码运行过程如下:

首先创建任务1和任务2,因为任务1优先级更高,所以任务1首先运行。为了让任务2有机会运行,在任务1中提升任务2的优先级高于任务1由于任务1提升了任务2的优先级,所以任务2此时在运行,然后任务2降低自己的优先级这时任务1优先级最高,任务1得以运行反复以上循环由于任务1和2都不会进入阻塞状态,所以空闲任务永远也不会得到执行

如下图可直观了解整个运行过程: 在这里插入图片描述



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3