Форум проекта "Минилаб-Мастер"

Клуб по интересам - отдыхаем от фотобизнеса => Программирование => Тема начата: Andy от 07 Ноября 2013, 08:01:11

Название: Траблики с меню
Отправлено: 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;
                         }
}

}
Название: Re: Траблики с меню
Отправлено: Andy от 07 Ноября 2013, 08:04:16
Так вот, после ентра, функция отрабатывает и возвращается в 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.
Название: Re: Траблики с меню
Отправлено: pumpkin от 08 Ноября 2013, 18:54:53
То есть, когда ты передаешь текст в виде ссылки:
Цитировать
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 и структуры, где меню хранятся. Завтра посмотрю.
А как звучит ошибка?
Название: Re: Траблики с меню
Отправлено: Andy от 09 Ноября 2013, 08:15:44
Совершенно верно:
Цитировать
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;

Название: Re: Траблики с меню
Отправлено: pumpkin от 09 Ноября 2013, 08:52:09
Попробуй объявить структуру так:
Цитировать
typedef struct PROGMEM {
     void       *Next;
     void       *Previous;
     void       *Parent;
     void       *Sibling;
     FuncPtr     SelectFunc;
     FuncPtr     EnterFunc;
     const char  *Text;
} Menu_Item;
Название: Re: Траблики с меню
Отправлено: pumpkin от 09 Ноября 2013, 08:55:54
Если проблема изначально только в выводе на экран в кривой кодировке LED-индикатора, то идеологически правильно будет просто фильтровать вывод на экран. То есть обернуть функцию вывода на индикатор в свою, с предварительной перекодировкой текста.
Название: Re: Траблики с меню
Отправлено: Andy от 09 Ноября 2013, 14:15:41
Не получается, я так пробовал
Цитировать
     - 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

Леко сказать фильтровать, если одну переменную изменишь, придется код в нескльких местах менять.
Название: Re: Траблики с меню
Отправлено: pumpkin от 09 Ноября 2013, 15:47:07
Я бы сделал вот так, ресурсы (строки) отделил от меню:
Цитировать
#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 );
Название: Re: Траблики с меню
Отправлено: Andy от 10 Ноября 2013, 06:11:13
Но магические числа опять же, мало чем отличаетя от моего варианта,  хотя я в свое время пол интернета перерыл, чтобы узнать как вставить в строку бинарный код, так и не нашел, спасибо.

Но опять же вставить дефайны туда нельзя:

Цитировать
"\\_SP_ \\ _SP_ \\ _SP_\\_SP_\\ _END_" 


unknown escape sequence \'\\_\'

не работает...
Название: Re: Траблики с меню
Отправлено: pumpkin от 10 Ноября 2013, 08:13:08
Цитировать
Но опять же вставить дефайны туда нельзя
Дефайн работает только с идентификаторами.

Цитировать
Но магические числа опять же, мало чем отличаетя от моего варианта,хотя я в свое время пол интернета перерыл, чтобы узнать как вставить в строку бинарный код, так и не нашел, спасибо. 
Но так - правильно, в одном месте задал все строки (да, значения нечитабельные, но идентификатор-то читабельный), и дальше работаешь с идентификаторам, не вспоминая про строки, кодировки и т.д. И код будет один и индикаторонезависимый.
Вдруг еще какой-нибудь тип экрана/кодировки появится - не надо будет искать строки по всему коду и снова ifdefами обильно обсыпать.
Название: Re: Траблики с меню
Отправлено: pumpkin от 10 Ноября 2013, 08:41:56
Если хочешь, можешь 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;
}
Название: Re: Траблики с меню
Отправлено: pumpkin от 10 Ноября 2013, 08:55:55
LED - сегментник что-ли?
Название: Re: Траблики с меню
Отправлено: Andy от 10 Ноября 2013, 09:08:14
Угу, сегментник,  буду расбиратся, спасибо!
Название: Re: Траблики с меню
Отправлено: pumpkin от 10 Ноября 2013, 09:35:58
По второму вопросу:
Цитировать
      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;
                       }
Название: Re: Траблики с меню
Отправлено: Andy от 10 Ноября 2013, 14:50:08
Точно, забыл старое меню удалить, там видимо искал эту функцию, спасибо.

Цитировать
BYTE table[256] = {0x00,0x35,0x77,0x55,0xff,0x00, ............. };

