Pull to refresh

Comments 21

Какой сильный флешбек во времена школы и квикбейсика.
А я своё так и не дописал до конечного состояния. Решил что я в тупик зашёл по ресурсам из за использования динамической памяти для всего этого, да и ардуинку уже подарил другому человеку в виде midi клавиатуры. Поделиться своим велосипедным меню? Отлаживать и демонстрировать буду на эмуляторе.
Вот одна из ранних-ранних стадий, еще без дисплея, но с логикой пунктов:

Интересно как вы реализовали скролл, а то я не смог придумать как это сделать красиво и в итоге написал что то стрёмное. Думаю вечерком отпишусь когда дома буду.
Постраничное отображение с контролем границ.
Вот вам на растерзание. Требует порт vector. Перекатался я на велосипеде сегодня… Извините сил нет объяснять. Как только буду в состоянии расскажу подробнее. Я уже и не помню рабочее ли оно.
Так вот что у меня. У меня менюха рендерится рекурсивным перебором, а у пункта меню присутствует ссылка на функцию, которая используется при активации.

Куски кода:
Скрытый текст
typedef byte ( * tCallback)(byte, byte, char * );

//prototypes NEED FOR CALLBACK
byte callback_StartTimer(byte id, byte parent, char * name);
//struct define typical menu item
struct menuItem {
	byte id;
	char * name;
	byte parent; //id parent
	tCallback callback; //callback function
	bool haveChilds;//using for optimizations
};
vector<menuItem> items;//vector of menu items

int freeRam () { //display free ram memory
	extern int __heap_start, * __brkval;
	int v;
	return (int) & v - (__brkval == 0 ? (int) & __heap_start : (int) __brkval);
}

void setup() {
        menuItem root;
	{
		root.id       = 10;
		root.name     = "Set octave";
		root.parent   = CONST_ROOT_ITEM;
		root.callback = & callback_SetOcave;
		items.push_back(root);

		root.id       = 12;
		root.name     = "Setup";
		root.parent   = CONST_ROOT_ITEM;
		root.callback = NULL;
		items.push_back(root);

		{
			root.id       = 24;
			root.name     = "Time";
			root.parent   = 12;
			root.callback = & callback_resetAllKeys;
			items.push_back(root);
		}
	}
        Serial.println("Free ram: " + String(freeRam()));
}

//call callback of menu item
items[i].callback(id, parent, name);

byte callback_StartTimer(byte id, byte parent, char * name) { //return true if need reprint menu
	//Serial.println("Callbacked function callback_StartTimer by " + String(name));
	return false;
}

//print menu recursively. return need for checking empty menu
bool printMenuRecurcive(vector<menuItem> & items, byte parent, byte level) {
	if (level > CONST_MAX_LEVEL) return false;
	bool haveChilds = false;//flag reports if have items
	for (byte i = 0; i < items.size(); i++) {

		if (parent == items[i].parent) {
			haveChilds = true;//yay! his have items
			String indent = "";

			for (int j = 0; j < level; j++) {
				indent += "  ";
			}

			if (items[i].haveChilds) {
				Serial.println(indent + String(items[i].name) + ">");
			} else {
				Serial.println(indent + String(items[i].name));
			}
			printMenuRecurcive(items, items[i].id, level + 1); //print next level

		}//end if parent
	}//end for
	return haveChilds;
}//end func


