Objectifying the FreeRTOS timers using static polymorphism through the CRTP pattern (source code included)

Here I showed you how to objectify the FreeRTOS’ timers using dynamic polymorphism, and it works! At the end of the post I made a promise, to objectify the FreeRTOS timers using static polymorphism. For such endeavor I’ll use the CRTP pattern.

What’s wrong with dynamic polymorphism? It introduces codebloats due to the use of pointers, references, and a pointer to functions table lookup. That’s ok when programming in a PC, but it eats the scarce resources in small embedded systems. The CRTP pattern solves those issues while retaining the polymorphism.

Here I’ve tried to explain the pattern. But if my explanation isn’t clear enough, then you’ll find some links at the end of the post. Before going further you should take a look on all those resources.

FreeRTOS timers

I won’t explain how the timers work and are used, I’m asumming you know how to use them. But in case, I wrote a lesson (in spanish) about the software timers in FreeRTOS, in a free course I called Arduino en tiempo real (Real-time Arduino).

Let’s review the function to create timers. Although I prefer the static creation for everything in FreeRTOS, I’ll be using the dynamic creation because it’s easier to use (but unsafe):

We’re interested in the last two arguments:

  • pxCallbackFunction. This is the user function that will be executed whenever the timer’s time expires, no matter the timer’s mode.
  • pvTimerID. This argument of type void* is intended to be used for passing on an ID to the callback in case such callback is called from two or more timers. Guess what? It’s a pointer to void! It means we can pass on to the callback whatever we want, not only IDs.

Isn’t that easy as to pass on the address of a class method to the pxCallbackFunction? Unfortunately, you can’t:

SomeClass some_object;

auto handler = 
xTimerCreate( 
    "TMR",  
    1000, 
    TRUE, 
    NULL, 
    some_object.some_method );

Why is so? Because all function members in a class include the this pointer, and despite your function some_method() has followed the given FreeRTOS callback’s signature, it still includes the this pointer. So, it won’t compile.

The solution are the static methods! Static methods doesn’t have the this pointer:

template<typename T>
class TimerX
{
// more methods and data members...

protected:
   // FreeRTOS registers and calls this function:
   static void callback( TimerHandle_t handler )
   {
        // details later on
   }
};

I called the base class TimerX, and you can see that the method that will be working as callback has been set as static. All objects you create will be using this same callback, as shown in the constructor:

TimerX( 
      TickType_t   period, 
      TimerMode    mode = TimerMode::ONE_SHOT,
      const char*  name = "",
      size_t       id = 0 
   ) : id{ id }
   {
      this->handler = 
         xTimerCreate(
            name,                              // timer's name
            pdMS_TO_TICKS( period ),           // timer's period
            static_cast<UBaseType_t>( mode ),  // timer's mode
            this,                              // working object
            callback );                        // timer's callback code
   }

Please note that the base class has been templatized, as it’s implmenting the CRTP pattern. Later on we’ll see how the class is created.

Before I show you how (and why) it works let’s talk about the pointer this that you can see in the line before the last one. Remember that the argument is of type void*, so we can use it to pass on the working object! In this way, FreeRTOS registers (and indirectly saves) the address of the object that is creating the timer; the timer’s handler is saved in a data member (don’t worry for it now). In this way the timer has been tied to the object.

Ok, the object’s address has been saved inside FreeRTOS, but how can we retrieve it? We do so using the FreeRTOS’ API function:

What this function does is to return the data we save when we create the timer, in our case this is the address of a working object. Of course, we need to cast the returned value to a type that makes sense to our application, which compels me to show you the complete callback’s code:

// FreeRTOS registers and calls this function:
   static void callback( TimerHandle_t handler )
   {
      TimerX* p = static_cast<TimerX*>( pvTimerGetTimerID( handler ) );
      static_cast<T&>( *p ).Run();
   }

Remember, FreeRTOS is the entity that calls this function, so it passes on the handler of the timer that has expired. The expression pvTimerGetTimerID( handler ) returns the address of the object that registered the timer; in other words, it return the this pointer of the object that need to handle the event. The casting static_cast<TimerX> returns the nature of the this pointer. Once the variable p has taken the form of the this pointer appropriately, then it sends the message (*p).Run(). First p (remember that p is the this pointer, and *p is the object itself) is downcasted to the derived class given by T so it can access the methods that were declared in the derived classes, and in particular it calls the method .Run(). Run() is the user’s code that is going to be executed every time the timer’s time expires:

class MyTimer : public TimerX<MyTimer>
{
public:
   void Run()
   {
      // user's code
   }
};

Please note that the derived class MyTimer inherits from a base class TimerX of type MyTimer. That’s the CRTP pattern in action!

How is this used?

Finally we need to see how all this stuff is used. Let me show you the timer client’s code, that in this case is insided an Arduino function Setup(), but of course you can use whatever function in your application:

void setup()
{
   MyTimer my_timer( 13, pdMS_TO_TICKS( 500 ), TimerMode::AUTO_RELOAD, "TMR1", 1 );

   OtherTimer other_timer( "HOLA MUNDO", pdMS_TO_TICKS( 1000 ), TimerMode::AUTO_RELOAD, "TMR2", 2 );

   MyTimer yet_other_timer( 2, pdMS_TO_TICKS( 2000 ), TimerMode::AUTO_RELOAD, "TMR3", 3 );

   my_timer.Start();

   other_timer.Start();

   yet_other_timer.Start();

   Serial.begin( 115200 );

   vTaskStartScheduler();
}

In the first line you can see how to create a new timer tied to the subclass MyTimer:

MyTimer my_timer( 13, pdMS_TO_TICKS( 500 ), TimerMode::AUTO_RELOAD, "TMR1", 1 );

Same in the next two instructions. You can create as many timers as you need either from the subclass MyTimer or OtherTimer. Of course, each one implements a specific behaviour. After being created the timers must be enabled; in this example I enabled them all in the same function, but it can be done in any place that your logics dictates.

What’s next?

The CRTP pattern and its uses carries serious intellectual challenges, but you avoid the codebloat and penalty performance of dynamic polymorphism. In the embedded system programming world is very likely that you already know that something isn’t gonna change when the program is running so you might hard-code the calls, but sometimes turning C code into the object programming paradigm isn’t that easy as to wrap an C API function into a class, as I mentioned before.

The TimerX class isn’t complete by any means, there are more functions from the timer’s API that must be included, but I hope you’ve enjoyed this C++ jewerly as I did while I was trying to understand it and developing the examples and its uses.

Here you can see how I used this pattern to objectify the task creation in FreeRTOS.

If you want to use FreeRTOS along Arduino, as I did in the example, you might take a look to my project Molcajete, which integrates Arduino, FreeRTOS, and Arduino-mk (a tool to compile Arduino from the command line).

Bibliography and source code

Here you can download the examples of this post, or you can clone them:

git clone https://fjrg76@bitbucket.org/fjrg76/crtp_freertos.git
  • CRTP. From Wikipedia.
  • CRTP. Great explanation.
  • CRTP. Another great explanation.


Hope you’ve enjoy this post. If so, you can consider to subscribe to my blog or write me a line.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Google photo

Estás comentando usando tu cuenta de Google. Salir /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s