OS
v7.3.3
Documentation
|
STimers are an essential extension as they allow for accurate and efficient timekeeping without blocking tasks. Using STimers enables the embedded application to perform other critical tasks while the timer is running in the background. STimers also provide flexibility in the event that the timer needs to be paused, restarted, or adjusted on the fly. This can be particularly useful in applications that require dynamic timing or have unpredictable event intervals.
The Non-blocking design of the STimer is particularly important in cooperative scheduling because the tasks and operations of the OS are dependent on the cooperation of other tasks. Blocking timers can be problematic in a cooperative model because they halt the execution of the task, preventing other tasks from running. This can cause delays, missed deadlines, and other performance issues.
The STimers implementation does not access resources from the interrupt context, does not consume any significant processing time unless a timer has actually expired, does not add any processing overhead to the sys-tick interrupt, and does not walk any other data structures. The timer service just takes the value of the existing kernel clock source for reference \( t_{sys}\) , allowing timer functionality to be added to an application with minimal impact.
As illustrated in the figure, the time expiration check is roll-over safe by restricting it, to the only calculation that makes sense for timestamps, \( t_{sys} − X_{T_x} \), that yields a duration namely the amount of time elapsed between the current instant \( t_{sys}\) and the later instant, specifically, the tick taken at the arming instant with qSTimer_Set(), \( X_{t_i}\) Thanks to modular arithmetic, both of these are guaranteed to work fine across the clock-source rollover (a 32bit unsigned-counter), at least, as long the delays involved are shorter than 49.7
days.
Features
A STimer is referenced by a handle, a variable of type qSTimer_t and preferably, should be initialized by the QSTIMER_INITIALIZER
constant before any usage.
To use them, the code should follow a specific pattern that deals with the states of this object. All related APIs are designed to be non-blocking, this means there are ideal for use in cooperative environments such as the one provided by the OS itself. To minimize the implementation, this object is intentionally created to behave like a binary object, this implies that it only handles two states, Armed and Disarmed. An Armed timer means that it is already running with a specified preset value and a Disarmed timer is the opposite, which means that it does not have a preset value, so consequently, it is not running at all.
The arming action can be performed with qSTimer_Set() or qSTimer_FreeRun() and disarming with qSTimer_Disarm(). For qSTimer_FreeRun(), it checks the timer and performs the arming. If disarmed, it gets armed immediately with the specified time. If armed, the time argument is ignored and the API only checks for expiration. When the time expires, the STimer gets armed immediately taking the specified time.
All possible checking actions are also provided for this object, including qSTimer_Elapsed(), qSTimer_Remaining() and qSTimer_Expired() , with the last one being the most commonly used for timing applications. Finally, to get the current status of the STimer (check if is Armed or Disarmed) the qSTimer_Status() API should be used.
The example below shows a simple usage of this object. It is noteworthy that arming is performed once using the qEvent_t::FirstCall flag. This prevents the timer from being re-armed every time the task runs. After the timer expires, it should be disarmed explicitly