====== OSA : Timers ======
===== Intro =====
Timers in OSA are used to simplify writing time-dependent processes. By using timers you can allocate a quantum of time to a task, or create delays or timeouts in functions that are not tasks. Time in OSA is incremented by the service ##[[en:osa:ref:allservices:OS_Timer|OS_Timer]]##. When this service is called, all active timers are incremented by 1. If any timer overflows, then the corresponding timeout bit is set.
===== Types of timers =====
OSA allows the use of four types of timer:
* TTIMERS - [[#task timers]]
* STIMERS - [[#static timers]]
* QTIMERS - [[#queue of timers]]
* DTIMERS - [[#dynamic timers]] (old type)
Why so many? Which of them is better? See [[#how to choose a timer's type]].
Each type of timer has its advantages. The selection of the timer's type depends on the number of timers used and the purpose of the timers. The features of each type of timer are described below.
==== Task timers ====
== Description ==
Each task descriptor may contain a counter for use by delays (##[[en:osa:ref:allservices:OS_Delay|OS_Delay]]##) and by waits for events with timeout (OS_xxx_Wait_TO). This counter is called a **task timer**. It can be used to count time in the background (a task can perform some actions and check time).
== Advantages ==
- After a task has called ##[[en:osa:ref:allservices:OS_Delay|OS_Delay]]## the scheduler does not check the task for readiness and does not compare the task's priority with others until the timer has expired. This feature increases the speed of scheduler.
- The services OS_xxx_Wait_TO work with task timers only.
== Disadvantages ==
- One task - one timer. Each task can only control its own timer.
- Work with timers can be performed only inside the task or inside functions called by the task.
== Usage ==
Defines in ##[[en:osa:ref:appendix:configuration|OSAcfg.h]]##:
^^^
| ##[[en:osa:ref:appendix:configuration|OS_ENABLE_TTIMERS]]## | You should define this constant to use task timers |
| ##[[en:osa:ref:appendix:configuration|OS_TTIMER_SIZE]]## | Timer width in bytes: 1, 2 or 4 (2 by default). |
| ##[[en:osa:ref:appendix:configuration|OS_TTIMERS_OPTIMIZE_SIZE]]## | Optimize ##[[en:osa:ref:allservices:OS_Timer|OS_Timer]]## for code size (the default is for speed) |
| ##[[en:osa:ref:appendix:configuration|OS_BANK_TASKS]]## | Define RAM-bank to allocate task descriptors: 0,1,2 or 3 (0 by default) |
~~UP~~
==== Static timers ====
== Description ==
Static timers are implemented by an array of counters of size ##[[en:osa:ref:appendix:configuration|OS_STIMERS]]##. The counter width is set by the constant ##[[en:osa:ref:appendix:configuration|OS_STIMER_SIZE]]## in ##[[en:osa:ref:appendix:configuration|OSAcfg.h]]##. To use a static timer through an OS service, the timer's ID must be passed as a parameter. The most significant bit of each counter means "counting" when set and "stopped" ("timeout") when cleared. Thus the counter is one bit less than the size of variable needed to store it (e.g. when ##[[en:osa:ref:appendix:configuration|OS_STIMER_SIZE]]## = 2, the counter width is 15 bits). Static timers can be used to reduce RAM and ROM usage.
In OSA versions 91219 and below static timers were assigned at compile time and could not be re-assigned. Each timer was accessed through a fixed ID. This was awkward since there were problems with using existing modules in new projects (the programmer had to re-define all timer IDs in ##[[en:osa:ref:appendix:configuration|OSAcfg.h]]## and match them with the ##[[en:osa:ref:appendix:configuration|OS_STIMERS]]## constant). Starting with OSA version 100210 you can assign the ID at run-time (see below).
== Advantages ==
- compact
- fast
== Disadvantages ==
- you have to know the maximum number of timers that can be used simultaneously (to set the ##[[en:osa:ref:appendix:configuration|OS_STIMERS]]## constant)
- ##[[en:osa:ref:appendix:configuration|OS_STIMERS]]## can't exceed 64
== Usage ==
Defines in ##[[en:osa:ref:appendix:configuration|OSAcfg.h]]##:
^^^
| ##[[en:osa:ref:appendix:configuration|OS_STIMERS]]## | This constant defines the number of elements in the array of static timers |
| ##[[en:osa:ref:appendix:configuration|OS_STIMER_SIZE]]## | Timer width in bytes: 1, 2 or 4 (2 by default) |
| ##[[en:osa:ref:appendix:configuration|OS_STIMERS_OPTIMIZE_SIZE]]## | Optimize ##[[en:osa:ref:allservices:OS_Timer|OS_Timer]]## for code size (the default is for speed) |
| ##[[en:osa:ref:appendix:configuration|OS_STIMERS_ENABLE_ALLOCATION]]## | Allow assignment of static timer IDs at run-time. Enables services ##[[en:osa:ref:allservices:OS_Stimer_Alloc|OS_Stimer_Alloc]]##, ##[[en:osa:ref:allservices:OS_Stimer_Free|OS_Stimer_Free]]##, ##[[en:osa:ref:allservices:OS_Stimer_Found|OS_Stimer_Found]]## |
| ##[[en:osa:ref:appendix:configuration|OS_BANK_STIMERS]]## | Define RAM-bank to allocate static timers: 0,1,2 or 3 (0 by default) |
There are two ways of using static timers:
- Assign timer IDs at run time. In this case two small functions will be added to the program. In addition, one byte of RAM will be allocated for each eight static timers to indicate their state (busy/free);
- Assign timer IDs at compile time and use constants. In this case the timer's purpose has to be defined once and for all, and each timer must have a fixed ID.
The first way is preferable since it allows the re-use of modules in new projects with minimal configuration modifications.
~~UP~~
==== Queue of timers ====
== Description ==
This type of timer was introduced in OSA version 100210. All running timers are put into a queue sorted by remaining count time. This method allows processing a large number of timers very quickly. Each call to ##[[en:osa:ref:allservices:OS_Timer|OS_Timer]]## only decreases the first timer in the queue. This can be very useful, since ##[[en:osa:ref:allservices:OS_Timer|OS_Timer]]## is usually called from an interrupt routine.
1. For **PIC16**, these timers can be allocated in bank0 and bank1 only.\\ 2. Queue of timers is not supported by 12-bit controllers.
== Advantages ==
- you can use as many timers as you want
- fast processing of a large number of timers in ##[[en:osa:ref:allservices:OS_Timer|OS_Timer]]##
== Disadvantages ==
- each timer needs an additional two bytes (or two words in C30) for flags and pointer to next timer in queue;
- needs a huge function to control queue (add/remove timers);
- adding a timer to the queue may take a long time, depending on the value of the timer to be added: a greater value -> longer time to add
== Usage ==
Defines in ##[[en:osa:ref:appendix:configuration|OSAcfg.h]]##:
^^^
| ##[[en:osa:ref:appendix:configuration|OS_ENABLE_QTIMERS]]## | You must define this constant to use a queue of timers |
| ##[[en:osa:ref:appendix:configuration|OS_QTIMER_SIZE]]## | Timer width in bytes: 1, 2 or 4 (2 by default) |
Timers are declared in your program as normal variables:
OST_QTIMER qt_1, qt_2;
The timer must be created before using it (by the service ##[[en:osa:ref:allservices:OS_Qtimer_Create|OS_Qtimer_Create]]##). After creation, the timer can be run, got, checked, etc. On every call to ##[[en:osa:ref:allservices:OS_Timer|OS_Timer]]## the first timer in the queue is decreased by 1. When it becomes zero it is marked as "timed-out" and deleted from the queue.
E.g. we want to run two timers with counter values 30 and 10:
OS_Qtimer_Run(qt_1, 30);
OS_Qtimer_Run(qt_2, 10);
The first timer qt_1 will be added to an empty queue. It is placed at the beginning of queue with counter value = 30. When qt_2 is added it will be placed at the beginning of the queue (since its counter value is less than qt_1) and qt_1 will now be in second position. The value of the counter of qt_1 will be changed to 20. On every call of ##[[en:osa:ref:allservices:OS_Timer|OS_Timer]]## qt_2's counter will be decreased by 1. When it becomes zero it will be deleted from the queue ("timeout" bit will be set), and timer qt_1 will become first in the queue with a counter value of 20 (10 ticks have elapsed, 20 remain, for a total = 30).
Now if we want to add a third timer with value 100:
OS_Qtimer_Run(qt_3, 100);
it will be placed at the end of the queue with a counter value = 100-20 = 80.
~~UP~~
==== Dynamic timers ====
== Description ==
(This is an old type of timer. It is not recommended for use in new projects.)
Dynamic timers are organized as a one-directional unsorted list. On every call to ##[[en:osa:ref:allservices:OS_Timer|OS_Timer]]##() all timers in the list are decremented by 1. This is done through indirect accessing, so the run time of ##[[en:osa:ref:allservices:OS_Timer|OS_Timer]]##() can be rather long when there are a lot of timers in the list.
The important feature of dynamic timers is that they continue counting even after timeout occurs. This feature allows you to make several time markers with greater accuracy than with other types of timer (without losses due to running the scheduler).
For **PIC16**, these timers can be allocated in bank0 and bank1 only.
== Advantages ==
- continue counting after overflow occurs
- you can use as many timers as RAM allows
== Disadvantages ==
- ##[[en:osa:ref:allservices:OS_Timer|OS_Timer]]## takes a long time
- each timer needs an additional two bytes (or two words in C30) for flags and pointer to the next timer in the list
== Usage ==
Defines in ##[[en:osa:ref:appendix:configuration|OSAcfg.h]]##:
^^^
| ##[[en:osa:ref:appendix:configuration|OS_ENABLE_DTIMERS]]## | You must define this constant to use dynamic timers |
| ##[[en:osa:ref:appendix:configuration|OS_DTIMER_SIZE]]## | Timer width in bytes: 1, 2 or 4 (2 by default) |
Timers are declared in your program as normal variables:
OST_DTIMER dt_1, dt_2;
First, timers should be created by the ##[[en:osa:ref:allservices:OS_Dtimer_Create|OS_Dtimer_Create]]## service. This will add a timer to the list (the timer will be cleared and stopped). After using the timer, it should be deleted from the list (by ##[[en:osa:ref:allservices:OS_Dtimer_Delete|OS_Dtimer_Delete]]##) to reduce ##[[en:osa:ref:allservices:OS_Timer|OS_Timer]]##'s processing time.
===== Maximum delays =====
Here are the maximum possible time intervals that timers can count for the most commonly-used sizes of system ticks:
^ ##[[en:osa:ref:allservices:OS_Timer|OS_Timer]]##'s period ^ 1-byte ^ 2-bytes ^ 4-bytes ^
| Range of allowed init values | 0-255 | 0-65535 | 0-4294967295 |
| 1 ms | 255 ms | 64 sec | 48 days |
| 10 ms | 2.4 sec | 10 min | 490 days |
| 18.2 ms | 9.2 sec | 18 min | 900 days |
| 256 us | 130 ms | 16 sec | 12 days |
| 1024 us | 520 ms | 65 sec | 50 days |
| 8192 us | 4 sec | 8 min | 400 days |
As noted above, a static timer's width is one bit less then its type. Thus all time intervals in the table must be divided by 2 for static timers.
~~UP~~
===== OS services for timers =====
^ Action ^ TTIMERS ^ STIMERS ^ DTIMERS ^ QTIMERS ^
|**Create/delete** |||||
|Creation | |##[[en:osa:ref:allservices:OS_Stimer_Alloc|OS_Stimer_Alloc]]## |##[[en:osa:ref:allservices:OS_Dtimer_Create|OS_Dtimer_Create]]## | ##[[en:osa:ref:allservices:OS_Qtimer_Create|OS_Qtimer_Create]]## |
|Deleting | |##[[en:osa:ref:allservices:OS_Stimer_Free|OS_Stimer_Free]]## |##[[en:osa:ref:allservices:OS_Dtimer_Delete|OS_Dtimer_Delete]]## | ##[[en:osa:ref:allservices:OS_Qtimer_Delete|OS_Qtimer_Delete]]## |
|Search for free | |##[[en:osa:ref:allservices:OS_Stimer_Found|OS_Stimer_Found]]## | | |
|**Use** |||||
|Running |##[[en:osa:ref:allservices:OS_Ttimer_Run|OS_Ttimer_Run]]## |##[[en:osa:ref:allservices:OS_Stimer_Run|OS_Stimer_Run]]## |##[[en:osa:ref:allservices:OS_Dtimer_Run|OS_Dtimer_Run]]## | ##[[en:osa:ref:allservices:OS_Qtimer_Run|OS_Qtimer_Run]]## |
|Breaking |##[[en:osa:ref:allservices:OS_Ttimer_Break|OS_Ttimer_Break]]## |##[[en:osa:ref:allservices:OS_Stimer_Break|OS_Stimer_Break]]## |##[[en:osa:ref:allservices:OS_Dtimer_Break|OS_Dtimer_Break]]## | ##[[en:osa:ref:allservices:OS_Qtimer_Break|OS_Qtimer_Break]]## |
|Delaying|##[[en:osa:ref:allservices:OS_Ttimer_Delay|OS_Ttimer_Delay]]## |##[[en:osa:ref:allservices:OS_Stimer_Delay|OS_Stimer_Delay]]## |##[[en:osa:ref:allservices:OS_Dtimer_Delay|OS_Dtimer_Delay]]## | ##[[en:osa:ref:allservices:OS_Qtimer_Delay|OS_Qtimer_Delay]]## |
|Waiting |##[[en:osa:ref:allservices:OS_Ttimer_Wait|OS_Ttimer_Wait]]## |##[[en:osa:ref:allservices:OS_Stimer_Wait|OS_Stimer_Wait]]## |##[[en:osa:ref:allservices:OS_Dtimer_Wait|OS_Dtimer_Wait]]## | ##[[en:osa:ref:allservices:OS_Qtimer_Wait|OS_Qtimer_Wait]]## |
|**Checking state** |||||
|Check if timeout|##[[en:osa:ref:allservices:OS_Ttimer_Check|OS_Ttimer_Check]]## |##[[en:osa:ref:allservices:OS_Stimer_Check|OS_Stimer_Check]]## |##[[en:osa:ref:allservices:OS_Dtimer_Check|OS_Dtimer_Check]]## | ##[[en:osa:ref:allservices:OS_Qtimer_Check|OS_Qtimer_Check]]## |
|Check if counting|##[[en:osa:ref:allservices:OS_Ttimer_IsRun|OS_Ttimer_IsRun]]## |##[[en:osa:ref:allservices:OS_Stimer_IsRun|OS_Stimer_IsRun]]## |##[[en:osa:ref:allservices:OS_Dtimer_IsRun|OS_Dtimer_IsRun]]## | ##[[en:osa:ref:allservices:OS_Qtimer_IsRun|OS_Qtimer_IsRun]]## |
|Getting current\\ counter's value|##[[en:osa:ref:allservices:OS_Ttimer_Get|OS_Ttimer_Get]]## |##[[en:osa:ref:allservices:OS_Stimer_Get|OS_Stimer_Get]]## |##[[en:osa:ref:allservices:OS_Dtimer_Get|OS_Dtimer_Get]]## | ##[[en:osa:ref:allservices:OS_Qtimer_Get|OS_Qtimer_Get]]## |
|**Addition** |||||
|Pausing |##[[en:osa:ref:allservices:OS_Ttimer_Pause|OS_Ttimer_Pause]]## |##[[en:osa:ref:allservices:OS_Stimer_Pause|OS_Stimer_Pause]]## |##[[en:osa:ref:allservices:OS_Dtimer_Pause|OS_Dtimer_Pause]]## | |
|Continue paused |##[[en:osa:ref:allservices:OS_Ttimer_Continue|OS_Ttimer_Continue]]## |##[[en:osa:ref:allservices:OS_Stimer_Continue|OS_Stimer_Continue]]## |##[[en:osa:ref:allservices:OS_Dtimer_Continue|OS_Dtimer_Continue]]## | |
|+|||||
|Is it active | | |##[[en:osa:ref:allservices:OS_Dtimer_IsActive|OS_Dtimer_IsActive]]## | |
|Increase counting time | | |##[[en:osa:ref:allservices:OS_Dtimer_Add|OS_Dtimer_Add]]## | |
|Check if paused | | |##[[en:osa:ref:allservices:OS_Dtimer_IsStopped|OS_Dtimer_IsStopped]]## | |
|Update counting time | | |##[[en:osa:ref:allservices:OS_Dtimer_Update|OS_Dtimer_Update]]## | |
~~UP~~
===== How to choose a timer's type =====
==== Why are there so many types of timer? ====
The large number of timer types is a result of OSA evolution. Initially, timers were in task descriptors only. But there are cases when one task needs more than one timer, or when several tasks want to check one timer. Thus user timers were added. First they looked like four arrays (of chars, ints, longs and 24-bit ints). But they were too awkward. So only one array remained (size of elements user-specified by ##[[en:osa:ref:appendix:configuration|OS_STIMER_SIZE]]## constant). But an array was still awkward, and after a while I added dynamic timers. They could be declared in any place in the program. But dynamic timers were too slow. And then I added a queue of timers.
==== "What timer should I use?" ====
In most cases task timers are enough. They have only one limitation: one task can use only one timer. But most problems can be solved with it.
But there are some problems that need more than one timer per task and the programmer has to use some other type of timer. There is no necessity to use several types of timers in one project (except paired with task timers). My opinion is that the best type of timer in most cases is the static timer. It is fast and compact. But if you use a large number of timers it may be reasonable to use a queue of timers.
==== Example formulas for PICC18 ====
The choice of timer type should depend on their functions and number. Look at the formulas below. They are for PICC18 and timer size = 2 bytes (for other compilers result will be rather close)
^ ^ TTIMERS ^^ STIMERS ^^ DTIMERS ^ QTIMERS ^
^ ^ (speed)* ^ (size)* ^ (speed)* ^ (size)* ^ ^ ^
| **ROM**, words | ##t##*6+30 | 57 | ##t##*4 (+60)* * | 14 (+60)* * | 74 | 482 |
| **RAM**, bytes | ##t##*2 || t*2 (+t/8)* * || ##t##*4+2 ||
| **##[[en:osa:ref:allservices:OS_Timer|OS_Timer]]##**, cycles |\ ##t##*4+##To##*1\ |\ ##t##*15+##Ta##*6+##To##*2+4\ |\ ##t##*3+##Ta##*1\ |\ ##t##*8+##Ta##*3+3\ |\ ##t##*9+##Ta##*13+5\ |\ 17+##To##*22\ |
* - task timers and static timers can be optimized by speed or by size by setting ##[[en:osa:ref:appendix:configuration|OS_TTIMERS_OPTIMIZE_SIZE]]## and ##[[en:osa:ref:appendix:configuration|OS_STIMERS_OPTIMIZE_SIZE]]## constants in ##[[en:osa:ref:appendix:configuration|OSAcfg.h]]##\\
* * - 60 bytes of ROM and 1 byte rep each 8 timers of RAM are added when static timer assignment is used (##[[en:osa:ref:appendix:configuration|OS_STIMERS_ENABLE_ALLOCATION]]## is defined)
Here:
* ##t## - total number of timers of chosen type used in program
* ##Ta## - number of currently active (counting) timers
* ##To## - number of currently overflowing timers
The timer type should be chosen depending on optimization criteria (ROM, RAM or speed) and available resources. The two tables below correspond to the number of timers equal to 5 and 50.
== Number of timers = 5 ==
^ ^ TTIMERS ^^ STIMERS ^^ DTIMERS ^ QTIMERS ^
^ ^ (Speed) ^ (size) ^ (speed) ^ (size) ^ ^ ^
| **ROM**, words | 60 | 57 | 80 | 74 | 74 | 482 |
| **RAM**, bytes | 10 || 11 || 22 ||
| **##[[en:osa:ref:allservices:OS_Timer|OS_Timer]]##**,\\ cycles min/cycles max* |\ \ 20 (25)\ \ |\ \ 109 (119)\ \ |\ \ 20 (20)\ \ |\ \ 58 (58)\ \ |\ \ 50 (115)\ \ |\ \ 17 (127)\ \ |
* - **cycles min** - ##[[en:osa:ref:allservices:OS_Timer|OS_Timer]]## execution time when there is no overflowing timers.\\
**cycle max** - rare situation when all timers overflow at the same time.
As you can see from this table, all parameters are almost identical when the number of timers is small (except ROM size for QTIMERS). Thus, if you use a small number of timers then you can use any type of timers you like.
== Number of timers = 50 ==
^ ^ TTIMERS ^^ STIMERS ^^ DTIMERS ^ QTIMERS ^
^ ^ (Speed) ^ (size) ^ (speed) ^ (size) ^ ^ ^
| **ROM**, words | 330 | 57 | 260 | 74 | 74 | 482 |
| **RAM**, bytes | 100 || 107 || 202 ||
| **##[[en:osa:ref:allservices:OS_Timer|OS_Timer]]##**,\\ cycles min/cycles max* |\ \ 200 (250)\ \ |\ \ 1054 (1154)\ \ |\ \ 200 (200)\ \ |\ \ 553 (553)\ \ |\ \ 1105\ \ |\ \ 17 (1122)\ \ |
* - **cycles min** - ##[[en:osa:ref:allservices:OS_Timer|OS_Timer]]## execution time when there is no overflowing timers.\\
**cycle max** - rare situation when all timers overflow at the same time.
Now we can see that the right choice will reduce RAM or ROM usage or will increase processing speed. For example we see that the most compact type is task timers with code size optimization. Or we see that the fastest is a queue of timers with one exception: when the first timer in the queue overflows, it should be removed from the queue. That takes some time, so if you need to use short delays, then a queue of timers will not be an effective choice.