We can extend the task context using our own structures, well this is also possible with timers and here is the example. You can notice the resemblance to the fourth code snippet. In this code I create 3 timers with 3 different times. Each timer has it’s own message and pin to toggle but this element are part of a structure. This elements are used in the callback function to determine what message and what pin should be printed and toggled.

#include "bsp.h"
#include "RTOS.h"

typedef struct
{
    OS_TIMER Timer;    // OS_TIMER has to be the first element
    char* Message;     // String to print a message
    uint16_t Pin;      // Pin number
    // add more elements if you need

} My_App_Timer;

static My_App_Timer Timer0, Timer1, Timer2;    // Timer control blocks

static void Callback(void);

int main( void )
{
    OS_Init();      // Initialize embOS (must be first)
    HAL_Init();
    OS_InitHW();   // Initialize Hardware for embOS

    __HAL_RCC_GPIOC_CLK_ENABLE( );                  //* Enabling the GPIO C clock

    GPIO_InitTypeDef GPIO_InitStruct;               //* GPIO Initialization struct

    GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull  = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Pin   = GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3;
    HAL_GPIO_Init( GPIOC, &GPIO_InitStruct );       //* Initializing GPIO C to use it in our callback

    Timer0.Message  = "TIMER 0";
    Timer0.Pin      = GPIO_PIN_0;

    Timer1.Message  = "TIMER 1";
    Timer1.Pin      = GPIO_PIN_2;

    Timer2.Message  = "TIMER 2";
    Timer2.Pin      = GPIO_PIN_3;

    OS_TIMER_CREATE( &Timer0.Timer, Callback, 100 );
    OS_TIMER_CREATE( &Timer1.Timer, Callback, 200 );
    OS_TIMER_CREATE( &Timer2.Timer, Callback, 300 );
    
    OS_Start();     // Start multitasking
    
    return 0u;
}

static void Callback(void)
{
    My_App_Timer* pThis;
    char* Message;
    uint16_t Pin;

    pThis = (My_App_Timer*)OS_TIMER_GetCurrent();   // Obtaining the pointer to the software timer object whose callback is currently executing.
    Message = pThis->Message;   // Obtaining the message to print
    Pin = pThis->Pin;           // Obtaining the Pin to toggle

    SEGGER_SYSVIEW_PrintfHost( Message );
    HAL_GPIO_TogglePin( GPIOC, Pin );

    /* Depending what message is received the next timer is restarted */
    if( strcmp( Message, "TIMER 0" ) == 0 )
    {
        OS_TIMER_Restart( &Timer1.Timer );
    }
    else if( strcmp( Message, "TIMER 1" ) == 0 )
    {
        OS_TIMER_Restart( &Timer2.Timer );
    }
    else
    {
        OS_TIMER_Restart( &Timer0.Timer );
    }  
}

SystemView

Build and flash this program and run SystemView to appreciate with more detail the behavior of this example. First let’s look at the timeline, notice how the timers are running according with their periods.

Looking closer you can notice how the timers are running.

Now in the event list and terminal window you can see the behavior in more detail.