Но это хавает многовато памяти, хотя и получше одной красивой менюхи (не помню как называется), но хавающей еще больше памяти. Моя хавает память с каждым пунктом меню.
Можно реализовать это следующим образом — есть несколько переменных, одна хранит максимальное количество пунктов помещающихся на экране (max_draw_num), вторая хранит номер пункта который отображается на экране первым (first_draw_num), третья хранит номер пункта выделенного на экране (select_draw_num).
Т.е. например всего на экране помещается четыре строки, при этом меню содержит 8 элементов, в текущий момент времени отображаются пункты с 3 по 6, а выделен пункт номер 4.
Тогда:
max_draw_num=4 (количество элементов на экране)
first_draw_num=3 (первый элемент меню на экране)
select_draw_num=2 (четвертый пункт при показе пунктов с 3 по 5 на экране будет находится на второй строке)
Тогда при нажатии кнопки вверх например нам надо проверить что мы не уперлись в экран, если мы не уперлись в экран то просто уменьшаем позицию на экране на единицу:
if (select_draw_num>1) {select_draw_num=select_draw_num-1);
Если всеже мы уперлись в экран то надо проверить какие элементы меню отображаются на экране, можем ли мы промотать меню ближе к началу:
if (first_draw_num>1) {first_draw_num=first_draw_num-1);
Аналогично делается и при нажатии кнопки вниз, только меняются знаки и сравнение делается не с 1, а с переменной хранящей в себе максимальное количество отображаемых на экране пунктов, соответственно если мы не уперлись в нижний край экрана:
if (select_draw_num<max_draw_num) {select_draw_num=select_draw_num+1);
Если мы не уперлись в конец меню:
if (first_draw_num+max_draw_num<max_menu_count) {first_draw_num=first_draw_num+1);
переменная max_menu_count — количество пунктов в текущем меню.

После останется только вывести на экран пункты меню с first_draw_num по first_draw_num+max_draw_num-1 и отобразить выделение вокруг пункта меню select_draw_num

Для удобства можно еще сверху отображать название родительского пункта меню, нумеровать автоматически пункты меню, прикрутить координаты к пунктам меню и отображать пункты на соответствующих координатах и т.д…

Годно. Надо будет попробовать. Спасибо за алгоритм.
Любопытно, как раз собирался искать меню для ардуины и GLCD. Единственное как-то неуверен насчет подтяжки кнопок к питанию. А если МК запустится раньше, чем питание подтяжки преодолеет барьер логической единицы — тогда будет ложное срабатывание?
При адекватных значениях резисторов подтяжка должна успеть отработать, но если страшно то после включения подтяжки можно установить маленькую задержку.
Настройка портов ввода-вывода для кнопок происходит раньше, чем меню пропишет себя в обработчике кнопок.
А я вот сижу сочиняю многоуровневое меню на одном светодиоде по мотивам менюх автосигнализаций :) Не везде можно дисплейчик вставить.
проще использовать связанный список:
struct menu_item {
  struct menu_item* parent; // указатель на родителя
  struct menu_item* child;    // указатель на потомка
  struct menu_item* prev;    // предыдущий в списке
  struct menu_item* next;    //  следующий в списке
  const char* name;            //  имя 
  void* data;                        // данные пункта
  int (*do_left)(void*);          //  действие для кнопок
  int (*do_right)(void*);
  int (*do_up)(void*);
  int (*do_down)(void*);
}

в принципе, чтобы разрядить меню можно вынести data и do_xxxx() в отдельную структуру, поскольку не все пункты будут иметь какую-то настройку (будут директорией)
да, наверное, но все равно как-то недодумано.
Кстати, parent тоже можно вынести отдельно для экономии памяти. Например, в структуру меню
А кто бы мне помог присосаться к жк драйверу msm5265?
Судя по датащиту, подключение возможно организовать по SPI, загружая 160-бит данных в сдвиговый регистр на один контроллер (каждый дает 80х2 точек, контроллеры стекируются).
Единственное что нужно знать для конкретного дисплея — сколько контроллеров на нем и в какой последовательности пронумерованы пикселы.
Изображение будет удобно хранить в формате XBM — оно будет сразу готово к загрузке.
Да уж, всё мудрено оказывается. Просто у меня в трупере висит над зеркалом бортовой компутер с компасом, барометром и температурой. Думал, если при помощи готовых библиотек текст туда выводить можно, то переделать его на что-нибудь по-подробнее.
В отличие от ks010, nt7534 и многих других, вашим драйвером очень просто управлять. Не надо реализовывать никакие команды настройки, достаточно просто загружать в сдвиговый регистр данные. В этом, правда, кроется его недостаток — драйвер тупой. Все нужно делать за него.
Для текста ведь это не так критично?
Мне пока не ясно даже, как его припиновать к ардуине.
А никто не делал меню в виде иконок для TFT экрана? Чтобы максимально удобно было добавлять новые пункты в меню.
Sign up to leave a comment.

Articles