Перейти к содержанию
    

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

Menu.zip

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

 

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

#define NULL 0

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

Изменено пользователем IceS

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

umenu.rar

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

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

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

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

 

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для 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 опущен знак &. Добавление & приводит к перезапуску программы. Манипуляции с размерами CSTACK(0x100), RSTACK(0x40) не помогает. В чем же дело?

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

#define PREVIOUS        *((Menu_Item*)pgm_read_word(&CurrMenuItem->Previous))

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...