Форум проекта "Минилаб-Мастер"
Клуб по интересам - отдыхаем от фотобизнеса => Программирование => Тема начата: Andy от 07 Ноября 2013, 08:01:11
-
Вообще у меня слабость к меню в контроллерах. Pumpkin как то помог сделать отличное меню на BASCOM-e, http://minilabmaster.com/cgi-bin/yabb2/YaBB.pl?num=1246889930/0 за что ему огромное спасибо. Из баскома я вырос, сейчас юзаю Си.
Есть классное меню для WinAvr, называется MicroMenu, исходники сдесь - https://github.com/abcminiuser/micromenu-v2. Отличное, компактное и быстрое меню, рекомендую, но у меня с ним небольшие проблемы, из за недостаточно глубокого знания Си.
С выводом на LCD дисплей проблем нет, само меню выглядит примерно так:
#ifdef LCD_DISPLAY
// Menus: уровень следущий предыдущий родитель дитя Выбор функции Enter функция Текст
MENU_ITEM(Lev1_It1 , Lev1_It2, Lev1_It1, Lev1_It1 , Lev1_It1 , NULL, X_Report , " X-отчет");
MENU_ITEM(Lev1_It2 , Lev1_It3, Lev1_It1, Lev1_It2 , Lev1_It2 , NULL, Z_Report , "Z-отчет/Гашение");
MENU_ITEM(Lev1_It3 , Lev1_It4, Lev1_It2, Lev1_It3 , Lev1_It3 , NULL, Setup , "Настройка цены");
MENU_ITEM(Lev1_It4 , Lev1_It5, Lev1_It3, Lev1_It3 , Lev1_It4 , NULL, Reset , "Техобнуление");
MENU_ITEM(Lev1_It5 , Lev1_It5, Lev1_It4, Lev1_It5 , Lev1_It5 , NULL, Exit , "Выход");
#endif
Все прекрастно работает и выводится. Но сделал проект с LED дисплеем, а там своя специфика. Во первых, недосимволы я реализовал через дефайны:
#ifdef LED_DISPLAY
#define _0_ 0x3f //0
#define _1_ 0x06 //1
#define _2_ 0x5b //2
#define _3_ 0x4f //3
#define _4_ 0x66 //4
#define _5_ 0x6d //5
#define _6_ 0x7d //6
#define _7_ 0x07 //7
#define _8_ 0x7f //8
#define _9_ 0x6f //9
#define _A_ 0x77 // A
#define _B_ 0x7D // B
#define _C_ 0x39 // C
#define _E_ 0x79 // E
#define _F_ 0x71 // F
#define _G_ 0x31 // Г
#define _H_ 0x76 // H
#define _SL_ 0x30 // /
#define _J_ 0x1e // J
#define _Pr_ 0x37 // П
#define _MINUS_ 0x40 // -
#define _RUB_ 0xf3 // P.
#define _H_ 0x76 // H
#define _U_ 0x3e // U
#define _L_ 0x38 // L
#define _P_ 0x73 // P
#define _T_ 0x78 // T
#define _SP_ 0x00 // Пробел
#define _POINT_ 0xbb // точка
#define _END_ 0xff // конец строки
#endif
Заметте, что нулевой символ используется как пробел. Хотя это можно исправить на уровне программы вывода, например сделав символ пробела 0x32 и отлавливать его при выводе и посылать вместь него на дисплей 0.
Далее из псевдосимволов формируются строки:
#ifdef LED_DISPLAY
BYTE Version [] PROGMEM = {_P_ , _L_ , _POINT_ , _1_, _0_, _END_} ; //"PLAT.10"
BYTE X_rep [] PROGMEM = {_0_ , _B_ , _G_, _MINUS_,_END_ } ; //X отчет
BYTE Price [] PROGMEM = {_C_ , _T_ , _0_, _MINUS_,_END_ } ; //Печать прайса
BYTE clr [] PROGMEM = {_SP_ , _SP_ , _SP_,_SP_, _END_, } ; //Очистка строки
#endif
Так вопрос в следующем - как вставить эти строки в вышеприведенное меню? как ни пихаю их - напрямую ли либо через указатель - компилятор ругается. Можно было сделать вставить в меню на место SELECT функцию вывода строки, но поазалось не очень хорошо. Пока же сделал такой костыль:
#ifdef LED_DISPLAY
#define XREP_A "@?}1я" //{_MINUS_,_0_ , _B_ , _G_,,_END_} Z отчет
#define ZREP_A "@?91я" //{_MINUS_,_0_ , _B_ , _G_,,_END_} Z отчет
#define PROG_A "@7s1я" //{_MINUS_,_Pr_ , _P_ , _G_, _END_} Программирование цены
#define REST_A "@9}sя" //{_MINUS_,_C_ , _6_ , _Pr_,_END_}
#define EXIT_A "yv0xя" //{_MINUS_,_0_ , _B_ , _G_,_END_}
#ifdef LED_DISPLAY
// Menus: уровень следущий предыдущий родитель дитя Выбор функции Enter функция Текст
MENU_ITEM(Lev1_It1 , Lev1_It2, Lev1_It1, Lev1_It1 , Lev1_It1 , NULL, X_Report , XREP_A);
MENU_ITEM(Lev1_It2 , Lev1_It3, Lev1_It1, Lev1_It2 , Lev1_It2 , NULL, Z_Report , ZREP_A);
MENU_ITEM(Lev1_It3 , Lev1_It4, Lev1_It2, Lev1_It3 , Lev1_It3 , NULL, Setup , PROG_A);
MENU_ITEM(Lev1_It4 , Lev1_It5, Lev1_It3, Lev1_It3 , Lev1_It4 , NULL, Reset , REST_A);
MENU_ITEM(Lev1_It5 , Lev1_It5, Lev1_It4, Lev1_It5 , Lev1_It5 , NULL, Exit , EXIT_A);
#endif
То есть превращаю бинарные символы в строку и типа впихиваю ее в меню. Ну не красиво это как то.
Как сделать красиво? Подскажите, плиз.
И еще один вопрос. В старой версии MicroMenu при выхождении из программы текст меню переинициализировался. С новой этой версией - нет. Вот исходник:
void My_Menu (void){
wdt_disable();
Menu_SetGenericWriteCallback(PrintLED_F);
Menu_Navigate(&Lev1_It1);
while(1)
{
switch (GetButtonPress())
{
case UP:
Menu_Navigate(MENU_PREVIOUS);
break;
case DOWN:
Menu_Navigate(MENU_NEXT);
break;
case ENTER:
Menu_EnterCurrentItem();
Menu_Navigate(MENU_NEXT);
break;
default:
break;
}
}
}
-
Так вот, после ентра, функция отрабатывает и возвращается в Menu_Navigate(MENU_NEXT), а надо чтобы возвращалась к вызывающему меню и печатала там свой текст снова.
На всякий случай функция вывода:
void PrintLED_F(BYTE *mass ){
SPI_Init();
STB_LOW;
OutLED(DispAdressSet);
while (pgm_read_byte(mass)!=_END_){
if (pgm_read_byte(mass)==_POINT_){
pgm_read_byte(mass++);
OutLED(pgm_read_byte(mass++)|0x80); //Выводим символ с точкой
}
OutLED(pgm_read_byte(mass++));
}
STB_HI;
}
Функция вывода меню:
/** Configuration for the macro or function required to read out a pointer from
* the memory storage space set by \\ref MENU_ITEM_STORAGE.
*
* \\param[in] Addr Address of the pointer to read
*/
#define MENU_ITEM_READ_POINTER(Addr) (void*)pgm_read_word(Addr)
Спасибо, Andy.
-
То есть, когда ты передаешь текст в виде ссылки:
char Text[] = "blah-blah";
MENU_ITEM(Lev1_It1, Lev1_It2, Lev1_It1, Lev1_It1, Lev1_It1, NULL, X_Report, Text);
выдает ошибку,
А когда в виде константы:MENU_ITEM(Lev1_It1, Lev1_It2, Lev1_It1, Lev1_It1, Lev1_It1, NULL, X_Report, "blah-blah");
, то всё OK?
Коли так, то надо глянуть макрос MENU_ITEM и структуры, где меню хранятся. Завтра посмотрю.
А как звучит ошибка?
-
Совершенно верно:
Description Resource Path Location Type
(near initialization for \'Lev1_It1.Text[0]\') fun.c /Game line 364 C/C++ Problem
initializer element is not computable at load time fun.c /Game line 364 C/C++ Problem
make: *** [fun.o] Error 1 Game C/C++ Problem
так же не принимает массив в виде {_SP_ , _SP_ , _SP_,_SP_, _END_, }
и [_SP_ , _SP_ , _SP_,_SP_, _END_, ]
Description Resource Path Location Type
macro "MENU_ITEM" passed 13 arguments, but takes just 8 fun.c /Game line 364 C/C++ Problem
Структура меню выглядит так:
typedef struct PROGMEM {
void *Next;
void *Previous;
void *Parent;
void *Sibling;
FuncPtr SelectFunc;
FuncPtr EnterFunc;
const char Text[];
} Menu_Item;
-
Попробуй объявить структуру так:
typedef struct PROGMEM {
void *Next;
void *Previous;
void *Parent;
void *Sibling;
FuncPtr SelectFunc;
FuncPtr EnterFunc;
const char *Text;
} Menu_Item;
-
Если проблема изначально только в выводе на экран в кривой кодировке LED-индикатора, то идеологически правильно будет просто фильтровать вывод на экран. То есть обернуть функцию вывода на индикатор в свою, с предварительной перекодировкой текста.
-
Не получается, я так пробовал
- initialization makes integer from pointer
without a cast
- (near initialization for \'Lev1_It1.Text\')
- (near initialization for \'Lev1_It1.Text[0]\')
- initializer element is not computable at load
time
- missing braces around initializer
Леко сказать фильтровать, если одну переменную изменишь, придется код в нескльких местах менять.
-
Я бы сделал вот так, ресурсы (строки) отделил от меню:
#ifdef LCD_DISPLAY
#define sMnuXReport "X-отчет"
#define sMnuZReport "Z-отчет/Гашение"
#define sMnuSetup "Настройка цены"
#define sMnuReset "Техобнуление"
#define sMnuExit "Выход"
#else
#ifdef LED_DISPLAY
#define sMnuXReport "\\x40\\x3f\\x7d\\x31\\xff" // Х отчет
#define sMnuZReport "\\x40\\x3f\\x7d\\x31\\xff" // Z отчет
#define sMnuSetup "\\x40\\x37\\x73\\x31\\xff" // Программирование цены
#define sMnuReset "\\x40\\x39\\x73\\x37\\xff" // Обнуление
#define sMnuExit "\\x40\\x3f\\x7d\\x31\\xff" // Выход
#endif
#endif
MENU_ITEM( Lev1_It1, Lev1_It2, Lev1_It1, Lev1_It1 , Lev1_It1, NULL, X_Report, sMnuXReport );
MENU_ITEM( Lev1_It2, Lev1_It3, Lev1_It1, Lev1_It2 , Lev1_It2, NULL, Z_Report, sMnuZReport );
MENU_ITEM( Lev1_It3, Lev1_It4, Lev1_It2, Lev1_It3 , Lev1_It3, NULL, Setup, sMnuSetup );
MENU_ITEM( Lev1_It4, Lev1_It5, Lev1_It3, Lev1_It3 , Lev1_It4, NULL, Reset, sMnuReset );
MENU_ITEM( Lev1_It5, Lev1_It5, Lev1_It4, Lev1_It5 , Lev1_It5, NULL, Exit, sMnuExit );
-
Но магические числа опять же, мало чем отличаетя от моего варианта, хотя я в свое время пол интернета перерыл, чтобы узнать как вставить в строку бинарный код, так и не нашел, спасибо.
Но опять же вставить дефайны туда нельзя:
"\\_SP_ \\ _SP_ \\ _SP_\\_SP_\\ _END_"
unknown escape sequence \'\\_\'
не работает...
-
Но опять же вставить дефайны туда нельзя
Дефайн работает только с идентификаторами.
Но магические числа опять же, мало чем отличаетя от моего варианта,хотя я в свое время пол интернета перерыл, чтобы узнать как вставить в строку бинарный код, так и не нашел, спасибо.
Но так - правильно, в одном месте задал все строки (да, значения нечитабельные, но идентификатор-то читабельный), и дальше работаешь с идентификаторам, не вспоминая про строки, кодировки и т.д. И код будет один и индикаторонезависимый.
Вдруг еще какой-нибудь тип экрана/кодировки появится - не надо будет искать строки по всему коду и снова ifdefами обильно обсыпать.
-
Если хочешь, можешь const из структуры убрать (чтоб он не был read only), инициализировать пустой строкой, а потом присваивать Text-у свой массив:
typedef struct Menu_Item {
const struct Menu_Item *Next; /**< Pointer to the next menu item of this menu item */
const struct Menu_Item *Previous; /**< Pointer to the previous menu item of this menu item */
const struct Menu_Item *Parent; /**< Pointer to the parent menu item of this menu item */
const struct Menu_Item *Child; /**< Pointer to the child menu item of this menu item */
void (*SelectCallback)(void); /**< Pointer to the optional menu-specific select callback of this menu item */
void (*EnterCallback)(void); /**< Pointer to the optional menu-specific enter callback of this menu item */
const char* Text; /**< Menu item text to pass to the menu display callback function */
} Menu_Item_t;
MENU_ITEM(Menu_1, Menu_2, Menu_3, NULL_MENU, NULL_MENU, NULL, NULL, "");
MENU_ITEM(Menu_2, Menu_3, Menu_1, NULL_MENU, NULL_MENU, NULL, NULL, "");
MENU_ITEM(Menu_3, Menu_1, Menu_2, NULL_MENU, NULL_MENU, NULL, NULL, "");
char Version[] PROGMEM = {_P_ , _L_ , _POINT_ , _1_, _0_, _END_} ; //"PLAT.10"
char X_rep[] PROGMEM = {_0_ , _B_ , _G_, _MINUS_,_END_ } ; //X отчет
char Price[] PROGMEM = {_C_ , _T_ , _0_, _MINUS_,_END_ } ; //Печать прайса
int main(int argc, char *argv[]) {
Menu_1.Text = Version;
Menu_2.Text = X_rep;
Menu_3.Text = Price;
//...................................................
//...................................................
//...................................................
return 0;
}
Но лучший, по моему, способ иметь читаемые строки в коде - фильтровать вывод, как я говорил (это легко на самом деле). Пусть, например, \'^\' будет обозначать END:
MENU_ITEM(Menu_1, Menu_2, Menu_3, NULL_MENU, NULL_MENU, NULL, NULL, "PL.10^");
MENU_ITEM(Menu_2, Menu_3, Menu_1, NULL_MENU, NULL_MENU, NULL, NULL, "0BG-^");
MENU_ITEM(Menu_3, Menu_1, Menu_2, NULL_MENU, NULL_MENU, NULL, NULL, "CT0-^");
// Таблица преобразования нормальных Win-1251 символов в кривые LED значения.
// индекс - нормальный символ, значение - новый LED-символ.
BYTE table[256] = {0x00,0x35,0x77,0x55,0xff,0x00, ............. };
void PrintLED_F(BYTE *mass ){
SPI_Init();
STB_LOW;
OutLED(DispAdressSet);
while (pgm_read_byte(mass)!=\'^\'){
if (pgm_read_byte(mass)==\'.\'){
pgm_read_byte(mass++);
OutLED(table[pgm_read_byte(mass++)]|0x80); //Выводим символ с точкой
}
OutLED(table[pgm_read_byte(mass++)]);
}
STB_HI;
}
-
LED - сегментник что-ли?
-
Угу, сегментник, буду расбиратся, спасибо!
-
По второму вопросу:
switch (GetButtonPress())
{
case UP:
Menu_Navigate(MENU_PREVIOUS);
break;
case DOWN:
Menu_Navigate(MENU_NEXT);
break;
case ENTER:
Menu_EnterCurrentItem();
Menu_Navigate(Menu_GetCurrentMenu());
break;
default:
break;
}
-
Точно, забыл старое меню удалить, там видимо искал эту функцию, спасибо.
BYTE table[256] = {0x00,0x35,0x77,0x55,0xff,0x00, ............. };
Жалко всю таблицу в код тащить, там нужен то десяток символов, а в твоем приемере она вообще будет в озу висеть. И раскиданы они по всему полю...
-
Там, в первом вопросе проблема в том, что глобальные статические переменные можно инициализировать только константами и литеральными строками (типа "юроюлрпаобаьрвтчпак").
Так что придется обходить одним из вышеперечисленных способов.
-
Ну я исходил из того, что строки - это суть массивы. Какая хрен разница - строка или массив, думал поменять пару строк где-то и все. И в меню передавать тупо массив. И макрос pgm_read_byte один на все типы массивов, только там он почему то pgm_read_word...
-
Жалко всю таблицу в код тащить, там нужен то десяток символов, а в твоем приемере она вообще будет в озу висеть. И раскиданы они по всему полю...
Ну это-то да, если бы не русская П, то в 85 уложилось бы. Можно попробовать оптимизировать.
Хотя избавление от конструкций вида BYTE Version [] PROGMEM = {_P_ , _L_ , _POINT_ , _1_, _0_, _END_} ; //"PLAT.10" должно компенсировать эти байты.
-
Как это интерестно избавится? Эта конструкция хотя бы леко редактируется. А на сегментник все равно строго определенные байты нужны, и нкуда от этого не денешся. Дефайны НЕ компилируюся в код в данном случае кроме цифр, а служат основой для удобного задания массивов псевдострок. Правда с названием меню приходится помучатся, но таково требование заказчика - поставить семисегментик. Он еще с драйвером, управляется по SPI.
-
Имею в виду избавиться, если пойти по вот этому пути:
MENU_ITEM(Menu_1, Menu_2, Menu_3, NULL_MENU, NULL_MENU, NULL, NULL, "PL.10^");
MENU_ITEM(Menu_2, Menu_3, Menu_1, NULL_MENU, NULL_MENU, NULL, NULL, "0BG-^");
MENU_ITEM(Menu_3, Menu_1, Menu_2, NULL_MENU, NULL_MENU, NULL, NULL, "CT0-^");
void PrintLED_F(BYTE *mass ){
char* table = "\\x00\\x35\\x77\\x55\\xff\\x00............. ";
SPI_Init();
STB_LOW;
OutLED(DispAdressSet);
while (pgm_read_byte(mass)!=\'^\'){
if (pgm_read_byte(mass)==\'.\'){
pgm_read_byte(mass++);
OutLED(table[pgm_read_byte(mass++)]|0x80); //Выводим символ с точкой
}
OutLED(table[pgm_read_byte(mass++)]);
}
STB_HI;
}
Тут еще легче радактируется.
-
Ага, таблицы я часто использую, особенно когда ее размер меньше, чем код программной реализации того же функцианала. Но в данном случае таблица занимает лишнее место в памяти. В массиве меню УЖЕ есть вся необходимая информация, просто "кодировка не та". И больше никогда не пригодися для других дисплеев, так как разводить плату с сегментниками и драйвером лучше и нужно всегда одинаково. Поэтому "всего-то" надо как то впихнуть массив вместо строки.
-
Поэтому "всего-то" надо как то впихнуть массив вместо строки.
Ну тогда инициализировать пустыми, а потом в коде присваивать.
Или не использовать это меню as is, а видоизменьить меню по-своему как-нибудь.
-
Перечитав некоторую инфу по С я так понял что нужно прописывать что-то вроде
wc.lpszMenuName=НАЗВАНИЕ_ФУНКЦИИ_С_МЕНЮ;
Где можно найти инфу о том как прописывать эту функцию?
_______________
Белоснежка И Охотник 2 (http://dramania.gr/index.php?option=com_k2&view=itemlist&task=user&id=19526)
-
Перечитав некоторую инфу по С я так понял что нужно прописывать что-то вроде
wc.lpszMenuName=НАЗВАНИЕ_ФУНКЦИИ_С_МЕНЮ;
Где можно найти инфу о том как прописывать эту функцию?
_______________
Белоснежка И Охотник 2 (http://dramania.gr/index.php?option=com_k2&view=itemlist&task=user&id=19526)
В данном синтаксисе переменной присваивается указатель на функцию. Прочитать можно много где, например здесь
http://www.sbp-program.ru/c/sbp-c-function-pointer.htm
-
Ёпрст... :o
Андрей, ты начал общаться с ботами? ;)
-
Боты какие то умные пошли, по Си вопросы задают... :o
-
Думаю, что это полуавтоматический бот (типа первых машин для игры в шахматы с человеком внутри) ;D