SylixOS里的时间【11】--- 软件定时器实现详解
时间:2023-01-29 15:30:01
基本原理
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 (;;)<