Программирование устройства. Проектирование конечного автомата
Электроника
“
Давайте рассмотрим процесс программирования нашего устройства на основе конечного автомата. На этом занятии мы пройдем все этапы программирования микроконтроллера
Глоссарий
Для успешного освоения материала рекомендуем вам изучить следующие понятия:
Внешние события
взаимодействие с пользователем или с другими устройствами
Внутренние события
обычные изменения переменных в самом микроконтроллере
Микроконтроллер
микросхема, предназначенная для управления электронными устройствами. Типичный микроконтроллер сочетает на одном кристалле функции процессора и периферийных устройств, содержит ОЗУ и (или) ПЗУ. По сути, это однокристальный компьютер, способный выполнять относительно простые задачи. Отличается от микропроцессора интегрированными в микросхему устройствами ввода-вывода, таймерами и другими периферийными устройствами
Видеолекция
Конспект
Давайте рассмотрим программирование нашего устройства на основе конечного автомата. Мы пройдем все этапы программирования микроконтроллера
Этапы
- Определение состояний
- Определение событий
- Переходы между событиями
- Действия по переходам из состояния в состояние
- Формирование шаблона программы на основе автомата
Конечный автомат
Логику функционирования нашего устройства удобно представить в виде конечного автомата. Он в один момент времени находится в одном состоянии.
Важно
Количество состояний может быть огромным, но при этом их конечное число
Переходы между состояниями осуществляются через события. При этом для нашего устройства события могут быть внутренними и внешними.
Важно
Внешними событиями является взаимодействие с пользователем или с другими устройствами, а внутренними — обычное изменение переменных в самом микроконтроллере
Нашей целью является определение событий и состояний, а также переходов между событиями и действия при переходе из одного состояния в другое
Определение событий для часов
- Кнопка энкодера нажата (обозначим p)
- Кнопка энкодера отжата (пока не будем использовать)
- Энкодер повернут по часовой стрелке (I)
- Энкодер повернут против часовой стрелки (D)
- Сработал будильник (A)
- Таймаут сигнализации (ТА)
- Сон по неактивности (SE)
- Активность по сенсору (AS)
- Нет событий (N)
Окошки
Взаимодействие пользователя с часами осуществляется с помощью окошек. Для каждого окошка свой список состояний. Каждое из них обозначим S с номером.
- Первое окошко — отображение циферблата
- Второе окошко — настройка времени
- Третье окошко — установка будильника
- Четвертое окошко — срабатывание будильника
- Пятое окошко — данные от аккумулятора и сенсора положения
Представление в виде графа
- Состояние S1 переходит в состояние S2 при событии P (кнопка нажата)
- Из S2 переходим в S3 при событии I. Т. е. при повороте энкодера по часовой на 1 время меняется на 1
- Если сработало событие D, то состояние S2 перейдет уже в S4, т. е. часы уменьшились на 1
- Если произошло событие P, т. е. энкодер нажат, то переходим в S5
- Если из S1 произошло событие SL, то происходит переход в S14, т. е. «спящее состояние», но если возникает любая активность пользователя (AS), то мы возвращаемся в S1
И так далее. Все состояния соединяются стрелками с условием (событием) или без. Нажимая энкодер мы по кольцу пройдем все экраны нашего устройства.
Важно
Практически для всех состояний есть возвратные стрелочки. То есть состояние возвращается само в себя (по сути не меняется), либо по тику программы, либо в бесконечном цикле
Представление конечного автомата
1. Бесконечный цикл While, в котором будут вызываться диспетчер событий, изменение состояний и действия по состоянию
2. Отдельно по прерыванию будет формироваться событие. Например, при нажатии кнопки или изменении сенсора и т. д., формируется событие, и диспетчер событий выбирает из всех одно подходящее
3. По выбранному событию происходит смена состояния
4. На основе переходов решаем, какое действие вызвать
5. Далее возвращаемся по циклу и повторяем все пункты
2. Отдельно по прерыванию будет формироваться событие. Например, при нажатии кнопки или изменении сенсора и т. д., формируется событие, и диспетчер событий выбирает из всех одно подходящее
3. По выбранному событию происходит смена состояния
4. На основе переходов решаем, какое действие вызвать
5. Далее возвращаемся по циклу и повторяем все пункты
Написание шаблона алгоритма в программе
1. Открываем проект, сформированный для тестирования модулей устройства
2. Описываем события. Указываем название каждого события и создаем переменную для текущего события
3. Описываем все используемые состояния и создаем переменную для текущего события
2. Описываем события. Указываем название каждого события и создаем переменную для текущего события
3. Описываем все используемые состояния и создаем переменную для текущего события
char str7[20]={0};
char str8[20]={0};
char str9[20]={0};
char ss[32];
enum SIGNAL
{
encoder_push,
encoder_push_int,
encoder_poll,
encoder_inc,
encoder_dec,
start_alarm,
timeout_alarm,
no_active_to_sleep,
sensor_to_active,
nosignal
};
enum SIGNAL signal=nosignal;
enum STATE
{
AnalogCurrentTime,
SetTimeHours,
SetTimeHoursInc,
SetTimeHoursDec,
SetTimeMinutes,
SetTimeMinutesInc,
SetTimeMinutesDec,
SetAlarmHours,
SetAlarmHoursInc,
SetAlarmHoursDec,
SetAlarmMinutes,
SetAlarmMinutesInc,
SetAlarmMinutesDec,
Sleep,
IMU,
Alarm,
Wake,
NoState
};
volatile enum STATE state=NoState;
4. В цикле While на основе предложенного алгоритма записываем код
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
enum STATE state_old;
signal= getsignal();
state_old = state;
state= change_state(&signal, state);
DO_work(state, state_old);
}
/* USER CODE END 3 */
}
- getsignal получает на вход флаг и формирует сигнал, например, «энкодер нажат»
enum SIGNAL getsignal(void)
{
if(btn_try_push==1 || btn_try_push_int==1)
{
btn_try_push=0;
btn_try_push_int=0;
signal = encoder_push;
return signal;
}
if(try_sensor_to_active==1)
{
try_sensor_to_active=0;
signal = sensor_to_active;
return signal;
}
if(enc_try_inc==1)
{
enc_try_inc=0;
signal = encoder_inc;
return signal;
}
if(enc_try_dec==1)
{
enc_try_dec=0;
signal = encoder_dec;
return signal;
}
if(signal_timeout_sleep==1)
{
signal_timeout_sleep=0; //priority
return signal=no_active_to_sleep;
}
if(signal_alarm==1)
{
signal_alarm=0; //priority
return signal=start_alarm;
}
if(signal_stop_alarm==1)
{
signal_stop_alarm=0; //priority
return signal=timeout_alarm;
}
return signal;
}
Важно
Нажатие кнопки на энкодере имеет максимальный приоритет, отключение будильника — минимальный
- change_state получает на вход текущее состояние и текущий сигнал, а на выходе формирует новое состояние. На основе состояния вызывает нужный метод
enum STATE change_state(enum SIGNAL *signal, enum STATE state)
{
switch(state)
{//by state
case AnalogCurrentTime:
state=change_state_from_AnalogCurrenTime(signal, state);
break;
case SetTimeHours:
state=change_state_from_SetTimeHours(signal, state);
break;
case SetTimeMinutes:
state=change_state_from_SetTimeMinutes(signal, state);
break;
case SetAlarmHours:
state=change_state_from_SetAlarmHours(signal, state);
break;
case SetAlarmMinutes:
state=change_state_from_SetAlarmMinutes(signal, state);
break;
case IMU:
state=change_state_from_IMU(signal, state);
break;
case Alarm:
state=change_state_from_Alarm(signal, state);
break;
case Sleep:
state=change_state_from_Sleep(signal, state);
break;
case SetTimeHoursInc:
state=change_state_from_SetTimeHoursInc(signal, state);
break;
case SetTimeHoursDec:
state=change_state_from_SetTimeHoursDec(signal, state);
break;
case SetTimeMinutesInc:
state=change_state_from_SetTimeMinutesInc(signal, state);
break;
case SetTimeMinutesDec:
state=change_state_from_SetTimeMinutesDec(signal, state);
break;
case SetAlarmHoursInc:
state=change_state_from_SetAlarmHoursInc(signal, state);
break;
case SetAlarmHoursDec:
state=change_state_from_SetAlarmHoursDec(signal, state);
break;
case SetAlarmMinutesInc:
state=change_state_from_SetAlarmMinutesInc(signal, state);
break;
case SetAlarmMinutesDec:
state=change_state_from_SetAlarmMinutesDec(signal, state);
break;
case NoState:
state=change_state_from_NoState(signal, state);
break;
case Wake:
state=change_state_from_Wake(signal, state);
break;
default:
state=change_state_from_NoState(signal, state);
break;
}
return state;
}
- DO_work рассматривает новое и предыдущее состояния и вызывает метод действий
void DO_work(enum STATE state, enum STATE state_old)
{
switch(state)
{//by state
case AnalogCurrentTime:
draw_analog_watch(state, state_old);
break;
case SetTimeHours:
draw_settime_hours(state, state_old);
break;
case SetTimeMinutes:
draw_settime_minutes(state, state_old);
break;
case SetAlarmHours:
draw_setalarm_hours(state, state_old);
break;
case SetAlarmMinutes:
draw_setalarm_minutes(state, state_old);
break;
case IMU:
draw_IMU(state, state_old);
break;
case Alarm:
draw_alarm(state, state_old);
break;
case Sleep:
to_sleep(state, state_old);
break;
case Wake:
to_wake(state, state_old);
break;
case SetTimeHoursInc:
inc_draw_settime_hours(state, state_old);
break;
case SetTimeHoursDec:
dec_draw_settime_hours(state, state_old);
break;
case SetTimeMinutesInc:
inc_draw_settime_minutes(state, state_old);
break;
case SetTimeMinutesDec:
dec_draw_settime_minutes(state, state_old);
break;
case SetAlarmHoursInc:
inc_draw_setalarm_hours(state, state_old);
break;
case SetAlarmHoursDec:
dec_draw_setalarm_hours(state, state_old);
break;
case SetAlarmMinutesInc:
inc_draw_setalarm_minutes(state, state_old);
break;
case SetAlarmMinutesDec:
dec_draw_setalarm_minutes(state, state_old);
break;
default:
break;
}
}
“
Мы сделали шаблон, которым можно будет пользоваться для многих устройств, а также рассмотрели проектирование конечного автомата. Давайте перейдем к заданиям.
Интерактивное задание
Тест
Для закрепления полученных знаний пройдите тест
Стартуем! |
Что является внутренним событием?
Дальше |
Проверить |
Узнать результат |
Как обозначается поворот энкодера по часовой стрелке?
Дальше |
Проверить |
Узнать результат |
Что делает DO_work ?
Дальше |
Проверить |
Узнать результат |
К сожалению, вы ответили неправильно
Прочитайте лекцию и посмотрите видео еще раз
Пройти еще раз |
Неплохо!
Но можно лучше. Прочитайте лекцию и посмотрите видео еще раз
Пройти еще раз |
Отлично!
Вы отлично справились. Теперь можете ознакомиться с другими компетенциями
Пройти еще раз |