App state machine 만들기 #5 app states switching

app 은 하나의 상태로 명명하기로 하였습니다. 예를 들자면 안드로이드 앱의 activity 처럼 시스템의 모든 이벤트를 받고 UI를 이루며 프로그램을 동작시키는 하나의 상태라고 이해하면 될 것 같습니다.

 

Full src : https://github.com/soloungos/h7_state_machine

위의 예제에서 app states는 세가지의 상태를 가지고 있습니다.

 

  • app_first.c :
    • 시스템이 시작할 때 최초의 상태, 그 후 합계를 구하는 알고리즘을 계속적으로 수행하다 key 입력 이벤트를 받으면 app second로 스위칭
  • app_second.c :
    • 평균을 구하는 알고리즘을 계속적으로 수행하다가 key 입력 이벤트를 받으면 app third로 스위칭
  • app_third.c :
    • 하는 일 없이 있다가 key 입력 이벤트를 받으면 app first로 스위칭

 

이전에 정의했던 app 타입을 먼저 보시면 이해가 쉬울 것입니다.

typedef struct
{
  app_id_t id;
  app_id_t prev_id;
  uint32_t flags;
  void (*init)(void);
  void (*start)(void);
  void (*stop)(void);
  void (*loop)(void);
  void (*on_msg)(msg_t msg, uint32_t param1, uint32_t param2);
}app_t;
 

첫번째 app first 입니다. 계속 말씀드리지만 app의 원형은 모두 동일합니다.

/*
 * app_first.c
 *
 *  Created on: Feb 18, 2021
 *      Author: soloungos
 */
#include "app.h"
#include "algorithm.h"
#include "algorithm_sum.h"

#define FLAGS_DEFALUT           (0x00000000)

static void init(void);
static void start(void);
static void stop(void);
static void loop(void);
static void on_msg(msg_t msg, uint32_t param1, uint32_t param2);

static app_t this =
{
    APP_first,
    APP_NONE,
    0x00000000,

    init,
    start,
    stop,
    loop,
    on_msg,
};

/**
  * @brief  This function is executed in case of ...
  * @retval ..
  */
void app_first_init(void)
{
  app_init_app(&this);
}
/**
  * @brief  This function is executed in case of ...
  * @retval ..
  */
static void init(void)
{
}

/**
  * @brief  This function is executed in case of ...
  * @retval ..
  */
static void start(void)
{
}

/**
  * @brief  This function is executed in case of ...
  * @retval ..
  */
static void stop(void)
{
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
}

/**
  * @brief  This function is executed in case of ...
  * @retval ..
  */
static void loop(void)
{
  uint32_t array[10] = {1,2,3,4,5,6,7,8,9,10};
  uint32_t length = 10;
  uint32_t result = 0;
  algorithm_t *algorithm = algorithm_sum();

  algorithm->init();
  algorithm->execute((void *)array, (void *)&length, (void *)&result);

  HAL_Delay(1000);
  HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
}

/**
  * @brief  This function is executed in case of ...
  * @retval ..
  */
static void on_msg(msg_t msg, uint32_t param1, uint32_t param2)
{
  switch(msg)
  {
  case MSG_KEY_INTERRUPT:
    app_switch_app(APP_second);
    break;

  default:
    break;
  }
}
 

loop() 에서 GPIO 토글과 sum algorithm을 strategy 패턴으로 받아와 동작시킵니다. 그리고 메시지로 key 입력을 받으면 app second로 스위칭 됩니다.

 

두번째 app second입니다.

/*
 * app_second.c
 *
 *  Created on: Feb 18, 2021
 *      Author: soloungos
 */
#include "app.h"
#include "algorithm.h"
#include "algorithm_avg.h"

#define FLAGS_DEFALUT           (0x00000000)

static void init(void);
static void start(void);
static void stop(void);
static void loop(void);
static void on_msg(msg_t msg, uint32_t param1, uint32_t param2);

static app_t this =
{
    APP_second,
    APP_NONE,
    0x00000000,

    init,
    start,
    stop,
    loop,
    on_msg,
};

/**
  * @brief  This function is executed in case of ...
  * @retval ..
  */
void app_second_init(void)
{
  app_init_app(&this);
}
/**
  * @brief  This function is executed in case of ...
  * @retval ..
  */
static void init(void)
{
}

/**
  * @brief  This function is executed in case of ...
  * @retval ..
  */
static void start(void)
{
}

/**
  * @brief  This function is executed in case of ...
  * @retval ..
  */
static void stop(void)
{
  HAL_GPIO_WritePin(GPIOE, GPIO_PIN_1, GPIO_PIN_RESET);
}

/**
  * @brief  This function is executed in case of ...
  * @retval ..
  */
static void loop(void)
{
  uint32_t array[10] = {1,2,3,4,5,6,7,8,9,10};
  uint32_t length = 10;
  uint32_t result = 0;
  algorithm_t *algorithm = algorithm_avg();

  algorithm->init();
  algorithm->execute((void *)array, (void *)&length, (void *)&result);

  HAL_Delay(250);
  HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_1);
}

/**
  * @brief  This function is executed in case of ...
  * @retval ..
  */
static void on_msg(msg_t msg, uint32_t param1, uint32_t param2)
{
  switch(msg)
  {
  case MSG_KEY_INTERRUPT:
    app_switch_app(APP_third);
    break;

  default:
    break;
  }
}
 

loop() 에서 GPIO 토글과 average algorithm을 strategy 패턴으로 받아와 동작시킵니다. 그리고 메시지로 key 입력을 받으면 app third로 스위칭 됩니다.

 

마지막으로 app third입니다.

/*
 * app_third.c
 *
 *  Created on: Feb 18, 2021
 *      Author: soloungos
 */
#include "app.h"

#define FLAGS_DEFALUT           (0x00000000)

static void init(void);
static void start(void);
static void stop(void);
static void loop(void);
static void on_msg(msg_t msg, uint32_t param1, uint32_t param2);

static app_t this =
{
    APP_third,
    APP_NONE,
    0x00000000,

    init,
    start,
    stop,
    loop,
    on_msg,
};

/**
  * @brief  This function is executed in case of ...
  * @retval ..
  */
void app_third_init(void)
{
  app_init_app(&this);
}
/**
  * @brief  This function is executed in case of ...
  * @retval ..
  */
static void init(void)
{
}

/**
  * @brief  This function is executed in case of ...
  * @retval ..
  */
static void start(void)
{
}

/**
  * @brief  This function is executed in case of ...
  * @retval ..
  */
static void stop(void)
{
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_RESET);
}

/**
  * @brief  This function is executed in case of ...
  * @retval ..
  */
static void loop(void)
{
  HAL_Delay(500);
  HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_14);
}

/**
  * @brief  This function is executed in case of ...
  * @retval ..
  */
static void on_msg(msg_t msg, uint32_t param1, uint32_t param2)
{
  switch(msg)
  {
  case MSG_KEY_INTERRUPT:
    app_switch_app(APP_first);
    break;

  default:
    break;
  }
}
 

loop() 에서 GPIO 토글동작을 시킵니다. 그리고 메시지로 key 입력을 받으면 app first로 스위칭 됩니다.

 

app 의 내부 함수는 필요에 따라 추가 삭제하여도 무방합니다. flag는 해당 app의 전용입니다. 필요에 따라 늘리거나 수정할 수 있습니다.

 

간단한 세가지 app states machine에 대해 구현해 보았습니다. 어느 어플리케이션에서든지 위와 같은 states machine은 적용될 수 있으며 소프트웨어를 보다 간단하고 유지보수 하기 쉽게 만들어 줍니다. 실제로 많은 어플리케이션에서 사용되는 패턴이며 RTOS와 같이 적용해도 아무런 문제가 되지 않습니다.

 

이상으로 app states machine에 대한 포스팅을 마치겠습니다.