Меню для текстового LCD: помогите оптимизировать
- Войдите на сайт для отправки комментариев
Чт, 10/11/2011 - 02:01
В общем, все работает. Но хотелось бы упростить решение и оптимизировать, ибо 4кб жрет. Что то мне подсказывает - это возможно, но с сиподобными не знаком близко..
Итак, движок для меню текстового дисплея 4 строки. В нулевой приглашение тыкнуть, в остальных строки текущей позиции меню. Поддерживает подменю любых вложенностей и конфигураций.
Первая программа для ардуины! ))
#include <LiquidCrystalRus.h>
LiquidCrystalRus lcd(2, 3, 4, 5, 6, 7);
//Константы
//пины кнопок
byte KEY_UP = 27;
byte KEY_DOWN = 22;
byte KEY_LEFT = 26;
byte KEY_RIGHT = 24;
byte KEY_OK = 25;
byte KEY_BACK = 23;
//задержка после нажатий, мс
int KEYS_DELAY = 100;
//пины светодиодов
byte LED_GREEN = 20;
byte LED_RED = 21;
//главное меню
char* MainMenu[] = {
"Menu 1",
"Menu 2",
"Menu 3",
"Menu 4",
"Menu 5",
"Menu 6",
"Menu 7",
"Menu 8"
};
//подменю 1 <= MainMenu
char* SubMenu1[] = {
"SubMenu1 1",
"SubMenu1 2",
"SubMenu1 3",
"SubMenu1 4",
"SubMenu1 5"
};
//подменю 2 <= MainMenu
char* SubMenu2[] = {
"SubMenu2 1",
"SubMenu2 2",
"SubMenu2 3",
"SubMenu2 4"
};
//подменю 3 <= SubMenu1
char* SubSubMenu1[] = {
"SbSbMnu1 1",
"SbSbMnu1 2",
"SbSbMnu1 3",
"SbSbMnu1 4"
};
//Размеры меню. Позиция в массиве должна соответствовать menu_number.
//Обработка подразумевается в void menu_start()
byte MenuSizes[] = {
7, //MainMenu
4, //SubMenu1
3, //SubMenu2
3, //SubSubMenu1
};
//Список родителей меню. Номер элемента в массиве соответствует номеру меню menu_num
byte MenuParents[] = {
0, //MainMenu - родитель MainMenu
0, //SubMenu1 - родитель MainMenu
0, //SubMenu2 - родитель MainMenu
1 //SubSubMenu1 - родитель SubMenu1
};
//необходимые переменные
byte menu_number = 0; //Номер текущего меню. Обработка подразумевается в void menu_start()
byte menu_offset = 0; //Позиция в текущем меню.
byte need_update = 1; //необходимость обновить дисплей
char CLEAR_STRING[] = " "; //пустая строка для заливки дисплея
void setup() {
//Инициализация pins, кнопки замыкаются на GND
pinMode(LED_GREEN, OUTPUT); //светодиод зеленый
digitalWrite(LED_GREEN, LOW); //выключаем
pinMode(LED_RED, OUTPUT); //светодиод красный
digitalWrite(LED_RED, LOW); //выключаем
pinMode(KEY_UP, INPUT); //кнопка
digitalWrite(KEY_UP, HIGH); //подтягиваем резистор
pinMode(KEY_DOWN, INPUT); //кнопка
digitalWrite(KEY_DOWN, HIGH); //подтягиваем резистор
pinMode(KEY_LEFT, INPUT); //кнопка
digitalWrite(KEY_LEFT, HIGH); //подтягиваем резистор
pinMode(KEY_RIGHT, INPUT); //кнопка
digitalWrite(KEY_RIGHT, HIGH); //подтягиваем резистор
pinMode(KEY_OK, INPUT); //кнопка
digitalWrite(KEY_OK, HIGH); //подтягиваем резистор
pinMode(KEY_BACK, INPUT); //кнопка
digitalWrite(KEY_BACK, HIGH); //подтягиваем резистор
//настройки дисплея
lcd.begin(16, 4);
}
//***********************
//Основное тело программы
void loop() {
menu_start();
delay(100);
}
//***********************
//вызов основного модуля меню
void menu_start() {
//Обработка нажатий кнопок
//menu key down
if (digitalRead(KEY_DOWN) == LOW) {
blink(LED_GREEN);
if (menu_offset < MenuSizes[menu_number]) {
menu_offset++;
need_update = 1;
delay(KEYS_DELAY);
}
}
//menu key up
if (digitalRead(KEY_UP) == LOW) {
blink(LED_GREEN);
if (menu_offset > 0) {
menu_offset--;
need_update = 1;
delay(KEYS_DELAY);
}
}
//menu key ok
if (digitalRead(KEY_OK) == LOW) {
blink(LED_GREEN);
menu_execute();
need_update = 1;
delay(KEYS_DELAY);
}
//menu key back
if (digitalRead(KEY_BACK) == LOW) {
if (menu_number != 0) {
menu_number = MenuParents[menu_number]; //Возвращаемся в предыдущее меню согласно списку MenuParents
menu_offset = 0;
need_update = 1;
blink(LED_GREEN);
}
else {
menu_offset = 0;
need_update = 1;
blink(LED_RED);
}
delay(KEYS_DELAY);
}
//Если необходимо обновление экрана
if (need_update == 1) {
//Вызов нужного нам меню (сопоставление с массивами)
switch(menu_number) {
case 0: //MainMenu
menu_print(MainMenu);
break;
case 1: //SubMenu1
menu_print(SubMenu1);
break;
case 2: //SubMenu1
menu_print(SubMenu2);
break;
case 3: //SubSubMenu1
menu_print(SubSubMenu1);
break;
}//switch
need_update = 0;
}//if need update
//debug: print offset
//lcd.setCursor(0,3);
//lcd.print(menu_offset);
//
}
//Рисование меню (для глючного четырехстрочного дисплея)
void menu_print(char* Menu[]) {
//print menu
lcd.clear();
//надпись
lcd.setCursor(1, 0);
lcd.print("Выберите пункт");
//указатель на текущий пункт
lcd.setCursor(0,2);
lcd.print("*");
//пункт меню выше текущего
lcd.setCursor(6,1);
if ((menu_offset - 1) >= 0) lcd.print(Menu[menu_offset - 1]); else lcd.print(CLEAR_STRING);
//текущий пункт меню
lcd.setCursor(2,2);
lcd.print(Menu[menu_offset]);
//пункт меню ниже текущего
lcd.setCursor(2,3);
if ((menu_offset + 1) <= MenuSizes[menu_number]) lcd.print(Menu[menu_offset + 1]); else lcd.print(CLEAR_STRING);
}//print_menu
//действия по кнопке KEY_OK
void menu_execute() {
//Определение текущего меню
switch (menu_number) {
case 0: //Для MainMenu
switch(menu_offset) { //выбор действий
case 0: //Вызов подменю 1
menu_number = 1; //настройка на SubMenu1
menu_offset = 0; //сброс позиции в меню
break;
case 1: //Вызов подменю 2
menu_number = 2; //настройка на SubMenu2
menu_offset = 0; //сброс позиции в меню
break;
case 2: //блинк
blink(LED_RED);
break;
case 3: //блинк
blink(LED_RED);
break;
case 4: //блинк
blink(LED_RED);
break;
case 5: //блинк
blink(LED_RED);
break;
case 6: //блинк
blink(LED_RED);
break;
case 7: //блинк
blink(LED_RED);
break;
}//switch
break;
case 1: //Для SubMenu1
switch(menu_offset) { //выбор действий
case 0: //блинк
menu_number = 3; //настройка на SubSubMenu1
menu_offset = 0; //сброс позиции в меню
break;
case 1: //блинк
blink(LED_RED);
break;
case 2: //блинк
blink(LED_RED);
break;
case 3: //блинк
blink(LED_RED);
break;
case 4: //блинк
blink(LED_RED);
break;
}//switch
break;
case 2: //Для SubMenu2
switch(menu_offset) { //выбор действий
case 0: //блинк
blink(LED_RED);
break;
case 1: //блинк
blink(LED_RED);
break;
case 2: //блинк
blink(LED_RED);
break;
case 3: //блинк
blink(LED_RED);
break;
}//switch
break;
case 3: //Для SubSubMenu1
switch(menu_offset) { //выбор действий
case 0: //блинк
blink(LED_RED);
break;
case 1: //блинк
blink(LED_RED);
break;
case 2: //блинк
blink(LED_RED);
break;
case 3: //блинк
blink(LED_RED);
break;
}//switch
break;
} //switch MenuSelect
}//menu_execute
//блинк светодиодом
void blink(byte LED) {
//flash led
digitalWrite(LED, HIGH);
delay(100);
digitalWrite(LED, LOW);
}//blink
что, нет возможности выяснить размер массива? нет возможности двух-трехмерный массив сделать?
не так много народу тут, подождите маленько и Вам ответят
надеюсь, скоро станет больше :)
вообще похоже стандартных решений на ардуино нет..
может тут чего найдете : http://rln.nnov.ru/index.php?ind=reviews&op=entry_view&iden=211
Спасибо, почитаю код на досуге, посмотрю на решения
что, нет возможности выяснить размер массива? нет возможности двух-трехмерный массив сделать?
насчет размера сходу не скажу, но двумерный масив сделать легко) MainMenu[][];
MainMenu [] [] = { {"Menu1", 0},{"Menu1", 0},{"Menu1", 0} }; и доступ MainMenu[0][0], MainMenu[0][1]?
Хотел спросить как это меню можно переделать под двух строчный индикатор..............?
//Рисование меню (для глючного четырехстрочного дисплея) void menu_print(char* Menu[]) { //print menu lcd.clear(); //надпись lcd.setCursor(1, 0); lcd.print("Выберите пункт"); //указатель на текущий пункт lcd.setCursor(0,2); lcd.print("*"); //пункт меню выше текущего lcd.setCursor(6,1); if ((menu_offset - 1) >= 0) lcd.print(Menu[menu_offset - 1]); else lcd.print(CLEAR_STRING); //текущий пункт меню lcd.setCursor(2,2); lcd.print(Menu[menu_offset]); //пункт меню ниже текущего lcd.setCursor(2,3); if ((menu_offset + 1) <= MenuSizes[menu_number]) lcd.print(Menu[menu_offset + 1]); else lcd.print(CLEAR_STRING); }//print_menu Что означают эти, я примерно понял, но не понятно как можно переделать под экран 2х16, уже все перепробовал ...........