Опубликовано: 09.10.2017 22:39
Автор: jazon
Всем доброго... В этой статье мы познакомимся с созданием одноуровневого меню. В основу создания меню заложен скетч, используя который мы постепенно наращивали функционал меню представленного в статье. Предпосылкой реализации меню для LCD дисплея послужили несколько статей, а именно:
И вот, на основе данных, полученных из этих статей(или описанных в этих статьях, это уж кому как, лол) мы пришли к выводу что всё.... Пора... Пора сделать хоть что то серьёзное для проекта GreyBox. Что у нас из этого вышло, об этом и будет данная статья. Вообще, в дальнейших планах, много работы с серым ящиком, и возможно это не последняя статья про него. И да, сразу хотим предупредить вас - в статье будет много видео, точнее больше чем обычно мы выкладывали в других статьях. Итак начнем, нижеследующий скетч послужил основой для базы создания меню, в нем реализовано корректное считывание нажатий кнопок, подавление дребезга контактов, работа с дисплеем LCD2004 и другим оборудованием установленном в сером ящике. Сразу же нужно отметить что, лист меню, реализованный в скетче, вызывается нажатием кнопки ENT на клавиатуре серого ящика, и выглядит он так:
Передвигаться по пунктам меню можно с помощью кнопок 2(вверх) и 8(вниз), кнопка ENT служит для подтверждения/входа, кнопка ESC служит для отмены/выхода.
Листинг №1. Базовый скетч, основа создания меню для LCD2004 и клавиатурного модуля
#include
#define BUZZER 4
LiquidCrystal LCDisplay(13, 12, 11, 10, 9, 8);
//Функция определения нажатой кнопки
int getPressedButton();
//Функция обработки нажатой кнопки
void ButtonEventProc(int analogValue);
short int Mode = 0;
//Символ указателя закодирован здесь - arrowToLeft
byte arrowToLeft[8] = {B00010, B00100, B01000, B11111, B01000, B00100, B00010, B00000};
int button;
const int BUTTON_0 = 0;
const int BUTTON_1 = 1;
const int BUTTON_2 = 2;
const int BUTTON_3 = 3;
const int BUTTON_4 = 4;
const int BUTTON_5 = 5;
const int BUTTON_6 = 6;
const int BUTTON_7 = 7;
const int BUTTON_8 = 8;
const int BUTTON_9 = 9;
const int BUTTON_ENT = 10;
const int BUTTON_ESC = 11;
const int BUTTON_NONE = 12;
unsigned char RELOUT1 = A2;
unsigned char RELOUT2 = A3;
short int COLS = 20;
short int ROWS = 4;
int currentButtonState, previousButtonState;
char rootMenu[10][20] =
{
"Set Date ",
"Date Format ",
"Date Divider ",
"Set Time ",
"Time Format ",
"Time Divider ",
"More Options ",
"Set Relays ",
"Buzzer ",
"Quit "
};
short SizeOfRootMenu = sizeof(rootMenu) / 20;
short point = 0;
short list = 0;
short IndexCheckSum = 0;
void setup()
{
pinMode(BUZZER, OUTPUT);
LCDisplay.begin(COLS, ROWS);
LCDisplay.clear();
LCDisplay.createChar(0, arrowToLeft);
}
void loop()
{
//Инструкции подавления дребезга контактов кнопки
currentButtonState = getPressedButton();
if(currentButtonState != previousButtonState)
{
delay(5);
currentButtonState = getPressedButton();
if(currentButtonState != previousButtonState)
{
//LCDisplay.clear();
ButtonEventProc(currentButtonState);
previousButtonState = currentButtonState;
}
}
switch(Mode)
{
//Точка начала отрисовки основного экрана
case(0):
//Инструкции для отображения/отрисовки главного экрана
break;
//Режим отрисовки корневого меню
case(1):
MenuRendering(rootMenu, list, point);
break;
}
//Обязательное условие для создания индексации
IndexCheckSum = list + point;
}
//Функция для получения кода нажатой кнопки
int getPressedButton()
{
// считываем значения с аналогового входа(A0)
int buttonValue = analogRead(0);
if(buttonValue < 480)
return BUTTON_NONE;
else if(buttonValue < 500)
return BUTTON_ESC;
else if(buttonValue < 525)
return BUTTON_0;
else if(buttonValue < 551)
return BUTTON_ENT;
else if(buttonValue < 582)
return BUTTON_3;
else if(buttonValue < 615)
return BUTTON_6;
else if(buttonValue < 652)
return BUTTON_9;
else if(buttonValue < 695)
return BUTTON_2;
else if(buttonValue < 744)
return BUTTON_5;
else if(buttonValue < 800)
return BUTTON_8;
else if(buttonValue < 865)
return BUTTON_1;
else if(buttonValue < 940)
return BUTTON_4;
else if(buttonValue < 1024)
return BUTTON_7;
}
//Функция обработчик события нажатой кнопки
void ButtonEventProc(int analogValue)
{
switch(analogValue)
{
case BUTTON_NONE:
//Serial.println();
break;
case BUTTON_0:
Peak();
break;
case BUTTON_1:
Peak();
break;
case BUTTON_2:
Peak();
upListScroll();
break;
case BUTTON_3:
Peak();
break;
case BUTTON_4:
Peak();
break;
case BUTTON_5:
Peak();
break;
case BUTTON_6:
Peak();
break;
case BUTTON_7:
Peak();
break;
case BUTTON_8:
Peak();
switch(Mode)
{
case(1):
downListScroll(SizeOfRootMenu);
break;
}
break;
case BUTTON_9:
Peak();
break;
case BUTTON_ENT:
Peak();
switch(Mode)
{
case(0):
//Обработка входа в режим 1
LCDisplay.clear();
Mode = 1;
break;
case(1):
//Обработка входов в режиме 1
RootMenuHandler(IndexCheckSum);
break;
}
break;
case BUTTON_ESC:
Peak();
LCDisplay.clear();
Mode = 0;
point = 0;
list = 0;
break;
}
}
void Peak()
{
tone(BUZZER, 2349, 100);
}
//Функция отрисовки листов корневого меню и подменю
void MenuRendering(char DispItem[4][20], short listIndex, short pointIndex)
{
for(int Row = 0; Row < 4; Row ++)
{
for(int Col = 0; Col < 20; Col ++)
{
LCDisplay.setCursor(Col, Row);
LCDisplay.write(DispItem[Row + listIndex][Col]);
if(Row == pointIndex)
{
LCDisplay.setCursor(19, pointIndex);
LCDisplay.write(byte(0));
}
}
}
}
//Прокрутка листа меню вниз
void downListScroll(short numbIndexes)
{
if(point >= 3)
{
point = 3;
if(list >= (numbIndexes - 4)) list = (numbIndexes - 4);
else list ++;
}
else point ++;
}
//Прокрутка листа меню вверх
void upListScroll()
{
if(point <= 0)
{
point = 0;
if(list <= 0) list = 0;
else list --;
}
else point --;
}
void RootMenuHandler(short itemIndex)
{
switch(itemIndex)
{
//Точка выхода из корневого меню на главный экран
case(9):
LCDisplay.clear();
Mode = 0;
list = 0;
point = 0;
break;
}
}
Как работает этот скетч? Это можно увидеть из нижеследующего видео.
GreyBox[Часть 1] - Создание простого нефункционального одноуровневого меню
Конечно же автор проекта на этом не остановился, когда создана основа, можно только наращивать функционал и потенциал. Дальнейшие действия были направлены на то чтобы реализовать функционал для пунктов уже имеющегося одноуровневого меню, так сказать создать для каждого пункта свой подпункт. Следующим шагом стало добавление функционала к пунктам меню Set Date и Set Time, соответственно при помощи пункты открывают доступ к настройкам/установкам времени/даты.
GreyBox[Часть 2] - Делаем функциональным подменю Set Date и Set Time
Наверно все знают такой компонент как RadioButton из нашей всеми любимой "винды", реализация этого компонента показана в нижеследующем видео при входе в пункты меню Date Format, Date Divider, Time Format, Time Divider и Buzzer.
GreyBox[Часть 3] - Реализуем компонент RadioButton для других пунктов подменю
В одном из экранов установки опций было решено реализовать движение курсора не только вверх/вниз при выборе нужных подпунктов но и вправо/влево, эта функция была прописана и дополнена к кнопкам 4(влево) и 5(вправо).
GreyBox[Часть 4] - Реализация движения курсора во всех направлениях, подменю More Options
Ну и заключение - было решено прописать функционал для включения/выключения релейных модулей. В экране-обработчике настроек каждого из релейных модулей был изменен вид курсора, он стал своего рода выделением строки с уставкой.
GreyBox[Часть 5] - Управляем реле с помощью временных уставок, подменю Set Relays
Итак, однозначно можно сказать что мощность платы Arduino Uno в этом проекте не была использована на все 100%. Остались еще свободны 6 портов ввода/вывода и шина I2C. Но всё же, в качестве теста технологии создания меню и управления подключенным к плате оборудованием - эта разработка очень даже себя показала с позитивной стороны. На этом пока всё, мы надеемся это не последний наш проект с использованием GreyBox, если у вас появятся какие либо вопросы, либо предложения пишите на нашу электронную почту или в комментариях, мы обязательно ответим вам.
Если у Вас есть опыт в работе с Arduino и собственно есть время для творчества, мы приглашаем всех желающих стать авторами статей публикуемых на нашем портале. Это могут быть как уроки, так и рассказы о ваших экспериментах с Arduino. Описание различных датчиков и модулей. Советы и наставления начинающим. Пишите и размещайте свои статьи в соответсвующей ветке форума.