Тема посвящена нарождающемуся проекту легкого, простого в обращении и не слишком простого по функционалу меню, использующему для вывода LCD экран типа 1602.
Несколько дней назад я создавал тему "Меню на LCD". Тогда проект уже наполовину работал и с каждым днем прогрессировал, потому я его не бросил и не пошел рассматривать готовый код. Тогда у меня возникла проблема с передачей меню названий пунктов, и быстро решилась при помощи массива структур. На данный момент код имеет версию 1.2 (все, что было до 1.0 - писалось без класса, подряд, взахлеб и зачастую необдуманно, и потому криво).
Меню можно представить как дерево, в самом корне которого - функция control. Она всегда крутится в цикле loop, к ней так или иначе привязаны главные переменные и в ней вызываются функции, определяющие работу меню. Ей передается один параметр - значение кнопки. Это может быть на самом деле что угодно, что может передавать значения от 1 до 4. Функций, в ней вызываемых, несколько: menuDisplay - функция, в которой реализуется вывод названия вроде "меню такой-то версии", вывод названий пунктов и их листание; pointDisplay - в ней содержится код, отвечающий за вывод и функционирование пунктов меню; а также displaySleep и displayClean - они, как понятно из их названий, отвечают за сон (отключение подсветки) и обновление экрана.
Кроме функций, доступных только внутри класса и напрямую связанных с control, есть публичные функции, дающие возможность управлять меню пользователю. Это: mainDisp, которой через запятую передаются данные для вывода на главном экране, secondValue, которая позволяет запретить или разрешить вывод второго значения в пункте, и begin, которая существует в качестве костыля.
Названия пунктов меню задаются при помощи массива структур, определяемого пользователем. В этом массиве нулевой элемент хранит название меню. Кроме названий, в структуре хранятся два значения, ограничивающие диапазон переменных, задаваемых в пункте. Сам массив кладется в FLASH память.
Переменные, связанные с пунктами, хранятся в трех массивах в EEPROM, что позволяет сохранять их значения. Почему массива три? В первом массиве значения определяют, можно ли выводить второе значение пункта и менять его. Во втором и третьем - первое и второе значения пункта.
Что за второе значение пункта, спросите вы? Это лучше показать фотографией:

Да, получается, что даже если в пункте не надо задавать два значения, он все равно будет занимать три ячейки. Неэкономично, но, думаю, простительно для столь сырого проекта.
Наверное, стоит пару слов сказать про передачу параметров главному экрану - это такая небольшая крутая фича меню. Функция реализована на вариативном шаблоне, поэтому ей можно передать различные значения через запятую (не знаю, насколько различные на самом деле, но int и char - точно). Переход на следующую строку осуществляется передачей ей аргумента “\r”.
Теперь об управлении. Как я уже сказал выше, главной функции передается значение кнопки или чего угодно, от 1 до 4. Этим значениям соответствуют ESC, DOWN, UP и ENTER. Если экран спит, то нажатием на любую клавишу он выводится из сна. Нажатие на ENTER, сделанное в главном экране, переключает его на экран меню. Здесь можно листать пункты клавишами UP и DOWN. Переход в пункт осуществляется ENTER’ом. Но в пункте ENTER выполняет только одно действие - переключает текущее изменяемое значение, что индицируется курсором. Как менять значения, думаю, понятно. Выход из пункта и из меню - это ESC. При выходе из пункта значение обоих переменных обновляется (если было изменено) в EEPROM.
Несмотря на то, что уже получился ничегосебе уже лонгридище, стоит написать еще кое-что. Да, это «еще одно меню», но конкретно это меню можно выделить из остальных проектов. Во-первых тем, что оно в теории (и такая была изначальная идея) проще в использовании, хоть и не умеет разных крутых штук. Во-вторых, оно создавалось (и создается) с целью оформить его в библиотеку и избавить конечного пользователя от необходимости созерцать в своем коде кучи вещей, которые было бы удобно спрятать «под капот». В-третьих, идея этого меню - быть как можно более легким по использованию памяти.
Код прилагаю (тут лежит еще моя библиотека myButton)
Несколько дней назад я создавал тему "Меню на LCD". Тогда проект уже наполовину работал и с каждым днем прогрессировал, потому я его не бросил и не пошел рассматривать готовый код. Тогда у меня возникла проблема с передачей меню названий пунктов, и быстро решилась при помощи массива структур. На данный момент код имеет версию 1.2 (все, что было до 1.0 - писалось без класса, подряд, взахлеб и зачастую необдуманно, и потому криво).
Меню можно представить как дерево, в самом корне которого - функция control. Она всегда крутится в цикле loop, к ней так или иначе привязаны главные переменные и в ней вызываются функции, определяющие работу меню. Ей передается один параметр - значение кнопки. Это может быть на самом деле что угодно, что может передавать значения от 1 до 4. Функций, в ней вызываемых, несколько: menuDisplay - функция, в которой реализуется вывод названия вроде "меню такой-то версии", вывод названий пунктов и их листание; pointDisplay - в ней содержится код, отвечающий за вывод и функционирование пунктов меню; а также displaySleep и displayClean - они, как понятно из их названий, отвечают за сон (отключение подсветки) и обновление экрана.
Кроме функций, доступных только внутри класса и напрямую связанных с control, есть публичные функции, дающие возможность управлять меню пользователю. Это: mainDisp, которой через запятую передаются данные для вывода на главном экране, secondValue, которая позволяет запретить или разрешить вывод второго значения в пункте, и begin, которая существует в качестве костыля.
Названия пунктов меню задаются при помощи массива структур, определяемого пользователем. В этом массиве нулевой элемент хранит название меню. Кроме названий, в структуре хранятся два значения, ограничивающие диапазон переменных, задаваемых в пункте. Сам массив кладется в FLASH память.
Массив структур:
const menuStruct PROGMEM points[MENU_points] = {
{"MENU v 1.2", 0, 0}, //0
{"point1", 0, 12}, //1
{"point2", 0, 12}, //2
{"point3", 1, 2}, //3
{"point4", 0, 10}, //4
{"point5", 0, 7}, //5
{"point6", 0, 10}, //6
};
Что за второе значение пункта, спросите вы? Это лучше показать фотографией:

Да, получается, что даже если в пункте не надо задавать два значения, он все равно будет занимать три ячейки. Неэкономично, но, думаю, простительно для столь сырого проекта.
Наверное, стоит пару слов сказать про передачу параметров главному экрану - это такая небольшая крутая фича меню. Функция реализована на вариативном шаблоне, поэтому ей можно передать различные значения через запятую (не знаю, насколько различные на самом деле, но int и char - точно). Переход на следующую строку осуществляется передачей ей аргумента “\r”.
Передача строк и чисел для вывода на главном экране:
menu.mainDisplay("abba", "is a great", "\r",
'g', 'r', 'o', 'u', 'p', 1234);
Несмотря на то, что уже получился ничегосебе уже лонгридище, стоит написать еще кое-что. Да, это «еще одно меню», но конкретно это меню можно выделить из остальных проектов. Во-первых тем, что оно в теории (и такая была изначальная идея) проще в использовании, хоть и не умеет разных крутых штук. Во-вторых, оно создавалось (и создается) с целью оформить его в библиотеку и избавить конечного пользователя от необходимости созерцать в своем коде кучи вещей, которые было бы удобно спрятать «под капот». В-третьих, идея этого меню - быть как можно более легким по использованию памяти.
Код прилагаю (тут лежит еще моя библиотека myButton)
Вложения
-
4.4 KB Просмотры: 233
Изменено: