Событийный автомат

Желая облегчить себе жизнь, придется создать еще одно перечисление и переменную для событий. Все два события нам известны — поступим так же, как и в прошлый раз:

// state_machine.h
typedef enum {
    EVENT_NONE,
    EVENT_BUTTON_PRESSED,
    EVENT_TIME_OUT,
} EVENT_t;
// state_machine.c
volatile EVENT_t event = EVENT_NONE;

Происходит событие — меняется значение event. Следовательно, обработчики прерываний необходимо переписать.

// stm32f10x.h
void EXTI_IRQHandler(void) {
    event = EVENT_BUTTON_PRESSED;
    // reset pending bit
}
// …
void TIM1_Handler(void) {
    event = EVENT_TIME_OUT;
    // reset pending bit
}

Перепишем главный цикл так, чтобы он реагировал на события в зависимости от состояния.

int main(void) {
    mcu_init();
    while(1) {
    switch (state) {
        default:
        case STATE_SHOW_TIME:
            switch(event) {
                case EVENT_BUTTON_PRESSED:
                    state = STATE_SHOW_TEMPERATURE;
                    event = EVENT_NONE;
                    countdown_start(RETURN_DELAY);
                    state = STATE_SHOW_TEMPERATURE;
                    event = EVENT_NONE;
                    countdown_start(RETURN_DELAY);
                    break;
                default:
                    display_time();
                    break;
            }
            break;
        case STATE_SHOW_TEMPERATURE:
            switch(event) {
                case EVENT_BUTTON_PRESSED:
                    state = (display_get_intensivity()) ?
                        STATE_SHOW_TIME : STATE_ADJUST_HOURS;
                    event = EVENT_NONE;
                    countdown_stop();
                    break;
                default:
                    display_temperature();
                    break;
            }
            break;
        case SHOW_ADJUST_HOURS:
            switch(event) {
                case EVENT_BUTTON_PRESSED:
                    state = STATE_ADJUST_MINUTES;
                    event = EVENT_NONE;
                    countdown_start(RETURN_DELAY);
                    break;
                default:
                    display_hours();
                    break;
            }
            break;
        case SHOW_ADJUST_MINUTES:
            switch(event) {
                case EVENT_BUTTON_PRESSED:
                    state = STATE_SHOW_TIME;
                    event = EVENT_NONE;
                    countdown_start(RETURN_DELAY);
                    break;
                default:
                    display_minutes();
                    break;
            }
            break;
    } // end of the main loop 
}

Мы сильно потеряли в читаемости кода — блоки получились очень большими. Дабы исправить ситуацию, всю логику инкапсулируем в функции.

void time_routine() {
    //
}
// …
int main(void) {
    mcu_init();
    while(1) {
    switch (state) {
        default:
        case STATE_SHOW_TIME:
            time_routine();
            // ...

Такой подход, с одной стороны, лучше, так как все переходы явно видны, это облегчает отладку. С другой стороны, хуже: сложнее добавлять новые состояния, код плохо читается и имеет меньшую производительность.


Изменено: