μ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()**功能是一样的,只是传入的等待时间变成了日常理解的时分秒制。

相关推荐
西岸行者4 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意4 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码4 天前
嵌入式学习路线
学习
毛小茛4 天前
计算机系统概论——校验码
学习
babe小鑫4 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms4 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下4 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。4 天前
2026.2.25监控学习
学习
im_AMBER4 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J4 天前
从“Hello World“ 开始 C++
c语言·c++·学习