Soft timers in object-oriented C

When time isn't of the essence, you can use a soft timer. Here's how to create one in object-oriented C.

Time is perhaps one of the most important and often used resources on any embedded project. There are times when we wish to do “hard” real time. These are cases where some service must run predictively and preemptively on a very precisely-defined time interval. Typically these “hard” time resources are dedicated to running one or more services in a very precise time fashion.

In this how-to article, I won't be explaining how to implement a “hard” real-time service. I can discuss hard real-time services in another article. All you have to do is ask!

Many times, we don't need such hard real-time precision in our implementation. There are many instances where we just wish to make sure a section of code will execute when “at least” some amount of time has elapsed. Also, we shall discuss object-oriented C programming and how to implement some of these resources on demand, that is, in object-C fashion. Yes, I too have jumped onto the object-C bandwagon. I did this many years ago when I discovered just how powerful this notion of data and function encapsulation really is. I can say this much for certain:

  1. Object-oriented C is really an advanced form of encapsulation
  2. To truly get object-oriented C, you need to define the main data type and a set of operations
  3. Object-oriented C really does take some getting used to
  4. Object-oriented C can be made to be very extensible; especially by other team members


Soft timer objective

Since we're talking objects here, we need to define what our objects need to do. In other words, for this kind of programming, a meta-style programming is involved. What you really are doing is creating something useful that may be used within your current or future project(s). This is what meta-programming for embedded is really about. Instead of programming for your customer, or the application, you really are programming for yourself for future use!

So, now that we see what we wish to do. Let us define some attributes that this soft timer shall have. Here are some attributes that I think I would like to see with this new “use everywhere” style of object.

Instantiate anywhere: Yes, these objects should be allowed to be used anywhere anytime. The soft timer should be allowed to be instanced in any of the following:

  1. Local variable stack, (yes even the local stack on an interrupt service routine)
  2. Heap, (if your system uses one)
  3. Public global instance
  4. Private global instance

Used anywhere: The soft timer object should be allowed for usage anywhere in the project application. However, one must be careful about using this object within an interrupt service routine. This is because of preemption issues affecting the behavior of the soft timer object.

Extensible object: One of the truly amazing things about object oriented C is the ability to extend these objects without affecting code outside of the object. This extensibility is what makes the object oriented C really remarkable.Soft timer operation
Two operations are required by the soft timer object. The primary operation is usually done by the system timer interrupt service routine. This service routine will update a globally accessible variable once per every hard timer interrupt. In general, this variable will be updated as a simple C increment operation. An example of such an update is shown in Listing 1 .

volatile unsigned long my_system_tick;void interrupt my_timer_service (void){    ++my_system_tick;}

Listing 1. Update of a globally accessible hard timer variable

Yes, that is all there is to it . . . other than the timer service initialization and hardware setup. It's also important to note that the variable my_system_tick is an unsigned long. This is important because of the type of arithmetic that the soft timer object will do.

The other operation is what to do with the global tick variable. This variable will get “captured” by the operation of the soft timer. The capture and compare will be part of the soft timer variable instance operation as discussed below.

Soft timer interface
One of the very first things to do with any object-C-based project is to define the interface. The interface is nothing more than a C header file. Defining and writing the header file for the soft timer is shown in Listing 2 .

#ifndef __GPTIMER_H    #define   __GPTIMER_H/****************************************************************************//*    FILE: gpTimer.h                                                       *//*                                                                          *//*    A general purpose C/C++ timer object                                  *//*                                                                          *//*    BY:   Ken Wada                                                        *//*          12-January-2013                                                 *//*                                                                          *//****************************************************************************/    #define   GP_MSEC_PER_TIC     1     /* Each TIC == 1 millisecond        */    #define   TIC_PER_SEC         (1000/GP_MSEC_PER_TIC)    typedef unsigned char (*TMR_IDLEFN)(void);/****************************************************************************//*    A structure that defines a generic timer type.                        *//****************************************************************************/    typedef struct general_purpose_timer_object  {      unsigned long lastCount;          /* Last saved count for this timer  */      unsigned long timeOut;            /* Number of tics to timeout        */      unsigned char timedOut;           /* time-out status                  */    } GPTIMER;    #ifdef  __cplusplus      extern  "C" {    #endif      void  GPTIMER_init            (GPTIMER *_this, const unsigned long tics);      void  GPTIMER_reTrigger       (GPTIMER *_this);      int   GPTIMER_isTimedOut      (GPTIMER *_this);//      void  GPTIMER_waitForTick   (GPTIMER *_this);//      long  GPTIMER_timeLeft      (GPTIMER *_this);//      void  GPTIMER_waitForTick_fn(GPTIMER *_this, TMR_IDLEFN idle_fn);      /*****        The following are the system call-backs needed for this library      ***/      unsigned long sysRetrieveClock    (void);    #ifdef  __cplusplus      }    #endif#endif

