Простое решение
Так как количество состояний конечно, и нам известны их названия, то для удобства стоит создать перечисление 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
}
}
Если количество состояний и переходов невелико, а сам автомат относительно прост, то такой способ более чем уместен. Проблемы начинаются, когда переходы не столь явные, а количество состояний больше — отладка принимает нетривиальный характер. Лучше всего, когда переходы описаны в том же файле, где и выбор действия.