Jump to content

    
Sign in to follow this  
sind-rom

Структура меню

Recommended Posts

// 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. Изменения незначительны. Из всех вариантов, которые мы использовали, это самый удобный в использовании, пасширении/изменении, и наименьший по объему памяти.

Share this post


Link to post
Share on other sites

Выкладываю исходник простого и удобного меню для HD44780.

Новигация осуществляется при помощи 3 клавиш, плюс добавлена процедура редактирования переменных этими же клавишами. В общем при помощи этого меню и трех клавиш можно сорать полноценное устройство.

Menu.zip

Share this post


Link to post
Share on other sites
А вот со второй ошибкой не могу справится. В чем же дело?

Поблема в разной реализации доступа к чтению констант. Неоспоримым преимуществом ИАРа есть свободное использование инструкции LPM в к любой константе с модификатором _flash.

В gcc это обходится с помошью специальных процедур чтения байтов. ИМХО в макросах затерялся модификатор _flash используемый некорректно. Для исправления можно объявить все простыми константами и работать с ними в памяти. Потом найти лишний _flash или PROGMEM

Share this post


Link to post
Share on other sites
Первую ошибку можно обойти, указав размер массива Text[6] при определении Menu_Item.

А вот со второй ошибкой не могу справится. В чем же дело?

Очевидно, что в параметрах некой функции вместо указателя на функцию используется указатель на unsigned char const. Или наоборот.

В любом случае полезно локализовывать место ошибки. Если в одной строке несколько мест, вызывающих сомнение, то такую строку полезно разбить на несколько, благо С это позволяет.

Share this post


Link to post
Share on other sites

Только сильно не ругайте:

Решил попробывать код предложеный 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]

Share this post


Link to post
Share on other sites
но чего то ругается, пишет undefined symbol 'Menu' в строке {"Volume ", NULL, NULL, NULL, &Menu[1]}, // [0]

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

 

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

#define NULL 0

все скомпилировалось нормально

Edited by IceS

Share this post


Link to post
Share on other sites

По поводу 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

Помогите, пожалуйста, разобраться с этим меню.

Share this post


Link to post
Share on other sites
Первую ошибку можно обойти, указав размер массива Text[6] при определении Menu_Item.

А вот со второй ошибкой не могу справится. В чем же дело?

Для IAR получилось так...

umenu.rar

Share this post


Link to post
Share on other sites

Очень интересная, а главная злободневная для меня тема, жаль что раньше не набрёл на этот форум )).

Меня интересует как вы решаете проблему размещения меню в памяти ? У меня сейчас всё меню создаётся при включении девайса и так там в памяти и висит. Для этих целей в прикрученой внешней оперативке под меню отведен килобайт памяти.

Ну и приведу схематично то что сделано у меня.

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

Первым делом объявляеться структурка. Эта структура сама по себе не используеться, но все остальные типы меню содержат точно такие же поля, объявленые в той же последовательности:

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 нового экземпляра меню.

 

В общем я привёл основную идею, в коде могут попасться и некоторые ошибки и очепятки, но думаю, вполне понятно что я имею в виду )).

Просьба большими кирпичами в меня не кидать и по голове не бить ; - ))

Share this post


Link to post
Share on other sites
Для 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) не помогает. В чем же дело?

Share this post


Link to post
Share on other sites
В исправленной версии от Id_Alx опущен знак &. Добавление & приводит к перезапуску программы. Манипуляции с размерами CSTACK(0x100), RSTACK(0x40) не помогает. В чем же дело?

 

Вот только не надо не надо добавлять значки ("&"), смысл которых Вам, видимо, не совсем ясен, а потом жаловаться. Вероятность того, что программа заработает корректно после подобных манипуляций очень мала. Версия из моего архива не собирается или не работает после сборки? Я пользуюсь IAR 4.20A.

Share this post


Link to post
Share on other sites

У меня меню заработало. Как теперь сделать, чтобы пункты меню выводились не последовательно в одно и то же место дисплея, а отобразить на экране пункты меню одного уровня, и навигацию осуществлять либо стрелочкой, либо инверсией пунктов меню?

Share this post


Link to post
Share on other sites

Продолжу тему. У меня возникла необходимость создать грамотное меню с кучей подменю для своего устройства. Облазив этот форум, понял, что наилучший вариант по возможности в дальнейшем расширять меню, по требовательности к памяти... - это micro-menu.

Программирую на CVAVR для МК Мега16

Общий смысл работы этого меню примерно, точнее сказать смутно :05:, представляю, поэтому начал потихоньку разбираться что где и как. В интернете по нему информации почти никакой. Нашел сайт создателя с его разработками, но там по теме микроменю ссылка на сайт 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)));

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

 

В общем если есть знающие люди и особенно те, кто уже работал с этим меню, отзовитесь.

Share this post


Link to post
Share on other sites
#define PREVIOUS        *((Menu_Item*)pgm_read_word(&CurrMenuItem->Previous))

Вот теперь возник затык на последней строчке.

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

с этим меню не работал , но вполне логично можна переписать под стандартный макрос Кодевизиона чтения слова из флеш, или создать свою функцию чтения, а удалять незя.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this