It is important to note that the general format of an object “C” interface is a function which references a pointer to the object as the 1st element in the call list. In this example, we define the object instance using the user defined symbol _this . We do this because we may want to use this simple object in C++ applications too.Given Listing 2 (the interface) and Listing 1 (system tick service interrupt), we can show an example of how we implement the clock retrieve function in Listing 3 .

unsigned long sysRetrieveClock (void){    return (my_system_tick);}

Listing 3. Access to the system tick hard timer variable

Yes! That is all there is to accessing this variable. All we need is the value of the updated variable. (Of course, we assume the fetch of my_system_tick is atomic with respect to the hardware).

Soft timer operations
It is tempting to say operator. However, the term “operator” has a well-formed meaning in C++. So instead of operator, I can say operation. After all, in our code we shall instance a soft timer and do “operations” on it. The 1st operation is the initialization. The general usage for object oriented C is a two or three step process. The steps are as follows:

  1. Instantiate the object, (global, stack etc)
  2. Initialize the object, (initialize near point of use)
  3. Setup, (this step is optional and may be needed to sync the object with the hardware)

Soft timer init
This is where the soft timer is initialized. In general, we do this initialization to allow the 1st capture of the system tick variable, and the setup of the timeOut value. The code for the soft timer initialization is shown in Listing 4 .

#define __GPTIMER_INIT_C/****************************************************************************//*    FILE: GPTIMER_init.c                                                  *//*                                                                          *//*    A general purpose C/C++ timer object                                  *//*                                                                          *//*    BY:   Ken Wada                                                        *//*          12-January-2003                                                 *//*                                                                          *//****************************************************************************/#include  "hgpTimer.h"/****************************************************************************//*                            CODE STARTS HERE                              *//****************************************************************************/void GPTIMER_init (GPTIMER *_this, const unsigned long tics){    _this->timeOut    = tics;    _this->lastCount  = sysRetrieveClock();    _this->timedOut   = 0;}

Listing 4. How to initialize a soft timer object

Here we see the sysRetrieveClock() interface that we defined earlier. We also show the timeOut member variable being initialized to the desired timeout in tics. We also show the timedOut control variable being set to zero. The timedOut variable is used to indicate whether or not the soft timer has indeed timed out.Soft timer isTimedOut
This is the main function which is called by the application. The sole purpose of this method is to determine whether or not the soft timer has timed out. Or in other words, the isTimedOut member function indicates whether or not at least some amount of time has elapsed since the last call to the interface. Listing 5 shows the isTimedOut member function for the soft timer object.

#define __GPTIMER_ISTIMEDOUT_C/****************************************************************************//*    FILE: GPTIMER_isTimedOut.c                                            *//*                                                                          *//*    A general purpose C/C++ timer object                                  *//*                                                                          *//*    BY:   Ken Wada                                                        *//*          12-January-2013                                                 *//*                                                                          *//****************************************************************************/#include  "hgpTimer.h"/****************************************************************************//*                            CODE STARTS HERE                              *//****************************************************************************/int GPTIMER_isTimedOut (GPTIMER *_this){    if (!(_this->timedOut))    {      _this->timedOut = ( sysRetrieveClock()-_this->lastCount >= _this->timeOut );    }    return (0!=_this->timedOut);}

Listing 5. The soft timer isTimedOut member function implementation

The isTimedOut function simply returns the timedOut member variable, if the timedOut variable is SET. If the object has not timed out, then the operation computes the difference between the current tick count and the captured tick count. It then tests this difference against the timeOut member value which was setup during the soft timer initialization.

