Меню для дисплея 16x2 и 20x4 что можно оптимизировать ?
- Войдите на сайт для отправки комментариев
Чт, 03/05/2018 - 14:42
Добрый день.
Накидал тут свое меню для дисплеев (16x2, 20x4) Arduino.
Попытался сделать код меню максимально простым для понимания, насколько смог на текущий момент.
Сделал был отдельную библиотеку, но пока увы не мею такие собирать.
Нужны советы по оптимизации кода и используемой памяти, если есть что можно поправить.
P.S. Если кому-то будет полезно, то пользуйтесь.
// ------------------------------------------------------------ Библиотеки ------------------------------------------------------------
#include <LiquidCrystal.h>
// -------------------------------------------------------------- Дисплей --------------------------------------------------------------
LiquidCrystal lcd(12, 11, 5, 4, 3, 2); // I2C модуля пока нет, поэтому подключаем по старинке
// ------------------------------------------------------------ Настройки ------------------------------------------------------------
// В наличии Button shield нет и энкодера тоже, поэтому обойдемся простыми кнопками
#define up 7 // Пин кнопки "Вверх"
#define down 6 // Пин кнопки "Вниз"
#define enter 8 // Пин кнопки "Вход"
#define back 9 // Пин кнопки "Выход"
#define lcdSize 4 // Количество строк на дисплее
#define lcdlong 20 // Количество символов в строке на дисплее
#define delayBtn 300 // Задержка после нажатия кнопки (для стабильности навигации)
// ------------------------------------------------------------ Переменные ------------------------------------------------------------
// Стрелки для меню
const uint8_t scroll_bar[2][8] = {
{B00100, B00100, B00100, B00100, B00100, B10101, B01110, B00100}, // Cтрелка вниз
{B00100, B01110, B10101, B00100, B00100, B00100, B00100, B00100} // Cтрелка вверх
};
byte currentMenuNumber[] = {1, 0, 0}; // Текущий номер пункта меню
byte currentMenuLVL; // Текущий уровень в меню
byte menuCount; // Общее количество пунктов меню
byte positionMainMenu; // Текущий номер пункта в структуре
byte btnNum; // Номер нажатой кнопки
boolean functionMenu; // Признак того, что мы работаем с функцией из текущего пункта меню (отключает навигацию по mainMenu)
// ------------------------------------------------------------- Структуры -------------------------------------------------------------
// Структура меню
struct structMenu {
byte Num[3]; // Номер пункта меню
char Name[12]; // Наименование пункта меню (пока только на англ.)
boolean enterFunction; // Флаг признака функции (если True, то выполнит функцию, иначе попытается перейти на следующий пункт меню)
void (*function)(); // Вызываемая функция
};
// Меню
struct structMenu mainMenu[] = {
{{1, 0, 0}, "Info >", true, printInfo},
{{2, 0, 0}, "Bloks >", false, zero},
{{2, 1, 0}, "Point 2-1 >", false, zero},
{{2, 1, 1}, "Point 2-1-1", false, zero},
{{2, 1, 2}, "Point 2-1-2", false, zero},
{{2, 2, 0}, "Point 2-2 >", false, zero},
{{2, 2, 1}, "Point 2-2-1", false, zero},
{{2, 2, 2}, "Point 2-2-2", false, zero},
{{2, 2, 3}, "Point 2-2-1", false, zero},
{{2, 2, 4}, "Point 2-2-2", false, zero},
{{2, 2, 5}, "Point 2-2-1", false, zero},
{{2, 2, 6}, "Point 2-2-2", false, zero},
{{3, 0, 0}, "Settings >" , false, zero},
{{3, 1, 0}, "Point 3-1 >", false, zero},
{{3, 1, 1}, "Point 3-1-1", false, zero},
{{3, 1, 2}, "Point 3-1-2", false, zero},
{{3, 2, 0}, "Point 3-2 >", false, zero},
{{3, 2, 1}, "Point 3-2-1", false, zero},
{{3, 2, 2}, "Point 3-2-2", false, zero},
{{4, 0, 0}, "Dop >", false, zero},
{{4, 1, 0}, "Point 4-1 >", false, zero},
{{4, 1, 1}, "Point 4-1-1", false, zero},
{{4, 1, 2}, "Point 4-1-2", false, zero},
{{4, 2, 0}, "Point 4-2 >", false, zero},
{{4, 2, 1}, "Point 4-2-1", false, zero},
{{4, 2, 2}, "Point 4-2-2", false, zero}
};
// -------------------------------------------------- Системные функции (setup, loop) --------------------------------------------------
void setup()
{
lcd.begin(lcdlong, lcdSize); // Инициализация дисплея
lcd.createChar(0, (uint8_t*)scroll_bar[0]); // Инициализация - стрелка вниз
lcd.createChar(1, (uint8_t*)scroll_bar[1]); // Инициализация - стрелка вверх
lcd.clear(); // Очистим экран
pinMode(up, INPUT_PULLUP); // Кнопка "Вверх"
pinMode(down, INPUT_PULLUP); // Кнопка "Вниз"
pinMode(enter, INPUT_PULLUP); // Кнопка "Вход"
pinMode(back, INPUT_PULLUP); // Кнопка "Выход"
// Зададим начальные значения переменных
currentMenuLVL = 0;
positionMainMenu = 0;
functionMenu = false;
// Посчитаем общее количество пунктов меню mainMenu
menuCount = sizeof(mainMenu) / sizeof(mainMenu[0]);
// Выведем меню на экран
arrowPrint();
}
void loop()
{
//Если нажали какую-то кнопку
btnNum = 0;
if (!digitalRead(up)) {btnNum = 1;}
if (!digitalRead(down)) {btnNum = 2;}
if (!digitalRead(enter)) {btnNum = 3;}
if (!digitalRead(back)) {btnNum = 4;}
if (btnNum > 0)
{
// Если стоит флаг, то выполняем функцию из текущего пункта меню, иначе отрабатываем нажатие кнопки в меню
if (functionMenu)
{
mainMenu[positionMainMenu].function();
}
else
{
pushButton();
}
}
}
// ---------------------------------------------------------------- END ----------------------------------------------------------------
// -------------------------------------------------------- Другие функции меню --------------------------------------------------------
// Функция заглушка, ничего не делает
void zero() {};
// Функция в теории делает какие-то вычисления и выводит данные на дисплей
void printInfo()
{
functionMenu = true;
boolean printText = false;
switch (btnNum)
{
case 1:
/*
Что-то делаем
*/
printText = true;
break;
case 2:
/*
Что-то делаем
*/
printText = true;
break;
case 3:
/*
Что-то делаем
*/
printText = true;
break;
case 4:
// Нажали кнопку Back - выйдем обратно в меню
functionMenu = false;
arrowPrint();
break;
}
if (printText)
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Выведем какой-то текст");
lcd.setCursor(0, 1);
lcd.print("Выведем какой-то текст");
lcd.setCursor(0, 2);
delay(delayBtn);
}
}
// ---------------------------------------------------------------- END ----------------------------------------------------------------
// ------------------------------------------ Работа с кнопками (навигация в меню mainMenu) --------------------------------------------
// Обработка нажатия кнопки
void pushButton()
{
if (!functionMenu)
{
switch (btnNum)
{
case 1:
pushButtonUp();
break;
case 2:
pushButtonDown();
break;
case 3:
pushButtonEnter();
break;
case 4:
pushButtonBack();
break;
}
}
else
{
// Заплатка вызова функции пункта меню, на всякий случай
// По идее программа сюда не должна зайти
mainMenu[positionMainMenu].function();
}
}
// Если нажата кнопка "Вверх"
void pushButtonUp()
{
// Если мы не на первом уровне меню, то отработаем нажатие кнопки
if (currentMenuNumber[currentMenuLVL] > 1)
{
currentMenuNumber[currentMenuLVL]--;
delay(delayBtn);
arrowPrint();
}
}
// Если нажата кнопка "Вниз"
void pushButtonDown()
{
boolean check = false;
// Если мы на первом уровне меню
if (currentMenuLVL == 0)
{
// Переберем все пункты меню
for (int i = 0; i < menuCount; i++)
{
// Если мы нашли пункт у которого номер больше текущего, то отработаем нажатие кнопки
if (mainMenu[i].Num[currentMenuLVL] > currentMenuNumber[currentMenuLVL]) {check = true; break;}
}
}
else
{
// Переберем все пункты меню
for (int i = 0; i < menuCount; i++)
{
// Если вышестоящий адрес совпадает и мы нашли пункт у которого номер больше текущего, то отработаем нажатие кнопки
if (checkAdress(i) && mainMenu[i].Num[currentMenuLVL] > currentMenuNumber[currentMenuLVL]) {check = true; break;}
}
}
// Отработаем нажатие кнопки
if (check)
{
currentMenuNumber[currentMenuLVL]++;
delay(delayBtn);
arrowPrint();
}
}
// Если нажата кнопка "Вход"
void pushButtonEnter()
{
// Если стоит флаг enterFunction, то выполним функцию из текущего пункта меню mainMenu, иначе навигация
if (mainMenu[positionMainMenu].enterFunction)
{
mainMenu[positionMainMenu].function();
}
else
{
// Если мы не на последнем уровне в меню
if (currentMenuLVL < sizeof(currentMenuNumber) - 1)
{
boolean subMenu = false;
// Переберем все пункты меню
for (int i = 0; i < menuCount; i++)
{
// Проверим, является ли выбранный пункт подменю для текущего пункта
if (checkAdress(i)) {subMenu = true; break;}
}
// Отработаем нажатие кнопки
if (subMenu)
{
currentMenuLVL++;
currentMenuNumber[currentMenuLVL]++;
delay(delayBtn);
arrowPrint();
}
}
}
}
// Если нажата кнопка "Выход"
void pushButtonBack()
{
// Если мы не на первом уровне меню
if (currentMenuLVL > 0)
{
// Если стоит флаг enterFunction, то выполним функцию из текущего пункта меню mainMenu, иначе навигация
if (mainMenu[positionMainMenu].enterFunction)
{
mainMenu[positionMainMenu].function();
}
else
{
currentMenuNumber[currentMenuLVL] = 0;
currentMenuLVL--;
delay(delayBtn);
arrowPrint();
}
}
}
// ---------------------------------------------------------------- END ----------------------------------------------------------------
// ----------------------------------------------------------- Работа с меню -----------------------------------------------------------
// Функция определяет необходимость показа стрелок на дисплее
void arrowPrint()
{
boolean bottomItem = false;
byte menuItemCount = 0; // Количество пунктов на текущем уровне меню с учетом адреса родителя
// Переберем все пункты меню
for (int i = 0; i < menuCount; i++)
{
// Если мы на первом уровне адреса
if (currentMenuLVL == 0)
{
// Если второй элемент адреса = 0
if (mainMenu[i].Num[currentMenuLVL + 1] == 0) {menuItemCount++;}
}
else
{
// Если мы на последнем уровне адреса
if (currentMenuLVL == sizeof(currentMenuNumber) - 1)
{
// Если вышестоящие элементы адреса совпадают "И" текущий эелемент > 0
if (checkAdress(i) && mainMenu[i].Num[currentMenuLVL] > 0) {menuItemCount++;}
}
else
{
// Если вышестоящие элементы адреса совпадают "И" текущий эелемент > 0 "И" следующий элемент адреса = 0
if (checkAdress(i) && mainMenu[i].Num[currentMenuLVL] > 0 && mainMenu[i].Num[currentMenuLVL + 1] == 0) {menuItemCount++;}
}
}
}
// Если строк на текущем уровне меню больше чем размер экрана, то выведем стрелки, иначе они не нужны
if (menuItemCount > lcdSize)
{
// Если мы на первом уровне меню
if (currentMenuNumber[currentMenuLVL] == 1)
{
// Отрисовать меню (Стрелка вверх - нет, стрелка вниз - да)
lcdScreen(false, true);
}
else
{
// Переберем все пункты меню
for (int i = 0; i < menuCount; i++)
{
// Если вышестоящие элементы адреса совпадают "И" адрес проверяемого пункта меню > текущего элемента адреса
if (checkAdress(i) && mainMenu[i].Num[currentMenuLVL] > currentMenuNumber[currentMenuLVL]) {bottomItem = true; break;}
}
// Если bottomItem, значит посередине, иначе мы в конце списка текущего уровня подменю
if (bottomItem)
{
// Отрисовать меню (Стрелка вверх - да, стрелка вниз - да)
lcdScreen(true, true);
}
else
{
// Отрисовать меню (Стрелка вверх - да, стрелка вниз - нет)
lcdScreen(true, false);
}
}
}
else
{
// Отрисовать меню (без стрелок)
lcdScreen(false, false);
}
}
// Функция вывода меню на дисплей
void lcdScreen(boolean arrowUp, boolean arrowDown)
{
lcd.clear(); // Очистим дисплей
int lcdPosition = 0; // Текущая строка дисплея
// Переберем все пункты меню и выведем на экран нужные
for (int i = 0; i < menuCount; i++)
{
boolean printMenu = false;
// Если место на экране еще есть "И" номер выбранного пункта меню попадает в диапазон размера дисплея
if (lcdPosition <= lcdSize - 1 && currentMenuNumber[currentMenuLVL] - lcdSize < mainMenu[i].Num[currentMenuLVL])
{
// Если мы на первом уровне адреса
if (currentMenuLVL == 0)
{
// Если второй элемент адреса = 0
if (mainMenu[i].Num[currentMenuLVL + 1] == 0) {printMenu = true;}
}
else
{
// Если вышестоящие элементы адреса совпадают "И" текущего элемента адреса > 0
if (checkAdress(i) && mainMenu[i].Num[currentMenuLVL] > 0)
{
// Если мы где-то посередине меню, иначе данный уровень последний
if (currentMenuLVL < sizeof(currentMenuNumber) - 1)
{
// Если элемент адреса следующего уровеня = 0 (текущий уровень + 1)
if (mainMenu[i].Num[currentMenuLVL + 1] == 0) {printMenu = true;}
}
else
{
// Мы на последенем уровне, дальше ничего нет, поэтому можно выводить без проверок
printMenu = true;
}
}
}
}
// Выведем текущий пункт меню на дисплей
if (printMenu)
{
lcd.setCursor(0, lcdPosition); // Установим курсор на 0 элемент нужной строки
// Если текущий адрес меню совпадает с сохраненным, то добавим перед именем указатель текущей позиции "->"
if (arrayCompare(currentMenuNumber, mainMenu[i].Num))
{
lcd.print("->" + String(mainMenu[i].Name));
positionMainMenu = i;
}
else
{
lcd.print(mainMenu[i].Name);
}
lcdPosition++;
}
// Если пришел флаг, то выведем стрелку вверх
if (arrowUp)
{
lcd.setCursor(lcdlong - 1, 0);
lcd.write((uint8_t)1);
}
// Если пришел флаг, то выведем стрелку вниз
if (arrowDown)
{
lcd.setCursor(lcdlong - 1, lcdSize - 1);
lcd.write((uint8_t)0);
}
// Если место на дисплее закончилось, то выйдем из цикла
if (lcdPosition >= lcdSize) {break;}
}
}
// Функция сравнения двух массивов
boolean arrayCompare(byte arr1[], byte arr2[])
{
boolean checkAdressPosition = true;
for (int posNum = 0; posNum < sizeof(currentMenuNumber); posNum++)
{
if (arr1[posNum] != arr2[posNum]) {checkAdressPosition = false; break;}
}
return checkAdressPosition;
}
// Функция проверки соответствия текущего адреса меню
boolean checkAdress(int pos)
{
boolean checkAdressPosition = true;
for (int posNum = 0; posNum < currentMenuLVL; posNum++)
{
if (currentMenuNumber[posNum] != mainMenu[pos].Num[posNum]) {checkAdressPosition = false; break;}
}
return checkAdressPosition;
}
// ---------------------------------------------------------------- END ----------------------------------------------------------------
// Надо попробовать, говорят для экономии памяти
//lcd.print( F( "message" ) );
Ах да, забыл добавить.
Писал все на Arduino IDE 1.6.5, все компилируется без проблем, в более новых версиях могут появиться ошибки объявления функций в структуре, например.
Совет по оптимизации памяти - положить все строковые константы в PROGMEM. А так как у вас меню займет всю оперативку, на данные самой программы места уже не останется.
Совет по оптимизации памяти - положить все строковые константы в PROGMEM. А так как у вас меню займет всю оперативку, на данные самой программы места уже не останется.
Надо почитать, что это такое и с чем его едят.
Спасибо, пошел кусать гранит науки.
Вынес меню и прочие константы в PROGMEM. Код занимает 104 bytes (5%) оперативки.
Что-то еще можно улучшить ?
// ------------------------------------------------------------ Библиотеки ------------------------------------------------------------ #include <LiquidCrystal.h> // -------------------------------------------------------------- Дисплей -------------------------------------------------------------- LiquidCrystal lcd(12, 11, 5, 4, 3, 2); // I2C модуля пока нет, поэтому подключаем по старинке // ------------------------------------------------------------ Настройки ------------------------------------------------------------ #define up 7 // Пин кнопки "Вверх" #define down 6 // Пин кнопки "Вниз" #define enter 8 // Пин кнопки "Вход" #define back 9 // Пин кнопки "Выход" #define lcdSize 4 // Количество строк на дисплее #define lcdlong 20 // Количество символов в строке на дисплее #define delayBtn 300 // Задержка после нажатия кнопки (для стабильности навигации) // ------------------------------------------------------------ Переменные ------------------------------------------------------------ // Стрелки для меню const uint8_t scroll_bar[2][8] PROGMEM = { {B00100, B00100, B00100, B00100, B00100, B10101, B01110, B00100}, // Cтрелка вниз {B00100, B01110, B10101, B00100, B00100, B00100, B00100, B00100} // Cтрелка вверх }; byte currentMenuNumber[] = {1, 0, 0}; // Текущий номер пункта меню byte currentMenuLVL; // Текущий уровень в меню byte menuCount; // Общее количество пунктов меню byte positionMainMenu; // Текущий номер пункта в структуре byte btnNum; // Номер нажатой кнопки boolean functionMenu; // Признак того, что мы работаем с функцией из текущего пункта меню (отключает навигацию по mainMenu) // --------------------------------------------------------- Объявление функций -------------------------------------------------------- // ------------------------------------------------------------- Структуры ------------------------------------------------------------- // Структура меню struct structMenu { byte Num[3]; // Номер пункта меню char Name[12]; // Наименование пункта меню (пока только на англ.) boolean enterFunction; // Флаг признака функции (если True, то выполнит функцию, иначе попытается перейти на следующий пункт меню) void (*function)(); // Вызываемая функция }; // Меню const struct structMenu mainMenu[] PROGMEM = { {{1, 0, 0}, "Info >", true, printInfo}, {{2, 0, 0}, "Bloks >", false, zero}, {{2, 1, 0}, "Point 2-1 >", false, zero}, {{2, 1, 1}, "Point 2-1-1", false, zero}, {{2, 1, 2}, "Point 2-1-2", false, zero}, {{2, 2, 0}, "Point 2-2 >", false, zero}, {{2, 2, 1}, "Point 2-2-1", false, zero}, {{2, 2, 2}, "Point 2-2-2", false, zero}, {{2, 2, 3}, "Point 2-2-1", false, zero}, {{2, 2, 4}, "Point 2-2-2", false, zero}, {{2, 2, 5}, "Point 2-2-1", false, zero}, {{2, 2, 6}, "Point 2-2-2", false, zero}, {{3, 0, 0}, "Settings >" , false, zero}, {{3, 1, 0}, "Point 3-1 >", false, zero}, {{3, 1, 1}, "Point 3-1-1", false, zero}, {{3, 1, 2}, "Point 3-1-2", false, zero}, {{3, 2, 0}, "Point 3-2 >", false, zero}, {{3, 2, 1}, "Point 3-2-1", false, zero}, {{3, 2, 2}, "Point 3-2-2", false, zero}, {{4, 0, 0}, "Dop >", false, zero}, {{4, 1, 0}, "Point 4-1 >", false, zero}, {{4, 1, 1}, "Point 4-1-1", false, zero}, {{4, 1, 2}, "Point 4-1-2", false, zero}, {{4, 2, 0}, "Point 4-2 >", false, zero}, {{4, 2, 1}, "Point 4-2-1", false, zero}, {{4, 2, 2}, "Point 4-2-2", false, zero} }; // -------------------------------------------------- Системные функции (setup, loop) -------------------------------------------------- void setup() { lcd.begin(lcdlong, lcdSize); // Инициализация дисплея lcd.createChar(0, (uint8_t*)scroll_bar[0]); // Инициализация - стрелка вниз lcd.createChar(1, (uint8_t*)scroll_bar[1]); // Инициализация - стрелка вверх lcd.clear(); // Очистим экран pinMode(up, INPUT_PULLUP); // Кнопка "Вверх" pinMode(down, INPUT_PULLUP); // Кнопка "Вниз" pinMode(enter, INPUT_PULLUP); // Кнопка "Вход" pinMode(back, INPUT_PULLUP); // Кнопка "Выход" // Зададим начальные значения переменных currentMenuLVL = 0; positionMainMenu = 0; functionMenu = false; // Посчитаем общее количество пунктов меню mainMenu menuCount = sizeof(mainMenu) / sizeof(mainMenu[0]); // Выведем меню на экран arrowPrint(); } void loop() { //Если нажали какую-то кнопку btnNum = 0; if (!digitalRead(up)) {btnNum = 1;} if (!digitalRead(down)) {btnNum = 2;} if (!digitalRead(enter)) {btnNum = 3;} if (!digitalRead(back)) {btnNum = 4;} if (btnNum > 0) { // Если стоит флаг, то выполняем функцию из текущего пункта меню, иначе отрабатываем нажатие кнопки в меню if (functionMenu) { mainMenu[positionMainMenu].function(); } else { pushButton(); } } } // ---------------------------------------------------------------- END ---------------------------------------------------------------- // -------------------------------------------------------- Другие функции меню -------------------------------------------------------- // Функция заглушка, ничего не делает void zero() {}; // Функция в теории делает какие-то вычисления и выводит данные на дисплей void printInfo() { functionMenu = true; boolean printText = false; switch (btnNum) { case 1: /* Что-то делаем */ printText = true; break; case 2: /* Что-то делаем */ printText = true; break; case 3: /* Что-то делаем */ printText = true; break; case 4: // Нажали кнопку Back - выйдем обратно в меню functionMenu = false; arrowPrint(); break; } if (printText) { lcd.clear(); lcd.setCursor(0, 0); lcd.print("Выведем какой-то текст"); lcd.setCursor(0, 1); lcd.print("Выведем какой-то текст"); lcd.setCursor(0, 2); delay(delayBtn); } } // ---------------------------------------------------------------- END ---------------------------------------------------------------- // ------------------------------------------ Работа с кнопками (навигация в меню mainMenu) -------------------------------------------- // Обработка нажатия кнопки void pushButton() { if (!functionMenu) { switch (btnNum) { case 1: pushButtonUp(); break; case 2: pushButtonDown(); break; case 3: pushButtonEnter(); break; case 4: pushButtonBack(); break; } } else { // Заплатка вызова функции пункта меню, на всякий случай // По идее программа сюда не должна зайти mainMenu[positionMainMenu].function(); } } // Если нажата кнопка "Вверх" void pushButtonUp() { // Если мы не на первом уровне меню, то отработаем нажатие кнопки if (currentMenuNumber[currentMenuLVL] > 1) { currentMenuNumber[currentMenuLVL]--; delay(delayBtn); arrowPrint(); } } // Если нажата кнопка "Вниз" void pushButtonDown() { boolean check = false; // Если мы на первом уровне меню if (currentMenuLVL == 0) { // Переберем все пункты меню for (int i = 0; i < menuCount; i++) { // Если мы нашли пункт у которого номер больше текущего, то отработаем нажатие кнопки if (mainMenu[i].Num[currentMenuLVL] > currentMenuNumber[currentMenuLVL]) {check = true; break;} } } else { // Переберем все пункты меню for (int i = 0; i < menuCount; i++) { // Если вышестоящий адрес совпадает и мы нашли пункт у которого номер больше текущего, то отработаем нажатие кнопки if (checkAdress(i) && mainMenu[i].Num[currentMenuLVL] > currentMenuNumber[currentMenuLVL]) {check = true; break;} } } // Отработаем нажатие кнопки if (check) { currentMenuNumber[currentMenuLVL]++; delay(delayBtn); arrowPrint(); } } // Если нажата кнопка "Вход" void pushButtonEnter() { // Если стоит флаг enterFunction, то выполним функцию из текущего пункта меню mainMenu, иначе навигация if (mainMenu[positionMainMenu].enterFunction) { mainMenu[positionMainMenu].function(); } else { // Если мы не на последнем уровне в меню if (currentMenuLVL < sizeof(currentMenuNumber) - 1) { boolean subMenu = false; // Переберем все пункты меню for (int i = 0; i < menuCount; i++) { // Проверим, является ли выбранный пункт подменю для текущего пункта if (checkAdress(i)) {subMenu = true; break;} } // Отработаем нажатие кнопки if (subMenu) { currentMenuLVL++; currentMenuNumber[currentMenuLVL]++; delay(delayBtn); arrowPrint(); } } } } // Если нажата кнопка "Выход" void pushButtonBack() { // Если мы не на первом уровне меню if (currentMenuLVL > 0) { // Если стоит флаг enterFunction, то выполним функцию из текущего пункта меню mainMenu, иначе навигация if (mainMenu[positionMainMenu].enterFunction) { mainMenu[positionMainMenu].function(); } else { currentMenuNumber[currentMenuLVL] = 0; currentMenuLVL--; delay(delayBtn); arrowPrint(); } } } // ---------------------------------------------------------------- END ---------------------------------------------------------------- // ----------------------------------------------------------- Работа с меню ----------------------------------------------------------- // Функция определяет необходимость показа стрелок на дисплее void arrowPrint() { boolean bottomItem = false; byte menuItemCount = 0; // Количество пунктов на текущем уровне меню с учетом адреса родителя // Переберем все пункты меню for (int i = 0; i < menuCount; i++) { // Если мы на первом уровне адреса if (currentMenuLVL == 0) { // Если второй элемент адреса = 0 if (mainMenu[i].Num[currentMenuLVL + 1] == 0) {menuItemCount++;} } else { // Если мы на последнем уровне адреса if (currentMenuLVL == sizeof(currentMenuNumber) - 1) { // Если вышестоящие элементы адреса совпадают "И" текущий эелемент > 0 if (checkAdress(i) && mainMenu[i].Num[currentMenuLVL] > 0) {menuItemCount++;} } else { // Если вышестоящие элементы адреса совпадают "И" текущий эелемент > 0 "И" следующий элемент адреса = 0 if (checkAdress(i) && mainMenu[i].Num[currentMenuLVL] > 0 && mainMenu[i].Num[currentMenuLVL + 1] == 0) {menuItemCount++;} } } } // Если строк на текущем уровне меню больше чем размер экрана, то выведем стрелки, иначе они не нужны if (menuItemCount > lcdSize) { // Если мы на первом уровне меню if (currentMenuNumber[currentMenuLVL] == 1) { // Отрисовать меню (Стрелка вверх - нет, стрелка вниз - да) lcdScreen(false, true); } else { // Переберем все пункты меню for (int i = 0; i < menuCount; i++) { // Если вышестоящие элементы адреса совпадают "И" адрес проверяемого пункта меню > текущего элемента адреса if (checkAdress(i) && mainMenu[i].Num[currentMenuLVL] > currentMenuNumber[currentMenuLVL]) {bottomItem = true; break;} } // Если bottomItem, значит посередине, иначе мы в конце списка текущего уровня подменю if (bottomItem) { // Отрисовать меню (Стрелка вверх - да, стрелка вниз - да) lcdScreen(true, true); } else { // Отрисовать меню (Стрелка вверх - да, стрелка вниз - нет) lcdScreen(true, false); } } } else { // Отрисовать меню (без стрелок) lcdScreen(false, false); } } // Функция вывода меню на дисплей void lcdScreen(boolean arrowUp, boolean arrowDown) { lcd.clear(); // Очистим дисплей int lcdPosition = 0; // Текущая строка дисплея // Переберем все пункты меню и выведем на экран нужные for (int i = 0; i < menuCount; i++) { boolean printMenu = false; // Если место на экране еще есть "И" номер выбранного пункта меню попадает в диапазон размера дисплея if (lcdPosition <= lcdSize - 1 && currentMenuNumber[currentMenuLVL] - lcdSize < mainMenu[i].Num[currentMenuLVL]) { // Если мы на первом уровне адреса if (currentMenuLVL == 0) { // Если второй элемент адреса = 0 if (mainMenu[i].Num[currentMenuLVL + 1] == 0) {printMenu = true;} } else { // Если вышестоящие элементы адреса совпадают "И" текущего элемента адреса > 0 if (checkAdress(i) && mainMenu[i].Num[currentMenuLVL] > 0) { // Если мы где-то посередине меню, иначе данный уровень последний if (currentMenuLVL < sizeof(currentMenuNumber) - 1) { // Если элемент адреса следующего уровеня = 0 (текущий уровень + 1) if (mainMenu[i].Num[currentMenuLVL + 1] == 0) {printMenu = true;} } else { // Мы на последенем уровне, дальше ничего нет, поэтому можно выводить без проверок printMenu = true; } } } } // Выведем текущий пункт меню на дисплей if (printMenu) { lcd.setCursor(0, lcdPosition); // Установим курсор на 0 элемент нужной строки // Если текущий адрес меню совпадает с сохраненным, то добавим перед именем указатель текущей позиции "->" if (arrayCompare(currentMenuNumber, mainMenu[i].Num)) { lcd.print("->" + String(mainMenu[i].Name)); positionMainMenu = i; } else { lcd.print(mainMenu[i].Name); } lcdPosition++; } // Если пришел флаг, то выведем стрелку вверх if (arrowUp) { lcd.setCursor(lcdlong - 1, 0); lcd.write((uint8_t)1); } // Если пришел флаг, то выведем стрелку вниз if (arrowDown) { lcd.setCursor(lcdlong - 1, lcdSize - 1); lcd.write((uint8_t)0); } // Если место на дисплее закончилось, то выйдем из цикла if (lcdPosition >= lcdSize) {break;} } } // Функция сравнения двух массивов boolean arrayCompare(const byte arr1[], const byte arr2[]) { boolean checkAdressPosition = true; for (int posNum = 0; posNum < sizeof(currentMenuNumber); posNum++) { if (arr1[posNum] != arr2[posNum]) {checkAdressPosition = false; break;} } return checkAdressPosition; } // Функция проверки соответствия текущего адреса меню boolean checkAdress(int pos) { boolean checkAdressPosition = true; for (int posNum = 0; posNum < currentMenuLVL; posNum++) { if (currentMenuNumber[posNum] != mainMenu[pos].Num[posNum]) {checkAdressPosition = false; break;} } return checkAdressPosition; } // ---------------------------------------------------------------- END ---------------------------------------------------------------- // Надо попробовать, говорят для экономии памяти //lcd.print( F( "message" ) );Вывсёврёти. Вот это
conststructstructMenu mainMenu[] PROGMEM = {056{{1, 0, 0},"Info >",true, printInfo},057{{2, 0, 0},"Bloks >",false, zero},058{{2, 1, 0},"Point 2-1 >",false, zero},059{{2, 1, 1},"Point 2-1-1",false, zero},060{{2, 1, 2},"Point 2-1-2",false, zero},061{{2, 2, 0},"Point 2-2 >",false, zero},062{{2, 2, 1},"Point 2-2-1",false, zero},063{{2, 2, 2},"Point 2-2-2",false, zero},064{{2, 2, 3},"Point 2-2-1",false, zero},065{{2, 2, 4},"Point 2-2-2",false, zero},066{{2, 2, 5},"Point 2-2-1",false, zero},067{{2, 2, 6},"Point 2-2-2",false, zero},068{{3, 0, 0},"Settings >",false, zero},069{{3, 1, 0},"Point 3-1 >",false, zero},070{{3, 1, 1},"Point 3-1-1",false, zero},071{{3, 1, 2},"Point 3-1-2",false, zero},072{{3, 2, 0},"Point 3-2 >",false, zero},073{{3, 2, 1},"Point 3-2-1",false, zero},074{{3, 2, 2},"Point 3-2-2",false, zero},075{{4, 0, 0},"Dop >",false, zero},076{{4, 1, 0},"Point 4-1 >",false, zero},077{{4, 1, 1},"Point 4-1-1",false, zero},078{{4, 1, 2},"Point 4-1-2",false, zero},079{{4, 2, 0},"Point 4-2 >",false, zero},080{{4, 2, 1},"Point 4-2-1",false, zero},081{{4, 2, 2},"Point 4-2-2",false, zero}082};не пихает сами строки в прогмем. Она пихает в прогмем массив записей, одно из полей которых указывает на строку в ОЗУ. 104 байта получилось аптамушта линкер выкинул всё, с его точки зрения, лишнее.
Поставь в начале своего .ino файла, после инклудов строчку
#pragma GCC optimize "O0"
и посмотри теперь.
Она пихает в прогмем массив записей, одно из полей которых указывает на строку в ОЗУ. 104 байта получилось аптамушта линкер выкинул всё, с его точки зрения, лишнее.
Да я вчера вечером, когда начал тестировать увидел, что не работает.
Не могу найти понятного описания, как это можно сделать.
Если можете помочь, то я буду привелико благодарен, т.к. не могу понять, как работать с PROGMEM
https://www.arduino.cc/reference/en/language/variables/utilities/progmem/ ^F "Arrays of strings"
Что-то никак не получается, не могу понять, что со структурой делать. Лезут ошибки.
void zero() {}; // Структура меню struct structMenu { byte Num[3]; const char* const Name[]; boolean enterFunction; void (*function)(); }; const char text100[] PROGMEM = "Info >"; const char text200[] PROGMEM = "Bloks >"; const char text300[] PROGMEM = "Settings >"; const char text400[] PROGMEM = "Dop >"; // Меню struct structMenu mainMenu[] = { {{1, 0, 0}, text100, false, zero}, {{2, 0, 0}, text200, false, zero}, {{3, 0, 0}, text300, false, zero}, {{4, 0, 0}, text400, false, zero} }; char buf[20]; char* txtName = buf; void setup() {} void loop() {}так попробуй. Этот скетч показывает как вместо lcd.print() использовать PrintLCD (), кладя строки в PROGMEM
#include <LiquidCrystal.h> LiquidCrystal lcd(12, 11, 5, 4, 3, 2); #include <avr/pgmspace.h> #define BUF_LENGTH 60 char buf[BUF_LENGTH]; // буффер для работы прогмем, если нужны строки более 60 символов, тут ставим бОльшее значение const char stringMES_0[] PROGMEM = "Info >"; const char stringMES_1[] PROGMEM = "Bloks >"; const char stringMES_2[] PROGMEM = "Settings >"; const char stringMES_3[] PROGMEM = "Dop >"; const char* const string_tableMES[] PROGMEM = {stringMES_0, stringMES_1, stringMES_2, stringMES_3}; #define INFO 0 #define BLOKS 1 #define SETTINGS 2 #define DOP 3 void setup() { lcd.begin(16, 2); PrintLCD (INFO); } void loop() {} void PrintLCD (byte Str_nomer){ strcpy_P(buf, (char*)pgm_read_word(&(string_tableMES[Str_nomer]))); lcd.print (buf); }так попробуй. Этот скетч показывает как вместо lcd.print() использовать PrintLCD (), кладя строки в PROGMEM
Спасибо большое, только вот можно ли эти String запихнуть в структуру ? Во всех примерах только char массив.
Или имеется ввиду, что нужно хранить наименования отдельно от структуры !? Т.е. № в массиве соответствует № в структуре.
Спасибо большое, только вот можно ли эти String запихнуть в структуру ? Во всех примерах только char массив.
Где вы там String увидали? там тоже char массив или ссылки на него.
Кладите свои строки в char массивы, а в структуру ссылки на них.
Где вы там String увидали? там тоже char массив или ссылки на него.
stringMES_0
Вроде все сделал и еще кое-какие моменты поправил.
// ------------------------------------------------------------ Библиотеки ------------------------------------------------------------ #include <LiquidCrystal.h> // -------------------------------------------------------------- Дисплей -------------------------------------------------------------- LiquidCrystal lcd(12, 11, 5, 4, 3, 2); // I2C модуля пока нет, поэтому подключаем по старинке // ------------------------------------------------------------ Настройки ------------------------------------------------------------ // В наличии Button shield нет и энкодера тоже, поэтому обойдемся простыми кнопками #define up 7 // Пин кнопки "Вверх" #define down 6 // Пин кнопки "Вниз" #define enter 8 // Пин кнопки "Вход" #define back 9 // Пин кнопки "Выход" #define lcdSize 4 // Количество строк на дисплее #define lcdlong 20 // Количество символов в строке на дисплее #define delayBtn 300 // Задержка после нажатия кнопки (для стабильности навигации) // ------------------------------------------------------------ Переменные ------------------------------------------------------------ // Стрелки для меню const uint8_t scroll_bar[2][8] = { {B00100, B00100, B00100, B00100, B00100, B10101, B01110, B00100}, // Cтрелка вниз {B00100, B01110, B10101, B00100, B00100, B00100, B00100, B00100} // Cтрелка вверх }; byte currentMenuNumber[] = {1, 0, 0}; // Текущий номер пункта меню byte currentMenuLVL; // Текущий уровень в меню byte menuCount; // Общее количество пунктов меню byte positionMainMenu; // Текущий номер пункта в структуре byte btnNum; // Номер нажатой кнопки boolean functionMenu; // Признак того, что мы работаем с функцией из текущего пункта меню (отключает навигацию по mainMenu) char buf[20]; // Буффер для работы PROGMEM (длинна 20 символов) // ------------------------------------------------------------- Структуры ------------------------------------------------------------- // Структура меню struct structMenu { byte Num[3]; // Номер пункта меню boolean enterFunction; // Флаг признака функции (если True, то выполнит функцию, иначе попытается перейти на следующий пункт меню) void (*function)(); // Вызываемая функция }; // Строки наименований пунктов меню (для экономии памяти хранятся в PROGMEM) // номер из названия (text100) = адресу в mainMenu ({1, 0, 0}) const char text100[] PROGMEM = "Info >"; const char text200[] PROGMEM = "Bloks >"; const char text210[] PROGMEM = "Point 2-1 >"; const char text211[] PROGMEM = "Point 2-1-1"; const char text212[] PROGMEM = "Point 2-1-2"; const char text220[] PROGMEM = "Point 2-2 >"; const char text221[] PROGMEM = "Point 2-2-1"; const char text222[] PROGMEM = "Point 2-2-2"; const char text223[] PROGMEM = "Point 2-2-3"; const char text224[] PROGMEM = "Point 2-2-4"; const char text225[] PROGMEM = "Point 2-2-5"; const char text226[] PROGMEM = "Point 2-2-6"; const char text300[] PROGMEM = "Settings >"; const char text310[] PROGMEM = "Point 3-1 >"; const char text311[] PROGMEM = "Point 3-1-1"; const char text312[] PROGMEM = "Point 3-1-2"; const char text320[] PROGMEM = "Point 3-2 >"; const char text321[] PROGMEM = "Point 3-2-1"; const char text322[] PROGMEM = "Point 3-2-2"; const char text400[] PROGMEM = "Dop >"; const char text410[] PROGMEM = "Point 4-1 >"; const char text411[] PROGMEM = "Point 4-1-1"; const char text412[] PROGMEM = "Point 4-1-2"; const char text420[] PROGMEM = "Point 4-2 >"; const char text421[] PROGMEM = "Point 4-2-1"; const char text422[] PROGMEM = "Point 4-2-2"; // Массив строк меню const char* const stringMenuTable[] PROGMEM = { text100, text200, text210, text211, text212, text220, text221, text222, text223, text224, text225, text226, text300, text310, text311, text312, text320, text321, text322, text400, text410, text411, text412, text420, text421, text422}; // Меню struct structMenu mainMenu[] = { {{1, 0, 0}, true, printInfo}, {{2, 0, 0}, false, zero}, {{2, 1, 0}, false, zero}, {{2, 1, 1}, true, printInfo}, {{2, 1, 2}, true, printInfo}, {{2, 2, 0}, false, zero}, {{2, 2, 1}, false, zero}, {{2, 2, 2}, false, zero}, {{2, 2, 3}, false, zero}, {{2, 2, 4}, false, zero}, {{2, 2, 5}, false, zero}, {{2, 2, 6}, false, zero}, {{3, 0, 0}, false, zero}, {{3, 1, 0}, false, zero}, {{3, 1, 1}, false, zero}, {{3, 1, 2}, false, zero}, {{3, 2, 0}, false, zero}, {{3, 2, 1}, false, zero}, {{3, 2, 2}, false, zero}, {{4, 0, 0}, false, zero}, {{4, 1, 0}, false, zero}, {{4, 1, 1}, false, zero}, {{4, 1, 2}, false, zero}, {{4, 2, 0}, false, zero}, {{4, 2, 1}, false, zero}, {{4, 2, 2}, false, zero} }; // -------------------------------------------------- Системные функции (setup, loop) -------------------------------------------------- void setup() { lcd.begin(lcdlong, lcdSize); // Инициализация дисплея lcd.createChar(0, (uint8_t*)scroll_bar[0]); // Инициализация - стрелка вниз lcd.createChar(1, (uint8_t*)scroll_bar[1]); // Инициализация - стрелка вверх lcd.clear(); // Очистим экран pinMode(up, INPUT_PULLUP); // Кнопка "Вверх" pinMode(down, INPUT_PULLUP); // Кнопка "Вниз" pinMode(enter, INPUT_PULLUP); // Кнопка "Вход" pinMode(back, INPUT_PULLUP); // Кнопка "Выход" // Зададим начальные значения переменных currentMenuLVL = 0; positionMainMenu = 0; functionMenu = false; // Посчитаем общее количество пунктов меню mainMenu menuCount = sizeof(mainMenu) / sizeof(mainMenu[0]); // Выведем меню на экран lcdScreen(); } void loop() { //Если нажали какую-то кнопку btnNum = 0; if (!digitalRead(up)) {btnNum = 1;} if (!digitalRead(down)) {btnNum = 2;} if (!digitalRead(enter)) {btnNum = 3;} if (!digitalRead(back)) {btnNum = 4;} if (btnNum > 0) { // Если стоит флаг, то выполняем функцию из текущего пункта меню, иначе отрабатываем нажатие кнопки в меню if (functionMenu) { mainMenu[positionMainMenu].function(); } else { pushButton(); } delay(delayBtn); } } // ---------------------------------------------------------------- END ---------------------------------------------------------------- // -------------------------------------------------------- Другие функции меню -------------------------------------------------------- // Функция заглушка, ничего не делает void zero() {}; // Функция в теории делает какие-то вычисления и выводит данные на дисплей void printInfo() { boolean printText = false; if (!functionMenu) {functionMenu = true; btnNum = 0; printText = true;} /* switch (btnNum) { case 1: Что-то делаем break; case 2: Что-то делаем break; case 3: Что-то делаем break; case 4: // Нажали кнопку Back - выйдем обратно в меню functionMenu = false; lcdScreen(); break; }*/ if (printText) { lcd.clear(); lcd.setCursor(0, 0); lcd.print("Test text 1"); lcd.setCursor(0, 1); lcd.print("Test text 2"); lcd.setCursor(0, 2); delay(delayBtn); while (1) { delay(50); // Нажали кнопку Back - выйдем обратно в меню if (!digitalRead(back)) {break;} } delay(delayBtn); functionMenu = false; lcdScreen(); } } // ---------------------------------------------------------------- END ---------------------------------------------------------------- // ------------------------------------------ Работа с кнопками (навигация в меню mainMenu) -------------------------------------------- // Обработка нажатия кнопки void pushButton() { switch (btnNum) { case 1: pushButtonUp(); break; case 2: pushButtonDown(); break; case 3: pushButtonEnter(); break; case 4: pushButtonBack(); break; } } // Если нажата кнопка "Вверх" void pushButtonUp() { // Если мы не на первом уровне меню, то отработаем нажатие кнопки if (currentMenuNumber[currentMenuLVL] > 1) { currentMenuNumber[currentMenuLVL]--; lcdScreen(); } } // Если нажата кнопка "Вниз" void pushButtonDown() { boolean check = false; // Если мы на первом уровне меню if (currentMenuLVL == 0) { // Переберем все пункты меню for (int i = 0; i < menuCount; i++) { // Если мы нашли пункт у которого номер больше текущего, то отработаем нажатие кнопки if (mainMenu[i].Num[currentMenuLVL] > currentMenuNumber[currentMenuLVL]) {check = true; break;} } } else { // Переберем все пункты меню for (int i = 0; i < menuCount; i++) { // Если вышестоящий адрес совпадает и мы нашли пункт у которого номер больше текущего, то отработаем нажатие кнопки if (checkAdress(i) && mainMenu[i].Num[currentMenuLVL] > currentMenuNumber[currentMenuLVL]) {check = true; break;} } } // Отработаем нажатие кнопки if (check) { currentMenuNumber[currentMenuLVL]++; lcdScreen(); } } // Если нажата кнопка "Вход" void pushButtonEnter() { // Если стоит флаг enterFunction, то выполним функцию из текущего пункта меню mainMenu, иначе навигация if (mainMenu[positionMainMenu].enterFunction) { mainMenu[positionMainMenu].function(); } else { // Если мы не на последнем уровне в меню if (currentMenuLVL < sizeof(currentMenuNumber) - 1) { boolean subMenu = false; // Переберем все пункты меню for (int i = 0; i < menuCount; i++) { // Проверим, является ли выбранный пункт подменю для текущего пункта if (checkAdress(i)) {subMenu = true; break;} } // Отработаем нажатие кнопки if (subMenu) { currentMenuLVL++; currentMenuNumber[currentMenuLVL]++; lcdScreen(); } } } } // Если нажата кнопка "Выход" void pushButtonBack() { // Если мы не на первом уровне меню if (currentMenuLVL > 0) { // Если стоит флаг enterFunction, то выполним функцию из текущего пункта меню mainMenu, иначе навигация if (mainMenu[positionMainMenu].enterFunction) { mainMenu[positionMainMenu].function(); } else { currentMenuNumber[currentMenuLVL] = 0; currentMenuLVL--; lcdScreen(); } } } // ---------------------------------------------------------------- END ---------------------------------------------------------------- // ----------------------------------------------------------- Работа с меню ----------------------------------------------------------- // Функция определяет необходимость показа стрелок на дисплее void arrowPrint() { boolean bottomItem = false; byte menuItemCount = 0; // Количество пунктов на текущем уровне меню с учетом адреса родителя // Переберем все пункты меню for (int i = 0; i < menuCount; i++) { // Если мы на первом уровне адреса if (currentMenuLVL == 0) { // Если второй элемент адреса = 0 if (mainMenu[i].Num[currentMenuLVL + 1] == 0) {menuItemCount++;} } else { // Если мы на последнем уровне адреса if (currentMenuLVL == sizeof(currentMenuNumber) - 1) { // Если вышестоящие элементы адреса совпадают "И" текущий эелемент > 0 if (checkAdress(i) && mainMenu[i].Num[currentMenuLVL] > 0) {menuItemCount++;} } else { // Если вышестоящие элементы адреса совпадают "И" текущий эелемент > 0 "И" следующий элемент адреса = 0 if (checkAdress(i) && mainMenu[i].Num[currentMenuLVL] > 0 && mainMenu[i].Num[currentMenuLVL + 1] == 0) {menuItemCount++;} } } } // Если строк на текущем уровне меню больше чем размер экрана, то выведем стрелки, иначе они не нужны if (menuItemCount > lcdSize) { // Если мы на первом уровне меню if (currentMenuNumber[currentMenuLVL] == 1) { // Cтрелка вниз arrowDown(); } else { // Переберем все пункты меню for (int i = 0; i < menuCount; i++) { // Если вышестоящие элементы адреса совпадают "И" адрес проверяемого пункта меню > текущего элемента адреса if (checkAdress(i) && mainMenu[i].Num[currentMenuLVL] > currentMenuNumber[currentMenuLVL]) {bottomItem = true; break;} } // Если bottomItem, значит посередине, иначе мы в конце списка текущего уровня подменю if (bottomItem) { // Стрелка вверх и стрелка вниз arrowUp(); arrowDown(); } else { // Стрелка вверх arrowUp(); } } } } void arrowUp() { lcd.setCursor(lcdlong - 1, 0); lcd.write((uint8_t)1); } void arrowDown() { lcd.setCursor(lcdlong - 1, lcdSize - 1); lcd.write((uint8_t)0); } // Функция вывода меню на дисплей void lcdScreen() { lcd.clear(); // Очистим дисплей int lcdPosition = 0; // Текущая строка дисплея // Переберем все пункты меню и выведем на экран нужные for (int i = 0; i < menuCount; i++) { boolean printMenu = false; // Если место на экране еще есть "И" номер выбранного пункта меню попадает в диапазон размера дисплея if (lcdPosition <= lcdSize - 1 && currentMenuNumber[currentMenuLVL] - lcdSize < mainMenu[i].Num[currentMenuLVL]) { // Если мы на первом уровне адреса if (currentMenuLVL == 0) { // Если второй элемент адреса = 0 if (mainMenu[i].Num[currentMenuLVL + 1] == 0) {printMenu = true;} } else { // Если вышестоящие элементы адреса совпадают "И" текущего элемента адреса > 0 if (checkAdress(i) && mainMenu[i].Num[currentMenuLVL] > 0) { // Если мы где-то посередине меню, иначе данный уровень последний if (currentMenuLVL < sizeof(currentMenuNumber) - 1) { // Если элемент адреса следующего уровеня = 0 (текущий уровень + 1) if (mainMenu[i].Num[currentMenuLVL + 1] == 0) {printMenu = true;} } else { // Мы на последенем уровне, дальше ничего нет, поэтому можно выводить без проверок printMenu = true; } } } } // Выведем текущий пункт меню на дисплей if (printMenu) { lcd.setCursor(0, lcdPosition); // Установим курсор на 0 элемент нужной строки // Если текущий адрес меню совпадает с сохраненным, то добавим перед именем указатель текущей позиции "->" if (arrayCompare(currentMenuNumber, mainMenu[i].Num)) { PrintLCD(i, true); positionMainMenu = i; } else { PrintLCD(i, false); } lcdPosition++; } // Если место на дисплее закончилось, то выйдем из цикла if (lcdPosition >= lcdSize) {break;} } // Нарисуем стрелки arrowPrint(); } void PrintLCD (byte strMenuNum, boolean arrow) { strcpy_P(buf, (char*)pgm_read_word(&(stringMenuTable[strMenuNum]))); if (arrow) {lcd.print("->" + String(buf));} else {lcd.print (buf);} } // Функция сравнения двух массивов boolean arrayCompare(byte arr1[], byte arr2[]) { boolean checkAdressPosition = true; for (int posNum = 0; posNum < sizeof(currentMenuNumber); posNum++) { if (arr1[posNum] != arr2[posNum]) {checkAdressPosition = false; break;} } return checkAdressPosition; } // Функция проверки соответствия текущего адреса меню boolean checkAdress(int pos) { boolean checkAdressPosition = true; for (int posNum = 0; posNum < currentMenuLVL; posNum++) { if (currentMenuNumber[posNum] != mainMenu[pos].Num[posNum]) {checkAdressPosition = false; break;} } return checkAdressPosition; } // ---------------------------------------------------------------- END ---------------------------------------------------------------- // Надо попробовать, говорят для экономии памяти //lcd.print( F( "message" ) );Не могу найти понятного описания, как это можно сделать.
Вот здесь разные аврианты расписаны.
Вот здесь разные аврианты расписаны.
Спасибо. Вроде уже закончил, все строки вынес в PROGMEM.
И вроде даже работает.
не поделитесь?
не поделитесь?