锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

SylixOS里的时间【11】--- 软件定时器实现详解

时间:2023-01-29 15:30:01 24ppcb板公整套连接器

基本原理

SylixOS软件定时器是通过普通定时器等待唤醒链表和高速定时器等待唤醒链表来实现的。所有操作都围绕着这两个链表进行。不同的是,普通定时器等待唤醒链表的时间和回调文中确定时间和回调,而高速定时器等待唤醒链表在系统心跳中断,也可以分别设置其最小有效定时器精度。

块结构体由定时器控制

/********************************************************************************************************* 定时器控制块 *********************************************************************************************************/ struct sigevent; typedef struct { 
             LW_LIST_MONO               TIMER_monoResrcList;                     /* 定时器资源表 */     LW_CLASS_WAKEUP_NODE       TIMER_wunTimer;                          /* 等待链表唤醒 */ #define TIMER_ulCounter TIMER_wunTimer.WUN_ulCounter          UINT8                      TIMER_ucType;                            /* 定时器类型 */     ULONG                      TIMER_ulCounterSave;                     /* 定时器计数值保留值 */     ULONG                      TIMER_ulOption;                          /* 定时器操作选项 */     UINT8                      TIMER_ucStatus;                          /* 定时器状态 */     PTIMER_CALLBACK_ROUTINE    TIMER_cbRoutine;                         /* 执行函数 */     PVOID                      TIMER_pvArg;                             /* 定时器参数 */     UINT16                     TIMER_usIndex;                           /* 数组中的索引 */      LW_OBJECT_HANDLE           TIMER_ulTimer;                           /* LW_CFG_PTIMER_AUTO_DEL_EN */           LW_OBJECT_HANDLE           TIMER_ulThreadId;                        /* 线程 ID */     struct sigevent            TIMER_sigevent;                          /* 定时器信号相关属性 */                                                                         /* SIGEV_THREAD 必须使能 POSIX */     UINT64                     TIMER_u64Overrun;                        /* timer_getoverrun */     clockid_t                  TIMER_clockid;                           /* 仅对 POSIX 定时器有效 */          PVOID                      TIMER_pvTimerfd;                         /* timerfd 结构 */          CHAR                       TIMER_cTmrName[LW_CFG_OBJECT_NAME_SIZE]; /* 定时器名 */     LW_SPINLOCK_DEFINE        (TIMER_slLock);                           /* 自旋 */ } LW_CLASS_TIMER; typedef LW_CLASS_TIMER        *PLW_CLASS_TIMER; 

定时器全局变量

/********************************************************************************************************* 定时器 *********************************************************************************************************/ __KERNEL_EXT LW_CLASS_TIMER          _K_tmrBuffer[W_CFG_MAX_TIMERS];   /* 定时器缓冲区 */
__KERNEL_EXT LW_CLASS_OBJECT_RESRC   _K_resrcTmr;                       /* 定时器对象资源管理 */
__KERNEL_EXT LW_CLASS_WAKEUP         _K_wuHTmr;                         /* 高速定时器管理表 */
__KERNEL_EXT LW_CLASS_WAKEUP         _K_wuITmr;                         /* 普通定时器管理表 */

初始化资源

/********************************************************************************************************* ** 函数名称: _TimerInit ** 功能描述: 定时器初始化 ** 输 入 : ** 输 出 : *********************************************************************************************************/
VOID  _TimerInit (VOID)
{ 
        
    ULONG                  ulI;
    PLW_LIST_MONO          pmonoTemp1;
    PLW_LIST_MONO          pmonoTemp2;
    PLW_CLASS_TIMER        ptmrTemp1;
    PLW_CLASS_TIMER        ptmrTemp2;
    
    //将定时器缓冲区中的定时器控制块初始化为空闲资源单链表,由定时器对象资源管理对象管理
    _K_resrcTmr.RESRC_pmonoFreeHeader = &_K_tmrBuffer[0].TIMER_monoResrcList;
    
    ptmrTemp1 = &_K_tmrBuffer[0];                                       /* 指向缓冲池首地址 */
    ptmrTemp2 = &_K_tmrBuffer[1];
    
    for (ulI = 0; ulI < (LW_CFG_MAX_TIMERS - 1); ulI++) { 
        
        //初始化定时器控制块
        ptmrTemp1->TIMER_ucType  = LW_TYPE_TIMER_UNUSED;
        ptmrTemp1->TIMER_usIndex = (UINT16)ulI;
        _LIST_MONO_LINK(pmonoTemp1, pmonoTemp2);
        //将定时器控制块链入空闲资源单链表
        pmonoTemp1 = &ptmrTemp1->TIMER_monoResrcList;
        pmonoTemp2 = &ptmrTemp2->TIMER_monoResrcList;
        LW_SPIN_INIT(&ptmrTemp1->TIMER_slLock);
        
        ptmrTemp1++;
        ptmrTemp2++;
    }
    ptmrTemp1->TIMER_ucType  = LW_TYPE_TIMER_UNUSED;
    ptmrTemp1->TIMER_usIndex = (UINT16)ulI;
    LW_SPIN_INIT(&ptmrTemp1->TIMER_slLock);
    
    pmonoTemp1 = &ptmrTemp1->TIMER_monoResrcList;
    _INIT_LIST_MONO_HEAD(pmonoTemp1);
    _K_resrcTmr.RESRC_pmonoFreeTail = pmonoTemp1;
    //初始化统计计数
    _K_resrcTmr.RESRC_uiUsed    = 0;
    _K_resrcTmr.RESRC_uiMaxUsed = 0;
    //初始化高速唤醒链表
    __WAKEUP_INIT(&_K_wuHTmr, LW_NULL, LW_NULL);
    //初始化普通唤醒链表,注意普通唤醒链表在插入项时会调用_ITimerWakeup函数,唤醒普通定时器执行线程
    __WAKEUP_INIT(&_K_wuITmr, _ITimerWakeup, LW_NULL);
}

分配定时器控制块

/********************************************************************************************************* ** 函数名称: _Allocate_Timer_Object ** 功能描述: 从空闲 Timer 控件池中取出一个空闲 Timer ** 输 入 : ** 输 出 : 获得的 Timer 地址,失败返回 NULL *********************************************************************************************************/
PLW_CLASS_TIMER  _Allocate_Timer_Object (VOID)
{ 
        
    REGISTER PLW_LIST_MONO         pmonoFree;
    REGISTER PLW_CLASS_TIMER       ptmrFree;
    
    if (_LIST_MONO_IS_EMPTY(_K_resrcTmr.RESRC_pmonoFreeHeader)) { 
        
        return  (LW_NULL);
    }
    //获得资源链表节点
    pmonoFree = _list_mono_allocate_seq(&_K_resrcTmr.RESRC_pmonoFreeHeader, &_K_resrcTmr.RESRC_pmonoFreeTail);
    //获得定时器控制块地址
    ptmrFree  = _LIST_ENTRY(pmonoFree, LW_CLASS_TIMER, TIMER_monoResrcList);
    //统计统计信息
    _K_resrcTmr.RESRC_uiUsed++;
    if (_K_resrcTmr.RESRC_uiUsed > _K_resrcTmr.RESRC_uiMaxUsed) { 
        
        _K_resrcTmr.RESRC_uiMaxUsed = _K_resrcTmr.RESRC_uiUsed;
    }
    
    return  (ptmrFree);
}

释放和回收定时器控制块

/********************************************************************************************************* ** 函数名称: _Free_Timer_Object ** 功能描述: 将 Timer 控制块交还缓冲池 ** 输 入 : ** 输 出 : *********************************************************************************************************/
VOID  _Free_Timer_Object (PLW_CLASS_TIMER    ptmrFree)
{ 
        
    REGISTER PLW_LIST_MONO    pmonoFree;
    
    pmonoFree = &ptmrFree->TIMER_monoResrcList;
    
    _list_mono_free_seq(&_K_resrcTmr.RESRC_pmonoFreeHeader, 
                        &_K_resrcTmr.RESRC_pmonoFreeTail, 
                        pmonoFree);
    
    _K_resrcTmr.RESRC_uiUsed--;
}

创建定时器

/********************************************************************************************************* ** 函数名称: API_TimerCreate ** 功能描述: 建立一个定时器,不得在中断中调用 ** 输 入 : ** pcName 名字 ** ulOption 定时器类型:高速、普通 ** pulId Id 号 ** 输 出 : *********************************************************************************************************/
LW_API  LW_OBJECT_HANDLE  API_TimerCreate (CPCHAR  pcName, ULONG  ulOption, LW_OBJECT_ID  *pulId)
{ 
        
    REGISTER PLW_CLASS_TIMER       ptmr;
    REGISTER ULONG                 ulIdTemp;
    
    //在内核态分配定时器控制块
    __KERNEL_MODE_PROC(
        ptmr = _Allocate_Timer_Object();                                /* 获得一个定时器控制块 */
    );
    if (!ptmr) { 
        
        return  (LW_OBJECT_HANDLE_INVALID);
    }
    //初始化定时器控制块各个成员
    if (pcName) { 
                                                               /* 拷贝名字 */
        lib_strcpy(ptmr->TIMER_cTmrName, pcName);
    } else { 
        
        ptmr->TIMER_cTmrName[0] = PX_EOS;                               /* 清空名字 */
    }
    if (ulOption & LW_OPTION_ITIMER) { 
                                          /* 应用级定时器 */
        ptmr->TIMER_ucType = LW_TYPE_TIMER_ITIMER;                      /* 定时器类型 */
    } else { 
        
        ptmr->TIMER_ucType = LW_TYPE_TIMER_HTIMER;
    }
    __WAKEUP_NODE_INIT(&ptmr->TIMER_wunTimer);
    ptmr->TIMER_ulCounterSave = 0ul;                                    /* 定时器计数值保留值 */
    ptmr->TIMER_ulOption      = 0ul;                                    /* 定时器操作选项 */
    ptmr->TIMER_ucStatus      = LW_TIMER_STATUS_STOP;                   /* 定时器状态 */
    ptmr->TIMER_cbRoutine     = LW_NULL;                                /* 执行函数 */
    ptmr->TIMER_pvArg         = LW_NULL;
    ptmr->TIMER_ulThreadId    = 0ul;                                    /* 没有线程拥有 */
    ptmr->TIMER_u64Overrun    = 0ull;
    ptmr->TIMER_clockid       = CLOCK_REALTIME;

    //构建对象 id
    ulIdTemp = _MakeObjectId(_OBJECT_TIMER, LW_CFG_PROCESSOR_NUMBER, ptmr->TIMER_usIndex);
    
    if (pulId) { 
        
        *pulId = ulIdTemp;
    }
    
    return  (ulIdTemp);
}

删除定时器

/********************************************************************************************************* ** 函数名称: API_TimerDelete ** 功能描述: 删除一个定时器,不得在中断中调用 ** 输 入 : ** pulId 定时器句柄 ** 输 出 : ERROR_CODE *********************************************************************************************************/
LW_API  ULONG  API_TimerDelete (LW_OBJECT_HANDLE  *pulId)
{ 
        
             INTREG                    iregInterLevel;
    REGISTER UINT16                    usIndex;
    REGISTER PLW_CLASS_TIMER           ptmr;
    REGISTER LW_OBJECT_HANDLE          ulId;
    
    //根据根据系统ID索引定时器控制块
    ulId = *pulId;
    usIndex = _ObjectGetIndex(ulId);
    
    __KERNEL_ENTER();                                                   /* 进入内核 */
    ptmr = &_K_tmrBuffer[usIndex];
    
    _ObjectCloseId(pulId);
    iregInterLevel = KN_INT_DISABLE();                                  /* 关闭中断 */
    
    if (ptmr->TIMER_ucStatus == LW_TIMER_STATUS_STOP) { 
                         /* 关闭状态下删除 */
        goto    __delete;
    }
    
    ptmr->TIMER_ucStatus = LW_TIMER_STATUS_STOP;
    //根据不同定时器类型,从普通定时器或高速定时器唤醒链表中删除节点
    if (ptmr->TIMER_ucType == LW_TYPE_TIMER_ITIMER) { 
                           /* 从扫描队列删除 */
        _WakeupDel(&_K_wuITmr, &ptmr->TIMER_wunTimer, LW_TRUE);
    
    } else { 
        
        _WakeupDel(&_K_wuHTmr, &ptmr->TIMER_wunTimer, LW_FALSE);
    }
    ptmr->TIMER_ulCounter = 0ul;                                        /* 清除计数器 */

__delete:
    ptmr->TIMER_ucType = LW_TYPE_TIMER_UNUSED;                          /* 删除标志 */
    
    KN_INT_ENABLE(iregInterLevel);
    //释放并回收定时器控制块
    _Free_Timer_Object(ptmr);
    __KERNEL_EXIT();                                                    /* 退出内核 */

    return  (ERROR_NONE);
}

启动定时器

/********************************************************************************************************* ** 函数名称: API_TimerStartEx ** 功能描述: 启动一个定时器扩展接口 ** 输 入 : ulId 定时器句柄 ** ulInitCounter 计数初始值 ** ulCounter 重复计数初始值 ** ulOption 操作选项 ** cbTimerRoutine 回调函数 ** pvArg 参数 ** 输 出 : ERROR_CODE *********************************************************************************************************/
LW_API  ULONG  API_TimerStartEx (LW_OBJECT_HANDLE         ulId,
                         ULONG                    ulInitCounter,
                         ULONG                    ulCounter,
                         ULONG                    ulOption,
                         PTIMER_CALLBACK_ROUTINE  cbTimerRoutine,
                         PVOID                    pvArg)
{ 
        
             INTREG                    iregInterLevel;
    REGISTER UINT16                    usIndex;
    REGISTER PLW_CLASS_TIMER           ptmr;
    usIndex = _ObjectGetIndex(ulId);
    

    __KERNEL_ENTER();                                                   /* 进入内核 */
    //根据根据系统ID索引定时器控制块
    ptmr = &_K_tmrBuffer[usIndex];
    
    iregInterLevel = KN_INT_DISABLE();                                  /* 关闭中断 */
    //如果定时器正在运行,则先将其从对应延时队列删除
    if (ptmr->TIMER_ucStatus == LW_TIMER_STATUS_RUNNING) { 
                      /* 正在工作 */
        if (ptmr->TIMER_ucType == LW_TYPE_TIMER_ITIMER) { 
                       /* 从扫描队列删除 */
            _WakeupDel(&_K_wuITmr, &ptmr->TIMER_wunTimer, LW_TRUE);
    
        } else { 
        
            _WakeupDel(&_K_wuHTmr, &ptmr->TIMER_wunTimer, LW_FALSE);
        }
    }
    //配置定时器控制块各个成员
    ptmr->TIMER_ulCounter     = ulInitCounter;
    ptmr->TIMER_ulCounterSave = ulCounter;
    ptmr->TIMER_ulOption      = ulOption;
    ptmr->TIMER_ucStatus      = LW_TIMER_STATUS_RUNNING;                /* 定时器开始运行 */
    ptmr->TIMER_cbRoutine     = cbTimerRoutine;
    ptmr->TIMER_pvArg         = pvArg;
    ptmr->TIMER_u64Overrun    = 0ull;
    //将定时器控制块,添加到对应延时队列,这样计时到后定时器控制块会被响应处理
    if (ptmr->TIMER_ucType == LW_TYPE_TIMER_ITIMER) { 
                           /* 加入扫描队列 */
        _WakeupAdd(&_K_wuITmr, &ptmr->TIMER_wunTimer, LW_TRUE);
    } else { 
        
        _WakeupAdd(&_K_wuHTmr, &ptmr->TIMER_wunTimer, LW_FALSE);
    }
    
    __KERNEL_EXIT_IRQ(iregInterLevel);                                  /* 退出内核并打开中断 */
    
    return  (ERROR_NONE);
}

取消定时器

/********************************************************************************************************* ** 函数名称: API_TimerCancel ** 功能描述: 停止一个定时器 ** 输 入 : ** ulId 定时器句柄 ** 输 出 : ERROR_CODE *********************************************************************************************************/
LW_API  ULONG  API_TimerCancel (LW_OBJECT_HANDLE  ulId)
{ 
        
             INTREG                    iregInterLevel;
    REGISTER UINT16                    usIndex;
    REGISTER PLW_CLASS_TIMER           ptmr;
    usIndex = _ObjectGetIndex(ulId);


    __KERNEL_ENTER();                                                   /* 进入内核 */
    ptmr = &_K_tmrBuffer[usIndex];
    
    iregInterLevel = KN_INT_DISABLE();                                  /* 关闭中断 */
    //如果已停止,则退出并返回错误
    if (ptmr->TIMER_ucStatus == LW_TIMER_STATUS_STOP) { 
                         /* 没有工作 */
        __KERNEL_EXIT_IRQ(iregInterLevel);                              /* 退出内核并打开中断 */
        _ErrorHandle(ERROR_TIMER_TIME);
        return  (ERROR_TIMER_TIME);
    }
    
    ptmr->TIMER_ucStatus = LW_TIMER_STATUS_STOP;
    //将其从对应延时队列删除
    if (ptmr->TIMER_ucType == LW_TYPE_TIMER_ITIMER) { 
                           /* 从扫描队列删除 */
        _WakeupDel(&_K_wuITmr, &ptmr->TIMER_wunTimer, LW_TRUE);
    } else { 
        
        _WakeupDel(&_K_wuHTmr, &ptmr->TIMER_wunTimer, LW_FALSE);
    }
    
    ptmr->TIMER_ulCounter = 0ul;                                        /* 清除计数器 */

    __KERNEL_EXIT_IRQ(iregInterLevel);                                  /* 退出内核并打开中断 */
    
    return  (ERROR_NONE);
}

重启定时器

/********************************************************************************************************* ** 函数名称: API_TimerReset ** 功能描述: 在定时器运行状态下复位一个定时器 ** 输 入 : ** ulId 定时器句柄 ** 输 出 : ERROR_CODE *********************************************************************************************************/
LW_API  ULONG  API_TimerReset (LW_OBJECT_HANDLE  ulId)
{ 
        
             INTREG                    iregInterLevel;
    REGISTER UINT16                    usIndex;
    REGISTER PLW_CLASS_TIMER           ptmr;
    usIndex = _ObjectGetIndex(ulId);
    

    
    __KERNEL_ENTER();                                                   /* 进入内核 */
    ptmr = &_K_tmrBuffer[usIndex];
    
    iregInterLevel = KN_INT_DISABLE();                                  /* 关闭中断 */
    //如果已停止,则退出并返回错误
    if (ptmr->TIMER_ucStatus == LW_TIMER_STATUS_STOP) { 
                         /* 没有工作 */
        __KERNEL_EXIT_IRQ(iregInterLevel);                              /* 退出内核并打开中断 */
        _ErrorHandle(ERROR_TIMER_TIME);
        return  (ERROR_TIMER_TIME);
    }
    //首先将其从对应延时队列删除
    if (ptmr->TIMER_ucType == LW_TYPE_TIMER_ITIMER) { 
                           /* 从扫描队列删除 */
        _WakeupDel(&_K_wuITmr, &ptmr->TIMER_wunTimer, LW_TRUE);
    } else { 
        
        _WakeupDel(&_K_wuHTmr, &ptmr->TIMER_wunTimer, LW_FALSE);
    }
    //然后更新计时
    ptmr->TIMER_ulCounter = ptmr->TIMER_ulCounterSave;                  /* 恢复计数值 */
    //最后重新添加到对应延时队列
    if (ptmr->TIMER_ucType == LW_TYPE_TIMER_ITIMER) { 
                           /* 添加到扫描队列 */
        _WakeupAdd(&_K_wuITmr, &ptmr->TIMER_wunTimer, LW_TRUE);
    } else { 
        
        _WakeupAdd(&_K_wuHTmr, &ptmr->TIMER_wunTimer, LW_FALSE);
    }
    
    __KERNEL_EXIT_IRQ(iregInterLevel);                                  /* 退出内核并打开中断 */
        
    return  (ERROR_NONE);
}

高速定时器执行函数

高速定时器是在系统心跳中判定时间和回调执行的

/********************************************************************************************************* ** 函数名称: API_TimerHTicks ** 功能描述: 这是高速定时器周期中断服务函数 ** 输 入 : ** 输 出 : *********************************************************************************************************/
LW_API  VOID  API_TimerHTicks (VOID)
{ 
        
             INTREG                     iregInterLevel;
    REGISTER PLW_CLASS_TIMER            ptmr;
             PLW_CLASS_WAKEUP_NODE      pwun;
             PTIMER_CALLBACK_ROUTINE    pfuncRoutine;
             PVOID                      pvRoutineArg;
             ULONG                      ulCounter = 1;
             LW_CLASS_WAKEUP            pwu = &_K_wuHTmr;
    
    
    iregInterLevel = __KERNEL_ENTER_IRQ();                              /* 进入内核同时关闭中断 */
    
    __WAKEUP_PASS_FIRST(&_K_wuHTmr, pwun, ulCounter);
    //获取队列首项,循环执行,直到队列为空
    pwu->WU_plineOp = pwu->WU_plineHeader;
    while (pwu->WU_plineOp) { 
            
        pwun = _LIST_ENTRY(pwu->WU_plineOp, LW_CLASS_WAKEUP_NODE, WUN_lineManage);  
        //检查首项等待时间是否已到,未到则更新等待时间,并退出循环检查
        if (pwun->WUN_ulCounter > ulCounter) { 
            
            pwun->WUN_ulCounter -= ulCounter; 
            break;  
        }
        //等待时间已到
        //更新等待时间
        ulCounter -= pwun->WUN_ulCounter; 
        pwun->WUN_ulCounter = 0;
        //获取定时器控制块
        ptmr = _LIST_ENTRY(pwun, LW_CLASS_TIMER, TIMER_wunTimer);
        //将其从高速延时队列删除
        _WakeupDel(&_K_wuHTmr, pwun, LW_FALSE);
        //如果是周期定时器,则再次添加节点到高速延时队列,否则标记停止
        if (ptmr->TIMER_ulOption & LW_OPTION_AUTO_RESTART) { 
        
            ptmr->TIMER_ulCounter = ptmr->TIMER_ulCounterSave;
            _WakeupAdd(&_K_wuHTmr, pwun, LW_FALSE);
            
        } else { 
        
            ptmr->TIMER_ucStatus = LW_TIMER_STATUS_STOP;                    /* 填写停止标志位 */
        }
        //备份回调函数和回调函数参数
        pfuncRoutine = ptmr->TIMER_cbRoutine;
        pvRoutineArg = ptmr->TIMER_pvArg;
        
        __KERNEL_EXIT_IRQ(iregInterLevel);                                  /* 退出内核同时打开中断 */
        //执行回调操作
        pfuncRoutine(pvRoutineArg);
        
        iregInterLevel = __KERNEL_ENTER_IRQ();                              /* 进入内核同时关闭中断 */
        //为了避免长时间操作影响高优先级中断响应,这里要开关一下中断,使得高优先级中断得以响应
        KN_INT_ENABLE(iregInterLevel);                                      /* 这里允许响应中断 */
        iregInterLevel = KN_INT_DISABLE();
        //获取新的队列首项,循环执行,直到队列为空
        pwu->WU_plineOp = _list_line_get_next(pwu->WU_plineOp);
    
    }
    
    __KERNEL_EXIT_IRQ(iregInterLevel);                                  /* 退出内核同时打开中断 */
}

普通定时器执行函数

普通定时器是在t_itimer线程中执行的,默认优先级为20.

/********************************************************************************************************* ** 函数名称: _ITimerThread ** 功能描述: 这是系统普通定时器周期服务线程。 ** 输 入 : ** 输 出 : *********************************************************************************************************/
PVOID  _ITimerThread (PVOID  pvArg)
{ 
        
             INTREG                     iregInterLevel;
    REGISTER PLW_CLASS_TIMER            ptmr;
             PTIMER_CALLBACK_ROUTINE    pfuncRoutine;
             PVOID                      pvRoutineArg;

             PLW_CLASS_TCB              ptcbCur;
             PLW_CLASS_PCB              ppcb;

             PLW_CLASS_WAKEUP_NODE      pwun;
             INT64                      i64CurTime;
             ULONG                      ulCounter;
             BOOL                       bNoTimer;
    LW_CLASS_WAKEUP            pwu = &_K_wuITmr;
    (VOID)pvArg;
    
    LW_TCB_GET_CUR_SAFE(ptcbCur);                                       /* 当前任务控制块 */

    for (;;)<

相关文章