Application timers

The idea of application timers was introduced in an earlier article. They are kernel objects that provide tasks with simple means to time events or, more commonly, perform an activity on a regular basis. All the detail of timing functionality – accuracy, interrupt handling, etc. – in Nucleus SE was covered in the last article.

Using Timers

Application timers may be configured to be one-shot – i.e. they are started and then, after the assigned time period, they simply terminate. Or a timer may be configured to repeat – i.e. when it expires, it automatically restarts; the time interval for the restart period may be different from the initial time. A timer may also be optionally configured to run a specific function – an expiration routine – when (or each time) the timer expires.

Configuring Timers

Number of Timers

As with most aspects of Nucleus SE, the configuration of timers is primarily controlled by #define statements in nuse_config.h . The key setting is NUSE_TIMER_NUMBER , which determines how many timers are configured for the application. The default setting is 0 (i.e. no timers are in use) and you can set it to any value up to 16. An erroneous value will result in a compile time error, which is generated by a test in nuse_config_check.h (this is included into nuse_config.c and hence compiled with this module) resulting in a #error statement being compiled.

Choosing a non-zero value is the “master enable” for timers. This results in some data structures being defined and sized accordingly, of which more later in this article. It also activates the API enabling settings.

Expiration Routine Enable

In Nucleus SE, I looked for opportunities to make functionality optional, where its omission would save memory. A good example of that is support for timer expiration routines. Apart from being optional for each individual timer, the facility may be enabled (or not) for the whole application by means of the NUSE_TIMER_EXPIRATION_ROUTINE_SUPPORT setting in nuse_config.h . Setting this to FALSE suppresses the definition of two ROM data structures, of which more later in this article.

API Enables

Every API function (service call) in Nucleus SE has an enabling #define symbol in nuse_config.h . For timers, these are:

   NUSE_TIMER_CONTROL
   NUSE_TIMER_GET_REMAINING
   NUSE_TIMER_RESET
   NUSE_TIMER_INFORMATION
   NUSE_TIMER_COUNT

By default, all of these are set to FALSE , thus disabling each service call and inhibiting the inclusion of any implementation code. To configure timers for an application, you need to select the API calls that you want to use and set their enabling symbols to TRUE .

Here is an extract from the default nuse_config.h file.

   #define NUSE_TIMER_NUMBER  0  /* Number of application timers in
                                 the system – 0-16 */
                                 /* Service call enablers */
   #define NUSE_TIMER_CONTROL          FALSE
   #define NUSE_TIMER_GET_REMAINING    FALSE
   #define NUSE_TIMER_RESET            FALSE
   #define NUSE_TIMER_INFORMATION      FALSE
   #define NUSE_TIMER_COUNT            FALSE
    

A compile time error will result if a timer API function is enabled and no timers are configured (except for NUSE_Timer_Count() which is always permitted). If your code uses an API call, which has not been enabled, a link time error will result, as no implementation code will have been included in the application.

Timer Service Calls

Nucleus RTOS supports eight service calls that appertain to timers, which provide the following functionality:

  • Control (start/stop) a timer. Implemented by NUSE_Timer_Control() in Nucleus SE.

  • Obtain remaining time from a timer. Implemented by NUSE_Timer_Get_Remaining() in Nucleus SE.

  • Restore a timer to the unused state (reset). Implemented by NUSE_Timer_Reset() in Nucleus SE.

  • Provide information about a specified timer. Implemented by NUSE_Timer_Information() in Nucleus SE.

  • Return a count of how many timers are (currently) configured for the application. Implemented by NUSE_Timer_Count() in Nucleus SE.

  • Add a new timer to the application (create). Not implemented in Nucleus SE.

  • Remove a timer from the application (delete). Not implemented in Nucleus SE.

  • Return pointers to all the timers (currently) in the application. Not implemented in Nucleus SE.

The implementation of each of these service calls is examined in detail.

Timer Services