Soft timer retrigger
Once the soft timer has timed out, the timedOut member variable will stay latched. This ensures the soft timer will always indicate a timed out condition on future calls.

There may be times when we need to retrigger the soft timer and use the soft timeout condition again. For this, we need a retrigger operation. Listing 6 shows how the retrigger method is implemented.

#define __GPTIMER_RETRIGGER_C/****************************************************************************//*    FILE: GPTIMER_reTrigger.c                                             *//*                                                                          *//*    A general purpose C/C++ timer object                                  *//*                                                                          *//*    BY:   Ken Wada                                                        *//*          12-January-2013                                                 *//*                                                                          *//****************************************************************************/#include  "hgpTimer.h"/****************************************************************************//*                            CODE STARTS HERE                              *//****************************************************************************/void  GPTIMER_reTrigger (GPTIMER *_this){    _this->lastCount  = sysRetrieveClock();    _this->timedOut   = 0;}

Listing 6. The reTrigger member function for the soft timer

In Listing 6 , we see the lastCount and timedOut variable is re-initialized as in the init method. In fact, the only difference between the init and retrigger methods is the setting of the timeOut count variable in the init member method.Soft timer examples
What follows here are a couple of examples of how to use the soft timer object. We show a primitive main method stack implementation. In this example, we show how the soft timer is used to guarantee the main update loop is called no faster than once per second. This example is shown in Listing 7 .

#define __GPTIMER_MAIN_EXAMPLE_C/****************************************************************************//*    FILE: GPTIMER_main_example.c                                          *//*                                                                          *//*    A general purpose C/C++ timer object example                          *//*                                                                          *//*    BY:   Ken Wada                                                        *//*          12-January-2013                                                 *//*                                                                          *//****************************************************************************/#include  "hgpTimer.h"static GPTIMER my_1second_timer;extern void do_1second_update (void);extern void initialize_hw_timerService (void);  // initialize system tick/****************************************************************************//*                            CODE STARTS HERE                              *//****************************************************************************/void update_process (void){    if (GPTIMER_isTimedOut(&my_1second_timer))  // is it timed out?    {      GPTIMER_reTrigger (&my_1second_timer);    // retrigger the timer      do_1second_update ();                     // do some update    }}void main(void){    initialize_hw_timerService ();   // initialize system tick    GPTIMER_init (&my_1second_timer, 1000);  // initialize for 1 second    while (1)    {      update_process ();    }}

Listing 7. An example of an update process using a soft timer object

Sometimes, we want a delay. And sometimes we want the delay to be somewhat predictable even with using different microprocessor clock frequencies. So, we will instance a soft timer on the stack and use it in a function. The soft timer instance will simply evaporate away when the function returns. This example is shown in Listing 8 .

#define __GPTIMER_DELAY_EXAMPLE_C/****************************************************************************//*    FILE: GPTIMER_delay_example.c                                         *//*                                                                          *//*    A general purpose C/C++ timer object example                          *//*                                                                          *//*    BY:   Ken Wada                                                        *//*          12-January-2013                                                 *//*                                                                          *//****************************************************************************/#include  "hgpTimer.h"extern void set_bit (void);extern void clr_bit (void);void my_delay_process (void){    GPTIMER my_timer;              // instance on the CALL stack    GPTIMER_init (&my_timer, 3);   // initialize for 3 ticks    set_bit ();          // set the bit    while (!GPTIMER_isTimedOut(&my_timer))    {      ;    }    clr_bit ();          // clear the bit}

Listing 8 The instancing of a soft timer on the CALL stack and using it as a delay element.

Simple, small, useful
We show a small soft timer object can be used in an infinite number of instances. The only real hardware resource that is used is a simple global unsigned long variable that gets updated inside a hard real-time timer service.

We also show some of the basic elements of an object-oriented C type programming. Here we show how to properly define and use this type to affect a generic reusable object-oriented C soft timer.

If you have any questions; please feel free to comment below, or email me.

