打印
[uCOS/RTOS]

FreeRTOS操作系统——时间管理系统时钟(九)

[复制链接]
802|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
我于nano|  楼主 | 2022-7-28 20:57 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
# FreeRTOS操作系统学习


@[TOC](文章目录)

</font>

<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

# 前言

上一节学习了相对延时函数和绝对延时函数,在FreeRTOS中系统时钟更加重要,这一节我们来学习系统时钟。
# 一、滴答定时器
FreeRTOS中有一个系统时钟节拍SysTicks,这个系统时钟犹如心脏一样维持系统的运行,通过xTickCount时刻反应当前是什么时间段。滴答定时器会定期产生中断,每产生一次中断,xTickCount就会自动+1。是为系统提供服务的



# 二、详解
## 1.具体代码

```c
BaseType_t xTaskIncrementTick( void )
{
TCB_t * pxTCB;
TickType_t xItemValue;
BaseType_t xSwitchRequired = pdFALSE;
//每个时钟节拍中断(滴答定时器中断)调用一次本函数,增加时钟节拍计数器 xTickCount 的
//值,并且检查是否有任务需要取消阻塞。
traceTASK_INCREMENT_TICK( xTickCount );
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) (1)
{
const TickType_t xConstTickCount = xTickCount + 1; (2)
//增加系统节拍计数器 xTickCount 的值,当为 0,也就是溢出的话就交换延时和溢出列
//表指针值。
xTickCount = xConstTickCount;
if( xConstTickCount == ( TickType_t ) 0U ) (3) {
taskSWITCH_DELAYED_LISTS(); (4) }
else
{
mtCOVERAGE_TEST_MARKER();
}
//判断是否有任务延时时间到了,任务都会根据唤醒时间点值按照顺序(由小到大的升
//序排列)添加到延时列表中,这就意味这如果延时列表中第一个列表项对应的任务的
//延时时间都没有到的话后面的任务就不用看了,肯定也没有到。
if( xConstTickCount >= xNextTaskUnblockTime ) (5) {
for( ;; )
{
if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) (6) {
//延时列表为空,设置 xNextTaskUnblockTime 为最大值。
xNextTaskUnblockTime = portMAX_DELAY; (7)
break;
else
{
//延时列表不为空,获取延时列表的第一个列表项的值,根据判断这个值
//判断任务延时时间是否到了, 如果到了的话就将任务移除延时列表。
pxTCB = ( TCB_t * )\ (8)
listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
xItemValue =\ (9)
listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );
if( xConstTickCount < xItemValue ) (10) {
//任务延时时间还没到,但是 xItemValue 保存着下一个即将解除
//阻塞态的任务对应的解除时间点,所以需要用 xItemValue 来更新
//变量 xNextTaskUnblockTime
xNextTaskUnblockTime = xItemValue; (11)
break;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//将任务从延时列表中移除
( void ) uxListRemove( &( pxTCB->xStateListItem ) ); (12)
//任务是否还在等待其他事件?如信号量、队列等,如果是的话就将这些
//任务从相应的事件列表中移除。相当于等待事件超时退出!
if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) !\ (13)
= NULL )
{
( void ) uxListRemove( &( pxTCB->xEventListItem ) ); (14)
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//将任务添加到就绪列表中
prvAddTaskToReadyList( pxTCB ); (15)
#if ( configUSE_PREEMPTION == 1 )
{
//使用抢占式内核,判断解除阻塞的任务优先级是否高于当前正在
//运行的任务优先级,如果是的话就需要进行一次任务切换!
if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) (16) {
xSwitchRequired = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
} }
#endif /* configUSE_PREEMPTION */
} } }
//如果使能了时间片的话还需要处理同优先级下任务之间的调度
#if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) )(17) {
if( listCURRENT_LIST_LENGTH( &( \
pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 )
{
xSwitchRequired = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
} }
#endif
//使用时钟节拍钩子函数
#if ( configUSE_TICK_HOOK == 1 )
{
if( uxPendedTicks == ( UBaseType_t ) 0U )
{
vApplicationTickHook(); (18) }
else
{
mtCOVERAGE_TEST_MARKER();
} }
#endif /* configUSE_TICK_HOOK */
else //任务调度器挂起 (19) {
++uxPendedTicks; (20)
#if ( configUSE_TICK_HOOK == 1 )
{
vApplicationTickHook();
}
#endif
}
#if ( configUSE_PREEMPTION == 1 )
{
if( xYieldPending != pdFALSE ) (21) {
xSwitchRequired = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
} }
#endif /* configUSE_PREEMPTION */
return xSwitchRequired; (22)
}
```
(1)、判断任务调度器是否被挂起。
(2)、将时钟节拍计数器 xTickCount 加一,并将结果保存在 xConstTickCount 中,下一行程序会将 xConstTickCount 赋值给 xTickCount,相当于给 xTickCount 加一。
(3)、xConstTickCount 为 0,说明发生了溢出!
(4)、如果发生了溢出的话使用函数 taskSWITCH_DELAYED_LISTS 将延时列表指针
pxDelayedTaskList 和溢出列表指针 pxOverflowDelayedTaskList 所指向的列表进行交换,函数
taskSWITCH_DELAYED_LISTS()本质上是个宏,在文件 tasks.c 中有定义,将这两个指针所指向的列表交换以后还需要更新 xNextTaskUnblockTime 的值。
(5)、变量 xNextTaskUnblockTime 保存着下一个要解除阻塞的任务的时间点值,如果xConstTickCount 大于 xNextTaskUnblockTime 的话就说明有任务需要解除阻塞了。
(6)、判断延时列表是否为空。
(7)、如果延时列表为空的话就将 xNextTaskUnblockTime 设置为 portMAX_DELAY。
(8)、延时列表不为空,获取延时列表第一个列表项对应的任务控制块。
(9)、获取(8)中获取到的任务控制块中的壮态列表项值。
(10)、任务控制块中的壮态列表项值保存了任务的唤醒时间点,如果这个唤醒时间点值大于当前的系统时钟(时钟节拍计数器值),说明任务的延时时间还未到。
(11)、任务延时时间还未到,而且 xItemValue 已经保存了下一个要唤醒的任务的唤醒时间点,所以需要用 xItemValue 来更新 xNextTaskUnblockTime。
(12)、任务延时时间到了,所以将任务先从延时列表中移除。
(13)、检查任务是否还等待某个事件,比如等待信号量、队列等。如果还在等待的话就任务从相应的事件列表中移除。因为超时时间到了!
(14)、将任务从相应的事件列表中移除。
(15)、任务延时时间到了,并且任务已经从延时列表或者事件列表中已经移除。所以这里需要将任务添加到就绪列表中。
(16)、延时时间到的任务优先级高于正在运行的任务优先级,所以需要进行任务切换了,标记 xSwitchRequired 为 pdTRUE,表示需要进行任务切换。
(17)、如果使能了时间片调度的话,还要处理跟时间片调度有关的工作
(18)、如果使能了时间片钩子函数的话就执行时间片钩子函数 vApplicationTickHook(),函数的具体内容由用户自行编写。
(19)、如果调用函数 vTaskSuspendAll()挂起了任务调度器的话在每个滴答定时器中断就不不会更新 xTickCount 了。取而代之的是用 uxPendedTicks 来记录调度器挂起过程中的时钟节拍
数。这样在调用函数 xTaskResumeAll()恢复任务调度器的时候就会调用 uxPendedTicks 次函数
xTaskIncrementTick(),这样 xTickCount 就会恢复,并且那些应该取消阻塞的任务都会取消阻塞。
(20)、uxPendedTicks 是个全局变量,在文件 tasks.c 中有定义,任务调度器挂起以后此变量
用来记录时钟节拍数。
(21)、有时候调用其他的 API 函数会使用变量 xYieldPending 来标记是否需要进行上下文切换
(22)、返回 xSwitchRequired 的值,xSwitchRequired 保存了是否进行任务切换的信息,如果为 pdTRUE 的话就需要进行任务切换,pdFALSE 的话就不需要进行任务切换。函数
xPortSysTickHandler()中调用 xTaskIncrementTick()的时候就会判断返回值,并且根据返回值决定
是否进行任务切换。
# 总结
FreeRTOS操作系统中已经配置好了滴答定时器,不需要我们再去配置,但是其工作原理也要了解。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

9

主题

9

帖子

1

粉丝