Меню для текстового LCD: помогите оптимизировать

whoim
Offline
Зарегистрирован: 03.11.2011

 В общем, все работает. Но хотелось бы упростить решение и оптимизировать, ибо 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

 

whoim
Offline
Зарегистрирован: 03.11.2011

 что, нет возможности выяснить размер массива? нет возможности двух-трехмерный массив сделать?

76region
Offline
Зарегистрирован: 08.07.2011

не так много народу тут, подождите маленько и Вам ответят

whoim
Offline
Зарегистрирован: 03.11.2011

 надеюсь, скоро станет больше :)

вообще похоже стандартных решений на ардуино нет..

76region
Offline
Зарегистрирован: 08.07.2011

может тут чего найдете : http://rln.nnov.ru/index.php?ind=reviews&op=entry_view&iden=211

whoim
Offline
Зарегистрирован: 03.11.2011

 Спасибо, почитаю код на досуге, посмотрю на решения

Adessit
Adessit аватар
Offline
Зарегистрирован: 12.04.2011

whoim пишет:

 что, нет возможности выяснить размер массива? нет возможности двух-трехмерный массив сделать?

насчет размера сходу не скажу, но двумерный масив сделать легко) MainMenu[][];

whoim
Offline
Зарегистрирован: 03.11.2011

 MainMenu [] [] = { {"Menu1", 0},{"Menu1", 0},{"Menu1", 0} }; и доступ MainMenu[0][0], MainMenu[0][1]?

mik
Offline
Зарегистрирован: 29.01.2012

Хотел спросить как это меню можно переделать под двух строчный индикатор..............?  

mik
Offline
Зарегистрирован: 29.01.2012

//необходимые переменные
byte menu_number = 0; //Номер текущего меню. Обработка подразумевается в void menu_start()
byte menu_offset = 0; //Позиция в текущем меню.
byte need_update = 1; //необходимость обновить дисплей
char CLEAR_STRING[] = "                "; //пустая строка для заливки дисплея

Объясните пожалуйста, что эти строки означают......... Если я правильно они созданы для хранения данных или для чего то другого.........

 

mik
Offline
Зарегистрирован: 29.01.2012
//Рисование меню (для глючного четырехстрочного дисплея)
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, уже все перепробовал ...........