μC/OS-III---I笔记4---软件定时器
软件定时器是在硬件定时器的基础上开发的,通过将一个硬件定时器进行分频及管理就可以的到多个软件定时器。他和时间管理共同组成了系统的时间管理大部分的内容。系统一开始的系统初始化函数OSInit函数内调用了OS_TmrInit()这个函数并在这个函数内创建了一个定时器任务(OS_TmrTask),在这个任务内有等待滴答定时器中断处发布信号量的信号量请求操作。
滴答定时器内的信号量发布函数部分:
#if OS_CFG_TMR_EN > 0u
OSTmrUpdateCtr--;
if (OSTmrUpdateCtr == (OS_CTR)0u) {
OSTmrUpdateCtr = OSTmrUpdateCnt;
OSTaskSemPost((OS_TCB *)&OSTmrTaskTCB, /* Signal timer task */
(OS_OPT ) OS_OPT_POST_NONE,
(OS_ERR *)&err);
}
#endif
而OS_TmrInit函数:
************************************************************************************************************************
* INITIALIZE THE TIMER MANAGER
*
* Description: This function is called by OSInit() to initialize the timer manager module.
*
* Argument(s): p_err is a pointer to a variable that will contain an error code returned by this function.
*
* OS_ERR_NONE
* OS_ERR_TMR_STK_INVALID if you didn't specify a stack for the timer task
* OS_ERR_TMR_STK_SIZE_INVALID if you didn't allocate enough space for the timer stack
* OS_ERR_PRIO_INVALID if you specified the same priority as the idle task
* OS_ERR_xxx any error code returned by OSTaskCreate()
*
* Returns : none
*
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/ void OS_TmrInit (OS_ERR *p_err)
{
OS_TMR_SPOKE_IX i;
OS_TMR_SPOKE *p_spoke; #ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif #if OS_CFG_DBG_EN > 0u
OSTmrDbgListPtr = (OS_TMR *)0;
#endif if (OSCfg_TmrTaskRate_Hz > (OS_RATE_HZ)0) {
//计算分频系数
OSTmrUpdateCnt = OSCfg_TickRate_Hz / OSCfg_TmrTaskRate_Hz;
} else {
//OSCfg_TmrTaskRate_Hz设置错误 则按默认的时钟节拍1/10
OSTmrUpdateCnt = OSCfg_TickRate_Hz / (OS_RATE_HZ)10;
}
//更新分频计数变量,此处联想到滴答定时器内的那一段程序
OSTmrUpdateCtr = OSTmrUpdateCnt;
//清零
OSTmrTickCtr = (OS_TICK)0;
OSTmrTaskTimeMax = (CPU_TS)0;
//跟新定时器列表
for (i = 0u; i < OSCfg_TmrWheelSize; i++) {
p_spoke = &OSCfg_TmrWheel[i];
p_spoke->NbrEntries = (OS_OBJ_QTY)0;
p_spoke->NbrEntriesMax = (OS_OBJ_QTY)0;
p_spoke->FirstPtr = (OS_TMR *)0;
} /* ---------------- CREATE THE TIMER TASK --------------- */
//定时器堆栈基地址
if (OSCfg_TmrTaskStkBasePtr == (CPU_STK*)0) {
*p_err = OS_ERR_TMR_STK_INVALID;
return;
}
//定时器任务堆栈检查
if (OSCfg_TmrTaskStkSize < OSCfg_StkSizeMin) {
*p_err = OS_ERR_TMR_STK_SIZE_INVALID;
return;
}
//定时器优先级检查
if (OSCfg_TmrTaskPrio >= (OS_CFG_PRIO_MAX - 1u)) {
*p_err = OS_ERR_TMR_PRIO_INVALID;
return;
}
//创建定时器任务
OSTaskCreate((OS_TCB *)&OSTmrTaskTCB,
(CPU_CHAR *)((void *)"uC/OS-III Timer Task"),
(OS_TASK_PTR )OS_TmrTask,
(void *)0,
(OS_PRIO )OSCfg_TmrTaskPrio,
(CPU_STK *)OSCfg_TmrTaskStkBasePtr,
(CPU_STK_SIZE)OSCfg_TmrTaskStkLimit,
(CPU_STK_SIZE)OSCfg_TmrTaskStkSize,
(OS_MSG_QTY )0,
(OS_TICK )0,
(void *)0,
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR | OS_OPT_TASK_NO_TLS),
(OS_ERR *)p_err);
}
OS_TmrInit ()
注意其中的任务创建函数创建了的任务就是上面提到的等待定时器处的调用发布信号量。下面就是创建TmrInit函数创建的任务TmrTask。
************************************************************************************************************************
* TIMER MANAGEMENT TASK
*
* Description: This task is created by OS_TmrInit().
*
* Arguments : none
*
* Returns : none
*
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/ void OS_TmrTask (void *p_arg)
{
CPU_BOOLEAN done;
OS_ERR err;
OS_TMR_CALLBACK_PTR p_fnct;
OS_TMR_SPOKE *p_spoke;
OS_TMR *p_tmr;
OS_TMR *p_tmr_next;
OS_TMR_SPOKE_IX spoke;
CPU_TS ts;
CPU_TS ts_start;
CPU_TS ts_end; //去警告
p_arg = p_arg; /* Not using 'p_arg', prevent compiler warning */
while (DEF_ON) {
//请求信号量,信号发布在滴答定时器处
(void)OSTaskSemPend((OS_TICK )0, /* Wait for signal indicating time to update tmrs */
(OS_OPT )OS_OPT_PEND_BLOCKING,
(CPU_TS *)&ts,
(OS_ERR *)&err);
OSSchedLock(&err);
ts_start = OS_TS_GET();
//任务运行次数
OSTmrTickCtr++; /* Increment the current time */
spoke = (OS_TMR_SPOKE_IX)(OSTmrTickCtr % OSCfg_TmrWheelSize);
p_spoke = &OSCfg_TmrWheel[spoke];
p_tmr = p_spoke->FirstPtr;
done = DEF_FALSE;
//找出列表里到期的任务
while (done == DEF_FALSE) {
if (p_tmr != (OS_TMR *)0) {
p_tmr_next = (OS_TMR *)p_tmr->NextPtr; /* Point to next tmr to update because current ... */
//到期 /* ... timer could get unlinked from the wheel. */
if (OSTmrTickCtr == p_tmr->Match) { /* Process each timer that expires */
OS_TmrUnlink(p_tmr); /* Remove from current wheel spoke */
if (p_tmr->Opt == OS_OPT_TMR_PERIODIC) {
//周期定时器继续插入定时器列表
OS_TmrLink(p_tmr,
OS_OPT_LINK_PERIODIC); /* Recalculate new position of timer in wheel */
} else {
//一次性定时器完成
p_tmr->State = OS_TMR_STATE_COMPLETED; /* Indicate that the timer has completed */
}
//取出回调函数并执行
p_fnct = p_tmr->CallbackPtr; /* Execute callback function if available */
if (p_fnct != (OS_TMR_CALLBACK_PTR)0) {
(*p_fnct)((void *)p_tmr,
p_tmr->CallbackPtrArg);
}
//第一个到期了还要继续检查后面的是否到期
p_tmr = p_tmr_next; /* See if next timer matches */
} else {
//根据定时器的插入规则,第一个未到期后面的肯定没有到期
done = DEF_TRUE;
}
} else {
done = DEF_TRUE;
}
}
//计算运行时间
ts_end = OS_TS_GET() - ts_start; /* Measure execution time of timer task */
OSSchedUnlock(&err);
if (OSTmrTaskTimeMax < ts_end) {
OSTmrTaskTimeMax = ts_end;
}
}
} #endif
OS_TmrTask
注意代码中的OSTaskSemPend()函数就是和开头的信号发布相对应,当用户创建一个定时器(注意:在进行创建定时器时一定要先定义一个定时器变量一般都是全局的)OSmrCreate()函数是用来创建一个定时器,创建完成后还需要在调用OSTmrStart()来启动定时器,时内部调用的OSTmrLink()函数将按照定时器剩余时间采用哈希算法将其加入定时器列表,其中OSTmrCreate主要有这样几个变量需要注意一下。
- P_tmr是一个指向自定义的定时器变量
- Opt 共有2个情况 OS_OPT_TMR_ONE_SHOT 一次性的 OS_OPT_TMR_PERIODIC 重复的
- dly 对于第一种情况 DIY就是延时时间,第二种情况就是第一次的延时时间,第二次和以后重装值是下一个变量period
- 对于这里的回调函数,书上给的解释是一个由用户编写系统自动调用的函数,这里放在定时器里就肯定是一个定时一定时间自动调用的函数,这一类函数满足相同的函数签名(函数原型,参数,返回值等)。
具体操作见代码:
/*
************************************************************************************************************************
* CREATE A TIMER
*
* Description: This function is called by your application code to create a timer.
*
* Arguments : p_tmr Is a pointer to a timer control block
*
* p_name Is a pointer to an ASCII string that is used to name the timer. Names are useful for
* debugging.
*
* dly Initial delay.
* If the timer is configured for ONE-SHOT mode, this is the timeout used
* If the timer is configured for PERIODIC mode, this is the first timeout to wait for
* before the timer starts entering periodic mode
*
* period The 'period' being repeated for the timer.
* If you specified 'OS_OPT_TMR_PERIODIC' as an option, when the timer expires, it will
* automatically restart with the same period.
*
* opt Specifies either:
*
* OS_OPT_TMR_ONE_SHOT The timer counts down only once
* OS_OPT_TMR_PERIODIC The timer counts down and then reloads itself
*
* p_callback Is a pointer to a callback function that will be called when the timer expires. The
* callback function must be declared as follows:
*
* void MyCallback (OS_TMR *p_tmr, void *p_arg);
*
* p_callback_arg Is an argument (a pointer) that is passed to the callback function when it is called.
*
* p_err Is a pointer to an error code. '*p_err' will contain one of the following:
*
* OS_ERR_NONE
* OS_ERR_ILLEGAL_CREATE_RUN_TIME if you are trying to create the timer after you called
* OSSafetyCriticalStart().
* OS_ERR_OBJ_CREATED if the timer has already been created
* OS_ERR_OBJ_PTR_NULL is 'p_tmr' is a NULL pointer
* OS_ERR_OBJ_TYPE if the object type is invalid
* OS_ERR_OPT_INVALID you specified an invalid option
* OS_ERR_TMR_INVALID_DLY you specified an invalid delay
* OS_ERR_TMR_INVALID_PERIOD you specified an invalid period
* OS_ERR_TMR_ISR if the call was made from an ISR
*
* Returns : none
*
* Note(s) : 1) This function only creates the timer. In other words, the timer is not started when created. To
* start the timer, call OSTmrStart().
************************************************************************************************************************
*/ void OSTmrCreate (OS_TMR *p_tmr,
CPU_CHAR *p_name,
OS_TICK dly,
OS_TICK period,
OS_OPT opt,
OS_TMR_CALLBACK_PTR p_callback,
void *p_callback_arg,
OS_ERR *p_err)
{
CPU_SR_ALLOC(); #ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif #ifdef OS_SAFETY_CRITICAL_IEC61508
if (OSSafetyCriticalStartFlag == DEF_TRUE) {
*p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME;
return;
}
#endif
//不允许在中断中创建定时器
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
if (OSIntNestingCtr > (OS_NESTING_CTR)0) { /* See if trying to call from an ISR */
*p_err = OS_ERR_TMR_ISR;
return;
}
#endif #if OS_CFG_ARG_CHK_EN > 0u
if (p_tmr == (OS_TMR *)0) { /* Validate 'p_tmr' */
*p_err = OS_ERR_OBJ_PTR_NULL;
return;
}
//选择定时模式
switch (opt) {
case OS_OPT_TMR_PERIODIC:
//检查定时时间有效性
if (period == (OS_TICK)0) {
*p_err = OS_ERR_TMR_INVALID_PERIOD;
return;
}
break; case OS_OPT_TMR_ONE_SHOT:
if (dly == (OS_TICK)0) {
*p_err = OS_ERR_TMR_INVALID_DLY;
return;
}
break; default:
*p_err = OS_ERR_OPT_INVALID;
return;
}
#endif
//关中断
OS_CRITICAL_ENTER();
//初始化这个定时器数据
p_tmr->State = (OS_STATE )OS_TMR_STATE_STOPPED; /* Initialize the timer fields */
p_tmr->Type = (OS_OBJ_TYPE )OS_OBJ_TYPE_TMR;
p_tmr->NamePtr = (CPU_CHAR *)p_name;
p_tmr->Dly = (OS_TICK )dly;
p_tmr->Match = (OS_TICK )0;
p_tmr->Remain = (OS_TICK )0;
p_tmr->Period = (OS_TICK )period;
p_tmr->Opt = (OS_OPT )opt;
p_tmr->CallbackPtr = (OS_TMR_CALLBACK_PTR)p_callback;
p_tmr->CallbackPtrArg = (void *)p_callback_arg;
p_tmr->NextPtr = (OS_TMR *)0;
p_tmr->PrevPtr = (OS_TMR *)0;
//仿真调试
#if OS_CFG_DBG_EN > 0u
OS_TmrDbgListAdd(p_tmr);
#endif
OSTmrQty++; /* Keep track of the number of timers created */
//打开中断并没有任务调度
OS_CRITICAL_EXIT_NO_SCHED();
*p_err = OS_ERR_NONE;
//具体的返回错误见函数的描述
}
OSTmrCreate
其中涉及的几个函数有
1.启动定时器
************************************************************************************************************************
* START A TIMER
*
* Description: This function is called by your application code to start a timer.
*
* Arguments : p_tmr Is a pointer to an OS_TMR
*
* p_err Is a pointer to an error code. '*p_err' will contain one of the following:
*
* OS_ERR_NONE
* OS_ERR_OBJ_TYPE if 'p_tmr' is not pointing to a timer
* OS_ERR_TMR_INVALID
* OS_ERR_TMR_INACTIVE if the timer was not created
* OS_ERR_TMR_INVALID_STATE the timer is in an invalid state
* OS_ERR_TMR_ISR if the call was made from an ISR
*
* Returns : DEF_TRUE is the timer was started
* DEF_FALSE if not or upon an error
*
* Note(s) : 1) When starting/restarting a timer, regardless if it is in PERIODIC or ONE-SHOT mode, the timer is
* linked to the timer wheel with the OS_OPT_LINK_DLY option. This option sets the initial expiration
* time for the timer. For timers in PERIODIC mode, subsequent expiration times are handled by
* the OS_TmrTask().
************************************************************************************************************************
*/ CPU_BOOLEAN OSTmrStart (OS_TMR *p_tmr,
OS_ERR *p_err)
{
OS_ERR err;
CPU_BOOLEAN success; #ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return (DEF_FALSE);
}
#endif
//检查是否在中断内
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
if (OSIntNestingCtr > (OS_NESTING_CTR)0) { /* See if trying to call from an ISR */
*p_err = OS_ERR_TMR_ISR;
return (DEF_FALSE);
}
#endif #if OS_CFG_ARG_CHK_EN > 0u
if (p_tmr == (OS_TMR *)0) {
*p_err = OS_ERR_TMR_INVALID;
return (DEF_FALSE);
}
#endif
//对传入数据的类型检查
#if OS_CFG_OBJ_TYPE_CHK_EN > 0u
if (p_tmr->Type != OS_OBJ_TYPE_TMR) { /* Make sure timer was created */
*p_err = OS_ERR_OBJ_TYPE;
return (DEF_FALSE);
}
#endif
//锁调度器,
OSSchedLock(&err);
//根据定时器状态操作
switch (p_tmr->State) {
//定时器正在运行--重启
case OS_TMR_STATE_RUNNING: /* Restart the timer */
OS_TmrUnlink(p_tmr); /* ... Stop the timer */
OS_TmrLink(p_tmr, OS_OPT_LINK_DLY); /* ... Link timer to timer wheel (see Note #1). */
OSSchedUnlock(&err);
*p_err = OS_ERR_NONE;
success = DEF_TRUE;
break;
//定时器已经停止--重启
case OS_TMR_STATE_STOPPED: /* Start the timer */
case OS_TMR_STATE_COMPLETED:
OS_TmrLink(p_tmr, OS_OPT_LINK_DLY); /* ... Link timer to timer wheel (see Note #1). */
OSSchedUnlock(&err);
*p_err = OS_ERR_NONE;
success = DEF_TRUE;
break;
//定时器正已删除返回错误
case OS_TMR_STATE_UNUSED: /* Timer not created */
OSSchedUnlock(&err);
*p_err = OS_ERR_TMR_INACTIVE;
success = DEF_FALSE;
break; default:
OSSchedUnlock(&err);
*p_err = OS_ERR_TMR_INVALID_STATE;
success = DEF_FALSE;
break;
}
return (success);
}
OSTmrStart
2.加入定时器列表操作
************************************************************************************************************************
* INSERT A TIMER INTO THE TIMER WHEEL
*
* Description: This function is called to insert the timer into the timer wheel. The timer is always inserted at the
* beginning of the list.
*
* Arguments : p_tmr Is a pointer to the timer to insert.
* -----
*
* opt Is either:
*
* OS_OPT_LINK_PERIODIC Means to re-insert the timer after a period expired
* OS_OPT_LINK_DLY Means to insert the timer the first time
*
* Returns : none
*
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/ void OS_TmrLink (OS_TMR *p_tmr,
OS_OPT opt)
{
OS_TMR_SPOKE *p_spoke;
OS_TMR *p_tmr0;
OS_TMR *p_tmr1;
OS_TMR_SPOKE_IX spoke; //因为这个列表里的定时器都是运行态
p_tmr->State = OS_TMR_STATE_RUNNING;
if (opt == OS_OPT_LINK_PERIODIC) { /* Determine when timer will expire */
//周期延时
p_tmr->Match = p_tmr->Period + OSTmrTickCtr;
} else {
//周期延时
if (p_tmr->Dly == (OS_TICK)0) {
p_tmr->Match = p_tmr->Period + OSTmrTickCtr;
} else {
//单次延时
p_tmr->Match = p_tmr->Dly + OSTmrTickCtr;
}
}
//哈希算法按到时剩余长短排序加入定时link
spoke = (OS_TMR_SPOKE_IX)(p_tmr->Match % OSCfg_TmrWheelSize);
p_spoke = &OSCfg_TmrWheel[spoke];
//一下就是一个双向链表的插入操作
if (p_spoke->FirstPtr == (OS_TMR *)0) { /* Link into timer wheel */
p_tmr->NextPtr = (OS_TMR *)0;
p_tmr->PrevPtr = (OS_TMR *)0;
p_spoke->FirstPtr = p_tmr;
p_spoke->NbrEntries = 1u;
} else {
p_tmr->Remain = p_tmr->Match /* Compute remaining time for timer */
- OSTmrTickCtr;
p_tmr1 = p_spoke->FirstPtr; /* Point to current first timer in the list */
while (p_tmr1 != (OS_TMR *)0) {
p_tmr1->Remain = p_tmr1->Match /* Compute time remaining of current timer in list */
- OSTmrTickCtr;
if (p_tmr->Remain > p_tmr1->Remain) { /* Do we need to insert AFTER current timer in list? */
if (p_tmr1->NextPtr != (OS_TMR *)0) { /* Yes, are we pointing at the last timer in the list? */
p_tmr1 = p_tmr1->NextPtr; /* No, Point to next timer in the list */
} else {
p_tmr->NextPtr = (OS_TMR *)0;
p_tmr->PrevPtr = p_tmr1;
p_tmr1->NextPtr = p_tmr; /* Yes, timer to insert is now new last entry in the list */
p_tmr1 = (OS_TMR *)0; /* Break loop */
}
} else { /* Insert before the current timer */
if (p_tmr1->PrevPtr == (OS_TMR *)0) { /* Are we inserting before the first timer? */
p_tmr->PrevPtr = (OS_TMR *)0;
p_tmr->NextPtr = p_tmr1;
p_tmr1->PrevPtr = p_tmr;
p_spoke->FirstPtr = p_tmr;
} else { /* Insert in between 2 timers already in the list */
p_tmr0 = p_tmr1->PrevPtr;
p_tmr->PrevPtr = p_tmr0;
p_tmr->NextPtr = p_tmr1;
p_tmr0->NextPtr = p_tmr;
p_tmr1->PrevPtr = p_tmr;
}
p_tmr1 = (OS_TMR *)0; /* Break loop */
}
}
p_spoke->NbrEntries++;
}
// 更新link长
if (p_spoke->NbrEntriesMax < p_spoke->NbrEntries) { /* Keep track of maximum number of entries in each spoke */
p_spoke->NbrEntriesMax = p_spoke->NbrEntries;
}
}
OS_TmrLink
3.删除定时器
************************************************************************************************************************
* DELETE A TIMER
*
* Description: This function is called by your application code to delete a timer.
*
* Arguments : p_tmr Is a pointer to the timer to stop and delete.
*
* p_err Is a pointer to an error code. '*p_err' will contain one of the following:
*
* OS_ERR_NONE
* OS_ERR_OBJ_TYPE 'p_tmr' is not pointing to a timer
* OS_ERR_TMR_INVALID 'p_tmr' is a NULL pointer
* OS_ERR_TMR_ISR if the function was called from an ISR
* OS_ERR_TMR_INACTIVE if the timer was not created
* OS_ERR_TMR_INVALID_STATE the timer is in an invalid state
*
* Returns : DEF_TRUE if the timer was deleted
* DEF_FALSE if not or upon an error
************************************************************************************************************************
*/ #if OS_CFG_TMR_DEL_EN > 0u
CPU_BOOLEAN OSTmrDel (OS_TMR *p_tmr,
OS_ERR *p_err)
{
OS_ERR err;
CPU_BOOLEAN success; #ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return (DEF_FALSE);
}
#endif
//中断内
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
if (OSIntNestingCtr > (OS_NESTING_CTR)0) { /* See if trying to call from an ISR */
*p_err = OS_ERR_TMR_ISR;
return (DEF_FALSE);
}
#endif
//数据检差
#if OS_CFG_ARG_CHK_EN > 0u
if (p_tmr == (OS_TMR *)0) {
*p_err = OS_ERR_TMR_INVALID;
return (DEF_FALSE);
}
#endif
//内核对象类型检差
#if OS_CFG_OBJ_TYPE_CHK_EN > 0u
if (p_tmr->Type != OS_OBJ_TYPE_TMR) { /* Make sure timer was created */
*p_err = OS_ERR_OBJ_TYPE;
return (DEF_FALSE);
}
#endif
//进入临界
OSSchedLock(&err);
#if OS_CFG_DBG_EN > 0u
OS_TmrDbgListRemove(p_tmr);
#endif
OSTmrQty--; /* One less timer */
//定时器膈个数--
switch (p_tmr->State) {
//定时器在运行
case OS_TMR_STATE_RUNNING:
OS_TmrUnlink(p_tmr); /* Remove from current wheel spoke */
//清空定时器
OS_TmrClr(p_tmr);
//开启调度器
OSSchedUnlock(&err);
*p_err = OS_ERR_NONE;
success = DEF_TRUE;
break;
//定时器已经被删或完成,也就是已经脱离定时器列表
case OS_TMR_STATE_STOPPED: /* Timer has not started or ... */
case OS_TMR_STATE_COMPLETED: /* ... timer has completed the ONE-SHOT time */
OS_TmrClr(p_tmr); /* Clear timer fields */
OSSchedUnlock(&err);
*p_err = OS_ERR_NONE;
success = DEF_TRUE;
break;
//定时器已经删除
case OS_TMR_STATE_UNUSED: /* Already deleted */
OSSchedUnlock(&err);
*p_err = OS_ERR_TMR_INACTIVE;
success = DEF_FALSE;
break; default:
OSSchedUnlock(&err);
*p_err = OS_ERR_TMR_INVALID_STATE;
success = DEF_FALSE;
break;
}
return (success);
}
#endif
OSTmrDel
4.脱离定时器列表
************************************************************************************************************************
* REMOVE A TIMER FROM THE TIMER WHEEL
*
* Description: This function is called to remove the timer from the timer wheel.
*
* Arguments : p_tmr Is a pointer to the timer to remove.
* -----
*
* Returns : none
*
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/ void OS_TmrUnlink (OS_TMR *p_tmr)
{
OS_TMR_SPOKE *p_spoke;
OS_TMR *p_tmr1;
OS_TMR *p_tmr2;
OS_TMR_SPOKE_IX spoke; //哈希算法找到目标定时器
spoke = (OS_TMR_SPOKE_IX)(p_tmr->Match % OSCfg_TmrWheelSize);
p_spoke = &OSCfg_TmrWheel[spoke];
//双向链表删除节点操作
if (p_spoke->FirstPtr == p_tmr) { /* See if timer to remove is at the beginning of list */
p_tmr1 = (OS_TMR *)p_tmr->NextPtr;
p_spoke->FirstPtr = (OS_TMR *)p_tmr1;
if (p_tmr1 != (OS_TMR *)0) {
p_tmr1->PrevPtr = (OS_TMR *)0;
}
} else {
p_tmr1 = (OS_TMR *)p_tmr->PrevPtr; /* Remove timer from somewhere in the list */
p_tmr2 = (OS_TMR *)p_tmr->NextPtr;
p_tmr1->NextPtr = p_tmr2;
if (p_tmr2 != (OS_TMR *)0) {
p_tmr2->PrevPtr = (OS_TMR *)p_tmr1;
}
}
p_tmr->State = OS_TMR_STATE_STOPPED;
p_tmr->NextPtr = (OS_TMR *)0;
p_tmr->PrevPtr = (OS_TMR *)0;
p_spoke->NbrEntries--;
}
OS_TmrUnlink
5.停止定时器
************************************************************************************************************************
* STOP A TIMER
*
* Description: This function is called by your application code to stop a timer.
*
* Arguments : p_tmr Is a pointer to the timer to stop.
*
* opt Allows you to specify an option to this functions which can be:
*
* OS_OPT_TMR_NONE Do nothing special but stop the timer
* OS_OPT_TMR_CALLBACK Execute the callback function, pass it the callback argument
* specified when the timer was created.
* OS_OPT_TMR_CALLBACK_ARG Execute the callback function, pass it the callback argument
* specified in THIS function call
*
* callback_arg Is a pointer to a 'new' callback argument that can be passed to the callback function
* instead of the timer's callback argument. In other words, use 'callback_arg' passed in
* THIS function INSTEAD of p_tmr->OSTmrCallbackArg
*
* p_err Is a pointer to an error code. '*p_err' will contain one of the following:
* OS_ERR_NONE
* OS_ERR_OBJ_TYPE if 'p_tmr' is not pointing to a timer
* OS_ERR_OPT_INVALID if you specified an invalid option for 'opt'
* OS_ERR_TMR_INACTIVE if the timer was not created
* OS_ERR_TMR_INVALID 'p_tmr' is a NULL pointer
* OS_ERR_TMR_INVALID_STATE the timer is in an invalid state
* OS_ERR_TMR_ISR if the function was called from an ISR
* OS_ERR_TMR_NO_CALLBACK if the timer does not have a callback function defined
* OS_ERR_TMR_STOPPED if the timer was already stopped
*
* Returns : DEF_TRUE If we stopped the timer (if the timer is already stopped, we also return DEF_TRUE)
* DEF_FALSE If not
************************************************************************************************************************
*/ CPU_BOOLEAN OSTmrStop (OS_TMR *p_tmr,
OS_OPT opt,
void *p_callback_arg,
OS_ERR *p_err)
{
OS_TMR_CALLBACK_PTR p_fnct;
OS_ERR err;
CPU_BOOLEAN success; #ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return (DEF_FALSE);
}
#endif
//中断内
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
if (OSIntNestingCtr > (OS_NESTING_CTR)0) { /* See if trying to call from an ISR */
*p_err = OS_ERR_TMR_ISR;
return (DEF_FALSE);
}
#endif #if OS_CFG_ARG_CHK_EN > 0u
if (p_tmr == (OS_TMR *)0) {
*p_err = OS_ERR_TMR_INVALID;
return (DEF_FALSE);
}
#endif
//内核对象类型检差
#if OS_CFG_OBJ_TYPE_CHK_EN > 0u
if (p_tmr->Type != OS_OBJ_TYPE_TMR) { /* Make sure timer was created */
*p_err = OS_ERR_OBJ_TYPE;
return (DEF_FALSE);
}
#endif
//锁调度器进入临界区
OSSchedLock(&err);
switch (p_tmr->State) {
case OS_TMR_STATE_RUNNING:
OS_TmrUnlink(p_tmr); /* Remove from current wheel spoke */
*p_err = OS_ERR_NONE;
switch (opt) {
case OS_OPT_TMR_CALLBACK:
p_fnct = p_tmr->CallbackPtr; /* Execute callback function ... */
if (p_fnct != (OS_TMR_CALLBACK_PTR)0) { /* ... if available */
(*p_fnct)((void *)p_tmr, p_tmr->CallbackPtrArg); /* Use callback arg when timer was created */
} else {
*p_err = OS_ERR_TMR_NO_CALLBACK;
}
break;
//停止时调用回调函数
case OS_OPT_TMR_CALLBACK_ARG:
p_fnct = p_tmr->CallbackPtr; /* Execute callback function if available ... */
if (p_fnct != (OS_TMR_CALLBACK_PTR)0) {
(*p_fnct)((void *)p_tmr, p_callback_arg); /* .. using the 'callback_arg' provided in call */
} else {
*p_err = OS_ERR_TMR_NO_CALLBACK;
}
break;
//停止时不调用回调函数
case OS_OPT_TMR_NONE:
break; default:
OSSchedUnlock(&err);
*p_err = OS_ERR_OPT_INVALID;
return (DEF_FALSE);
}
//退临界
OSSchedUnlock(&err);
success = DEF_TRUE;
break;
//定时已经完成or停止
case OS_TMR_STATE_COMPLETED: /* Timer has already completed the ONE-SHOT or */
case OS_TMR_STATE_STOPPED: /* ... timer has not started yet. */
OSSchedUnlock(&err);
*p_err = OS_ERR_TMR_STOPPED;
success = DEF_TRUE;
break;
//定时器已删除
case OS_TMR_STATE_UNUSED: /* Timer was not created */
OSSchedUnlock(&err);
*p_err = OS_ERR_TMR_INACTIVE;
success = DEF_FALSE;
break; default:
OSSchedUnlock(&err);
*p_err = OS_ERR_TMR_INVALID_STATE;
success = DEF_FALSE;
break;
}
return (success);
}
OSTmrStop
还有就是一些不常用的函数比如,定时器剩余时间获取,定时状态获取等函数从函数描述就可以知道其用法。
软件定时器设计的数据结构:
最后整个定时工作的原理就是,在系统初始化时候的TmrInit()函数将配置分频系数OSTmrUpdatecnt并创建一个任务OS_TmrTask任务等待信号量,在滴答定时器内检查内次到达分频系数减到0的时候Post信号给OS_TmrTask任务,此任务就可以继续执行检查是哪一个定时器到期此处同样使用哈希求余算法快速查找,查找到定时器到期根据是否为周期定时器进行相应的操作并调用对应函数的回调函数;在创建一个定时器并启动时系统会将这个定时器加入定时器列表里在OS_TmrTask任务里查找,对于定时器列表是到时间剩余长短排列,剩余时间越短排序越靠前,因此在检查定时器 是否到期时,如果第一个没有到期就不用检查后面的定时器是否到期,同理如果前面的定时器到期了,就还要继续检查后面的定时器是否到期。
μC/OS-III---I笔记4---软件定时器的更多相关文章
- 基于μC/OS—III的CC1120驱动程序设计
基于μC/OS—III的CC1120驱动程序设计 时间:2014-01-21 来源:电子设计工程 作者:张绍游,张贻雄,石江宏 关键字:CC1120 嵌入式操作系统 STM32F103ZE ...
- 6.1-uC/OS-III软件定时器
1.软件定时器是 uC/OS 操作系统的一个内核对象,软件定时器是基于时钟节拍和系统管理创建的软件性定时器,理论上可以创建无限多个,但精准度肯定比硬件定时稍逊一筹. 2.软件定时器启动之后是由软件定时 ...
- 6.0-uC/OS-III软件定时器管理
1.软件定时器管理 uC/OS-III提供了软件定时器服务(相关代码在OS_TMR.C中).当设置OS_CFG.H中的OS_CFG_TMR_EN为1时软件定时器服务被使能. 2.uC/OS-III 定 ...
- uC/OS-III 软件定时器(三)
软件定时器是uC/OS 操作系统的一个内核对象,软件定时器是基于时钟节拍和系统管理创建的软件性定时器,理论上可以创建无限多个,操作简单,但精准度肯定比硬件定时稍逊一筹. 原理和实现过程 要用到的函数: ...
- liteos软件定时器(十)
1 概述 1.1 基本概念 软件定时器,是基于系统Tick时钟中断且由软件来模拟的定时器,当经过设定的Tick时钟计数值后会触发用户定义的回调函数.定时精度与系统Tick时钟的周期有关. 硬件定时器受 ...
- μC/OS-II中使用软件定时器
在试着将μC/OS-II移植到ARM7芯片(LPC2138)上的过程中,发现使用OSTmrCreate创建的OSTmr始终都不能执行CallbackFunction,OS版本是v2.85,最后是这么解 ...
- uc/os iii移植到STM32F4---IAR开发环境
也许是先入为主的原因,时钟用不惯Keil环境,大多数的教程都是拿keil写的,尝试将官方的uc/os iii 移植到IAR环境. 1.首先尝试从官网上下载的官方移植的代码,编译通过,但是执行会报堆栈溢 ...
- rt-thread中软件定时器组件超时界限的一点理解
@2019-01-15 [小记] 对 rt-thread 中的软件定时器组件中超时界限的一点理解 rt_thread_timer_entry(void *parameter)函数中if ((next_ ...
- 【iCore4 双核心板_uC/OS-II】例程四:软件定时器
一.实验说明: 一些应用程序执行它们的任务时需要延迟一段特定的时间,因此uC/OS-II为我们提供了一些相应的 延时函数,本例程我们使用软件定时器定时500ms点亮相应的LED实现三色LED循环闪烁. ...
- muduo网络库学习笔记(三)TimerQueue定时器队列
目录 muduo网络库学习笔记(三)TimerQueue定时器队列 Linux中的时间函数 timerfd简单使用介绍 timerfd示例 muduo中对timerfd的封装 TimerQueue的结 ...
随机推荐
- MySQL库和表的操作
MySQL库和表的操作 库操作 创建库 1.1 语法 CREATE DATABASE 数据库名 charset utf8; 1.2 数据库命名规则 可以由字母.数字.下划线.@.#.$ 区分大小写 唯 ...
- 拒演"拼命工作"的苦情戏,如何更聪明地提高工作效率?
前几天PDD的事情又把互联网打工人的工作状态推向了大众视野,引起了大家的口诛笔伐.但是目前来看这种愤慨终究是暂时的,作用甚微.在大环境短时间无法改变的前提下,想想如何应对,或许比在网上愤愤不平破口大骂 ...
- 【Redis系列】Spring boot实现监听Redis key失效事件
talk is cheap, show me the code. 一.开启Redis key过期提醒 方式二:修改配置文件 redis.conf # 默认 notify-keyspace-events ...
- java虚拟机入门(五)- 常见垃圾回收器及jvm实现
上节讲完了垃圾回收的基础,包括java的垃圾是什么,如何寻找以及常用的垃圾回收算法,那么那么多的理论知识讲完了,具体是什么样的东西在做着回收垃圾的事情呢?我们接下来就好好聊聊jvm中常用的垃圾回收器. ...
- 第一个 Maven 应用程序
概述 使用 Maven 创建一个 Java Web 应用程序 创建 Maven 项目 选择 File -> New -> Project... 选择 Maven 项目 填写项目信息 选择工 ...
- 编码占用的字节数 1 byte 8 bit 1 sh 1 bit 中文字符编码 2. 字符与编码在程序中的实现 变长编码 Unicode UTF-8 转换 在网络上传输 保存到磁盘上 bytes
小结: 1.UNICODE 字符集编码的标准有很多种,比如:UTF-8, UTF-7, UTF-16, UnicodeLittle, UnicodeBig 等: 2 服务器->网页 utf-8 ...
- : cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs
: cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs
- Advanced Go Concurrency Patterns
https://talks.golang.org/2013/advconc.slide#5 It's easy to go, but how to stop? Long-lived programs ...
- SpringMVC听课笔记(十一:国际化)
1. 关于国际化 -- 在页面上根据浏览器的语言设置情况对文本(不是内容),时间,数值进行本地化处理 使用JSTL的fmt标签 -- 可以在bean中获取国际化资源文件 Locale对应的消息 在be ...
- Effective Java读书笔记--类和接口
1.使类和成员的可访问性最小化不指定访问级别,就是包私有.protected = 包私有 + 子类一般private不会被访问到,如果实现了Serializable,可能会泄露.反射.final集合或 ...