OpenRTX/rtos/uC-OS3/Source/os_time.c

601 wiersze
22 KiB
C

/*
*********************************************************************************************************
* uC/OS-III
* The Real-Time Kernel
*
* Copyright 2009-2020 Silicon Laboratories Inc. www.silabs.com
*
* SPDX-License-Identifier: APACHE-2.0
*
* This software is subject to an open source license and is distributed by
* Silicon Laboratories Inc. pursuant to the terms of the Apache License,
* Version 2.0 available at www.apache.org/licenses/LICENSE-2.0.
*
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* TIME MANAGEMENT
*
* File : os_time.c
* Version : V3.08.00
*********************************************************************************************************
*/
#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_OS_NOT_RUNNING If uC/OS-III is not running yet
* 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 the effective delay is zero
* OS_ERR_TICK_DISABLED If kernel ticks are disabled
*
* Returns : none
*
* Note(s) : none
************************************************************************************************************************
*/
void OSTimeDly (OS_TICK dly,
OS_OPT opt,
OS_ERR *p_err)
{
#if (OS_CFG_TICK_EN > 0u)
CPU_SR_ALLOC();
#endif
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#if (OS_CFG_TICK_EN == 0u)
*p_err = OS_ERR_TICK_DISABLED;
return;
#else
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u)
if (OSIntNestingCtr > 0u) { /* Not allowed to call from an ISR */
*p_err = OS_ERR_TIME_DLY_ISR;
return;
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN > 0u)
if (OSRunning != OS_STATE_OS_RUNNING) { /* Is the kernel running? */
*p_err = OS_ERR_OS_NOT_RUNNING;
return;
}
#endif
if (OSSchedLockNestingCtr > 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:
case OS_OPT_TIME_MATCH:
break;
default:
*p_err = OS_ERR_OPT_INVALID;
return;
}
#if (OS_CFG_TICK_EN > 0u)
CPU_CRITICAL_ENTER();
OS_TickListInsertDly(OSTCBCurPtr,
dly,
opt,
p_err);
if (*p_err != OS_ERR_NONE) {
CPU_CRITICAL_EXIT();
return;
}
OS_RdyListRemove(OSTCBCurPtr); /* Remove current task from ready list */
CPU_CRITICAL_EXIT();
OSSched(); /* Find next task to run! */
#endif
#endif
}
/*
************************************************************************************************************************
* 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_OS_NOT_RUNNING If uC/OS-III is not running yet
* 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 the effective delay is zero
* OS_ERR_TICK_DISABLED If kernel ticks are disabled
*
* 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_TICK_EN > 0u)
#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();
#endif
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#if (OS_CFG_TICK_EN == 0u)
*p_err = OS_ERR_TICK_DISABLED;
return;
#else
#if (OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u)
if (OSIntNestingCtr > 0u) { /* Not allowed to call from an ISR */
*p_err = OS_ERR_TIME_DLY_ISR;
return;
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN > 0u)
if (OSRunning != OS_STATE_OS_RUNNING) { /* Is the kernel running? */
*p_err = OS_ERR_OS_NOT_RUNNING;
return;
}
#endif
if (OSSchedLockNestingCtr > 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:
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 = ((((opt) & (~OS_OPT_TIME_OPTS_MASK)) == 0u) ? (OS_FALSE) : (OS_TRUE));
if (opt_invalid == OS_TRUE) {
*p_err = OS_ERR_OPT_INVALID;
return;
}
opt_non_strict = ((((opt) & (OS_OPT_TIME_HMSM_NON_STRICT)) == 0u) ? (OS_FALSE) : (OS_TRUE));
if (opt_non_strict != OS_TRUE) {
if (milli > 999u) {
*p_err = OS_ERR_TIME_INVALID_MILLISECONDS;
return;
}
if (seconds > 59u) {
*p_err = OS_ERR_TIME_INVALID_SECONDS;
return;
}
if (minutes > 59u) {
*p_err = OS_ERR_TIME_INVALID_MINUTES;
return;
}
if (hours > 99u) {
*p_err = OS_ERR_TIME_INVALID_HOURS;
return;
}
} else {
if (minutes > 9999u) {
*p_err = OS_ERR_TIME_INVALID_MINUTES;
return;
}
if (hours > 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);
CPU_CRITICAL_ENTER();
OS_TickListInsertDly(OSTCBCurPtr,
ticks,
opt_time,
p_err);
if (*p_err != OS_ERR_NONE) {
CPU_CRITICAL_EXIT();
return;
}
OS_RdyListRemove(OSTCBCurPtr); /* Remove current task from ready list */
CPU_CRITICAL_EXIT();
OSSched(); /* Find next task to run! */
#endif
}
#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_OS_NOT_RUNNING If uC/OS-III is not running yet
* OS_ERR_STATE_INVALID Task is in an invalid state
* OS_ERR_TASK_NOT_DLY Task is not waiting for time to expire
* OS_ERR_TASK_SUSPENDED Task cannot be resumed, it was suspended by OSTaskSuspend()
* OS_ERR_TCB_INVALID If 'p_tcb' is a NULL pointer
* OS_ERR_TIME_DLY_RESUME_ISR If called from an ISR
*
* Returns : none
*
* 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 > 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) /* ---------------- VALIDATE ARGUMENTS ---------------- */
if (p_tcb == (OS_TCB *)0) { /* User must supply a valid OS_TCB */
*p_err = OS_ERR_TCB_INVALID;
return;
}
#endif
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN > 0u)
if (OSRunning != OS_STATE_OS_RUNNING) { /* Is the kernel running? */
*p_err = OS_ERR_OS_NOT_RUNNING;
return;
}
#endif
CPU_CRITICAL_ENTER();
switch (p_tcb->TaskState) {
case OS_TASK_STATE_RDY: /* Cannot Abort delay if task is ready */
case OS_TASK_STATE_PEND:
case OS_TASK_STATE_PEND_TIMEOUT:
case OS_TASK_STATE_SUSPENDED:
case OS_TASK_STATE_PEND_SUSPENDED:
case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_TASK_NOT_DLY;
break;
case OS_TASK_STATE_DLY:
p_tcb->TaskState = OS_TASK_STATE_RDY;
#if (OS_CFG_TICK_EN > 0u)
OS_TickListRemove(p_tcb); /* Remove task from tick list */
OS_RdyListInsert(p_tcb); /* Add to ready list */
#endif
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
break;
case OS_TASK_STATE_DLY_SUSPENDED:
p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;
#if (OS_CFG_TICK_EN > 0u)
OS_TickListRemove(p_tcb); /* Remove task from tick list */
#endif
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_TASK_SUSPENDED;
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
* OS_ERR_TICK_DISABLED If kernel ticks are disabled
*
* Returns : The current value of OSTickCtr
*
* Note(s) : none
************************************************************************************************************************
*/
OS_TICK OSTimeGet (OS_ERR *p_err)
{
OS_TICK ticks;
#if (OS_CFG_TICK_EN > 0u)
CPU_SR_ALLOC();
#endif
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return (0u);
}
#endif
#if (OS_CFG_TICK_EN > 0u)
CPU_CRITICAL_ENTER();
#if (OS_CFG_DYN_TICK_EN > 0u)
if (OSRunning == OS_STATE_OS_RUNNING) {
ticks = OSTickCtr + OS_DynTickGet();
} else {
ticks = OSTickCtr;
}
#else
ticks = OSTickCtr;
#endif
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
#else
ticks = 0u;
*p_err = OS_ERR_TICK_DISABLED;
#endif
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
* OS_ERR_TICK_DISABLED If kernel ticks are disabled
*
* Returns : none
*
* Note(s) : none
************************************************************************************************************************
*/
void OSTimeSet (OS_TICK ticks,
OS_ERR *p_err)
{
#if (OS_CFG_TICK_EN > 0u)
CPU_SR_ALLOC();
#else
(void)ticks;
#endif
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#if (OS_CFG_TICK_EN > 0u)
CPU_CRITICAL_ENTER();
OSTickCtr = ticks;
OS_TRACE_TICK_INCREMENT(OSTickCtr);
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
#else
*p_err = OS_ERR_TICK_DISABLED;
#endif
}
/*
************************************************************************************************************************
* 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
*
* Note(s) : none
************************************************************************************************************************
*/
void OSTimeTick (void)
{
if (OSRunning != OS_STATE_OS_RUNNING) {
return;
}
OSTimeTickHook(); /* Call user definable hook */
#if (OS_CFG_SCHED_ROUND_ROBIN_EN > 0u)
OS_SchedRoundRobin(&OSRdyList[OSPrioCur]); /* Update quanta ctr for the task which just ran */
#endif
#if (OS_CFG_TICK_EN > 0u)
OS_TickUpdate(1u); /* Update from the ISR */
#endif
}
/*
************************************************************************************************************************
* PROCESS SYSTEM TICK (DYNAMIC)
*
* 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
*
* Note(s) : none
************************************************************************************************************************
*/
#if (OS_CFG_DYN_TICK_EN > 0u)
void OSTimeDynTick (OS_TICK ticks)
{
if (OSRunning != OS_STATE_OS_RUNNING) {
return;
}
OSTimeTickHook();
OS_TickUpdate(ticks); /* Update from the ISR */
}
#endif