|
Структура меню |
|
|
|
Nov 15 2006, 17:36
|
Участник

Группа: Участник
Сообщений: 74
Регистрация: 21-10-06
Пользователь №: 21 540

|
Народ, как Вы делаете меню для своих девайсов? Интересует структура. Столкнулся с проблемой: вывод на дисплей (HD44780 совместимый) меню получается малопонятной и (главное) неприспособленный для добавления новых пунктов меню. Никогда раньше не приходилось делать меню, так что научите  Может у кого есть каки-нить наработки\библиотеки? Заранее большое спасибо...
|
|
|
|
2 страниц
1 2 >
|
 |
Ответов
(1 - 29)
|
Nov 15 2006, 22:00
|
Частый гость
 
Группа: Свой
Сообщений: 83
Регистрация: 25-11-05
Из: odessa
Пользователь №: 11 397

|
присоединяюсь к вопросу. тоже только начал стряпать меню для своего устройства. организовал страничное меню. (ничего не читал на эту тему, проосто придумал сам) Код /* * страницы меню * 16 символов :: "1234567890123456" */
const __flash byte str1_0[] = "*Введите пароль*"; // password menu const __flash byte str2_0[] = " "; // MENU_PAGE_0
// кнопка "опции" - затемнена const __flash byte str1_1[] = "**Слушаю эфир**"; // main menu const __flash byte str2_1[] = "*Режим Опции*"; // MENU_PAGE_1
//... //... объявление строк меню //... // далее описываем реакцию кнопок на определённой странице меню (кнопок у меня 3 - правая, // левая и вернуться на пункт выше
void key_handle(byte key_num, byte page_num) { switch (page_num) { case MENU_PAGE_0 : // password menu { switch (key_num) { case KEY_1 : { // что-то делаем } break; // KEY_1 case KEY_2 : { // что-то делаем } break; // KEY_2 case KEY_BACK : { // что-то делаем } break; // KEY_BACK default : break; } // switch (key_num) } break; // MENU_PAGE_0 case MENU_PAGE_1 : // main menu ------------------------------- { switch (key_num) { case KEY_1 : { menu_page = MENU_PAGE_1_1; // собственно вот так происходит обработка draw_menu(menu_page); } break; // KEY_1 case KEY_2 : { // grayed } break; // KEY_2 case KEY_BACK : { // что-то делаем } break; // KEY_BACK default : break; } // switch (key_num) } break; // MENU_PAGE_1 ----------------------------------------------------
//... //... //... // ну и собственно отрисовка строк меню на дисплее void draw_menu(byte page_num) { switch (page_num) { case MENU_PAGE_0 : // password menu { LCD_PrintAt(0, 0, &str1_0[0]); LCD_PrintAt(0, 1, &str2_0[0]); } break; case MENU_PAGE_1 : // main menu { LCD_PrintAt(0, 0, &str1_1[0]); LCD_PrintAt(0, 1, &str2_1[0]); } break; //... //... //... хотелось бы услышать мнение гуру в этом вопросе - как ПРАВИЛЬНО стряпать меню? для маленького проектика мне и такая организация подходит, а вот если что-то большое...
Сообщение отредактировал junoSynthesizer - Nov 15 2006, 22:04
--------------------
Вся жизнь - ништяк, все бабы - леди, а солнце - шар дающий свет
|
|
|
|
|
Nov 16 2006, 09:28
|
Местный
  
Группа: Участник
Сообщений: 416
Регистрация: 18-04-06
Из: Челябинск
Пользователь №: 16 219

|
Цитата(sind-rom @ Nov 15 2006, 20:36)  Народ, как Вы делаете меню для своих девайсов? Интересует структура. Столкнулся с проблемой: вывод на дисплей (HD44780 совместимый) меню получается малопонятной и (главное) неприспособленный для добавления новых пунктов меню. Никогда раньше не приходилось делать меню, так что научите  Может у кого есть каки-нить наработки\библиотеки? Заранее большое спасибо... Я, например, так делал: Код /* Global data type definitions */
typedef struct _menuItem { char flash *itemText; void (*itemFunction)(char); char itemArg; } MenuItem;
static void Run(char _dummy) { }
static void Stop(char _dummy) { }
static flash char _read[] = "Read device ", _program[] = "Program device ", _set[] = "Set device ", _insert[] = "Insert ", _edit[] = "Edit buffer ", _clear[] = "Clear buffer ", _append[] = "Append ", _run[] = "Run ", _stop[] = "Stop "; static MenuItem __flash MenuItems[] = { _set, SetType, 0, _read, 0, 0, _program, 0, 0, _insert, Edit, INSERT_BUFFER, _edit, Edit, EDIT_BUFFER, _clear, Edit, CLEAR_BUFFER, _append, Edit, APPEND_BUFFER, _run, Run, 0, _stop, Stop, 0 }; // // ** Menu -- the main menu fuction // void Menu(void) { MenuItem __flash *_mptr = MenuItems; static __flash char _unKnown[] = "Unknown device";
for (;;) { Clear_LCD(); puts_P(_mptr->itemText); switch (getchar()) { case '+': // Next item if (++_mptr > &MenuItems[(sizeof MenuItems)/(sizeof (struct _menuItem)) - 1]) // Set pointer to the first item _mptr = MenuItems; break; case '-': // Previous item if (--_mptr < MenuItems) // Set pointer to the last item _mptr = &MenuItems[(sizeof MenuItems)/(sizeof (struct _menuItem)) - 1]; break; case '!': // Do the selected menu item if (Device->_devType==UNKNOWN_TYPE && _mptr->itemFunction!=SetType) { LCD_MoveCurs2(0); // Move the LCD cursor puts_P(_unKnown); // Put the message getchar(); break; } (*_mptr->itemFunction)(_mptr->itemArg); } } }
|
|
|
|
|
Nov 16 2006, 09:39
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
До кучи. Код //--------------------------------------------------------------------------- // //--------------------------------------------------------------------------- MenuState menu_state[] = { // State State Text Function { ST_MAIN, "Main ", NULL }, { ST_MAIN_REV, " ", mf_revision },
{ ST_TIME_FUNC, "Time ", mf_showtime }, { ST_TIME_SET_FUNC, "SetTime ", mf_settime },
{ ST_DATE_FUNC, "Date ", mf_showdate },
{ ST_SOUND, "Sound ", NULL }, { ST_SOUND_SELECT_FUNC, NULL, mf_selectsound }, { ST_SOUND_PLAY_FUNC, NULL, mf_playsound },
{ ST_TEMPERATURE, "Temperature ", NULL }, { ST_TEMPERATURE_FUNC, NULL, mf_temperature },
{ ST_VOLTAGE, "Voltage ", NULL }, { ST_VOLTAGE_FUNC, NULL, mf_voltage },
{ ST_LIGHT, "Light ", NULL }, { ST_LIGHT_FUNC, NULL, mf_light },
{ ST_OPTIONS, "Options ", NULL }, { ST_OPTIONS_DISPLAY, "Display ", NULL }, { ST_OPTIONS_COMMON, "Common ", NULL }, { ST_OPTIONS_TEST, "Test ", NULL }, // End of list { 0, NULL, NULL },
};
//--------------------------------------------------------------------------- // //--------------------------------------------------------------------------- MenuNavigate menu_nextstate[] = { // State Input NEXT State { ST_MAIN, KEY_UP, ST_OPTIONS }, { ST_MAIN, KEY_PREV, ST_MAIN_REV }, { ST_MAIN, KEY_NEXT, ST_MAIN_REV }, { ST_MAIN, KEY_DOWN, ST_TIME_FUNC },
{ ST_MAIN_REV, KEY_PREV, ST_MAIN }, { ST_MAIN_REV, KEY_NEXT, ST_MAIN }, { ST_SOUND, KEY_UP, ST_DATE_FUNC }, { ST_SOUND, KEY_NEXT, ST_SOUND_SELECT_FUNC }, { ST_SOUND, KEY_PREV, ST_MAIN }, { ST_SOUND, KEY_DOWN, ST_TEMPERATURE },
{ ST_TEMPERATURE, KEY_UP, ST_SOUND }, { ST_TEMPERATURE, KEY_NEXT, ST_TEMPERATURE_FUNC }, { ST_TEMPERATURE, KEY_PREV, ST_MAIN }, { ST_TEMPERATURE, KEY_DOWN, ST_VOLTAGE },
{ ST_VOLTAGE, KEY_UP, ST_TEMPERATURE }, { ST_VOLTAGE, KEY_NEXT, ST_VOLTAGE_FUNC }, { ST_VOLTAGE, KEY_PREV, ST_MAIN }, { ST_VOLTAGE, KEY_DOWN, ST_LIGHT },
{ ST_LIGHT, KEY_UP, ST_VOLTAGE }, { ST_LIGHT, KEY_NEXT, ST_LIGHT_FUNC }, { ST_LIGHT, KEY_PREV, ST_MAIN }, { ST_LIGHT, KEY_DOWN, ST_OPTIONS },
{ ST_OPTIONS, KEY_UP, ST_LIGHT }, { ST_OPTIONS, KEY_NEXT, ST_OPTIONS_DISPLAY }, { ST_OPTIONS, KEY_PREV, ST_MAIN }, { ST_OPTIONS, KEY_DOWN, ST_MAIN },
{ ST_OPTIONS_TEST, KEY_UP, ST_OPTIONS_COMMON }, { ST_OPTIONS_TEST, KEY_PREV, ST_OPTIONS }, { ST_OPTIONS_TEST, KEY_BREAK, ST_MAIN }, { ST_OPTIONS_TEST, KEY_DOWN, ST_OPTIONS_DISPLAY }, { ST_OPTIONS_DISPLAY, KEY_UP, ST_OPTIONS_TEST }, { ST_OPTIONS_DISPLAY, KEY_PREV, ST_OPTIONS }, { ST_OPTIONS_DISPLAY, KEY_BREAK, ST_MAIN }, { ST_OPTIONS_DISPLAY, KEY_DOWN, ST_OPTIONS_COMMON }, { ST_OPTIONS_COMMON, KEY_UP, ST_OPTIONS_DISPLAY }, { ST_OPTIONS_COMMON, KEY_PREV, ST_OPTIONS }, { ST_OPTIONS_COMMON, KEY_BREAK, ST_MAIN }, { ST_OPTIONS_COMMON, KEY_DOWN, ST_OPTIONS_TEST },
// End of list { 0, 0, 0 } };
BYTE state; // Helds the current Menu state BYTE enter = TRUE; BYTE enter_func = FALSE;
//--------------------------------------------------------------------------- // //--------------------------------------------------------------------------- void MenuProcess( void *params ) { int rotor_cnt = 0xF7; char tmp_str[16]; ulong rtc_time; BYTE nextstate; BYTE prevstate; static char *statetext; BYTE (*statefunc)(BYTE); BYTE input; char i;
params = params;
// Initial state variables prevstate = ST_DUMMY; state = nextstate = ST_MAIN; statetext = "Hello!"; statefunc = NULL; for(;; ) // Main Menu loop { // Plain menu text if( statetext ) { lcd_setpos( 1, 1 ); lcd_string( statetext ); statetext = NULL; }
input = key_get(); // Read buttons
if( statefunc ) { // When in this state, we must call the state function nextstate = statefunc( input ); } else if( input != KEY_NULL ) { // Plain menu, clock the state machine nextstate = statemachine( state, input ); } if( nextstate != state ) { state = nextstate; for( i=0; menu_state[i].state; i++ ) { if( menu_state[i].state == state ) { statetext = menu_state[i].text; statefunc = menu_state[i].func; break; } } } if( state == ST_MAIN ) { if( prevstate != ST_MAIN ) { prevstate = ST_MAIN; lcd_setpos( 2, 1 ); // 1234567890123456 lcd_string( "No Alarms " ); } if( xQueueReceive( RTCMSG_handle, &rtc_time, 0 ) ) { sprintf( &tmp_str[0], "%02i:%02i", (BYTE)(rtc_time>>16), (BYTE)(rtc_time>>8) ); lcd_setpos( 1, 17-8 ); lcd_string( tmp_str ); } if( ++rotor_cnt > 0xF7+5 ) rotor_cnt = 0xF7; lcd_setpos( 1, 16 ); lcd_char( rotor_cnt ); vTaskDelay_ms( 100 );
} else { prevstate = state; vTaskDelay_ms( 100 ); } } //End Main loop }
//--------------------------------------------------------------------------- // //--------------------------------------------------------------------------- BYTE statemachine( BYTE state, BYTE stimul ) { int i; BYTE newstate = state; // Default stay in same state
for( i=0; menu_nextstate[i].state; i++ ) { if( menu_nextstate[i].state == state && menu_nextstate[i].input == stimul ) { newstate = menu_nextstate[i].state_next; break; } } return( newstate ); }
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Nov 16 2006, 09:42
|
Частый гость
 
Группа: Свой
Сообщений: 109
Регистрация: 27-07-06
Из: С.-Петербург
Пользователь №: 19 148

|
Например, вот так: Код struct menu_item { unsigned char Name[17]; struct menu_item *Parent; struct menu_item *Child; struct menu_item *Prev; struct menu_item *Next; };
struct menu_item Menu[5]={ {"Volume ", NULL, NULL, NULL, &Menu[1]}, // [0] {"Power ", NULL, NULL, &Menu[0], &Menu[2]}, // [1] {"Light ", NULL, NULL, &Menu[1], &Menu[3]}, // [2] {"U min of batt ", NULL, NULL, &Menu[2], &Menu[4]}, // [3] {"Autotune ", NULL, NULL, &Menu[3], NULL} // [4] }; Позволяет легко описывать многоуровневое меню (в приведённом фрагменте объявлено одноуровневое) и работать с ним с достаточной степенью абстракции.
|
|
|
|
|
Mar 28 2007, 12:11
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Забыл приложить файл... Я GCC не силен. Сделал следующее : 1) typedef struct { void *Next; void *Previous; void *Parent; void *Sibling; FuncPtr SelectFunc; FuncPtr EnterFunc; const char Text[]; } __flash Menu_Item; //PROGMEM 2)pgm_read_word заменил на __load_program_memory. Хотя первая читает слово из памяти, а вторая байт. В результате: Error[Pe146]: too many initializer values D:\...\menu.c 29 Error[Pe167]: argument of type "FuncPtr __flash *" is incompatible with parameter of type "unsigned char const __flash *" D:\...\menu.c 53 Вопрос тем, кто не считает ниже своего достоинства разбираться в чужом коде. Очень нужно организовать пользовательское меню для графического индикатора.
|
|
|
|
|
Mar 28 2007, 14:09
|
Участник

Группа: Участник
Сообщений: 40
Регистрация: 17-06-04
Пользователь №: 36

|
Используем это MicroMenu. Вещь просто супер. Легко использовать, памяти жрет мало. Работать удобно. Правда работаем под GCC. Подправили его немного под себя.
|
|
|
|
|
Mar 28 2007, 18:45
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(beer_warrior @ Mar 28 2007, 14:29)  А сказать, каким это строкам соответсвует слабо? Вот листинг. Что еще необходимо для портирования этого проекта на ИАР? Вот листинг... to MRW. А можно посмотреть на Вашу практическую реализацию данного меню?
|
|
|
|
|
Mar 28 2007, 19:25
|
Гуру
     
Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823

|
Цитата(alux @ Mar 28 2007, 18:45)  Вот листинг. Что еще необходимо для портирования этого проекта на ИАР? Вот листинг... to MRW. А можно посмотреть на Вашу практическую реализацию данного меню? По первой ошибке... Количество аргументов при определении Menu_Item меньше, чем Вы пытаетесь впихнуть. И общее замечание - люди готовы помочь, но заставлять их кликать столько раз, чтобы распаковать и посмотреть листинг ? Так можно отбить эту готовность
--------------------
Уходя, оставьте свет...
|
|
|
|
|
Mar 28 2007, 20:01
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(Dog Pawlowa @ Mar 28 2007, 19:25)  По первой ошибке... Количество аргументов при определении Menu_Item меньше, чем Вы пытаетесь впихнуть. При определении структура Menu_Item имеет 7 членов, при инициализации - тоже 7. Очевидно, что проблема связана с инициализацией cons char Text[] = {0x00}. Почему GCC это схавал, а IAR - плюется? Цитата(Dog Pawlowa @ Mar 28 2007, 19:25)  И общее замечание - люди готовы помочь, но заставлять их кликать столько раз, чтобы распаковать и посмотреть листинг ? Так можно отбить эту готовность  Почему-то мне не удалось присоединить файлы с расширением .с Поэтому прилагаю оригинальный архив с исходником для GCC. Господа "знающие", может проще и быстрее будет проверить данный исходник на IAR, дабы избежать ненужных вопросов. Уверен, данный проект интересен многим.
|
|
|
|
|
Mar 28 2007, 20:38
|
Участник

Группа: Участник
Сообщений: 40
Регистрация: 17-06-04
Пользователь №: 36

|
// Menus define: void KeySC (void); // Прототипы фуекций void TimeMenu1 (void); void TimeMenu0 (void); void FPout (void); void Fauto (void); void Fstop (void); void FPr0 (void); void FPr1 (void); void FPr2 (void); void FPr3 (void); // Главное меню MAKE_MENU(POUT,STAT,PARAM,NULL_ENTRY,NULL_ENTRY,FPout,TimeMenu0,"Pout"); MAKE_MENU(STAT,PARAM,POUT,POUT,AUTO,KeySC,TimeMenu1,"Stat"); MAKE_MENU(PARAM,POUT,STAT,POUT,PR1,KeySC,TimeMenu1,"Prtr"); // Подменю STAT MAKE_MENU(AUTO,STOP,HAND,STAT,okAUTO,KeySC,TimeMenu0,"Auto"); MAKE_MENU(okAUTO,NULL_ENTRY,NULL_ENTRY,NULL_ENTRY,NULL_ENTRY,KeySC,Fauto,"end "); MAKE_MENU(STOP,HAND,AUTO,STAT,okSTOP,KeySC,TimeMenu0,"Stop"); MAKE_MENU(okSTOP,NULL_ENTRY,NULL_ENTRY,NULL_ENTRY,NULL_ENTRY,KeySC,Fstop,"end "); MAKE_MENU(HAND,AUTO,STOP,STAT,NULL_ENTRY,KeySC,TimeMenu0,"Hand"); // Подменю PARAM MAKE_MENU(PR1,NULL_ENTRY,NULL_ENTRY,PARAM,SetParam,FPr1,FPr0,"Pr "); MAKE_MENU(SetParam,NULL_ENTRY,NULL_ENTRY,PR1,NULL_ENTRY,FPr3,FPr2,"__ ");
Это основное описание. Как я уже говорил, работаем на GCC. Изменения незначительны. Из всех вариантов, которые мы использовали, это самый удобный в использовании, пасширении/изменении, и наименьший по объему памяти.
|
|
|
|
|
Mar 30 2007, 13:59
|
Частый гость
 
Группа: Свой
Сообщений: 188
Регистрация: 28-09-06
Из: Minsk
Пользователь №: 20 762

|
Выкладываю исходник простого и удобного меню для HD44780. Новигация осуществляется при помощи 3 клавиш, плюс добавлена процедура редактирования переменных этими же клавишами. В общем при помощи этого меню и трех клавиш можно сорать полноценное устройство.
Прикрепленные файлы
Menu.zip ( 2.83 килобайт )
Кол-во скачиваний: 653
|
|
|
|
|
Mar 30 2007, 14:44
|

Профессионал
    
Группа: Свой
Сообщений: 1 065
Регистрация: 8-10-05
Из: Kiev, UA
Пользователь №: 9 380

|
Цитата А вот со второй ошибкой не могу справится. В чем же дело? Поблема в разной реализации доступа к чтению констант. Неоспоримым преимуществом ИАРа есть свободное использование инструкции LPM в к любой константе с модификатором _flash. В gcc это обходится с помошью специальных процедур чтения байтов. ИМХО в макросах затерялся модификатор _flash используемый некорректно. Для исправления можно объявить все простыми константами и работать с ними в памяти. Потом найти лишний _flash или PROGMEM
--------------------
Вони шукають те, чого нема, Щоб довести, що його не існує.
|
|
|
|
|
Jul 13 2007, 14:44
|
Частый гость
 
Группа: Новичок
Сообщений: 110
Регистрация: 8-01-07
Из: Украина
Пользователь №: 24 216

|
Только сильно не ругайте: Решил попробывать код предложеный Andy_F: Код struct menu_item { unsigned char Name[17]; struct menu_item *Parent; struct menu_item *Child; struct menu_item *Prev; struct menu_item *Next; };
struct menu_item Menu[5]={ {"Volume ", NULL, NULL, NULL, &Menu[1]}, // [0] {"Power ", NULL, NULL, &Menu[0], &Menu[2]}, // [1] {"Light ", NULL, NULL, &Menu[1], &Menu[3]}, // [2] {"U min of batt ", NULL, NULL, &Menu[2], &Menu[4]}, // [3] {"Autotune ", NULL, NULL, &Menu[3], NULL} // [4] }; но чего то ругается, пишет undefined symbol 'Menu' в строке {"Volume ", NULL, NULL, NULL, &Menu[1]}, // [0]
|
|
|
|
|
Jul 16 2007, 21:43
|
Участник

Группа: Свой
Сообщений: 43
Регистрация: 17-10-06
Из: Санкт Петербург
Пользователь №: 21 387

|
Цитата(Яrik @ Jul 13 2007, 18:44)  но чего то ругается, пишет undefined symbol 'Menu' в строке {"Volume ", NULL, NULL, NULL, &Menu[1]}, // [0] У меня IAR 4.30 скомпилил без проблем. Вернее у меня не был определен NULL, после Код #define NULL 0 все скомпилировалось нормально
Сообщение отредактировал IceS - Jul 16 2007, 21:44
|
|
|
|
|
Aug 7 2007, 10:58
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
По поводу Micro-Menu... При попытке вызова SET_MENU(Level1Item1); как указано в псевдокоде (MicroMenu.zip) программа вылетает. Пробовал определить структуру меню в ОЗУ. Не помогает. Вот мой пример: Код #include <inavr.h> #include "defines.h" #include "i2c.h" #include "tic154.h" #include "Menu.c"
// Prototypes: void Level1Item1Sub1_Text(void); void Level1Item1Sub1_Func(void);
const char Buffer[]="OPTIONS";
// Menus: MAKE_MENU(Level1Item1, Level1Item2, Level1Item3, NULL_ENTRY, Level1Item1Sub1, NULL_FUNC, NULL_FUNC, "ITEM 1"); MAKE_MENU(Level1Item2, Level1Item3, Level1Item1, NULL_ENTRY , NULL_ENTRY, NULL_FUNC, NULL_FUNC, "ITEM 2"); MAKE_MENU(Level1Item3, Level1Item1, Level1Item2, NULL_ENTRY, NULL_ENTRY, NULL_FUNC, NULL_FUNC, "ITEM 3");
MAKE_MENU(Level1Item1Sub1, NULL_ENTRY , NULL_ENTRY , Level1Item1, NULL_ENTRY,Level1Item1Sub1_Func, Level1Item1Sub1_Text, NULL_TEXT);
// Functions: void Level1Item1Sub1_Text(void) { char Buffer[] = "*YNAMIC MENU TEXT";
Buffer[0] = 'D';
LCD_Put_Str(Buffer);
// LCD_Write_SRAM_String(Buffer); }
void Level1Item1Sub1_Func(void) { // Shutdown_App(); }
//..........................................
void main (void) { // Initialise TWI_Init(100); // set TWI bit rate to 100KHz LCD_Init();
SET_MENU_WRITE_FUNC(Buffer); SET_MENU(Level1Item1); SET_MENU(PARENT); SET_MENU(SIBLING); SET_MENU(PREVIOUS); SET_MENU(NEXT); GO_MENU_FUNC(SELECTFUNC); }
//################################ // Menu.c
#include "Menu.h" #include <string.h>
Menu_Item Null_Menu = {(void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, {0x00}}; Menu_Item* CurrMenuItem; WriteFuncPtr* WriteFunc;
void MenuChange(Menu_Item* NewMenu) { if ((void*)NewMenu == (void*)&NULL_ENTRY) return;
CurrMenuItem = NewMenu; #if defined(MENU_USE_SRAM_BUFFER) #if (MENU_USE_SRAM_BUFFER < 1) #error Menu SRAM Buffer Size not Defined! #endif char Buffer[MENU_USE_SRAM_BUFFER]; strcpy(Buffer, CurrMenuItem->Text); //strcpy_P
((WriteFuncPtr)WriteFunc)((const char*)Buffer); #else ((WriteFuncPtr)WriteFunc)((const char*)CurrMenuItem->Text); #endif
GO_MENU_FUNC(ENTERFUNC); }
void MenuFunc(FuncPtr* Function) { if ((void*)Function == (void*)NULL_FUNC) return;
((FuncPtr)Function)(); }
//##################################################### //Menu.h
#ifndef MENU_H #define MENU_H
//#include <pgmspace.h>
// Typedefs: typedef void (*FuncPtr)(void); typedef void (*WriteFuncPtr)(const char*);
typedef struct { // __flash void *Next; void *Previous; void *Parent; void *Sibling; FuncPtr SelectFunc; FuncPtr EnterFunc; const char Text[6]; } Menu_Item;
// Externs: extern WriteFuncPtr* WriteFunc; extern Menu_Item Null_Menu; extern Menu_Item* CurrMenuItem;
// Defines and Macros: #define NULL_ENTRY Null_Menu #define NULL_FUNC (void*)0 #define NULL_TEXT 0x00 #define MENU_USE_SRAM_BUFFER 32
#define PREVIOUS *((Menu_Item*)(&CurrMenuItem->Previous)) #define NEXT *((Menu_Item*)(&CurrMenuItem->Next)) #define PARENT *((Menu_Item*)(&CurrMenuItem->Parent)) #define SIBLING *((Menu_Item*)(&CurrMenuItem->Sibling)) #define ENTERFUNC *((Menu_Item*)(&CurrMenuItem->EnterFunc)) #define SELECTFUNC *((Menu_Item*)(&CurrMenuItem->SelectFunc))
#define MAKE_MENU(Name, Next, Previous, Parent, Sibling, SelectFunc, EnterFunc, Text) \ extern Menu_Item Next; \ extern Menu_Item Previous; \ extern Menu_Item Parent; \ extern Menu_Item Sibling; \ Menu_Item Name = {(void*)&Next, (void*)&Previous, (void*)&Parent, (void*)&Sibling, (FuncPtr)SelectFunc, (FuncPtr)EnterFunc, { Text }}
#define SET_MENU_WRITE_FUNC(x) \ WriteFunc = (WriteFuncPtr*)&x;
#define SET_MENU(x) \ MenuChange((Menu_Item*)&x); #define GO_MENU_FUNC(x) \ MenuFunc((FuncPtr*)&x) #define EXTERN_MENU(Name) \ extern Menu_Item Name;
// Prototypes: void MenuChange(Menu_Item* NewMenu); void MenuFunc(FuncPtr* Function);
#endif Помогите, пожалуйста, разобраться с этим меню.
|
|
|
|
|
Aug 28 2007, 18:13
|
Частый гость
 
Группа: Участник
Сообщений: 79
Регистрация: 12-08-06
Из: Минск
Пользователь №: 19 504

|
Цитата(alux @ Mar 29 2007, 13:28)  Первую ошибку можно обойти, указав размер массива Text[6] при определении Menu_Item. А вот со второй ошибкой не могу справится. В чем же дело? Для IAR получилось так...
Прикрепленные файлы
umenu.rar ( 1.44 килобайт )
Кол-во скачиваний: 321
|
|
|
|
|
Sep 9 2007, 18:18
|
Группа: Новичок
Сообщений: 11
Регистрация: 9-09-07
Пользователь №: 30 404

|
Очень интересная, а главная злободневная для меня тема, жаль что раньше не набрёл на этот форум )). Меня интересует как вы решаете проблему размещения меню в памяти ? У меня сейчас всё меню создаётся при включении девайса и так там в памяти и висит. Для этих целей в прикрученой внешней оперативке под меню отведен килобайт памяти. Ну и приведу схематично то что сделано у меня. Требовалось создать иерархическое меню, причём в пунктах меню может быть что угодно от логотипа прибора и его настроек до отображения сигнала. Первым делом объявляеться структурка. Эта структура сама по себе не используеться, но все остальные типы меню содержат точно такие же поля, объявленые в той же последовательности: struct CustomMenu{ unsigned int ID;//номер, уникальный для каждого экземпляра меню unsigned char Count;//количество подменю unsigned char State;//текущее состояние меню struct CustomMenu *Prev;//указатель на предыдущее меню, у главного меню равен NULL struct CustomMenu **SubMenus;//массив указателей на подменю, на какое из этих подменю нужно //переходить видно по полю State void ( *OnDraw )( struct CustomMenu *Menu );//указатель на функцию отрисовки void ( *OnEvent )( struct CustomMenu *Menu, unsigned char Event );//указатель на функцию реакции на события }; //////////// /// /////////// Далее создаются "потомки" этой структуры, например строчное меню: struct StringMenu{ //поля "унаследованые" от CustomMenu: unsigned int ID;//номер, уникальный для каждого экземпляра меню unsigned char Count;//количество подменю unsigned char State;//текущее состояние меню struct CustomMenu *Prev;//указатель на предыдущее меню, у главного меню равен NULL struct CustomMenu **SubMenus;//массив указателей на подменю, на какое из этих подменю нужно //переходить видно по полю State void ( *OnDraw )( struct StringMenu *Menu );//функция отрисовки void ( *OnEvent )( struct StringMenu *Menu, unsigned char Event );//функция реакции на события
//собственные поля unsigned char **TextStrings;//строки меню; Текущая строка в меню определяеться полем State };
Далее объявляем функции
void StringMenuOnDraw( struct StringMenu *Menu )//функция отрисовки StringMenu { }
void StringMenuOnEven( struct StringMenu *Menu, unsigned char Event );//функция реакции на события //StringMenu { }
При создании экземпляра StringMenu присваиваем его полям - указателям на функции соответствующие обработчики.
Здесь удобно то, что можно ко всем экземплярам меню обращаться так же, как к CustomMenu. Если теперь объявим переменную CustomMenu* CMenu и присвоим ей адрес созданного StringMenu *SMenu, то при выполнении кода СMenu->OnDraw( SMenu ); вызовется StringMenuOnDraw.
Добавление нвых элементов можно реализовать так:
void AddMenu( CustomMenu *Menu, unsigned char ID_Parent, unsigned char N_Child, unsigned char ID_Child );
где *Menu - указатель на главное меню; ID_Parent - ID экземпляра меню, в который хотим добавить новое меню: N_Child - номер нового меню в родительском ( по сути индекс нового экземпляра в массиве SubMenus родителя ); ID_Child - ID нового экземпляра меню.
В общем я привёл основную идею, в коде могут попасться и некоторые ошибки и очепятки, но думаю, вполне понятно что я имею в виду )). Просьба большими кирпичами в меня не кидать и по голове не бить ; - ))
|
|
|
|
|
Sep 27 2007, 15:19
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата Для IAR получилось так... У меня не получается вызвать меню через символические имена: Код void menu_Init(void) { Level1Item1Sub1_Text(); SET_MENU_WRITE_FUNC(Level1Item1Sub1_Text); SET_MENU(Level1Item1); delay_ms(1000); SET_MENU(PARENT); delay_ms(1000); SET_MENU(SIBLING); delay_ms(1000); SET_MENU(PREVIOUS); delay_ms(1000); SET_MENU(NEXT); } Если вызывать через имя меню : Код void menu_Init(void) { Level1Item1Sub1_Text(); SET_MENU_WRITE_FUNC(Level1Item1Sub1_Text); SET_MENU(Level1Item1); delay_ms(1000); SET_MENU(Level1Item2); delay_ms(1000); SET_MENU(Level1Item3); delay_ms(1000); SET_MENU(Level1Item1); delay_ms(1000); SET_MENU(Level1Item2); } ... то все нормально, т.е. с интервалом в секунду выводятся пункты меню. В оригинальном исходнике символические имя определены так: Код #define PREVIOUS *((Menu_Item*)pgm_read_word(&CurrMenuItem->Previous)) #define NEXT *((Menu_Item*)pgm_read_word(&CurrMenuItem->Next)) #define PARENT *((Menu_Item*)pgm_read_word(&CurrMenuItem->Parent)) #define SIBLING *((Menu_Item*)pgm_read_word(&CurrMenuItem->Sibling)) #define ENTERFUNC *((FuncPtr*)pgm_read_word(&CurrMenuItem->EnterFunc)) #define SELECTFUNC *((FuncPtr*)pgm_read_word(&CurrMenuItem->SelectFunc)) В исправленной версии от Id_Alx опущен знак &. Добавление & приводит к перезапуску программы. Манипуляции с размерами CSTACK(0x100), RSTACK(0x40) не помогает. В чем же дело?
|
|
|
|
|
Oct 2 2007, 07:19
|
Частый гость
 