Жалко всю таблицу в код тащить, там нужен то десяток символов, а в твоем приемере она вообще будет в озу висеть. И раскиданы они по всему полю...
Название: Re: Траблики с меню
Отправлено: pumpkin от 10 Ноября 2013, 14:59:32
Там, в первом вопросе проблема в том, что глобальные статические переменные можно инициализировать только константами и литеральными строками (типа "юроюлрпаобаьрвтчпак").
Так что придется обходить одним из вышеперечисленных способов.
Название: Re: Траблики с меню
Отправлено: Andy от 10 Ноября 2013, 15:14:06
Ну я исходил из того, что строки - это суть массивы. Какая хрен разница - строка или массив, думал поменять пару строк где-то и все. И в меню передавать тупо массив. И макрос pgm_read_byte один на все  типы массивов, только там он почему то pgm_read_word...
Название: Re: Траблики с меню
Отправлено: pumpkin от 10 Ноября 2013, 15:31:33
Цитировать
Жалко всю таблицу в код тащить, там нужен то десяток символов, а в твоем приемере она вообще будет в озу висеть. И раскиданы они по всему полю...
Ну это-то да, если бы не русская П, то в 85 уложилось бы. Можно попробовать оптимизировать.

Хотя избавление от конструкций вида   BYTE Version  [] PROGMEM = {_P_ , _L_ , _POINT_ , _1_, _0_, _END_}  ; //"PLAT.10"   должно компенсировать эти байты.
Название: Re: Траблики с меню
Отправлено: Andy от 10 Ноября 2013, 16:53:26
Как это интерестно избавится? Эта конструкция хотя бы леко редактируется. А на сегментник все равно строго определенные байты нужны, и нкуда от этого не денешся. Дефайны НЕ компилируюся в код в данном случае кроме цифр, а служат основой для удобного задания массивов псевдострок. Правда с названием меню приходится помучатся, но таково требование заказчика - поставить семисегментик. Он еще с драйвером, управляется по SPI.
Название: Re: Траблики с меню
Отправлено: pumpkin от 10 Ноября 2013, 17:01:44
Имею в виду избавиться, если пойти по вот этому пути:
Цитировать
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;
}

Тут еще легче радактируется.
Название: Re: Траблики с меню
Отправлено: Andy от 10 Ноября 2013, 17:11:24
Ага, таблицы я часто использую, особенно когда ее размер меньше, чем код программной реализации того же функцианала. Но в данном случае таблица занимает лишнее место в памяти. В массиве меню УЖЕ  есть вся необходимая информация, просто "кодировка не та". И больше никогда не пригодися для других дисплеев, так как разводить плату с сегментниками  и драйвером лучше и нужно всегда одинаково. Поэтому "всего-то" надо как то впихнуть массив вместо строки.
Название: Re: Траблики с меню
Отправлено: pumpkin от 10 Ноября 2013, 17:20:10
Цитировать
Поэтому "всего-то" надо как то впихнуть массив вместо строки. 
Ну тогда инициализировать пустыми, а потом в коде присваивать.
Или не использовать это меню as is, а видоизменьить меню по-своему как-нибудь.
Название: Траблики с меню
Отправлено: Thomaslism от 11 Апреля 2016, 20:59:40
Перечитав некоторую инфу по С я так понял что нужно прописывать что-то вроде

wc.lpszMenuName=НАЗВАНИЕ_ФУНКЦИИ_С_МЕНЮ;

Где можно найти инфу о том как прописывать эту функцию?

_______________
 
Белоснежка И Охотник 2 (http://dramania.gr/index.php?option=com_k2&view=itemlist&task=user&id=19526)
Название: Re: Траблики с меню
Отправлено: Andy от 12 Апреля 2016, 03:28:06
Перечитав некоторую инфу по С я так понял что нужно прописывать что-то вроде

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
Название: Re: Траблики с меню
Отправлено: kodak_service от 12 Апреля 2016, 09:05:11
Ёпрст... :o
Андрей, ты начал общаться с ботами? ;)
Название: Re: Траблики с меню
Отправлено: Andy от 12 Апреля 2016, 10:20:07
Боты какие то умные пошли, по Си вопросы задают...  :o
Название: Re: Траблики с меню
Отправлено: kodak_service от 12 Апреля 2016, 10:30:17
Думаю, что это полуавтоматический бот (типа первых машин для игры в шахматы с человеком внутри)  ;D