The fundamental operations, which can be performed on a timer, are controlling it – starting and stopping – and reading its current value. Nucleus RTOS and Nucleus SE each provide two basic API calls for these operations, which will be discussed here.

Controlling a Timer

The Nucleus RTOS API call for controlling a timer simply permits the timer to be enabled or disabled (i.e. started or stopped). Nucleus SE provides the same service.

Nucleus RTOS API Call for Controlling a Timer

Service call prototype:

   STATUS NU_Control_Timer(NU_TIMER *timer, OPTION enable);

Parameters:

timer – pointer to the user-supplied timer control block
enable – required function; may be NU_ENABLE_TIMER or NU_DISABLE_TIMER

Returns:

NU_SUCCESS – the call was completed successfully
NU_INVALID_TIMER – the timer pointer is invalid
NU_INVALID_ENABLE – the specified function is invalid

Nucleus SE API Call for Controlling a Timer

This API call supports the full functionality of the Nucleus RTOS API.

Service call prototype:

   STATUS NUSE_Timer_Control(NUSE_TIMER timer, OPTION enable);

Parameters:

timer – the index (ID) of the timer to be utilized
enable – required function; may be NUSE_ENABLE_TIMER or NUSE_DISABLE_TIMER

Returns:

NUSE_SUCCESS – the call was completed successfully
NUSE_INVALID_TIMER – the timer index is invalid
NUSE_INVALID_ENABLE – the specified function is invalid

Nucleus SE Implementation of Timer Control

The code of the NUSE_Timer_Control() API function – after parameter checking – is reasonably straightforward:

NUSE_CS_Enter();if (enable == NUSE_ENABLE_TIMER){   NUSE_Timer_Status[timer] = TRUE;   if (NUSE_Timer_Expirations_Counter[timer] == 0)   {      NUSE_Timer_Value[timer] = NUSE_Timer_Initial_Time[timer];   }   else   {      NUSE_Timer_Value[timer] = NUSE_Timer_Reschedule_Time[timer];   }}else  /* enable == NUSE_DISABLE_TIMER */{   NUSE_Timer_Status[timer] = FALSE;}NUSE_CS_Exit();

If the specified function is NUSE_DISABLE_TIMER , the timer’s status (NUSE_Timer_Status[] entry) is set to FALSE , which results in it being ignored by the clock ISR.

If NUSE_ENABLE_TIMER is specified, the timer counter (NUSE_Timer_Value[] ) is set to NUSE_Timer_Initial_Time[] , if the timer has never expired since last reset. Otherwise it is set to NUSE_Timer_Reschedule_Time[] . Then the timer’s status (NUSE_Timer_Status[] entry) is set to TRUE , which results in it being processed by the clock ISR.

Reading a Timer

The Nucleus RTOS API call for getting the remaining time from a timer does just that – returns the number of ticks before it will expire. Nucleus SE provides the same service.

Nucleus RTOS API Call for Getting Remaining Time

Service call prototype:

   STATUS NU_Get_Remaining_Time (NU_TIMER *timer,
                                 UNSIGNED *remaining_time);

Parameters:

timer – pointer to the user-supplied timer control block.
remaining_time – a pointer to storage for the remaining time value, which is a single variable of type UNSIGNED

Returns:

NU_SUCCESS – the call was completed successfully
NU_INVALID_TIMER – the timer pointer is invalid

Nucleus SE API Call for Getting Remaining Time

This API call supports the full functionality of the Nucleus RTOS API.

Service call prototype:

   STATUS NUSE_Timer_Get_Remaining(NUSE_TIMER timer,
                                   U16 *remaining_time);

Parameters:

timer – the index (ID) of the timer to be utilized
remaining_time – a pointer to storage for the remaining time value, which is a single variable of type U16

Returns:

NUSE_SUCCESS – the call was completed successfully
NUSE_INVALID_TIMER – the timer index is invalid
NUSE_INVALID_POINTER – the remaining time pointer is NULL

Nucleus SE Implementation of Timer Read

The bulk of the code of the NUSE_ Timer_Get_Remaining() API function – after parameter checking – is almost trivially simple. The value of NUSE_Timer_Value[] is obtained and returned within a critical section.

Timer Utility Services

Nucleus RTOS has four API calls which provide utility functions associated with timers: reset timer, return information about a timer, return number of timers in the application and return pointers to all timers in the application. The first three of these are implemented in Nucleus SE.

Resetting a Timer

This API call restores the timer to its initial, unused state. The timer may be enabled or disabled after the completion of this call. It is only allowed after a timer has been disabled (using NUSE_Timer_Control() ). On the next occasion that the timer is enabled, it will be initialized with its entry from NUSE_Timer_Initial_Time[] . Nucleus RTOS permits new initial and reschedule times to be provided and the expiration routine to be specified when a timer is reset; in Nucleus SE, these values are set at configuration time and cannot be changed, as they may be stored in ROM.

Nucleus RTOS API Call for Resetting a Timer

Service call prototype:

   STATUS NU_Reset_Timer(NU_TIMER *timer,
   VOID (*expiration_routine)(UNSIGNED), UNSIGNED initial_time,
   UNSIGNED reschedule_time, OPTION enable);

Parameters:

timer – pointer to the timer to be reset
expiration_routine – specifies the application routine to execute when the timer expires
initial_time – the initial number of timer ticks for timer expiration
reschedule_time – the number of timer ticks for expiration after the first expiration
enable – required state after reset; may be NU_ENABLE_TIMER or NU_DISABLE_TIMER

Returns:

NU_SUCCESS – the call was completed successfully
NU_INVALID_TIMER – the timer control block pointer is not valid
NU_INVALID_FUNCTION – the expiration function pointer is NULL
NU_INVALID_ENABLE – the specified state is invalid
NU_NOT_DISABLED – the time is currently enabled (disable required before reset)

Nucleus SE API Call for Resetting a Timer

This API call supports a simplified version of the key functionality of the Nucleus RTOS API.

Service call prototype:

   STATUS NUSE_Timer_Reset(NUSE_TIMER timer, OPTION enable);

Parameters:

timer – the index (ID) of the timer to be reset
enable – required state after reset; may be NUSE_ENABLE_TIMER or NUSE_DISABLE_TIMER

Returns:

NUSE_SUCCESS – the call was completed successfully
NUSE_INVALID_TIMER – the timer index is not valid
NUSE_INVALID_ENABLE – the specified state is invalid
NUSE_NOT_DISABLED – the time is currently enabled (disable required before reset)

Nucleus SE Implementation of Timer Reset

The bulk of the code of the NUSE_Timer_Reset() API function – after parameter and current status checking – is quite straightforward:

NUSE_CS_Enter();NUSE_Init_Timer(timer);if (enable == NUSE_ENABLE_TIMER){   NUSE_Timer_Status[timer] = TRUE;}/* else enable == NUSE_DISABLE_TIMER and status remains FALSE */NUSE_CS_Exit();

NUSE_Init_Timer() is called which initializes the time value and clears the expiration counter. A check is then made on required state and the timer enabled, if required.

Timer Information

This service call obtains a selection of information about a timer. The Nucleus SE implementation differs from Nucleus RTOS in that it returns less information, as object naming is not supported.

Nucleus RTOS API Call for Timer Information

Service call prototype:

   STATUS NU_Timer_Information(NU_TIMER *timer, CHAR *name,
   OPTION *enable, UNSIGNED *expirations, UNSIGNED *id,
   UNSIGNED *initial_time, UNSIGNED *reschedule_time);

Parameters:

timer – pointer to the timer about which information is being requested
name – pointer to an 8-character destination area for the timer’s name.
enable – a pointer to a variable, which will receive the timer’s current enable state: NU_ENABLE_TIMER or NU_DISABLE_TIMER
expirations – a pointer to a variable of type which will receive the count of the number of times the timer has expired since it was last reset
id – a pointer to a variable which will receive the value of the parameter passed to the timer’s expiration routine
initial_time – a pointer to a variable which will receive the value to which the timer is initialized on reset
reschedule_time – a pointer to a variable which will receive the value to which the timer is initialized on expiration

Returns:

NU_SUCCESS – the call was completed successfully
NU_INVALID_TIMER – the timer pointer is not valid

Nucleus SE API Call for Timer Information

This API call supports the key functionality of the Nucleus RTOS API.

Service call prototype:

   STATUS NUSE_Timer_Information(NUSE_TIMER timer, OPTION *enable,
   U8 *expirations, U8 *id, U16 *initial_time, U16 *reschedule_time);

Parameters:

timer – the index of the timer about which information is being requested
enable – a pointer to a variable, which will receive a value of TRUE or FALSE depending on whether the timer is enabled or not
expirations – a pointer to a variable of type U8 which will receive the count of the number of times the timer has expired since it was last reset
id – a pointer to a variable of type U8 which will receive the value of the parameter passed to the timer’s expiration routine (nothing returned if expiration routines are disabled)
initial_time – a pointer to a variable of type U16 which will receive the value to which the timer is initialized on reset
reschedule_time – a pointer to a variable of type U16 which will receive the value to which the timer is initialized on expiration

Returns:

NUSE_SUCCESS – the call was completed successfully
NUSE_INVALID_TIMER – the timer index is not valid
NUSE_INVALID_POINTER – one or more of the pointer parameters is invalid

Nucleus SE Implementation of Timer Information

The implementation of this API call is quite straightforward:

NUSE_CS_Enter();if (NUSE_Timer_Status[timer]){   *enable = NUSE_ENABLE_TIMER;}else{   *enable = NUSE_DISABLE_TIMER;}*expirations = NUSE_Timer_Expirations_Counter[timer];#if NUSE_TIMER_EXPIRATION_ROUTINE_SUPPORT   *id = NUSE_Timer_Expiration_Routine_Parameter[timer];#endif*initial_time = NUSE_Timer_Initial_Time[timer];*reschedule_time = NUSE_Timer_Reschedule_Time[timer];NUSE_CS_Exit();

The function returns the timer status. The value of the expiration routine parameter is only returned if expiration routines have been enabled in the application.

Obtaining the Number of Timers

This service call returns the number of timers configured in the application. Whilst in Nucleus RTOS this will vary over time and the returned value will represent the current number of timers, in Nucleus SE the value returned is set at build time and cannot change.

Nucleus RTOS API Call for Timer Count

Service call prototype:

   UNSIGNED NU_Established_Timers(VOID);

Parameters:

None

Returns:

The number of created timers in the system.

Nucleus SE API Call for Timer Count

This API call supports the key functionality of the Nucleus RTOS API.

Service call prototype:

   U8 NUSE_Timer_Count(void);

Parameters:

None

Returns:

The number of configured timers in the application.

Implementation of Timer Count

The implementation of this API call is almost trivially simple: the value of the #defin e symbol NUSE_TIMER_NUMBER is returned.

Data Structures

Timers utilize five or seven data structures – in RAM and ROM – which, like other Nucleus SE objects, are a series of tables, included and dimensioned according to the number of timers configured and options selected.

I strongly recommend that application code does not access these data structures directly, but uses the provided API functions. This avoids incompatibility with future versions of Nucleus SE and unwanted side-effects and simplifies porting of an application to Nucleus RTOS. The details of data structures are included here to facilitate easier understanding of the working of the service call code and for debugging.

RAM Data

These data structures are:

NUSE_Timer_Status[] – This is an array of type U8 , with an entry for each configured timer and is where the timer status (running or stopped: TRUE or FALSE ) is stored.

NUSE_Timer_Value[] – This is an array of type U16 , with one entry for each configured timer, which contains the current value of the timer’s counter.

NUSE_Timer_Expirations_Counter[] – This type U8 array contains the counts of how many times the timers have reached expiration since they were last reset.