Ken Wada is president and owner of Aurium Technologies, an independent product design and consulting firm in California's Silicon Valley. Ken has over 25 years of experience architecting and designing high-tech products and systems, including the FASTRAK vehicle-sensing system for toll roads and bridges. His expertise includes industrial automation, biotechnology, and high-speed optical networks. Ken holds four patents. You may reach him at .

20 thoughts on “Soft timers in object-oriented C

  1. Regarding the actual timer function, I usually design similar timers by including a function pointer of type void(*)(void) in the struct, pointing at a callback function which will be executed automatically when the timer is done. It will be called from th

    Log in to Reply
  2. With all the talk about OO and encapsulation in C, I would have expected a more elegant design. From a “get-it-done” perspective, this code is just fine, but from an OO perspective it is naive. The struct implementation is handed to the caller on silver pl

    Log in to Reply
  3. How are you handling roll-over of the global counter? Also, I find a teeter-totter relationship with atomic operations and datatype size, especially for 8- or 16-bit devices: the wider the datatype, the less likely it is to be atomic. So, to get an atomic

    Log in to Reply
  4. (A note: I tried to reply all in one comment, but the website bars me from posting anything with more than 2000 words!)

    “From a “get-it-done” perspective, this code is just fine”
    That is a great compliment! All of my clients expect this of me … especial

    Log in to Reply
  5. I love LINUX and what it can do … I hate its documentation … which is pretty much nonexistent. Even the HOWTO on the Internet for Linux is well … weak.

    “Especially if the code is designed so that one software timer equals one hardware timer. For exa

    Log in to Reply
  6. “A more realistic risk is that the caller misunderstands the implementation, since the struct implementation is available, and therefore performs direct access to the struct members.”

    This is one of the things that Stroustrup considered when he created C+

    Log in to Reply
  7. “… “opaque type” (formally: “incomplete type”), where the contents of the struct are truly unknown to the caller, and thus inaccessible.”

    Yes, in my original implementation of the GPTIMER encapsulation, I declared the 1st pointer in all my functions as

    Log in to Reply
  8. As an extra note on opaqueness. Yes, I have used opaqueness on several of my C projects. Usually this is how I do it; (this appears to have met some acceptance, and not generated too many complaints).

    1. Define a core enapsulation that has everything publ

    Log in to Reply
  9. The danger of opaqueness in C is that there is no strict typesafe mechanisms in C as in C++. C is a bit more 'wild and wooly' when compared with the more formal C++ … in my opinion, this gives C a more low-level feel to coding than C++.

    That is NOT to s

    Log in to Reply
  10. “One could perhaps make the callback function pointer as an optional parameter: if it is NULL, the code will rely on polling, if it is set, it will be interrupt driven.”

    Yes, I do this all the time. This is very similar to the “default argument” that one

    Log in to Reply
  11. This is the beauty of unsigned arithmetic. When you do unsigned arithmetic, the carry bit is virtual. You can treat the carry bit as much as one would do using a half-adder.

    Here is an example using 8-bit unsigned arithmetic
    CURRENT_COUNT = 0xFE
    CAPTURE_C

    Log in to Reply
  12. Dang! I did not know that embedded.com will list the comments in latest 1st. I should have reversed my replies. I will do so next time.

    You will need to read my replies in reverse order.

    Sorry!

    Log in to Reply
  13. Again … I forgot to add:
    You need to instance an XPTIMER object, then use it on the function with a cast.

    For example:
    XPTIMER my_xptimer01;

    void main (void)
    {
    . some code …
    .
    .
    XPTIMER_update ((GPTIMER *)&my_xptimer01);
    }

    Log in to Reply
  14. The only reliable way you can get true type safety in C is to use static analysis tools. I wouldn't personally change the program design of a C program just to achieve stronger typing at some place. With a good static analyser you will not only catch suspi

    Log in to Reply
  15. I think you misunderstood what I meant. I didn't suggest to use void pointers. I suggested incomplete type. That is, in the h file you have typedef struct myclass myclass;. This enables the caller to declare a pointer to a myclass object but they can't cre

    Log in to Reply
  16. Hello Lundin;
    Yes, I think I did misunderstand what you meant.

    So, in your proposal, the struct definition would be hidden. How would the function call in C be implemented … without an object that is? Would all the instances be carried in the C source f

    Log in to Reply

Leave a Reply

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