μC/OS-Ⅱ源码学习(8)---同步与延时

快速回顾

μC/OS-Ⅱ中的多任务

μC/OS-Ⅱ源码学习(1)---多任务系统的实现

μC/OS-Ⅱ源码学习(2)---多任务系统的实现(下)

μC/OS-Ⅱ源码学习(3)---事件模型

μC/OS-Ⅱ源码学习(4)---信号量

μC/OS-Ⅱ源码学习(5)---消息队列

μC/OS-Ⅱ源码学习(6)---事件标志组

μC/OS-Ⅱ源码学习(7)---软件定时器

本文进一步解析μC/OS-Ⅱ中,与时间相关的函数源码。

系统的心跳

在前面的源码解析中,更多是通过学习各种对象的函数源码来理解对象的运行逻辑,但函数只是一个接口(或者说是改变对象状态的入口),如果需要等待延时,系统是如何处理的呢?例如:我们在Pend信号量时,会传入一个等待超时值Delay,超时后谁来通知任务重新就绪?再比如,我们使用的软件定时器,到期后谁去通知执行回调函数?

在上一节的软件定时器里,已经通过案例引出了μC/OSⅡ的系统滴答机制,可以看作是系统的心跳 ,每一次跳动都相当于一次轮询,对μC/OSⅡ的各种延时等待进行管理。在μC/OSⅡ的内核里,提供了一个系统节拍服务函数**OSTimeTick()**用来处理等待过程中各种对象的状态变化,但使用它是有条件的,必须周期性地调用才有意义,就像心跳一样能够按固定频率跳动。

对此,μC/OSⅡ官方建议可以将该函数放在系统的滴答中断system tick中调用,或放在一个高优先级的任务中,目的就是为了周期且稳定地触发。

cpp 复制代码
void SysTick_Handler(void)
{
	OS_CPU_SR  cpu_sr;


    OS_ENTER_CRITICAL();       /* 进入临界区 */
    OSIntNesting++;
    OS_EXIT_CRITICAL();

    OSTimeTick();         /* 系统滴答函数 */

    OSIntExit(); 
}

具体看系统节拍服务函数源码OSTimeTick()

cpp 复制代码
//os_core.c
void  OSTimeTick (void)
{
    OS_TCB    *ptcb;
#if OS_TICK_STEP_EN > 0u
    BOOLEAN    step;
#endif
#if OS_CRITICAL_METHOD == 3u      /* 初始化临界区变量 */
    OS_CPU_SR  cpu_sr = 0u;
#endif

#if OS_TIME_TICK_HOOK_EN > 0u
    OSTimeTickHook();       /* 用户回调函数,如有设置定时器则可以在此函数释放定时器任务信号量 */
#endif
#if OS_TIME_GET_SET_EN > 0u
    OS_ENTER_CRITICAL();
    OSTime++;      /* 系统绝对时间更新(系统节拍数) */
    OS_EXIT_CRITICAL();
#endif
    if (OSRunning == OS_TRUE) {
#if OS_TICK_STEP_EN > 0u
        switch (OSTickStepState) {      /* 查看系统节拍步进状态,来决定是否要处理当前节拍 */
            case OS_TICK_STEP_DIS:      /* 禁用步进状态,正常处理节拍 */
                 step = OS_TRUE;
                 break;

            case OS_TICK_STEP_WAIT:     /* 等待状态,等待被处理成OS_TICK_STEP_ONCE */
                 step = OS_FALSE;      //不处理当次节拍
                 break;

            case OS_TICK_STEP_ONCE:      /* 单次处理 */
                 step            = OS_TRUE;
                 OSTickStepState = OS_TICK_STEP_WAIT;
                 break;

            default:      /* 非法状态,修改为禁用步进状态 */
                 step            = OS_TRUE;
                 OSTickStepState = OS_TICK_STEP_DIS;
                 break;
        }
        if (step == OS_FALSE) {     /* 只有step为TRUE才能处理当次节拍 */
            return;
        }
#endif
        ptcb = OSTCBList;      /* 指向TCB链表头 */
        while (ptcb->OSTCBPrio != OS_TASK_IDLE_PRIO) {     /* 遍历所有任务的TCB */
            OS_ENTER_CRITICAL();
            if (ptcb->OSTCBDly != 0u) {       /* 如果存在等待延时,则将延时-1 */
                ptcb->OSTCBDly--;
                if (ptcb->OSTCBDly == 0u) {       /* 如果等待延时减为0,则设置为就绪 */
                    if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) {
                        ptcb->OSTCBStat  &= (INT8U)~(INT8U)OS_STAT_PEND_ANY;     /* 清除任务状态中的等待 */
                        ptcb->OSTCBStatPend = OS_STAT_PEND_TO;     /* pend状态设置为超时 */
                    } else {
                        ptcb->OSTCBStatPend = OS_STAT_PEND_OK;
                    }

                    if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) {    /* 任务不能处于suspend状态 */
                        OSRdyGrp               |= ptcb->OSTCBBitY;     /* 在优先级就绪表中将相应位置1 */
                        OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
                    }
                }
            }
            ptcb = ptcb->OSTCBNext;     /* 取链表中的下一个TCB */
            OS_EXIT_CRITICAL();
        }
    }
}

