/* ************************************************************************************************************************ * uC/OS-III * The Real-Time Kernel * * (c) Copyright 2009-2014; Micrium, Inc.; Weston, FL * All rights reserved. Protected by international copyright laws. * * TIME MANAGEMENT * * File : OS_TIME.C * By : JJL * Version : V3.04.04 * * LICENSING TERMS: * --------------- * uC/OS-III is provided in source form for FREE short-term evaluation, for educational use or * for peaceful research. If you plan or intend to use uC/OS-III in a commercial application/ * product then, you need to contact Micrium to properly license uC/OS-III for its use in your * application/product. We provide ALL the source code for your convenience and to help you * experience uC/OS-III. The fact that the source is provided does NOT mean that you can use * it commercially without paying a licensing fee. * * Knowledge of the source code may NOT be used to develop a similar product. * * Please help us continue to provide the embedded community with the finest software available. * Your honesty is greatly appreciated. * * You can find our product's user manual, API reference, release notes and * more information at https://doc.micrium.com. * You can contact us at www.micrium.com. ************************************************************************************************************************ */ #define MICRIUM_SOURCE #include "os.h" #ifdef VSC_INCLUDE_SOURCE_FILE_NAMES const CPU_CHAR *os_time__c = "$Id: $"; #endif /* ************************************************************************************************************************ * DELAY TASK 'n' TICKS * * Description: This function is called to delay execution of the currently running task until the specified number of * system ticks expires. This, of course, directly equates to delaying the current task for some time to * expire. No delay will result if the specified delay is 0. If the specified delay is greater than 0 * then, a context switch will result. * * Arguments : dly is a value in 'clock ticks' that the task will either delay for or, the target match value * of the tick counter (OSTickCtr). Note that specifying 0 means the task is not to delay. * * depending on the option argument, the task will wake up when OSTickCtr reaches: * * OS_OPT_TIME_DLY : OSTickCtr + dly * OS_OPT_TIME_TIMEOUT : OSTickCtr + dly * OS_OPT_TIME_MATCH : dly * OS_OPT_TIME_PERIODIC : OSTCBCurPtr.TickCtrPrev + dly * * opt specifies whether 'dly' represents absolute or relative time; default option marked with *** : * * *** OS_OPT_TIME_DLY specifies a relative time from the current value of OSTickCtr. * OS_OPT_TIME_TIMEOUT same as OS_OPT_TIME_DLY. * OS_OPT_TIME_MATCH indicates that 'dly' specifies the absolute value that OSTickCtr * must reach before the task will be resumed. * OS_OPT_TIME_PERIODIC indicates that 'dly' specifies the periodic value that OSTickCtr * must reach before the task will be resumed. * * p_err is a pointer to a variable that will contain an error code from this call. * * OS_ERR_NONE the call was successful and the delay occurred. * OS_ERR_OPT_INVALID if you specified an invalid option for this function. * OS_ERR_SCHED_LOCKED can't delay when the scheduler is locked. * OS_ERR_TIME_DLY_ISR if you called this function from an ISR. * OS_ERR_TIME_ZERO_DLY if you specified a delay of zero. * * Returns : none ************************************************************************************************************************ */ void OSTimeDly (OS_TICK dly, OS_OPT opt, OS_ERR *p_err) { CPU_SR_ALLOC(); #ifdef OS_SAFETY_CRITICAL if (p_err == (OS_ERR *)0) { OS_SAFETY_CRITICAL_EXCEPTION(); return; } #endif #if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u if (OSIntNestingCtr > (OS_NESTING_CTR)0u) { /* Not allowed to call from an ISR */ *p_err = OS_ERR_TIME_DLY_ISR; return; } #endif if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0u) { /* Can't delay when the scheduler is locked */ *p_err = OS_ERR_SCHED_LOCKED; return; } switch (opt) { case OS_OPT_TIME_DLY: case OS_OPT_TIME_TIMEOUT: case OS_OPT_TIME_PERIODIC: if (dly == (OS_TICK)0u) { /* 0 means no delay! */ *p_err = OS_ERR_TIME_ZERO_DLY; return; } break; case OS_OPT_TIME_MATCH: break; default: *p_err = OS_ERR_OPT_INVALID; return; } OS_CRITICAL_ENTER(); OS_TickListInsertDly(OSTCBCurPtr, dly, opt, p_err); if (*p_err != OS_ERR_NONE) { OS_CRITICAL_EXIT_NO_SCHED(); return; } #if (defined(TRACE_CFG_EN) && (TRACE_CFG_EN > 0u)) TRACE_OS_TASK_DLY(dly); /* Record the event. */ #endif OS_RdyListRemove(OSTCBCurPtr); /* Remove current task from ready list */ OS_CRITICAL_EXIT_NO_SCHED(); OSSched(); /* Find next task to run! */ *p_err = OS_ERR_NONE; } /* ************************************************************************************************************************ * DELAY TASK FOR SPECIFIED TIME * * Description: This function is called to delay execution of the currently running task until some time expires. This * call allows you to specify the delay time in HOURS, MINUTES, SECONDS and MILLISECONDS instead of ticks. * * Arguments : hours specifies the number of hours that the task will be delayed (max. is 999 if the tick rate is * 1000 Hz or less otherwise, a higher value would overflow a 32-bit unsigned counter). * * minutes specifies the number of minutes (max. 59 if 'opt' is OS_OPT_TIME_HMSM_STRICT) * * seconds specifies the number of seconds (max. 59 if 'opt' is OS_OPT_TIME_HMSM_STRICT) * * milli specifies the number of milliseconds (max. 999 if 'opt' is OS_OPT_TIME_HMSM_STRICT) * * opt specifies time delay bit-field options logically OR'd; default options marked with *** : * * *** OS_OPT_TIME_DLY specifies a relative time from the current value of OSTickCtr. * OS_OPT_TIME_TIMEOUT same as OS_OPT_TIME_DLY. * OS_OPT_TIME_MATCH indicates that the delay specifies the absolute value that OSTickCtr * must reach before the task will be resumed. * OS_OPT_TIME_PERIODIC indicates that the delay specifies the periodic value that OSTickCtr * must reach before the task will be resumed. * * *** OS_OPT_TIME_HMSM_STRICT strictly allow only hours (0...99) * minutes (0...59) * seconds (0...59) * milliseconds (0...999) * OS_OPT_TIME_HMSM_NON_STRICT allow any value of hours (0...999) * minutes (0...9999) * seconds (0...65535) * milliseconds (0...4294967295) * * p_err is a pointer to a variable that will receive an error code from this call. * * OS_ERR_NONE If the function returns from the desired delay * OS_ERR_OPT_INVALID If you specified an invalid option for 'opt' * OS_ERR_SCHED_LOCKED Can't delay when the scheduler is locked * OS_ERR_TIME_DLY_ISR If called from an ISR * OS_ERR_TIME_INVALID_HOURS If you didn't specify a valid value for 'hours' * OS_ERR_TIME_INVALID_MINUTES If you didn't specify a valid value for 'minutes' * OS_ERR_TIME_INVALID_SECONDS If you didn't specify a valid value for 'seconds' * OS_ERR_TIME_INVALID_MILLISECONDS If you didn't specify a valid value for 'milli' * OS_ERR_TIME_ZERO_DLY If hours, minutes, seconds and milli are all 0 * * Returns : none * * Note(s) : 1) The resolution on the milliseconds depends on the tick rate. For example, you can't do a 10 mS delay * if the ticker interrupts every 100 mS. In this case, the delay would be set to 0. The actual delay * is rounded to the nearest tick. * * 2) Although this function allows you to delay a task for many, many hours, it's not recommended to put * a task to sleep for that long. ************************************************************************************************************************ */ #if OS_CFG_TIME_DLY_HMSM_EN > 0u void OSTimeDlyHMSM (CPU_INT16U hours, CPU_INT16U minutes, CPU_INT16U seconds, CPU_INT32U milli, OS_OPT opt, OS_ERR *p_err) { #if OS_CFG_ARG_CHK_EN > 0u CPU_BOOLEAN opt_invalid; CPU_BOOLEAN opt_non_strict; #endif OS_OPT opt_time; OS_RATE_HZ tick_rate; OS_TICK ticks; CPU_SR_ALLOC(); #ifdef OS_SAFETY_CRITICAL if (p_err == (OS_ERR *)0) { OS_SAFETY_CRITICAL_EXCEPTION(); return; } #endif #if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u if (OSIntNestingCtr > (OS_NESTING_CTR)0u) { /* Not allowed to call from an ISR */ *p_err = OS_ERR_TIME_DLY_ISR; return; } #endif if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0u) { /* Can't delay when the scheduler is locked */ *p_err = OS_ERR_SCHED_LOCKED; return; } opt_time = opt & OS_OPT_TIME_MASK; /* Retrieve time options only. */ switch (opt_time) { case OS_OPT_TIME_DLY: case OS_OPT_TIME_TIMEOUT: case OS_OPT_TIME_PERIODIC: if (milli == (CPU_INT32U)0u) { /* Make sure we didn't specify a 0 delay */ if (seconds == (CPU_INT16U)0u) { if (minutes == (CPU_INT16U)0u) { if (hours == (CPU_INT16U)0u) { *p_err = OS_ERR_TIME_ZERO_DLY; return; } } } } break; case OS_OPT_TIME_MATCH: break; default: *p_err = OS_ERR_OPT_INVALID; return; } #if OS_CFG_ARG_CHK_EN > 0u /* Validate arguments to be within range */ opt_invalid = DEF_BIT_IS_SET_ANY(opt, ~OS_OPT_TIME_OPTS_MASK); if (opt_invalid == DEF_YES) { *p_err = OS_ERR_OPT_INVALID; return; } opt_non_strict = DEF_BIT_IS_SET(opt, OS_OPT_TIME_HMSM_NON_STRICT); if (opt_non_strict != DEF_YES) { if (milli > (CPU_INT32U)999u) { *p_err = OS_ERR_TIME_INVALID_MILLISECONDS; return; } if (seconds > (CPU_INT16U)59u) { *p_err = OS_ERR_TIME_INVALID_SECONDS; return; } if (minutes > (CPU_INT16U)59u) { *p_err = OS_ERR_TIME_INVALID_MINUTES; return; } if (hours > (CPU_INT16U)99u) { *p_err = OS_ERR_TIME_INVALID_HOURS; return; } } else { if (minutes > (CPU_INT16U)9999u) { *p_err = OS_ERR_TIME_INVALID_MINUTES; return; } if (hours > (CPU_INT16U)999u) { *p_err = OS_ERR_TIME_INVALID_HOURS; return; } } #endif /* Compute the total number of clock ticks required.. */ /* .. (rounded to the nearest tick) */ tick_rate = OSCfg_TickRate_Hz; ticks = ((OS_TICK)hours * (OS_TICK)3600u + (OS_TICK)minutes * (OS_TICK)60u + (OS_TICK)seconds) * tick_rate + (tick_rate * ((OS_TICK)milli + (OS_TICK)500u / tick_rate)) / (OS_TICK)1000u; if (ticks > (OS_TICK)0u) { OS_CRITICAL_ENTER(); OS_TickListInsertDly(OSTCBCurPtr, ticks, opt_time, p_err); if (*p_err != OS_ERR_NONE) { OS_CRITICAL_EXIT_NO_SCHED(); return; } #if (defined(TRACE_CFG_EN) && (TRACE_CFG_EN > 0u)) TRACE_OS_TASK_DLY(ticks); /* Record the event. */ #endif OS_RdyListRemove(OSTCBCurPtr); /* Remove current task from ready list */ OS_CRITICAL_EXIT_NO_SCHED(); OSSched(); /* Find next task to run! */ *p_err = OS_ERR_NONE; } else { *p_err = OS_ERR_TIME_ZERO_DLY; } } #endif /* ************************************************************************************************************************ * RESUME A DELAYED TASK * * Description: This function is used resume a task that has been delayed through a call to either OSTimeDly() or * OSTimeDlyHMSM(). Note that you cannot call this function to resume a task that is waiting for an event * with timeout. * * Arguments : p_tcb is a pointer to the TCB of the task to resume. * * p_err is a pointer to a variable that will receive an error code * * OS_ERR_NONE Task has been resumed * OS_ERR_STATE_INVALID Task is in an invalid state * OS_ERR_TIME_DLY_RESUME_ISR If called from an ISR * OS_ERR_TIME_NOT_DLY Task is not waiting for time to expire * OS_ERR_TASK_SUSPENDED Task cannot be resumed, it was suspended by OSTaskSuspend() * * Note(s) : none ************************************************************************************************************************ */ #if OS_CFG_TIME_DLY_RESUME_EN > 0u void OSTimeDlyResume (OS_TCB *p_tcb, OS_ERR *p_err) { CPU_SR_ALLOC(); #ifdef OS_SAFETY_CRITICAL if (p_err == (OS_ERR *)0) { OS_SAFETY_CRITICAL_EXCEPTION(); return; } #endif #if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u if (OSIntNestingCtr > (OS_NESTING_CTR)0u) { /* Not allowed to call from an ISR */ *p_err = OS_ERR_TIME_DLY_RESUME_ISR; return; } #endif #if OS_CFG_ARG_CHK_EN > 0u if (p_tcb == (OS_TCB *)0) { /* Not possible for the running task to be delayed! */ *p_err = OS_ERR_TASK_NOT_DLY; return; } #endif CPU_CRITICAL_ENTER(); if (p_tcb == OSTCBCurPtr) { /* Not possible for the running task to be delayed! */ *p_err = OS_ERR_TASK_NOT_DLY; CPU_CRITICAL_EXIT(); return; } switch (p_tcb->TaskState) { case OS_TASK_STATE_RDY: /* Cannot Abort delay if task is ready */ CPU_CRITICAL_EXIT(); *p_err = OS_ERR_TASK_NOT_DLY; break; case OS_TASK_STATE_DLY: OS_CRITICAL_ENTER_CPU_EXIT(); p_tcb->TaskState = OS_TASK_STATE_RDY; OS_TickListRemove(p_tcb); /* Remove task from tick list */ OS_RdyListInsert(p_tcb); /* Add to ready list */ OS_CRITICAL_EXIT_NO_SCHED(); *p_err = OS_ERR_NONE; break; case OS_TASK_STATE_PEND: CPU_CRITICAL_EXIT(); *p_err = OS_ERR_TASK_NOT_DLY; break; case OS_TASK_STATE_PEND_TIMEOUT: CPU_CRITICAL_EXIT(); *p_err = OS_ERR_TASK_NOT_DLY; break; case OS_TASK_STATE_SUSPENDED: CPU_CRITICAL_EXIT(); *p_err = OS_ERR_TASK_NOT_DLY; break; case OS_TASK_STATE_DLY_SUSPENDED: OS_CRITICAL_ENTER_CPU_EXIT(); p_tcb->TaskState = OS_TASK_STATE_SUSPENDED; OS_TickListRemove(p_tcb); /* Remove task from tick list */ OS_CRITICAL_EXIT_NO_SCHED(); *p_err = OS_ERR_TASK_SUSPENDED; break; case OS_TASK_STATE_PEND_SUSPENDED: CPU_CRITICAL_EXIT(); *p_err = OS_ERR_TASK_NOT_DLY; break; case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED: CPU_CRITICAL_EXIT(); *p_err = OS_ERR_TASK_NOT_DLY; break; default: CPU_CRITICAL_EXIT(); *p_err = OS_ERR_STATE_INVALID; break; } OSSched(); } #endif /* ************************************************************************************************************************ * GET CURRENT SYSTEM TIME * * Description: This function is used by your application to obtain the current value of the counter which keeps track of * the number of clock ticks. * * Arguments : p_err is a pointer to a variable that will receive an error code * * OS_ERR_NONE If the call was successful * * Returns : The current value of OSTickCtr ************************************************************************************************************************ */ OS_TICK OSTimeGet (OS_ERR *p_err) { OS_TICK ticks; CPU_SR_ALLOC(); #ifdef OS_SAFETY_CRITICAL if (p_err == (OS_ERR *)0) { OS_SAFETY_CRITICAL_EXCEPTION(); return ((OS_TICK)0); } #endif CPU_CRITICAL_ENTER(); ticks = OSTickCtr; CPU_CRITICAL_EXIT(); *p_err = OS_ERR_NONE; return (ticks); } /* ************************************************************************************************************************ * SET SYSTEM CLOCK * * Description: This function sets the counter which keeps track of the number of clock ticks. * * Arguments : ticks is the desired tick value * * p_err is a pointer to a variable that will receive an error code * * OS_ERR_NONE If the call was successful * * Returns : none ************************************************************************************************************************ */ void OSTimeSet (OS_TICK ticks, OS_ERR *p_err) { CPU_SR_ALLOC(); #ifdef OS_SAFETY_CRITICAL if (p_err == (OS_ERR *)0) { OS_SAFETY_CRITICAL_EXCEPTION(); return; } #endif CPU_CRITICAL_ENTER(); OSTickCtr = ticks; CPU_CRITICAL_EXIT(); *p_err = OS_ERR_NONE; } /* ************************************************************************************************************************ * PROCESS SYSTEM TICK * * Description: This function is used to signal to uC/OS-III the occurrence of a 'system tick' (also known as a * 'clock tick'). This function should be called by the tick ISR. * * Arguments : none * * Returns : none ************************************************************************************************************************ */ void OSTimeTick (void) { OS_ERR err; #if OS_CFG_ISR_POST_DEFERRED_EN > 0u CPU_TS ts; #endif OSTimeTickHook(); /* Call user definable hook */ #if OS_CFG_ISR_POST_DEFERRED_EN > 0u ts = OS_TS_GET(); /* Get timestamp */ OS_IntQPost((OS_OBJ_TYPE) OS_OBJ_TYPE_TICK, /* Post to ISR queue */ (void *)&OSRdyList[OSPrioCur], (void *) 0, (OS_MSG_SIZE) 0u, (OS_FLAGS ) 0u, (OS_OPT ) 0u, (CPU_TS ) ts, (OS_ERR *)&err); #else (void)OSTaskSemPost((OS_TCB *)&OSTickTaskTCB, /* Signal tick task */ (OS_OPT ) OS_OPT_POST_NONE, (OS_ERR *)&err); #if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u OS_SchedRoundRobin(&OSRdyList[OSPrioCur]); #endif #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 #endif }