These data structures are all initialized by NUSE_Init_Timer() when Nucleus SE starts up. A future article will provide a full description of Nucleus SE start-up procedures.

Here are the definitions of these data structures in nuse_init.c file:


   RAM U8 NUSE_Timer_Status[NUSE_TIMER_NUMBER];
   RAM U16 NUSE_Timer_Value[NUSE_TIMER_NUMBER];
   RAM U8 NUSE_Timer_Expirations_Counter[NUSE_TIMER_NUMBER];

ROM Data

These data structures are:

NUSE_Timer_Initial_Time[] – This is an array of type U16 , with one entry for each configured timer and is where the initial value for each timer’s counter is stored.

NUSE_Timer_Reschedule_Time[] – This is an array of type U16 , with one entry for each configured timer, which contains the value to which each timer’s counter should be set on expiration. A value of zero indicates that the timer is a “one shot” and should not be restarted automatically.

NUSE_Timer_Expiration_Routine_Address[] – This type ADDR array contains the address of the timers’ expiration routines. This array only exists if timer expiry routine support has been enabled.

NUSE_Timer_Expiration_Routine_Parameter[] – This type U8 array contains the value of a parameter which will be passed to the timers’ expiration routines. This array only exists if timer expiry routine support has been enabled.

These data structures are all declared and initialized (statically, of course) in nuse_config.c , thus:

ROM U16 NUSE_Timer_Initial_Time[NUSE_TIMER_NUMBER] ={   /* timer initial times ------ */};ROM U16 NUSE_Timer_Reschedule_Time[NUSE_TIMER_NUMBER] ={   /* timer reschedule times ------ */};#if NUSE_TIMER_EXPIRATION_ROUTINE_SUPPORT || NUSE_INCLUDE_EVERYTHING   /* need prototypes of expiration routines here */   ROM ADDR      NUSE_Timer_Expiration_Routine_Address[NUSE_TIMER_NUMBER] =   {      /* addresses of timer expiration routines ------ */      /* can be NULL */   };   ROM U8      NUSE_Timer_Expiration_Routine_Parameter[NUSE_TIMER_NUMBER] =   {      /* timer expiration routine parameters ------ */   };#endif

Timer Data Footprint

Like all kernel objects in Nucleus SE, the amount of data memory required for timers is readily predictable.

The RAM data footprint (in bytes) for all the timers in an application may be computed thus:

   NUSE_TIMER_NUMBER * 4

The ROM data footprint (in bytes) for all the timers in an application, if expiration routines are not supported, may be computed thus:

   NUSE_TIMER_NUMBER * 4

Otherwise, it is:

   NUSE_TIMER_NUMBER * (sizeof(ADDR) + 5)

Unimplemented API Calls

Three timer API calls found in Nucleus RTOS are not implemented in Nucleus SE:

Create Timer

This API call creates a timer. It is not needed with Nucleus SE, as timers are created statically.

Service call prototype:

   STATUS NU_Create_Timer(NU_TIMER *timer, CHAR *name,
   VOID (*expiration_routine)(UNSIGNED), UNSIGNED id,
   UNSIGNED initial_time, UNSIGNED reschedule_time, OPTION  enable);

Parameters:

timer – pointer to a user-supplied timer control block; this will be used as a “handle” for the timer in other API calls
name – pointers to a 7-character, null-terminated name for the timer
expiration_routines pecifies the application routine to execute when the timer expires
ida n UNSIGNED data element supplied to the expiration routine; the parameter may be used to help identify timers that use the same expiration routine
initial_time – specifies the initial number of timer ticks for timer expiration
reschedule_time – specifies the number of timer ticks for expiration after the first expiration; if this parameter is zero, the timer only expires once
enable – valid options for this parameter are NU_ENABLE_TIMER and NU_DISABLE_TIMER; NU_ENABLE_TIMER activates the timer after it is created; NU_DISABLE_TIMER leaves the timer disabled; timers created with the NU_DISABLE_TIMER must be enabled by a call to NU_Control_Timer later