主要做了三件事:

①如有设置定时器任务,则尝试释放定时器任务信号量;

②如有设置系统时间(系统节拍),则系统节拍数+1;

③如有设置节拍的步进状态,则根据步进状态判断是否要进入节拍服务。每次节拍服务的内容:遍历所有任务的TCB,将有等待延时的任务,其等待延时-1,如延时变为0,将其设置为就绪态并加入优先级就绪表。

延时服务

另一类和时间服务有关的函数就是系统时延了,比如OSTimeDly()OSTimeDlyHMSM() ,他们只有传入参数的区别,本质做的是一样的工作,即延时并尝试切换上下文(避免阻塞在当前任务)

OSTimeDly(INT32U ticks)

传入参数ticks表示等待延时时间,也即系统的时钟节拍数。

cpp 复制代码
//os_time.c
void  OSTimeDly (INT32U ticks)
{
    INT8U      y;
#if OS_CRITICAL_METHOD == 3u       /* 初始化临界区变量 */
    OS_CPU_SR  cpu_sr = 0u;
#endif

    if (OSIntNesting > 0u) {       /* 不能在中断中调用 */
        return;
    }
    if (OSLockNesting > 0u) {      /* 调度器不能上锁 */
        return;
    }
    if (ticks > 0u) {      /* 零延时没有意义 */
        OS_ENTER_CRITICAL();
        y            =  OSTCBCur->OSTCBY;      /* 将当前任务从就绪表中清除 */
        OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX;
        if (OSRdyTbl[y] == 0u) {
            OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;
        }
        OSTCBCur->OSTCBDly = ticks;      /* 设置任务等待延时 */
        OS_EXIT_CRITICAL();
        OS_Sched();       /* 切换上下文 */
    }
}

INT8U OSTimeDlyHMSM(INT8U hours, INT8U minutes, INT8U seconds, INT16U ms)

该函数和**OSTimeDly()**功能是一样的,只是传入的等待时间变成了日常理解的时分秒制。

相关推荐
小Mie不吃饭27 分钟前
Linux | Ubuntu零基础安装学习cURL文件传输工具
学习
A懿轩A39 分钟前
C/C++ 数据结构与算法【哈夫曼树】 哈夫曼树详细解析【日常学习,考研必备】带图+详细代码
c语言·c++·学习·算法·哈夫曼树·王卓
新中地GIS开发老师40 分钟前
25考研希望渺茫,工作 VS 二战,怎么选?
javascript·学习·考研·arcgis·地理信息科学·地信
sensen_kiss1 小时前
CAN201 Introduction to Networking(计算机网络)Pt.3 网络层
网络·学习·计算机网络·智能路由器
朝九晚五ฺ1 小时前
【Linux探索学习】第二十四弹——软硬链接:Linux 中的软链接与硬链接详解
linux·运维·chrome·学习
虾球xz2 小时前
游戏引擎学习第64天
redis·学习·游戏引擎
虾球xz2 小时前
游戏引擎学习第63天
学习·游戏引擎
朝九晚五ฺ2 小时前
【Linux探索学习】第二十三弹——理解文件系统:认识硬件、探索文件在硬件上的存储问题
linux·运维·学习
两水先木示3 小时前
【Unity3D】ECS入门学习(六)状态组件 ISystemStateComponentData
学习·unity·ecs
Jillyli4 小时前
气相色谱-质谱联用分析方法中的常用部件,分流平板更换
科技·学习·其他·电脑