Let’s do something a little bit more complex. A mouse state machine, this machine will identifies whether a button is pressed once, twice, or held down.

Basically, if the button is press, a timer with a period of 500ms is start, if the timer execute the callback and the button is still press the state is changed to “HOLD” and a led lights up and the state machine will wait until the button is release to change the state to “IDLE”. If the button is release, the timer is restarted and if the button is not press again the variable “flag” is change to “SINGLE” and another led lights up, when the callback is called the state is changed to “IDLE” but if the button is press before the timer runs out the state is changed to “DOUBLE”, the timer is stopped and another led lights up. Once in “DOUBLE” state, we have to release the button to change the state to “IDLE”

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

static OS_TIMER Timer;                    //* Timer control structure
static OS_TASK  Task;                     //* Task control structure
static OS_STACKPTR  int TaskStack[128];   //* Task Stack

uint8_t State   = IDLE;                   //* Global variable to know the state
uint8_t Counter = 0;                      //* Flag that we'll use in the callback

static void Callback(void);               //* Callback function
static void PeriodicTask(void);           //* Task routine

static void StateMachine(void); //* Function to be call within our task routine

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

    OS_TASK_CREATE( &Task, "State Machine", 100, PeriodicTask, TaskStack );     //* Creating the task
    OS_TIMER_Create(&Timer, Callback, 500u);                                   //* Creating the software timer

    OS_Start();     // Start multitasking
    
    return 0u;
}

static void PeriodicTask(void)
{
    __HAL_RCC_GPIOC_CLK_ENABLE();       //* Enabling GPIO C clock
    __HAL_RCC_GPIOB_CLK_ENABLE();       //* Enabling GPIO B clock

    GPIO_InitTypeDef GPIO_InitStruct;

    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 the leds

    GPIO_InitStruct.Mode    = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull    = GPIO_NOPULL;
    GPIO_InitStruct.Speed   = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Pin     = GPIO_PIN_5;
    HAL_GPIO_Init( GPIOB, &GPIO_InitStruct );

    while( 1 )
    {
        StateMachine();
    }
}

static void StateMachine(void)
{
    uint8_t Pin_State;

    switch( State )
    {
        /* In this state the event machine is waiting for a button press and once is press the state change
        to state "PRESS" and the timer is satarted*/
        case IDLE:
            OS_TIMER_Stop( &Timer );

            Pin_State = HAL_GPIO_ReadPin( GPIOB, GPIO_PIN_5 );

            if( Pin_State == RESET )
            {
                OS_TIMER_Start( &Timer );
                State = PRESS;
            }

        break;
        
        /* If the button is release before the timer runs out the state will change to single
        and the timer will be restarted, otherwise and due to "flag" variable is equal to "0"
        the state will change to "HOLD" */
        case PRESS:
            Pin_State = HAL_GPIO_ReadPin( GPIOB, GPIO_PIN_5 );

            if( Pin_State == SET )
            {
                Counter++;
                OS_TIMER_Restart( &Timer );
                State = SINGLE;
            }
        break;
        
        /* In this state the led in GPIO0 lights up and the variable flag is now equals to "SINGLE".
        If the timer runs out and the button is not press again the state is changed to "IDLE" in the callback*/
        case SINGLE:
            Pin_State = HAL_GPIO_ReadPin( GPIOB, GPIO_PIN_5 );

            HAL_GPIO_WritePin( GPIOC, GPIO_PIN_0, SET );
            HAL_GPIO_WritePin( GPIOC, GPIO_PIN_2, RESET );
            HAL_GPIO_WritePin( GPIOC, GPIO_PIN_3, RESET );

            if( Pin_State == RESET)
            {
                Counter--;
                OS_TIMER_Restart( &Timer );
                State = DOUBLE;
            }
        break;
        
        /* If the button is press before the timer runs out the timer is stopped and the state is changed
        to "DOUBLE. Once in this state the GPIO0 is Reset and the led in GPIO2 lights up. The event machine
        waits until the button is released to change the state to "IDLE" */
        case DOUBLE:
            Pin_State = HAL_GPIO_ReadPin( GPIOB, GPIO_PIN_5 );

            HAL_GPIO_WritePin( GPIOC, GPIO_PIN_0, RESET );
            HAL_GPIO_WritePin( GPIOC, GPIO_PIN_2, SET );
            HAL_GPIO_WritePin( GPIOC, GPIO_PIN_3, RESET );

            if( Pin_State == SET )
            {
                Counter = 0;
                OS_TIMER_Stop( &Timer );
                State = IDLE;
            }
        break;
        
        /* If the button is press and the timer runs out the state will change to "HOLD". 
        Once in this state, the GPIO3 LED turns on and the other LEDs turn off. The event machine
        waits until the button is released to change the state to "IDLE" */
        case HOLD:
            OS_TIMER_Stop( &Timer );

            Pin_State = HAL_GPIO_ReadPin( GPIOB, GPIO_PIN_5 );

            HAL_GPIO_WritePin( GPIOC, GPIO_PIN_0, RESET );
            HAL_GPIO_WritePin( GPIOC, GPIO_PIN_2, RESET );
            HAL_GPIO_WritePin( GPIOC, GPIO_PIN_3, SET );

            if( Pin_State == SET )
            {
                State = IDLE;
            }
        break;
    }  
}

/* When the callback is executed the flag is used to determine the state from which the callback was called */
static void Callback(void)
{
    /* Depending on the "counter" variable value the callback will change the state to HOLD or IDLE */
    if( Counter == 0 )
    {
        State = HOLD;
        OS_TIMER_Restart( &Timer );
    }
    else
    {
        Counter = 0;
        State = IDLE;
        OS_TIMER_Restart( &Timer );
    }
}