FreeRTOS系列学习笔记九–>时间管理
文章目录
前言
在前面的章节实验例程中,频繁地使用了 FreeRTOS 提供的延时函数,使用延时函数会使得任务进入阻塞态,直至延时完成,任务才会重新进入就绪态。FreeRTOS 是如何对延时任务进行阻塞的,又是如何判断任务延时超时的,这些都是属于 FreeRTOS 时间管理的相关内容。
一、FreeRTOS 系统时钟节拍
1:FreeRTOS系统时钟节拍简介
任务的操作系统都需要时钟节拍,FreeRTOS 也不例外。FreeRTOS 有一个系统时钟节拍计数器——xTickCount,xTickCount 是一个全局变量,在 tasks.c 文件中有定义,具体的定义代码如下所示:
/*如果configINITIAL_TICK_COUNT没有被而外定义,那么将它定义为0*/
#ifndef configINITIAL_TICK_COUNT
#define configINITIAL_TICK_COUNT 0
PRIVILEGED_DATA static volatile TickType_t xTickCount =( TickType_t ) configINITIAL_TICK_COUNT;
从 上 面 的 代 码 可 以 看 到 , xTickCount 在 定 义 时 , 被 赋 了 初 值 , 初 值 由 宏 定 义configINITIAL_TICK_COUNT 定义,在通常情况下系统使用节拍计数器的初值都是设置为 0,除非在个别特殊使用场合,初值是可以由使用者手动配置的。
2:FreeRTOS 系统时钟节拍来源
FreeRTOS 的系统时钟节拍计数器为全局变量 xTickCount,那么 FreeRTOS 又是何时操作这个系统时钟节拍计数器的呢?
一般情况而言我们可以通过SysTick 的中断服务函数中去操作这个全局变量xTickCount,但是我们也可以通过其他硬件定时器来提供时钟节拍,这里我采用了SysTick 的中断服务函数来处理时钟节拍。
在FreeRTOS 启动任务调度器的过程中会对 SysTick 进行初始化,会覆盖掉系统配置的SysTick初始化,因此最终 SysTick 的配置为 FreeRTOS 对 SysTick 的配置。
有些板子的SysTick的时钟源频率与 CPU 时钟频率不一致,而为了让SysTick 的时钟源频率与 CPU 的时钟源频率相同来正常的使用相关的函数定义,我们会自己定义一个宏来适配:configSYSTICK_CLOCK_HZ
/* 定义 SysTick 时钟频率,
* 当 SysTick 时钟频率与内核时钟频率不同时才可以定义,
* 单位: Hz, 默认: 不定义
*/
#define configSYSTICK_CLOCK_HZ (configCPU_CLOCK_HZ / 8)
而想知道FreeRTOS 启动任务调度器的过程中会对 SysTick 进行初始化,可以在任务切换那个章节中看到xPortStartScheduler()会对 SysTick进行配置。
/*在xPortStartScheduler()函数中截取的有关对SysTick 配置的关键代码*/
portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI; /*第一行代码用于设置 SysTick 的中断优先级为最低优先等级*/
vPortSetupTimerInterrupt(); /*调用函数vPortSetupTimerInterrupt()对 SysTick 进行配置*/
接下来介绍函数vPortSetupTimerInterrupt()的具体代码:
__weak void vPortSetupTimerInterrupt( void )
{
/* 此宏为低功耗的相关配置,无需理会 */
#if ( configUSE_TICKLESS_IDLE == 1 )
{
ulTimerCountsForOneTick = (configSYSTICK_CLOCK_HZ /
configTICK_RATE_HZ);
xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER /
ulTimerCountsForOneTick;
ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR /
(configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ);
}
#endif
/* 清空 SysTick 控制状态寄存器 */
portNVIC_SYSTICK_CTRL_REG = 0UL;
/* 清空 SysTick 当前数值寄存器 */
portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
/* 根据配置的系统时钟节拍频率,
* 设置 SysTick 重装载寄存器
*/
portNVIC_SYSTICK_LOAD_REG =
(configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ) - 1UL;
/* 设置 SysTick 控制状态寄存器,
* 设置 SysTick 时钟源不分频(与 CPU 时钟源同频率)、
* 开启 SysTick 计数清零中断(SysTick 为向下计数)、
* 开启 SysTick 计数
*/
portNVIC_SYSTICK_CTRL_REG = (portNVIC_SYSTICK_CLK_BIT |
portNVIC_SYSTICK_INT_BIT |
portNVIC_SYSTICK_ENABLE_BIT);
}
3:FreeRTOS 系统时钟节拍处理
了解完在FreeRTOS 系统中系统时钟节拍的来源后,我们来看看他是怎么对时钟节拍进行处理的。
在上面的配置中我们设置了FreeRTOS 的系统时钟节拍来自 SysTick,那么FreeRTOS 系统时钟节拍的处理,自然就是在 SysTick 的中断服务函数中完成的。在HAL库生成的代码中SysTick 的中断服务函数被定义stm32f1xx_it.c文件中。
具体代码如下:
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
HAL_IncTick();
/* OS 开始跑了,才执行正常的调度处理 */
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
{
xPortSysTickHandler();
}
/* USER CODE END SysTick_IRQn 0 */
/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
}
从上面的代码可以看出,在 SysTick 的中断服务函数中,除了调用函数 HAL_IncTick()外,还通过函数xTaskGetSchedulerState()判断任务调度器是否运行,如果任务调度器运行,那么就调用函数 xPortSysTickHandler()处理 FreeRTOS 的时钟节拍。
让我们来看看xPortSysTickHandler()的具体函数定义: