Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Структура меню
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
sind-rom
Народ, как Вы делаете меню для своих девайсов? Интересует структура. Столкнулся с проблемой: вывод на дисплей (HD44780 совместимый) меню получается малопонятной и (главное) неприспособленный для добавления новых пунктов меню. Никогда раньше не приходилось делать меню, так что научите smile.gif Может у кого есть каки-нить наработки\библиотеки?
Заранее большое спасибо...
junoSynthesizer
присоединяюсь к вопросу.

тоже только начал стряпать меню для своего устройства.
организовал страничное меню. (ничего не читал на эту тему, проосто придумал сам)

Код
/*
* страницы меню
*           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;
//...
//...
//...


хотелось бы услышать мнение гуру в этом вопросе - как ПРАВИЛЬНО стряпать меню? для маленького проектика мне и такая организация подходит, а вот если что-то большое...
bodja74
Делал двух-координатное меню.
Создаем массив переменных Y,X1,X2,X3...Xn.
Y- значение Y от 0 до n
X1-n - перемещение по X по каждой позиции Y (X отдельные ,чтобы не переносить значения)
На все - четыре кнопки.

1 Проверяем кнопки по Y
2 По значению Y=n переходим на подпрограмму допустим SUB_Xn
3 Проверяем кнопки по X
4 Переходим на операцию соответствующую значению Х (или изменияем какие то значения)
5 Все ,у меня через менюшку проходило до 90% кода.
SergSit
Почитай тему на этом форуме : Отображение меня на ЖК...
electronix
_Bill
Цитата(sind-rom @ Nov 15 2006, 20:36) *
Народ, как Вы делаете меню для своих девайсов? Интересует структура. Столкнулся с проблемой: вывод на дисплей (HD44780 совместимый) меню получается малопонятной и (главное) неприспособленный для добавления новых пунктов меню. Никогда раньше не приходилось делать меню, так что научите smile.gif Может у кого есть каки-нить наработки\библиотеки?
Заранее большое спасибо...

Я, например, так делал:
Код
/*    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);
            }
        }
    }
zltigo
До кучи.
Код
//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
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 );
}
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]
  };


Позволяет легко описывать многоуровневое меню (в приведённом фрагменте объявлено одноуровневое) и работать с ним с достаточной степенью абстракции.
alux
Продолжаем разговор... Считаю, что тема организации структуры меню не исчерпана. На avrfreaks.net нашел интересный проект MicroMenu - организация иерархического меню. Написан под GCC. Как прикрутить данный проект к IAR (v.4.12A)?
beer_warrior
Цитата
Как прикрутить данный проект к IAR (v.4.12A)?

Ручками. С и в Африке С. Т.е. могут возникнуть вопросы только с нестандартыми вещеми. Это преодолимо.
alux
Забыл приложить файл... Я 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

Вопрос тем, кто не считает ниже своего достоинства разбираться в чужом коде. Очень нужно организовать пользовательское меню для графического индикатора.
MRW
Используем это MicroMenu. Вещь просто супер. Легко использовать, памяти жрет мало. Работать удобно. Правда работаем под GCC. Подправили его немного под себя.
beer_warrior
Цитата
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

А сказать, каким это строкам соответсвует слабо?
alux
Цитата(beer_warrior @ Mar 28 2007, 14:29) *
А сказать, каким это строкам соответсвует слабо?

Вот листинг. Что еще необходимо для портирования этого проекта на ИАР?


Вот листинг...
to MRW. А можно посмотреть на Вашу практическую реализацию данного меню?
Dog Pawlowa
Цитата(alux @ Mar 28 2007, 18:45) *
Вот листинг. Что еще необходимо для портирования этого проекта на ИАР?
Вот листинг...
to MRW. А можно посмотреть на Вашу практическую реализацию данного меню?

По первой ошибке...
Количество аргументов при определении Menu_Item меньше, чем Вы пытаетесь впихнуть.

И общее замечание - люди готовы помочь, но заставлять их кликать столько раз, чтобы распаковать и посмотреть листинг ? Так можно отбить эту готовность ohmy.gif
alux
Цитата(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) *
И общее замечание - люди готовы помочь, но заставлять их кликать столько раз, чтобы распаковать и посмотреть листинг ? Так можно отбить эту готовность ohmy.gif

Почему-то мне не удалось присоединить файлы с расширением .с Поэтому прилагаю оригинальный архив с исходником для GCC.
Господа "знающие", может проще и быстрее будет проверить данный исходник на IAR, дабы избежать ненужных вопросов. Уверен, данный проект интересен многим.
MRW
// 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. Изменения незначительны. Из всех вариантов, которые мы использовали, это самый удобный в использовании, пасширении/изменении, и наименьший по объему памяти.
alux
Первую ошибку можно обойти, указав размер массива Text[6] при определении Menu_Item.
А вот со второй ошибкой не могу справится. В чем же дело?
Kolia
Выкладываю исходник простого и удобного меню для HD44780.
Новигация осуществляется при помощи 3 клавиш, плюс добавлена процедура редактирования переменных этими же клавишами. В общем при помощи этого меню и трех клавиш можно сорать полноценное устройство.
beer_warrior
Цитата
А вот со второй ошибкой не могу справится. В чем же дело?

Поблема в разной реализации доступа к чтению констант. Неоспоримым преимуществом ИАРа есть свободное использование инструкции LPM в к любой константе с модификатором _flash.
В gcc это обходится с помошью специальных процедур чтения байтов. ИМХО в макросах затерялся модификатор _flash используемый некорректно. Для исправления можно объявить все простыми константами и работать с ними в памяти. Потом найти лишний _flash или PROGMEM
IgorKossak
Цитата(alux @ Mar 29 2007, 10:28) *
Первую ошибку можно обойти, указав размер массива Text[6] при определении Menu_Item.
А вот со второй ошибкой не могу справится. В чем же дело?

Очевидно, что в параметрах некой функции вместо указателя на функцию используется указатель на unsigned char const. Или наоборот.
В любом случае полезно локализовывать место ошибки. Если в одной строке несколько мест, вызывающих сомнение, то такую строку полезно разбить на несколько, благо С это позволяет.
Яrik
Только сильно не ругайте:
Решил попробывать код предложеный 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]
IceS
Цитата(Яrik @ Jul 13 2007, 18:44) *
но чего то ругается, пишет undefined symbol 'Menu' в строке {"Volume ", NULL, NULL, NULL, &Menu[1]}, // [0]

У меня IAR 4.30 скомпилил без проблем.

Вернее у меня не был определен NULL, после
Код
#define NULL 0

все скомпилировалось нормально
alux
По поводу 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

Помогите, пожалуйста, разобраться с этим меню.
id_Alx
Цитата(alux @ Mar 29 2007, 13:28) *
Первую ошибку можно обойти, указав размер массива Text[6] при определении Menu_Item.
А вот со второй ошибкой не могу справится. В чем же дело?

Для IAR получилось так...
Unknown_User
Очень интересная, а главная злободневная для меня тема, жаль что раньше не набрёл на этот форум )).
Меня интересует как вы решаете проблему размещения меню в памяти ? У меня сейчас всё меню создаётся при включении девайса и так там в памяти и висит. Для этих целей в прикрученой внешней оперативке под меню отведен килобайт памяти.
Ну и приведу схематично то что сделано у меня.
Требовалось создать иерархическое меню, причём в пунктах меню может быть что угодно от логотипа прибора и его настроек до отображения сигнала.
Первым делом объявляеться структурка. Эта структура сама по себе не используеться, но все остальные типы меню содержат точно такие же поля, объявленые в той же последовательности:
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 нового экземпляра меню.

В общем я привёл основную идею, в коде могут попасться и некоторые ошибки и очепятки, но думаю, вполне понятно что я имею в виду )).
Просьба большими кирпичами в меня не кидать и по голове не бить ; - ))
alux
Цитата
Для 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) не помогает. В чем же дело?
id_Alx
Цитата(alux @ Sep 27 2007, 18:19) *
В исправленной версии от Id_Alx опущен знак &. Добавление & приводит к перезапуску программы. Манипуляции с размерами CSTACK(0x100), RSTACK(0x40) не помогает. В чем же дело?


Вот только не надо не надо добавлять значки ("&"), смысл которых Вам, видимо, не совсем ясен, а потом жаловаться. Вероятность того, что программа заработает корректно после подобных манипуляций очень мала. Версия из моего архива не собирается или не работает после сборки? Я пользуюсь IAR 4.20A.
alux
У меня меню заработало. Как теперь сделать, чтобы пункты меню выводились не последовательно в одно и то же место дисплея, а отобразить на экране пункты меню одного уровня, и навигацию осуществлять либо стрелочкой, либо инверсией пунктов меню?
admiral
Продолжу тему. У меня возникла необходимость создать грамотное меню с кучей подменю для своего устройства. Облазив этот форум, понял, что наилучший вариант по возможности в дальнейшем расширять меню, по требовательности к памяти... - это micro-menu.
Программирую на CVAVR для МК Мега16
Общий смысл работы этого меню примерно, точнее сказать смутно 05.gif, представляю, поэтому начал потихоньку разбираться что где и как. В интернете по нему информации почти никакой. Нашел сайт создателя с его разработками, но там по теме микроменю ссылка на сайт 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)));

Вот с этой строкой я не могу разобраться, особенно с сочетанием &*

В общем если есть знающие люди и особенно те, кто уже работал с этим меню, отзовитесь.
sKWO
Цитата(admiral @ Apr 8 2008, 14:24) *
Код
#define PREVIOUS        *((Menu_Item*)pgm_read_word(&CurrMenuItem->Previous))

Вот теперь возник затык на последней строчке.
1. pgm_read_word - то я так понимаю функция из прикрепленного <avr/pgmspace.h> в оригинальном тексте. В примере выше для IAR эта строка отсутствовала, поэтому я тоже ее удалил.

с этим меню не работал , но вполне логично можна переписать под стандартный макрос Кодевизиона чтения слова из флеш, или создать свою функцию чтения, а удалять незя.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.