Returns:

NU_SUCCESS – indicates successful completion of the service
NU_INVALID_TIMER – indicates the timer control block pointer is NULL or already in use
NU_INVALID_FUNCTION – indicates the expiration function pointer is NULL
NU_INVALID_ENABLE – indicates that the enable parameter is invalid
NU_INVALID_OPERATION – indicates that initial_time parameter was zero

Delete Timer

This API call deletes a previously created timer. It is not needed with Nucleus SE, as timers are created statically and cannot be deleted.

Service call prototype:

   STATUS NU_Delete_Timer(NU_TIMER *timer);

Parameters:

timer – pointer to timer control block

Returns:

NU_SUCCESS – indicates successful completion of the service
NU_INVALID_TIMER – indicates the timer pointer is invalid
NU_NOT_DISABLED – indicates the specified timer is not disabled

Timer Pointers

This API call builds a sequential list of pointers to all timers in the system. It is not needed with Nucleus SE, as timers are identified by a simple index, not a pointer, and it would be redundant.

Service call prototype:

   UNSIGNED NU_Timer_Pointers(NU_TIMER **pointer_list,
   UNSIGNED maximum_pointers);

Parameters:

pointer_list – pointer to an array of NU_TIMER pointers; this array will be filled with pointers to established timers in the system
maximum_pointers – the maximum number of pointers to place in the array

Returns:

The number of NU_TIMER pointers placed into the array

Compatibility with Nucleus RTOS

With all aspects of Nucleus SE, it was my goal to maintain as high a level of applications code compatibility with Nucleus RTOS as possible. Timers are no exception, and, from a user’s perspective, they are implemented in much the same way as in Nucleus RTOS. There are areas of incompatibility, which have come about where I determined that such an incompatibility would be acceptable, given that the resulting code is easier to understand, or, more likely, could be made more memory efficient. Otherwise, Nucleus RTOS API calls may be almost directly mapped onto Nucleus SE calls. A future article further information on using Nucleus SE for users of Nucleus RTOS.

Object Identifiers

In Nucleus RTOS, all objects are described by a data structure – a control block – which has a specific data type. A pointer to this control block serves as an identifier for the timer. In Nucleus SE, I decided that a different approach was needed for memory efficiency, and all kernel objects are described by a number of tables in RAM and/or ROM. The size of these tables is determined by the number of each object type that is configured. The identifier for a specific object is simply an index into those tables. So, I have defined NUSE_TIMER as being equivalent to U8 ; a variable – not a pointer – of this type then serves as the timer identifier. This is a small incompatibility, which is easily handled if code is ported to or from Nucleus RTOS. Object identifiers are normally just stored and passed around and not operated upon in any way.

Nucleus RTOS also supports naming of timers. These names are only used for target-based debug facilities. I omitted them from Nucleus SE to save memory.

Timer Size

In Nucleus RTOS, timers are implemented with 32-bit counters. I decided to reduce this to 16 bits in Nucleus SE. This change imparts significant increases in memory and execution time efficiency. Nucleus SE could be modified quite readily, if longer times were an application requirement.

Expiration Routines

Nucleus SE implements expiration routines in much the same way as in Nucleus RTOS, except that they may be disabled entirely (which saves some memory) and they are statically defined. It is not possible to change the expiration routine when a timer is reset.

Unimplemented API Calls

Nucleus RTOS supports eight service calls to work with timers. Of these, three are not implemented in Nucleus SE. Details of these and of the decision to omit them may be found in Unimplemented API Calls earlier in this article.

The next RTOS Revealed article will look at interrupts.


Colin Walls has nearly forty years’ experience in the electronics industry, largely dedicated to embedded software. A frequent presenter at conferences and seminars and author of numerous technical articles and two books on embedded software, Colin is an embedded software technologist with Mentor, a Siemens business, and is based in the UK. His regular blog is located at: http://blogs.mentor.com/colinwalls. He may be reached by email at colin_walls@mentor.com

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.