Меню для Arduino

Мне всегда нравились микроконтроллеры и компьютеры. С компьютером проще, были бы деньги, а вот с микроконтроллерами немного сложнее. Моделей много глаза разбегаются, да и в свободной продаже у нас долгое время не встречалось. Раньше были попытки научится с ними работать, но как то не сложилось. И ATtiny12L отправилась в дальний яшик. Встречались мне новости по конструкторам на микроконтроллерах но на тот момент заказать было его невозможно, да и дорого. Гдето на глаза попалась Arduino и он ли она меня заинтересовала. Немного подумав решил заказать сие чудо и шилд для него в виде ЖК экрана с кнопочками, потому как моргать светодиодами было скучно, а делать что то свое лень.

image

И вот Arduno у меня в руках, софт весь стоит, одним словом твори и твори. Мысль была сделать что то похожее на бортовой компьютер, пробег, напряжение, температура за бортом. Первоначально общий и суточный пробег, но куда девать кнопки? Значит добавим напряжение и температуру.
Итого у нас получается:
  • Arduino + LCD Keypad Shield (5 кнопок, 2 строки по 16 символов);
  • отображение общего и суточного пробега;
  • напряжение в бортовой сети;
  • температура за бортом.
Опыта в написании скетчей у меня не было, поэтому начал разбираться как выводить текст на экран и как работают кнопки. Для экрана есть своя стандартная библиотека с которой нашёлся общий язык. Для кнопок используется одна аналоговая линия и в зависимости от напряжения на соответствующем выводе можно определить какая кнопка нажата.
На этом вроде все сложности должны были закончится, пиши себе да пиши. Но не все так просто как кажется. При отображении определённого параметра кнопки выполняют не одинаковые функции. Вроде что тут сложного? Если то то так так или так. Да, если параметра два и кнопок тоже две, но получается каша, про поиск ошибок и дополнении я вообще молчу. Вообщем это не тот вариант который мне нужен был.
Помню в университете мне понравилась одна лекция про конечные автоматы, в частности речь идет об автомате Мили. Не буду вдаваться в математические подробности его работы, в Википедии про него прочитал, но ничего не понял. Не смотря на это продолжу. У нас есть N состояний, M кнопок и K выполняемых функций.

Входные сигналы
Кнопок у нас 5 штук (Right, Up, Down, Left, Select), они являются входными сигналами. Каждую кнопку можно представить одним событием или тремя (нажата, удерживается в нажатом положении, отжата), также можно добавить событие удерживается более X секунд, ничего сложного в этом нет. Фактически я использую только отпускание кнопок, чтобы исключить многократный повтор функций при удержании клавиши. Я буду использовать 5 различных входных сигналов.

Состояния автомата
Их получается не много больше чем мне показалось на первый взгляд. По мимо отображения основных значений (пробег, температура, напряжение, время, дата) добавляется ещё сброс суточного пробега, установка времени и даты. Всего получилось 12 состояний.

Функции. Выходные сигналы
Каждый выходной сигнал соответствует вызову той или иной функции, если требуется изменить только состояние без выполнения какой либо функции то предусмотрена для этих случаев пустая функция. Все остальные функции выполняют необходимые действия.

Таблица автомата Мили
Первая и вторая строка состояние автомата: отображаемый параметр и код состояния соответственно. Первая и вторая колонка клавиша и её код (входной сигнал).
На пересечении входного сигнала и текущего состояния записаны новое состояние и выполняемая функция через запятую.
image
Как этим пользоваться. MenuState хранит текущее состояние, допустим оно равно 1, на экране отображается суточный пробег. Нажимаем Select для сброса показаний, входной сигнал равен 4. Ищем пересечение входного сигнала 4 и текущего состояния 1, что в результате дает нам состояние 6 и функцию ff. Функция ff ничего не выполняет. В состоянии 6 на экране отобразится подтверждение сброса суточного пробега, влево отказ от сброса, в право подтверждение. Теперь если придёт сигнал 3 на пересечении получим “1,ff”, переход в состояние 1 без выполнения функция, а если придёт сигнал 0 то “1,0”. Переход в состояние 1 и выполнение функции 0. Ниже приведён список функций и их коды.
Коды функций:
F_NOP FF Нет действия
F_Reset 0 Сбросить суточный пробег
F_IncHour 1 Прибавить 1 час
F_DecHour 2 Отнять 1 час
F_IncMin 3 Прибавить 1 минуту
F_DecMin 4 Отнять 1 минуту
F_IncDay 5 Прибавить 1 день
F_DecDay 6 Отнять 1 дени
F_IncMon 7 Прибавить 1 месяц
F_DecMon 8 Отнять 1 месяц
F_IncYear 9 Прибавить 1 год
F_DecYear A Отнять 1 год
F_SetTime B Установить новое время
F_SetDate C Установить новую дату


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

// Функция перехода по меню и получения номера вызываемой функции
byte GetFunction(byte InSig){
 // если нажатых лавиш нет
 if (InSig==KeyUnpressed) 
 return F_NOP;
 //
 boolean Up=InSig&KeyUpFlag;
 boolean Down=InSig&KeyDownFlag;
 byte sig=InSig&0b00011111;
 // проверяем допустимость 
 if(sig>=InSigCount)
 return F_NOP;
 byte func=F_NOP;
 if(Up){
 // читаем вызываемую функцию
 func=menu[sig][MenuState].funct;
 // читаем новое сотояние
 MenuState=menu[sig][MenuState].state;
 }
 // Возвращаем номер функции
 return func; 
}

// Пример выполнения функций
byte k=ReadKey();
 k=GetFunction(k);
 if(k!=F_NOP){
 if(k==F_Reset){
 Trip=0; // Сбрасыаем суточный пробег
 }else if(k==F_IncHour){
 if(DT.Hour<23){ DT.Hour++; }else{ DT.Hour=0; }
 }else if(k==F_DecHour){
 if(DT.Hour>0){ DT.Hour--; }else{ DT.Hour=23; }
 }else if(k==F_IncMin){
 if(DT.Minute<59){DT.Minute++; }else{DT.Minute=0;}
 }else if(k==F_DecMin){


0 комментариев

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.