Простое решение

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

// state_machine.h
typedef enum {
    STATE_SHOW_TIME,
    STATE_SHOW_TEMPERATURE,
    STATE_ADJUST_HOURS,
    STATE_ADJUST_MINUTES,
} STATE_t
// state_machine.c
#include "state_machine.h"
 ​
volatile STATE_t state = STATE_SHOW_TIME;

Так как смена состояния должна происходить по нажатию на кнопку, то проще всего записать изменение state прямо в прерывании:

// stm32f10x_it.h
#include "state_machine.h"
 ​
extern STATE_t state;
// stm32f10x_it.c
void EXTI_IRQHandler(void) {
    switch (state) {
        default:
        case STATE_SHOW_TIME:
            state = STATE_SHOW_TEMPERATURE;
            // return to STATE_SHOW_TIME after RETURN_DELAY sec
            countdown_start(RETURN_DELAY);
            break;
        case STATE_SHOW_TEMPERATURE:
            state = (display_get_intensivity()) ? STATE_SHOW_TIME : STATE_ADJUST_HOURS;
            countdown_stop();
            break;
        case STATE_ADJUST_HOURS:
            state = STATE_ADJUST_MINUTES;
            break;
        case STATE_ADJUST_MINUTES:
            state = STATE_SHOW_TIME;
            rtc_set_time(); // save new time value to RTC registers
            break;
    }
    // reset pending bit
}

Прерывание должно выполняться как можно быстрее, а условные операции зачастую замедляют работу программы (помните про конвейер?) По-хорошему, их нужно избегать в таких местах, но в нашем случае это не столь критично.

Так как в случае бездействия в состоянии STATE_SHOW_TEMPERATURE нам следует вернуться в состояние отображения времени, то необходимо написать обработку прерывания от таймера, который запускается при первом нажатии кнопки.

void TIM1_IRQHandler() {
    countdown_stop();
    state = STATE_SHOW_TIME;
    // reset pending bit
}

В целом логика переходов описана, осталось только реагировать на состояния. Это удобно делать в главном цикле программы, в функции main().

int main(void) {
    mcu_init();
    while(1) {
        switch (state) {
        default:
        case STATE_SHOW_TIME:
            display_time();
            break;
        case STATE_SHOW_TEMPERATURE:
            display_temperature();
            break;
        case SHOW_ADJUST_HOURS:
            display_hours();
            break;
        case TEMPERATURE:
            display_minutes();
            break; } // end of the main loop
    }
}

Если количество состояний и переходов невелико, а сам автомат относительно прост, то такой способ более чем уместен. Проблемы начинаются, когда переходы не столь явные, а количество состояний больше — отладка принимает нетривиальный характер. Лучше всего, когда переходы описаны в том же файле, где и выбор действия.


Изменено: