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 );
}
}