Группа: Участник
Сообщений: 79
Регистрация: 12-08-06
Из: Минск
Пользователь №: 19 504

|
Цитата(alux @ Sep 27 2007, 18:19)  В исправленной версии от Id_Alx опущен знак &. Добавление & приводит к перезапуску программы. Манипуляции с размерами CSTACK(0x100), RSTACK(0x40) не помогает. В чем же дело? Вот только не надо не надо добавлять значки ("&"), смысл которых Вам, видимо, не совсем ясен, а потом жаловаться. Вероятность того, что программа заработает корректно после подобных манипуляций очень мала. Версия из моего архива не собирается или не работает после сборки? Я пользуюсь IAR 4.20A.
|
|
|
|
|
Apr 8 2008, 11:24
|
Участник

Группа: Участник
Сообщений: 19
Регистрация: 14-12-07
Из: Беларусь, Гомель
Пользователь №: 33 305

|
Продолжу тему. У меня возникла необходимость создать грамотное меню с кучей подменю для своего устройства. Облазив этот форум, понял, что наилучший вариант по возможности в дальнейшем расширять меню, по требовательности к памяти... - это micro-menu. Программирую на CVAVR для МК Мега16 Общий смысл работы этого меню примерно, точнее сказать смутно  , представляю, поэтому начал потихоньку разбираться что где и как. В интернете по нему информации почти никакой. Нашел сайт создателя с его разработками, но там по теме микроменю ссылка на сайт http://avrfreaks.net/, но туда пускают только зарегеных пользователей. Зарегится мне не получилось. Вот решил тогда здесь спросить у знающих людей. Предупреждаю заранее, СИ я начал изучать месяц назад, но меню грамотное нужно обязательно, так что не пинайте если что за то, что я взялся за работу не по зубам. По мне так лучше день потерять, потом за пять минут долететь. (с) Как раз поможет углубленно изучит СИ. В общем так, комментариев там нету. Поэтому первым делом я начал добавлять комментарии. Вот докуда я дошел: Код / MICRO_MENU.H
#ifndef _MENU_H_ #define _MENU_H_
//Тут указываются указатели на функции typedef void (* FuncPtr) (void); /*Новый тип FuncPtr - указатель на функцию, которая ничего не принимает и не возвращает */ typedef void (* WriteFuncPtr) (const char*); /*Новый тип WriteFuncPtr - указатель на функцию, входным параметром которой является указатель на строку в ПЗУ, которая ничего не вовращает */ typedef struct PROGMEM { void *Next; //Следующее меню void *Previous; //Предыдущее меню void *Parent; //Родительское меню void *Sibling; //Дочернее меню (вложение) FuncPtr SelectFunc; //Выбор фнкции при выборе меню FuncPtr EnterFunc; //Выбор функции при входе в меню (Применяется после того, как текст меню отображен) const char Text[22]; //Текст меню } Menu_Item;
//Externs extern WriteFuncPtr *WriteFunc; //Переменная-указатель на функцию extern Menu_Item Null_Menu; //Переменная типа PROGMEM extern Menu_Item *CurrMenuItem; //Переменная-указаетль на данные типа PROGMEM
//Defines and Macros #define NULL_ENTRY Null_Menu #define NULL_FUNC 0 #define NULL_TEXT 0x00
#define PREVIOUS *((Menu_Item*)pgm_read_word(&CurrMenuItem->Previous)) Вот теперь возник затык на последней строчке. 1. pgm_read_word - то я так понимаю функция из прикрепленного <avr/pgmspace.h> в оригинальном тексте. В примере выше для IAR эта строка отсутствовала, поэтому я тоже ее удалил. 2.Теперь я попытался разобраться для чего эта строка нужна. В примере использования было так: Код SET_MENU(PREVIOUS); Смотрим что такое SET_MENU Код #define SET_MENU(x) \ MenuChange((Menu_Item*)&x); Делаем подстановку: Код SET_MENU(PREVIOUS); == MenuChange((Menu_Item*)&*((Menu_Item*)(&CurrMenuItem->Previous))); Вот с этой строкой я не могу разобраться, особенно с сочетанием &*В общем если есть знающие люди и особенно те, кто уже работал с этим меню, отзовитесь.
|
|
|
|
|
Apr 8 2008, 12:16
|

Местный
  
Группа: Участник
Сообщений: 355
Регистрация: 27-03-07
Из: Україна, Чуднів
Пользователь №: 26 530

|
Цитата(admiral @ Apr 8 2008, 14:24)  Код #define PREVIOUS *((Menu_Item*)pgm_read_word(&CurrMenuItem->Previous)) Вот теперь возник затык на последней строчке. 1. pgm_read_word - то я так понимаю функция из прикрепленного <avr/pgmspace.h> в оригинальном тексте. В примере выше для IAR эта строка отсутствовала, поэтому я тоже ее удалил. с этим меню не работал , но вполне логично можна переписать под стандартный макрос Кодевизиона чтения слова из флеш, или создать свою функцию чтения, а удалять незя.
--------------------
нельзя недооценивать предсказуемость глупости
|
|
|
|
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|