Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Отображение меню на жк
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
ps1x
Ставлю три пива тому, кто даст мне пример (с комментариями) реализации вложенного меню для 2х строчного жк в среде CVAVR. Уже второй день бюсь ап стену, но ничего путного не выходит - то меню которое написал я, уже (недоделаное) занимает 1312 слов blink.gif . Сдается мне что это не есть гут. Но придумать ничего лучше бесконечных switch case не могу ( sad.gif
vet
Не зацикливайтесь на вложенности. Пункт меню - самодостаточный набор данных и кода для его прорисовки и переходов в другие пункты. Модель меню - простой граф, где вершины - пункты, а ребра - переходы по нажатиям клавиш. Задача программы - реализовать прорисовку и переходы. Это очень просто.
rezident
Попробуйте так.
Отделите интерпретатор меню и собственно описание каждого пункта меню.
В описании пункта меню должен содержаться заголовок меню, адрес функции выполняющейся при входе в меню, при выходе из меню и при нажатии каждой функциональной кнопки.
Интерпретатор меню должен прочитать описание пункта меню, выполнить какие-то действия по отображению заголовка/считыванию данных/отображению данных и выполнить функцию назначенную для нажатия клавиши, взяв ее из описания меню. Если данные отображаемые в данном пункте меню должны отображаться динамически, то периодически интерпретатор меню запускает функцию получения данных.
Отделение функциональной части меню от описательной немного усложняет его реализацию, но зато делает более наглядным исходный текст и позволяет легко расширять само меню как по функциональности, так и по количеству пунктов.
e-moe
Я реализовал так:

Код
//Структура описывает пункт меню
typedef struct _selection
{
  unsigned char *mas; // Указатель на название пункта
  void (*function)(void); //Указатель на функцию выполняющуюся по нажатии на enter/escape
  unsigned ent_f: 4; //Флаг входа 4 бита - обычно ID меню в которое надо войти
  unsigned esc_f: 4; //Флаг выхода 4 бита - обычно ID меню в которое надо вернуться
}SELECTION;

//Структура описывает меню/подменю
typedef struct _menu {
  unsigned char id; //Номер меню/подменю
  unsigned char num_selections; //Количество пунктов данного меню/подменю
  SELECTION *m; //Указатель намассив пунктов данного меню/подменю
}MENU;

//Номера меню/подменю
enum __menu__id {
  MAIN_MENU, //Главное меню
  SETTINGS_MENU, //Меню настроек
  REPORT_MENU //Меню отчёта
};

//Имена пунктов
unsigned char X1[]={"Пункт 1"};
...
unsigned char X7[]={"Пункт 1"};

//Заголовки функций
void func1(void);
...
void func7(void);

//Массив хранищий пункты главного меню (структура SELECTION)
static SELECTION menu_[]={
  {X1, func1, 0, 0}, //Пункт 1
  {X2, func2, 0, 0}, //Пункт 2
  {X3, goto_menu, SETTINGS_MENU, 0}, //Пункт 3
  {X4, goto_menu, REPORT_MENU, 0} //Пункт 4
};

//Массив хранищий пункты меню настроек (структура SELECTION)
static SELECTION menu_m0[]={
  {X5, func5, 0, MAIN_MENU}, //Пункт 1
  {X6, func6, 0, MAIN_MENU}, //Пункт 2
  {X7, func7, 0, MAIN_MENU}, //Пункт 3
};

//Главный массив хранит в себе все меню/подменю
//Все меню/подменю должны описываться в таком же порядке как и в   enum __menu__id ...
static MENU menu[] = {
  {MAIN_MENU, 4, menu_}, //Меню 1
  {SETTINGS_MENU, 3, menu_m0} //Меню 2
  };


Далее обявляю 2 переменные:

Код
int current_menu=0; //Переменная указывает на текущее меню
int current_poz=0; //Переменная указывает на текущий пункт меню/подменю


Далее что бы вывести текущее меню делаю так:
Код
Функция_вывода_строки(menu[current_menu].m[current_poz].mas);


Для перехода в подменю и обратно достаточчно организовать такой код для всех пунктов:

Код
void goto_menu(void) {
  switch (КодКнопки) {
  case ENTER: {current_menu=menu[current_menu].m[current_poz].ent_f; break;};//enter
  case ESC: {current_menu=menu[current_menu].m[current_poz].esc_f; break;};//escape
  }
  current_poz=0;
};


Вот так вот вызываю функцию для текущего пункта:

Код
menu[current_menu].m[current_poz].function();


Приблизительно так организовал работу с ним...
Код
while(TRUE) {
switch (КодКнопки) {
    case  DOWN: {
      (current_poz<=0)?current_poz=menu[current_menu].num_selections:current_poz--;
      print_menu();
      break;
    };
    case  UP: {
      (current_poz>=menu[current_menu].num_selections)?current_poz=0:current_poz++;
      print_menu();
      break;
    };
    case  ENTER: {
      menu[current_menu].m[current_poz].function();
      print_menu();
      break;
    };
    case  ESC: {
      menu[current_menu].m[current_poz].function();
      print_menu();
      break;
    };
    };
};


Вроде бы всё, поидее всё это должно работать с любой вложеностью...
ps1x
Спасибо за ответы, сейчас буду разбираться...
ps1x
Код
/*****************************************************
This program was produced by the
CodeWizardAVR V1.24.5 Standard
Automatic Program Generator
© Copyright 1998-2005 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
e-mail:office@hpinfotech.com

Project :
Version :
Date    : 28.03.2006
Author  : ps1x                            
Company : ccc                            
Comments:


Chip type           : ATmega8
Program type        : Application
Clock frequency     : 1,000000 MHz
Memory model        : Small
External SRAM size  : 0
Data Stack size     : 256
*****************************************************/

#include <mega8.h>

// Alphanumeric LCD Module functions
#asm
   .equ __lcd_port=0x18;PORTB
#endasm
#include <lcd.h>
        
char kod;
int current_menu=0; //Ïåðåìåííàÿ óêàçûâàåò íà òåêóùåå ìåíþ
int current_poz=0; //Ïåðåìåííàÿ óêàçûâàåò íà òåêóùèé ïóíêò ìåíþ/ïîäìåíþ
void goto_menu(void);

//Ñòðóêòóðà îïèñûâàåò ïóíêò ìåíþ
typedef struct _selection
{
  unsigned char *mas; // Óêàçàòåëü íà íàçâàíèå ïóíêòà
  void (*function)(void); //Óêàçàòåëü íà ôóíêöèþ âûïîëíÿþùóþñÿ ïî íàæàòèè íà enter/escape
  unsigned ent_f: 4; //Ôëàã âõîäà 4 áèòà - îáû÷íî ID ìåíþ â êîòîðîå íàäî âîéòè
  unsigned esc_f: 4; //Ôëàã âûõîäà 4 áèòà - îáû÷íî ID ìåíþ â êîòîðîå íàäî âåðíóòüñÿ
}SELECTION;

//Ñòðóêòóðà îïèñûâàåò ìåíþ/ïîäìåíþ
typedef struct _menu {
  unsigned char id; //Íîìåð ìåíþ/ïîäìåíþ
  unsigned char num_selections; //Êîëè÷åñòâî ïóíêòîâ äàííîãî ìåíþ/ïîäìåíþ
  SELECTION *m; //Óêàçàòåëü íàìàññèâ ïóíêòîâ äàííîãî ìåíþ/ïîäìåíþ
}MENU;

//Íîìåðà ìåíþ/ïîäìåíþ
enum __menu__id {
  MAIN_MENU, //Ãëàâíîå ìåíþ
  SETTINGS_MENU, //Ìåíþ íàñòðîåê
  REPORT_MENU //Ìåíþ îò÷¸òà
};

//Èìåíà ïóíêòîâ
unsigned char X1[]={"Ïóíêò 1"};
unsigned char X2[]={"Ïóíêò 2"};
unsigned char X3[]={"Ïóíêò 3"};
unsigned char X4[]={"Ïóíêò 4"};
unsigned char X5[]={"Ïóíêò 5"};
unsigned char X6[]={"Ïóíêò 6"};
unsigned char X7[]={"Ïóíêò 7"};

//Çàãîëîâêè ôóíêöèé
void func1(void);
void func2(void);
void func3(void);
void func4(void);
void func5(void);
void func6(void);
void func7(void);

//Ìàññèâ õðàíèùèé ïóíêòû ãëàâíîãî ìåíþ (ñòðóêòóðà SELECTION)
static SELECTION menu_[]={
  {X1, func1, 0, 0}, //Ïóíêò 1
  {X2, func2, 0, 0}, //Ïóíêò 2
  {X3, goto_menu, SETTINGS_MENU, 0}, //Ïóíêò 3
  {X4, goto_menu, REPORT_MENU, 0} //Ïóíêò 4
};

//Ìàññèâ õðàíèùèé ïóíêòû ìåíþ íàñòðîåê (ñòðóêòóðà SELECTION)
static SELECTION menu_m0[]={
  {X5, func5, 0, MAIN_MENU}, //Ïóíêò 1
  {X6, func6, 0, MAIN_MENU}, //Ïóíêò 2
  {X7, func7, 0, MAIN_MENU}, //Ïóíêò 3
} <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<--------ЗДЕСЬ РУГАЕТСЯ (invalid expression)
//////////////////////////////////////////#/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////#//#///////////////////////////////////////////////////////////////////////////
//////////////////////////////////////#//#///#////////////////////////////////////////////////////////////////////////
////////////////////////////////////#////#////#///////////////////////////////////////////////////////////////////////
//////////////////////////////////////////#////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////#////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////#////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////#####////////////////////////////////////////////////////////////////////////

//Ãëàâíûé ìàññèâ õðàíèò â ñåáå âñå ìåíþ/ïîäìåíþ
//Âñå ìåíþ/ïîäìåíþ äîëæíû îïèñûâàòüñÿ â òàêîì æå ïîðÿäêå êàê è â   enum __menu__id ...
static MENU menu[] = {
  {MAIN_MENU, 4, menu_}, //Ìåíþ 1
  {SETTINGS_MENU, 3, menu_m0} //Ìåíþ 2
  };

void goto_menu(void) {
  switch (kod) {
  case 'e': {current_menu=menu[current_menu].m[current_poz].ent_f; break;};//enter
  case 'b': {current_menu=menu[current_menu].m[current_poz].esc_f; break;};//escape
  }
  current_poz=0;
}



В коде я выделил ошибку вот так "<<<<<<<<<<<-----" почему ругается?
defunct
Когда копируете текст с русскоязычными коментариями из редактора, то не забывайте предварительно переключить раскладку клавиатры на "русскую", тогда при вставке в форум не будет в тексте нечитаемых иероглифов..

в этой ветке приводил пример для работы со строками меню. Может он и вам пригодится...
ps1x
Цитата(defunct @ Mar 29 2006, 01:28) *
Когда копируете текст с русскоязычными коментариями из редактора, то не забывайте предварительно переключить раскладку клавиатры на "русскую", тогда при вставке в форум не будет в тексте нечитаемых иероглифов..

в этой ветке приводил пример для работы со строками меню. Может он и вам пригодится...


1)про раскладку, я учту обязательно.
2)спасибо, конечно, посмотрел ссылку, много интересного, но я прошу помочь с конкретным кодом...
defunct
по конкретному коду - у Вас лишняя запятая.
ps1x
Большое спасибо!
С меня 2 пива для e-moe и одно defunct!

(current_poz<=0)?current_poz=menu[current_menu].num_selections:current_poz--;

- на эту строчку пишет missing ':'!

что это за оператор такой?
ps1x
Цитата
(current_poz<=0)?current_poz=menu[current_menu].num_selections:current_poz--;

- на эту строчку пишет missing ':'!

что это за оператор такой?

- с этим уже вроде разобрался, теперь вопрос:
void print_menu()
{
}
- как реализовывать?

что-нибудь типа:
lcd_puts(menu[current_menu].SELECTION);???
WHALE
И я пива хочу wink.gif А если серьезно,открой в хелпе CodeVision Library Function- в ней пункт LCD Function-
там все подробно описано.
ps1x
Да нет, с функциям лсд я разобрался, но вот как отобразить именно то что нужно, как к структуре обращаться??? Гляньте код если не лень, конечно )
e-moe
Выше написано:
Код
static SELECTION menu_m0[]={
  {X5, func5, 0, MAIN_MENU},
  {X6, func6, 0, MAIN_MENU},
  {X7, func7, 0, MAIN_MENU}, //!!!
}

Скорее всего из-за того, что запятая там случайно стоит... Потому и ругается.... У меня в IAR не ругался...

Вот такой вот код:
Код
(current_poz<=0)?current_poz=menu[current_menu].num_selections:current_poz--;

Можно заменить аналогом:
Код
if (current_poz<=0) {current_poz=menu[current_menu].num_selections;} else {current_poz--;};


Вывод меню на экран - print_menu() это уже фантазия... В CodeVision есть библиотеки работы с ЖКИ, при помощи них можно добиться вывода одной строки, а в print_menu() можно вызывать выод строки [сurrent_poz], потом переход курсора на сдлед. строку и вывод строки [сurrent_poz+1].
ps1x
Да, но вот как обратиться к структуре, чтоб отображала например
[current_poz]???
Я просто со структурами на Вы ))
e-moe
Код
menu[current_menu].m[current_poz].mas

menu - массив current_menu указывает на текущее меню .m обращаемся к помлю структуры в текущего меню т.к. там тоже массив, то current_poz указывает текущий пункт меню .mas есть указательна строку текущего меню и текущего пункта.

Если взять код выше, то:
Код
menu[1].m[1].mas

Активно второе меню (т.к. счёт начинается с 0): SETTINGS_MENU, далее в подменю SETTINGS_MENU выбран пункт 2 и далее .mas указатель на строку...
vet
ps1x
Совет: не пожалейте времени, прочитайте какой-нибудь учебник языка С, хотя бы тот же Керниган & Ритчи - будете программировать намного увереннее, и множество вопросов пропадёт.
ps1x
спасибо за совет, но я уже так и делаю, только учебник другой ))
ps1x
Код
/*****************************************************
This program was produced by the
CodeWizardAVR V1.24.5 Standard
Automatic Program Generator
© Copyright 1998-2005 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
e-mail:office@hpinfotech.com

Project :
Version :
Date    : 28.03.2006
Author  : ps1x                            
Company : ccc                            
Comments:


Chip type           : ATmega8
Program type        : Application
Clock frequency     : 1,000000 MHz
Memory model        : Small
External SRAM size  : 0
Data Stack size     : 256
*****************************************************/

#include <mega8.h>

// Alphanumeric LCD Module functions
#asm
   .equ __lcd_port=0x18;PORTB
#endasm
#include <lcd.h>
#include <stdio.h>
#include <delay.h>        
char kod;
int current_menu=0; //Переменная указывает на текущее меню
int current_poz=1; //Переменная указывает на текущий пункт меню/подменю
void goto_menu(void);
void print_menu(void);


//Структура описывает пункт меню
typedef struct _selection
{
  unsigned char *mas; // Указатель на название пункта
  void (*function)(void); //Указатель на функцию выполняющуюся по нажатии на enter/escape
  unsigned ent_f: 4; //Флаг входа 4 бита - обычно ID меню в которое надо войти
  unsigned esc_f: 4; //Флаг выхода 4 бита - обычно ID меню в которое надо вернуться
}SELECTION;

//Структура описывает меню/подменю
typedef struct _menu {
  unsigned char id; //Номер меню/подменю
  unsigned char num_selections; //Количество пунктов данного меню/подменю
  SELECTION *m; //Указатель намассив пунктов данного меню/подменю
}MENU;

//Номера меню/подменю
enum __menu__id {
  MAIN_MENU, //Главное меню
  SETTINGS_MENU, //Меню настроек
  REPORT_MENU //Меню отчёта
};

//Имена пунктов
unsigned char X1[]={"Punkt 1"};
unsigned char X2[]={"Punkt 2"};
unsigned char X3[]={"Settings"};
unsigned char X4[]={"Report"};
unsigned char X5[]={"set 1"};
unsigned char X6[]={"set 2"};
unsigned char X7[]={"set 3"};

//Заголовки функций

void func1(void){}
void func2(void){}
void func3(void){}
void func4(void){}
void func5(void){}
void func6(void){}
void func7(void){}

//Массив хранищий пункты главного меню (структура SELECTION)
static SELECTION menu_[]={
  {X1, func1, 0, 0}, //Punkt 1
  {X2, func2, 0, 0}, //Punkt 2
  {X3, goto_menu, SETTINGS_MENU, MAIN_MENU}, //Punkt 3
  {X4, goto_menu, REPORT_MENU, MAIN_MENU} //Punkt 4
};

//Массив хранищий пункты меню настроек (структура SELECTION)
static SELECTION menu_m0[]={
  {X5, func5, 0, 0}, //Punkt 1
  {X6, func6, 0, 0}, //Punkt 2
  {X7, func7, 0, 0} //Punkt 3
};

//Главный массив хранит в себе все меню/подменю
//Все меню/подменю должны описываться в таком же порядке как и в   enum __menu__id ...
static MENU menu[] = {
  {MAIN_MENU, 4, menu_}, //Меню 1
  {SETTINGS_MENU, 3, menu_m0} //Меню 2
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void goto_menu(void) {///////////////////////////в этой рутине возможно проблема///////////////////
  switch (kod) {////////////////////////////////////////////////////////////////////////////////////////////////
  case 'e': {current_menu=menu[current_menu].m[current_poz].ent_f; break;};//enter
  case 'b': {current_menu=menu[current_menu].m[current_poz].esc_f; break;};//escape
  }
  current_poz=0;
}
void print_menu()
{        
lcd_clear();
lcd_puts(menu[current_menu].m[current_poz].mas);
lcd_gotoxy(0,1);
if (current_poz==(int) menu[current_menu].num_selections)  /////////////////////////////////////////
{lcd_putsf("-------------------");}/////////////////проблема здесь!!!!!!!!!!!!//////////////////////////////
else {lcd_puts(menu[current_menu].m[current_poz+1].mas);}////////////////////////////////////////
delay_ms(100);  
kod='k';
}

// Timer 0 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
// Place your code here

}

// Timer 1 overflow interrupt service routine
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
// Place your code here

}

// Declare your global variables here


void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization
// Port B initialization
// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
// State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0
PORTB=0x00;
DDRB=0xFF;

// Port C initialization
// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTC=0x00;
DDRC=0x00;

// Port D initialization
// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
// State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0
PORTD=0xFF;
DDRD=0x00;

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 0,977 kHz
TCCR0=0x05;
TCNT0=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 3,906 kHz
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
TCCR1A=0x00;
TCCR1B=0x04;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;

// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
MCUCR=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x05;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;

// LCD module initialization
lcd_init(16);

// Global enable interrupts
#asm("sei")
while (1)
      {
      // Place your code here
        if(PIND.0==0){delay_ms(150);kod='u';}
        if(PIND.1==0){delay_ms(150);kod='d';}
        if(PIND.2==0){delay_ms(150);kod='e';}
        if(PIND.3==0){delay_ms(150);kod='b';}
              switch (kod) {
    case  'u': {
      if (current_poz<=0){current_poz=menu[current_menu].num_selections;}else{current_poz--;}
      print_menu();
      break;
    };
    case  'd': {
      if(current_poz>=menu[current_menu].num_selections-1){current_poz=0;}else{current_poz++;}
      print_menu();
      break;
    };
    case  'e': {
      menu[current_menu].m[current_poz].function();
      print_menu();
      break;
    };
    case  'b': {
      menu[current_menu].m[current_poz].function();
      print_menu();
      break;
    };
    };
      };

}


Возможные проблемы я выделил большим колличеством "//////".
Во первых, меню почти заработало, но есть две проблемы:

1) Почемуто не сравнивается количество пунктов меню и активный пункт меню???
(если не сравнивать то можно делать меню на котором отображается только 1 пункт, что не есть хорошо)

2) Вторая и САМАЯ ВАЖНАЯ проблема - не работает выход из подменю в предыдущее меню sad.gif
e-moe
может стоит без приведении к int попробовать?
Код
if (current_poz==menu[current_menu].num_selections)

или же поля (esc_f и ent_f) объявить как char или int
попробуй вызывать переход как
Код
void goto_menu(char);
////
void goto_menu(char kod) {...};
////
goto_menu(kod);
ps1x
Цитата
может стоит без приведении к int попробовать?


if (current_poz==menu[current_menu].num_selections)


- с этим уже разобрался, показывает сносно )

Цитата
или же поля (esc_f и ent_f) объявить как char или int
попробуй вызывать переход как

Код
void goto_menu(char);
////
void goto_menu(char kod) {...};
////
goto_menu(kod);



- В подменю входит, а выходить из него не хочет (((( ума не приложу, вроде-бы все seems to be alright....
ps1x
Неужели никто на этом форуме не знает язык С??? Это же не драйвер видеокарты, а всего лишь менюшка! Полный код программы есть немного выше. Спасибо всем кто откликнется.

p.s. не отправляйте меня сразу книжки читать, я считаю что начинать изучение языка с типов переменных неправильно. Лучше изучать в примерах, имхо доступнее для понимания.
prottoss
Делал меню под заказ. Правда для графических дисплеев. Вот исходник. Думаю, откинув (или заменив) функции вывода графики получите то что Вам надо
e-moe
Нашёл я проблему по которой невозможно быйти из подменю...
Допустим я захожу в меню настройки. Там 3 пункта:
Настройка 1
>Настройка 2
Настройка 3
Активен пункт 2.
тогда при нажатии на ESC происходит вызов:
Код
menu[current_menu].m[current_poz].function()

Совершенно очевидно что function() не является функцией goto_menu(), т.к. это функция вызовает опии пункта настройки 2.
Есть 2 выхода либо добавть отдельно пункт Выход в каждое подменю и поместить для вызова функцию goto_menu(), либо:
Код
case  ESC: {
      //убрать menu[current_menu].m[current_poz].function(&kkk);
      goto_menu(); //оставить
/*В таком случае при нажати на ESC всегда будет вызываться данная функция*/
      print_menu();
      . . .
      break;


P.S. В ИАР есть возможность посмотреть как ведёт себя код, какие переменные куда идут, в режиме отладки. Если нет возможности его использовать, то неплохо бы поставить AVRStudio и при критических ситуациях там просматривать, что происходит с кодом...
ps1x
Все получилось, хотя сначала делал вариант с пунктом "назад" в каждом подменю, но то что предложили Вы гораздо удобнее с точки зрения юзера )) Большое спасибо.
LSerg
Спасибо, сделал себе меню таким же образом. Легко меняется, код читабельный.
Хороший Сайт.
ARV
я тоже делал меню, правда на WinAVR. любая вложенность подменю (делается рекурсивно, потому надо осторожность соблюдать), пункты меню разных видов (просто пункт, пункт-опция [аналог галочки в винде, типа "посветка вкл."], пункт-параметр [т.е. типа "громкость 35%"]). выбор просто пункта - просто возврат его Id, выбор пункта-параметра - вызов специальной функции изменения параметра (можно очень гибко все делать), пункт опция - так же вызов особой функции. описание системы пунктов меню при помощи макросов - достаточно простое, как мне кажется. предусмотрен вариант для ЖКИ с числом строк более 2 - тогда меню выглядит примерно как в древних мобилках типа Siemens.

мне кажется, портировать под CVAVR несложно.

кому интересно, подробности тут
Qwertty
Цитата(ARV @ Jan 22 2010, 12:00) *
я тоже делал меню, правда на WinAVR. любая вложенность подменю (делается рекурсивно, потому надо осторожность соблюдать)

Есть же MicroMenu (http://www.avrfreaks.net/index.php?module=Freaks%20Files&func=viewFile&id=2178&showinfo=1), там без рекурсий строится меню любой вложенности. Не обязательно брать 1 в 1, можно только принцип построения позаимствовать. Меню это ОЗУ вообще не расходует, что не мешает в него параметры вводить через константные уазатели. Принцип же построения простой и достаточно эффективный. А главное - удобный.
ARV
Цитата(Qwertty @ Jan 22 2010, 13:03) *
Есть же MicroMenu (http://www.avrfreaks.net/index.php?module=Freaks%20Files&func=viewFile&id=2178&showinfo=1), там без рекурсий строится меню любой вложенности. Не обязательно брать 1 в 1, можно только принцип построения позаимствовать. Меню это ОЗУ вообще не расходует, что не мешает в него параметры вводить через константные уазатели. Принцип же построения простой и достаточно эффективный. А главное - удобный.
рекурсию я имел ввиду для вызова функции, "путешествующей" по пунктам и возвращающей результат выбора пользователя, т.е. расход памяти на рекурсию небольшой. а что касается MicroMenu - кой чего интересного там подсмотрел, попробую улучшить свой вариант (у моего возможности шире).
Qwertty
Цитата(ARV @ Jan 22 2010, 14:20) *
рекурсию я имел ввиду для вызова функции, "путешествующей" по пунктам и возвращающей результат выбора пользователя, т.е. расход памяти на рекурсию небольшой. а что касается MicroMenu - кой чего интересного там подсмотрел, попробую улучшить свой вариант (у моего возможности шире).

Дело то не в возможностях, а в принципах построения. Меню на основе структур очень легко дооснастить требующемся функционалом, но при этом оно не будет запутанным, а останется легко редактируемым. В Вашем же варианте без поллитры вообще не разобраться. smile.gif
Кстати остальные подходы в Вашей TUI тоже как то не очень оптимальны. Методы опроса кнопок с использованием софтовых задержек на 20мс удивляет.
ARV
для собственно TUI опрос кнопок без разницы smile.gif но скажите мне, где это может помешать?!

у меня меню так же на основе структур, только я отказался от связного списка, в пользу массивов, как раз из-за простоты восприятия. плюс в списке надо хранить ссылки на следующего-предыдущего-корневого, а для массива не надо - FLASH экономится smile.gif

а разобраться сложно - так можно и не разбираться, достаточно интерфейс понять smile.gif описание системы меню не сложнее, чем у MicroMenu - так же при помощи макросов, единственное, до чего я не догнал сразу - так это определить сразу тип для структуры в PROGMEM, и пришлось извращаться с предварительным описанием текстовых строк в виде констант.

MicroMenu весьма лаконично - признаю. у меня более путано - да. не стремился к суперлаконичности, старался больше вариантов охватить smile.gif
vvkka
да согласен код слишком запутаный, как то реолизовал нечто подобное. вопервых можно все масивы вынести во флеш, ОЗУ сильно секономит, вовторых с нажатием кнопок несовсем понял фиксация по временной задержке, непрощели одно нажатие одно срабатывание
ARV
Цитата(vvkka @ Jan 24 2010, 11:41) *
да согласен код слишком запутаный, как то реолизовал нечто подобное. вопервых можно все масивы вынести во флеш, ОЗУ сильно секономит, вовторых с нажатием кнопок несовсем понял фиксация по временной задержке, непрощели одно нажатие одно срабатывание
согласен с чем? wink.gif все массивы и так во FLASH smile.gif и кнопки именно так: одно нажатие - одно срабатывание. но предусмотрен случай автоповтора при длительном нажатии (у меня в TUI).
reload
Недавно переделал Micro-Menu под dsPIC (MPLAB C30 v2.04). Делюсь опытом:

Все изменения в файле Menu.h

Во-первых, выбрасываем
Код
#include <avr/pgmspace.h>

Во-вторых, вместо
Код
typedef struct {
    void       *Next;
    void       *Previous;
    void       *Parent;
    void       *Sibling;
    FuncPtr     SelectFunc;
    FuncPtr     EnterFunc;
    const char  Text[];
} Menu_Item PROGMEM;

должно быть
Код
typedef const struct {
    void          *Next;
    void          *Previous;
    void          *Parent;
    void          *Sibling;
    FuncPtr        SelectFunc;
    FuncPtr        EnterFunc;
    char           Text[];
}Menu_Item;

и в-третьих, вместо
Код
#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))

должно быть
Код
#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  *((FuncPtr*)(CurrMenuItem->EnterFunc))
#define SELECTFUNC *((FuncPtr*)(CurrMenuItem->SelectFunc))


Вроде, всё.

Надеюсь, кому-нибудь поможет.
ps1x
Вот окончательный полностью работающий вариант меню.
CODE
/*****************************************************
This program was produced by the
CodeWizardAVR V1.24.6 Standard
Automatic Program Generator
© Copyright 1998-2005 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
e-mail:office@hpinfotech.com

Project :
Version :
Date : 15.01.2010
Author : ps1x
Company : asd
Comments:


Chip type : ATmega8
Program type : Application
Clock frequency : 8,000000 MHz
Memory model : Small
External SRAM size : 0
Data Stack size : 256
*****************************************************/

#include <mega8.h>
#include <16x2.h>
// Declare your global variables here


char kod;
char last_item;
int current_menu=0; //Переменная указывает на текущее меню
int current_poz=1; //Переменная указывает на текущий пункт меню/подменю
void goto_menu(void);
void print_menu(void);


//Структура описывает пункт меню
typedef struct _selection
{
unsigned char *mas; // Указатель на название пункта
void (*function)(void); //Указатель на функцию выполняющуюся по нажатии на enter/escape
unsigned ent_f: 4; //Флаг входа 4 бита - обычно ID меню в которое надо войти
unsigned esc_f: 4; //Флаг выхода 4 бита - обычно ID меню в которое надо вернуться
}SELECTION;

//Структура описывает меню/подменю
typedef struct _menu {
unsigned char id; //Номер меню/подменю
unsigned char num_selections; //Количество пунктов данного меню/подменю
SELECTION *m; //Указатель намассив пунктов данного меню/подменю
}MENU;

//Номера меню/подменю
enum __menu__id {
MAIN_MENU, //Главное меню
DELAY_MENU, //Меню настроек
FOCUS_MENU,
QUANTITY_MENU,
TIME_MENU //Меню отчёта
};

//Имена пунктов
unsigned char X1[]={"Задержка"};
unsigned char X2[]={"1 сек"};
unsigned char X3[]={"2 сек"};
unsigned char X4[]={"3 сек"};
unsigned char X5[]={"4 сек"};
unsigned char X6[]={"5 сек"};
unsigned char X7[]={"8 сек"};
unsigned char X8[]={"10 сек"};
unsigned char X9[]={"15 сек"};
unsigned char X10[]={"20 сек"};
unsigned char X11[]={"30 сек"};
unsigned char X12[]={"Фокусировка"};
unsigned char X13[]={"Выкл"};
unsigned char X14[]={"0,5 сек"};
unsigned char X15[]={"1 сек"};
unsigned char X16[]={"Кол. фоток"};
unsigned char X17[]={"50 шт"};
unsigned char X18[]={"100 шт"};
unsigned char X19[]={"200 шт"};
unsigned char X20[]={"300 шт"};
unsigned char X21[]={"500 шт"};
unsigned char X22[]={"800 шт"};
unsigned char X23[]={"1000 шт"};
unsigned char X24[]={"Время фотогр."};
unsigned char X25[]={"Непрерывно"};
unsigned char X26[]={"5 мин"};
unsigned char X27[]={"10 мин"};
unsigned char X28[]={"15 мин"};
unsigned char X29[]={"пол часа"};
unsigned char X30[]={"час"};
unsigned char X31[]={"два часа"};
unsigned char X32[]={"Старт съемки"};

//Заголовки функций

void func1(void){}
void func2(void){}
void func3(void){}
void func4(void){}
void func5(void){}
void func6(){}
void func7(void){}

//Массив хранищий пункты главного меню (структура SELECTION)
static SELECTION menu_[]={
{X1, goto_menu, DELAY_MENU, MAIN_MENU}, //Punkt 1
{X12, goto_menu, FOCUS_MENU, MAIN_MENU}, //Punkt 2
{X16, goto_menu, QUANTITY_MENU, MAIN_MENU}, //Punkt 3
{X24, goto_menu, TIME_MENU, MAIN_MENU}, //Punkt 4
{X32, func7, 0, 0} //Punkt 4
};

//Массив хранищий пункты меню настроек (структура SELECTION)
static SELECTION menu_m0[]={
{X2, func6, 0, 0}, //Punkt 1
{X3, func6, 0, 0}, //Punkt 2
{X4, func6, 0, 0}, //Punkt 3
{X5, func6, 0, 0}, //Punkt 2
{X6, func6, 0, 0}, //Punkt 2
{X7, func6, 0, 0}, //Punkt 2
{X8, func6, 0, 0}, //Punkt 2
{X9, func6, 0, 0}, //Punkt 2
{X10, func6, 0, 0}, //Punkt 2
{X11, func6, 0, 0} //Punkt 2
};
static SELECTION menu_m1[]={
{X13, func6, 0, 0}, //Punkt 1
{X14, func6, 0, 0}, //Punkt 2
{X15, func6, 0, 0} //Punkt 3
};
static SELECTION menu_m2[]={
{X17, func6, 0, 0}, //Punkt 1
{X18, func6, 0, 0}, //Punkt 2
{X19, func6, 0, 0}, //Punkt 3
{X20, func6, 0, 0}, //Punkt 1
{X21, func6, 0, 0}, //Punkt 2
{X22, func6, 0, 0}, //Punkt 3
{X23, func6, 0, 0} //Punkt 1
};
static SELECTION menu_m3[]={
{X25, func6, 0, 0}, //Punkt 1
{X26, func6, 0, 0}, //Punkt 2
{X27, func6, 0, 0}, //Punkt 3
{X28, func6, 0, 0}, //Punkt 1
{X29, func6, 0, 0}, //Punkt 2
{X30, func6, 0, 0}, //Punkt 3
{X31, func6, 0, 0} //Punkt 1
};

//Главный массив хранит в себе все меню/подменю
//Все меню/подменю должны описываться в таком же порядке как и в enum __menu__id ...
static MENU menu[] = {
{MAIN_MENU, 5, menu_}, //Меню 1
{DELAY_MENU, 10, menu_m0}, //Меню 2
{FOCUS_MENU, 3, menu_m1},
{QUANTITY_MENU, 7, menu_m2},
{TIME_MENU, 7, menu_m3}
};

void goto_menu(void) {
switch (kod) {
case 'e': {current_menu=menu[current_menu].m[current_poz].ent_f; last_item=current_poz; break;}; //enter
case 'b': {current_menu=menu[current_menu].m[current_poz].esc_f; break;}; //escape
}
current_poz=0;
}
void print_menu()
{
lcd_clear();
lcd_putsf("> ");
lcd_puts(menu[current_menu].m[current_poz].mas);
lcd_gotoxy(2,1);
if (current_poz == menu[current_menu].num_selections-1) {
lcd_puts(menu[current_menu].m[0].mas);
// lcd_putsf("-------------------");
}
else {
lcd_puts(menu[current_menu].m[current_poz+1].mas);
}
delay_ms(100);
kod='k';
}

void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization
// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTB=0x00;
DDRB=0x00;

// Port C initialization
// Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
// State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0
PORTC=0x00;
DDRC=0xFF;

// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0xFF;
DDRD=0x00;

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
TCCR0=0x00;
TCNT0=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;

// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
MCUCR=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x00;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;

lcd_init();
lcd_clear();

lcd_putsf(" МИЭМ");
lcd_gotoxy(0,1);
lcd_putsf(" USB частотомер");
print_menu();

while (1)
{


if(PIND.0==0){delay_ms(150);kod='u';}
if(PIND.1==0){delay_ms(150);kod='d';}
if(PIND.2==0){delay_ms(150);kod='e';}
if(PIND.3==0){delay_ms(150);kod='b';}
switch (kod) {
case 'u': {
if (current_poz<=0){current_poz=menu[current_menu].num_selections-1;}else{current_poz--;}
print_menu();
break;
};
case 'd': {
if(current_poz>=menu[current_menu].num_selections-1){current_poz=0;}else{current_poz++;}
print_menu();
break;
};
case 'b': {
goto_menu();
current_poz=last_item;
print_menu();
break;
};
case 'e': {
menu[current_menu].m[current_poz].function();
print_menu();
break;
};
};
};

}
ut1wpr
Цитата(ps1x @ May 11 2010, 17:02) *
Вот окончательный полностью работающий вариант меню.
CODE
/*****************************************************
This program was produced by the
CodeWizardAVR V1.24.6 Standard
Automatic Program Generator
© Copyright 1998-2005 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
e-mail:office@hpinfotech.com

Project :
Version :
Date : 15.01.2010
Author : ps1x
Company : asd
Comments:


Chip type : ATmega8
Program type : Application
Clock frequency : 8,000000 MHz
Memory model : Small
External SRAM size : 0
Data Stack size : 256
*****************************************************/

#include <mega8.h>
#include <16x2.h>
// Declare your global variables here


char kod;
char last_item;
int current_menu=0; //Переменная указывает на текущее меню
int current_poz=1; //Переменная указывает на текущий пункт меню/подменю
void goto_menu(void);
void print_menu(void);


//Структура описывает пункт меню
typedef struct _selection
{
unsigned char *mas; // Указатель на название пункта
void (*function)(void); //Указатель на функцию выполняющуюся по нажатии на enter/escape
unsigned ent_f: 4; //Флаг входа 4 бита - обычно ID меню в которое надо войти
unsigned esc_f: 4; //Флаг выхода 4 бита - обычно ID меню в которое надо вернуться
}SELECTION;

//Структура описывает меню/подменю
typedef struct _menu {
unsigned char id; //Номер меню/подменю
unsigned char num_selections; //Количество пунктов данного меню/подменю
SELECTION *m; //Указатель намассив пунктов данного меню/подменю
}MENU;

//Номера меню/подменю
enum __menu__id {
MAIN_MENU, //Главное меню
DELAY_MENU, //Меню настроек
FOCUS_MENU,
QUANTITY_MENU,
TIME_MENU //Меню отчёта
};

//Имена пунктов
unsigned char X1[]={"Задержка"};
unsigned char X2[]={"1 сек"};
unsigned char X3[]={"2 сек"};
unsigned char X4[]={"3 сек"};
unsigned char X5[]={"4 сек"};
unsigned char X6[]={"5 сек"};
unsigned char X7[]={"8 сек"};
unsigned char X8[]={"10 сек"};
unsigned char X9[]={"15 сек"};
unsigned char X10[]={"20 сек"};
unsigned char X11[]={"30 сек"};
unsigned char X12[]={"Фокусировка"};
unsigned char X13[]={"Выкл"};
unsigned char X14[]={"0,5 сек"};
unsigned char X15[]={"1 сек"};
unsigned char X16[]={"Кол. фоток"};
unsigned char X17[]={"50 шт"};
unsigned char X18[]={"100 шт"};
unsigned char X19[]={"200 шт"};
unsigned char X20[]={"300 шт"};
unsigned char X21[]={"500 шт"};
unsigned char X22[]={"800 шт"};
unsigned char X23[]={"1000 шт"};
unsigned char X24[]={"Время фотогр."};
unsigned char X25[]={"Непрерывно"};
unsigned char X26[]={"5 мин"};
unsigned char X27[]={"10 мин"};
unsigned char X28[]={"15 мин"};
unsigned char X29[]={"пол часа"};
unsigned char X30[]={"час"};
unsigned char X31[]={"два часа"};
unsigned char X32[]={"Старт съемки"};

//Заголовки функций

void func1(void){}
void func2(void){}
void func3(void){}
void func4(void){}
void func5(void){}
void func6(){}
void func7(void){}

//Массив хранищий пункты главного меню (структура SELECTION)
static SELECTION menu_[]={
{X1, goto_menu, DELAY_MENU, MAIN_MENU}, //Punkt 1
{X12, goto_menu, FOCUS_MENU, MAIN_MENU}, //Punkt 2
{X16, goto_menu, QUANTITY_MENU, MAIN_MENU}, //Punkt 3
{X24, goto_menu, TIME_MENU, MAIN_MENU}, //Punkt 4
{X32, func7, 0, 0} //Punkt 4
};

//Массив хранищий пункты меню настроек (структура SELECTION)
static SELECTION menu_m0[]={
{X2, func6, 0, 0}, //Punkt 1
{X3, func6, 0, 0}, //Punkt 2
{X4, func6, 0, 0}, //Punkt 3
{X5, func6, 0, 0}, //Punkt 2
{X6, func6, 0, 0}, //Punkt 2
{X7, func6, 0, 0}, //Punkt 2
{X8, func6, 0, 0}, //Punkt 2
{X9, func6, 0, 0}, //Punkt 2
{X10, func6, 0, 0}, //Punkt 2
{X11, func6, 0, 0} //Punkt 2
};
static SELECTION menu_m1[]={
{X13, func6, 0, 0}, //Punkt 1
{X14, func6, 0, 0}, //Punkt 2
{X15, func6, 0, 0} //Punkt 3
};
static SELECTION menu_m2[]={
{X17, func6, 0, 0}, //Punkt 1
{X18, func6, 0, 0}, //Punkt 2
{X19, func6, 0, 0}, //Punkt 3
{X20, func6, 0, 0}, //Punkt 1
{X21, func6, 0, 0}, //Punkt 2
{X22, func6, 0, 0}, //Punkt 3
{X23, func6, 0, 0} //Punkt 1
};
static SELECTION menu_m3[]={
{X25, func6, 0, 0}, //Punkt 1
{X26, func6, 0, 0}, //Punkt 2
{X27, func6, 0, 0}, //Punkt 3
{X28, func6, 0, 0}, //Punkt 1
{X29, func6, 0, 0}, //Punkt 2
{X30, func6, 0, 0}, //Punkt 3
{X31, func6, 0, 0} //Punkt 1
};

//Главный массив хранит в себе все меню/подменю
//Все меню/подменю должны описываться в таком же порядке как и в enum __menu__id ...
static MENU menu[] = {
{MAIN_MENU, 5, menu_}, //Меню 1
{DELAY_MENU, 10, menu_m0}, //Меню 2
{FOCUS_MENU, 3, menu_m1},
{QUANTITY_MENU, 7, menu_m2},
{TIME_MENU, 7, menu_m3}
};

void goto_menu(void) {
switch (kod) {
case 'e': {current_menu=menu[current_menu].m[current_poz].ent_f; last_item=current_poz; break;}; //enter
case 'b': {current_menu=menu[current_menu].m[current_poz].esc_f; break;}; //escape
}
current_poz=0;
}
void print_menu()
{
lcd_clear();
lcd_putsf("> ");
lcd_puts(menu[current_menu].m[current_poz].mas);
lcd_gotoxy(2,1);
if (current_poz == menu[current_menu].num_selections-1) {
lcd_puts(menu[current_menu].m[0].mas);
// lcd_putsf("-------------------");
}
else {
lcd_puts(menu[current_menu].m[current_poz+1].mas);
}
delay_ms(100);
kod='k';
}

void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization
// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTB=0x00;
DDRB=0x00;

// Port C initialization
// Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
// State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0
PORTC=0x00;
DDRC=0xFF;

// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0xFF;
DDRD=0x00;

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
TCCR0=0x00;
TCNT0=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;

// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
MCUCR=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x00;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;

lcd_init();
lcd_clear();

lcd_putsf(" МИЭМ");
lcd_gotoxy(0,1);
lcd_putsf(" USB частотомер");
print_menu();

while (1)
{


if(PIND.0==0){delay_ms(150);kod='u';}
if(PIND.1==0){delay_ms(150);kod='d';}
if(PIND.2==0){delay_ms(150);kod='e';}
if(PIND.3==0){delay_ms(150);kod='b';}
switch (kod) {
case 'u': {
if (current_poz<=0){current_poz=menu[current_menu].num_selections-1;}else{current_poz--;}
print_menu();
break;
};
case 'd': {
if(current_poz>=menu[current_menu].num_selections-1){current_poz=0;}else{current_poz++;}
print_menu();
break;
};
case 'b': {
goto_menu();
current_poz=last_item;
print_menu();
break;
};
case 'e': {
menu[current_menu].m[current_poz].function();
print_menu();
break;
};
};
};

}

А теперь то же самое, но для четырёх языков, русский, украинский, польский и английский. smile.gif
Во! А мне приходилось наворачивать.... В принципе, ничего особенного, просто добавляются ещё вложения.
ChaiSER
Здравствуйте! Понравилась данная реализация меню для ЖКИ. Сделал себе по такому же принципу, но нужно добавить две функции:
1. Вывод текстовой информации на ЖКИ и выход из функции по нажатию ESC.
2. Ввод значения целого числа с клавиатуры (использую клавиатуру 3х4) и выход из функции с сохранением значения по нажатию ENTER и без сохранения по ESC.

Пока разбираюсь с первой функцией вывод текста и выход из неё по ESC. Делаю так:

Код
void func2(void)
{
    do {
        LCDClear(); //очистить LCD
        LCDWriteString("Hello World!"); //вывод строки на LCD
        _delay_ms(1000); //задержка для проверки
    } while (key == ESC); //Для выхода - ESC
}

И получаеться так, что после отображения текста 1сек. меня выбрасывает в меню, хотя возврат должен происходить по нажатию ESC... Что я не так делаю?
Saadov
Цитата
1. Вывод текстовой информации на ЖКИ и выход из функции по нажатию ESC.

В кей по идее и остается ESC, просто перед входом в цикл обнулите кей.

Цитата
1. Вывод текстовой информации на ЖКИ и выход из функции по нажатию ESC.

В кей по идее и остается ESC, просто перед входом в цикл обнулите кей.
Код
do
while (key != ESC)


Выполняем цикл пока кей не равен ESC, как только кей равен, т.е. нажата кнопка ESC, выходим из цикла.
ChaiSER
Цитата(Saadov @ Feb 15 2011, 19:57) *
В кей по идее и остается ESC, просто перед входом в цикл обнулите кей.

Saadov, спасибо, заработало как надо! sm.gif

Осталось реализовать вторую функцию:
Цитата
2. Ввод значения целого числа с клавиатуры (использую клавиатуру 3х4) и выход из функции с сохранением значения по нажатию ENTER и без сохранения по ESC.
Saadov
Все названные функции для примера
Код
vvod_chisla(){

key = null;
znachenie = 0;

do{

znachenie = obrabotka_knopok(); //смотрите апноут у атмела по подключению и обработке матрицы кнопок, ну или сами придумаете.

}while(key == null);

if(key == ENTER) eeprom_write( znachenie );

}


Лучше не оффтопить, а создать отдельную тему в
http://electronix.ru/forum/index.php?showforum=191
Loshara
Цитата(ps1x @ May 11 2010, 17:02) *
Вот окончательный полностью работающий вариант меню.


Спасибо за код,в Протеусе посмотрел,всё-ОК rolleyes.gif
Теперь,извините меня за тупость 01.gif , обьясните пожалуйста, как связать это с реальной жизнью, т.е. как менять свои переменные связав их именно с нужными пунктами и значениями меню. help.gif
EmDMAl
Разобрался с меню. Но при размещении большого количества структур забилась вся оперативная память. И встал вопрос о размещении структур в флешь памяти.
CODE
...

//Структура описывает пункт меню
typedef struct _selection
{
unsigned char *mas; // Указатель на название пункта
void (*function)(void); //Указатель на функцию выполняющуюся по нажатии на enter/escape
unsigned ent_f: 4; //Флаг входа 4 бита - обычно ID меню в которое надо войти
unsigned esc_f: 4; //Флаг выхода 4 бита - обычно ID меню в которое надо вернуться
}SELECTION;

//Структура описывает меню/подменю
typedef struct _menu {
unsigned char id; //Номер меню/подменю
unsigned char num_selections; //Количество пунктов данного меню/подменю
SELECTION *m; //Указатель намассив пунктов данного меню/подменю
}MENU;
...
//Массив хранищий пункты главного меню (структура SELECTION)
static SELECTION menu_[]={
{X1, goto_menu, DELAY_MENU, MAIN_MENU}, //Punkt 1
{X12, goto_menu, FOCUS_MENU, MAIN_MENU}, //Punkt 2
{X16, goto_menu, QUANTITY_MENU, MAIN_MENU}, //Punkt 3
{X24, goto_menu, TIME_MENU, MAIN_MENU}, //Punkt 4
{X32, func7, 0, 0} //Punkt 4
};

//Массив хранищий пункты меню настроек (структура SELECTION)
static SELECTION menu_m0[]={
{X2, func6, 0, 0}, //Punkt 1
{X3, func6, 0, 0}, //Punkt 2
{X4, func6, 0, 0}, //Punkt 3
{X5, func6, 0, 0}, //Punkt 2
{X6, func6, 0, 0}, //Punkt 2
{X7, func6, 0, 0}, //Punkt 2
{X8, func6, 0, 0}, //Punkt 2
{X9, func6, 0, 0}, //Punkt 2
{X10, func6, 0, 0}, //Punkt 2
{X11, func6, 0, 0} //Punkt 2
};
...

Так вот, кто-нибудь размещал эти структуры во флешь памяти?
demiurg1978
Как-то так:

menu.h:
CODE
//========================================================================
#ifndef MENU_H

#define MENU_H

#include "menu.h"
//========================================================================

//========================================================================
#include <pgmspace.h>
#include <stdbool.h>

#include "avrlibtypes.h"
#include "kbd_drv.h"
//========================================================================

//========================================================================
// Typedefs:
typedef void (*FuncPtr)(void);
//========================================================================

//========================================================================
typedef struct menu_item
{
void *Parent;
void *Child;
void *Next;
void *Prev;
FuncPtr NumFunc;
FuncPtr EnterFunc;
FuncPtr MenuFunc;
char Text[20];
} menu_item;
//========================================================================

// Externs:
//========================================================================
extern menu_item __flash *CurrMenuItem; // Текущий пункт меню.
extern menu_item __flash *BeginCurrMenuLevel; // Начало массива текущего уровня меню.

extern __flash menu_item Null_Menu;

extern char Menu_Str_Buf []; // Буфер для вывода текста.

extern void (*MenuFuncPtr)(void);
//========================================================================

// Defines and Macros:
//========================================================================
#define NULL_ENTRY Null_Menu
#define NULL_FUNC (void*)0
#define NULL_TEXT 0x00
#define PAGE_MENU 3
//========================================================================

//========================================================================
#define MAKE_MENU(Name, Parent, Child, Next, Prev, NumFunc, EnterFunc, MenuFunc, Text) \
extern menu_item __flash Parent; \
extern menu_item __flash Child; \
extern menu_item __flash Next; \
extern menu_item __flash Prev; \
menu_item __flash Name \
{ \
(menu_item*) &Parent, \
(menu_item*) &Child, \
(menu_item*) &Next, \
(menu_item*) &Prev, \
NumFunc, \
EnterFunc, \
MenuFunc, \
{Text} \
}
//========================================================================

//========================================================================
#define PARENT *((menu_item __flash*) (CurrMenuItem->Parent))
#define CHILD *((menu_item __flash*) (CurrMenuItem->Child))
#define NEXT *((menu_item __flash*) (CurrMenuItem->Next))
#define PREV *((menu_item __flash*) (CurrMenuItem->Prev))
#define NUM_FUNC *((FuncPtr) (CurrMenuItem->NumFunc))
#define ENTER_FUNC *((FuncPtr) (CurrMenuItem->EnterFunc))
#define MENU_FUNC *((FuncPtr) (CurrMenuItem->MenuFunc))
//========================================================================

//========================================================================
#define SET_MENU_LEVEL(x) \
Set_Menu_Level(&x)

#define SET_MENU_ITEM(x) \
Set_Menu_Item(&x)

#define GO_MENU_FUNC(x) \
MenuFunc((FuncPtr*)&x)

#define EXTERN_MENU(Name) \
extern menu_item __flash Name;
//========================================================================

//========================================================================
enum
{
SET_LEVEL = 0,
SET_NEXT,
SET_PREV,
};
//========================================================================

// Prototypes:
//========================================================================
void Set_Menu_Level (menu_item __flash *NewMenu);
void Set_Menu_Item (menu_item __flash *NewMenu);
void MenuFunc(FuncPtr* Function);
//========================================================================

//========================================================================
EXTERN_MENU (L_OUT_MODE);
//========================================================================

//========================================================================
void Out_Menu_Items_Init (void);
void Out_Menu_Items (void);
//========================================================================

//========================================================================
bool proc_menu_keys (void);
//========================================================================

//========================================================================
void out_name_level (void);
u08 count_chars (char __flash *data);
void make_page_menu (void);
void inc_pos_y_curs (void);
void dec_pos_y_curs (void);
void set_pos_curs (void);
//========================================================================

#endif


menu.c:
CODE
//========================================================================
#include "menu.h"
//========================================================================

//========================================================================
static u08 quant_items;
static u08 pos_y_curs;
//========================================================================

//========================================================================
==============================================================
menu_item __flash *CurrMenuItem; // Текущий пункт меню.

menu_item __flash *BeginCurrMenuLevel; // Начало массива текущего уровня меню.

menu_item __flash *temp_menu;

menu_item __flash Null_Menu = {(void*)0, (void*)0, (void*)0, (void*)0, NULL_FUNC, NULL_FUNC,
NULL_FUNC, {NULL_TEXT}};

void (*MenuFuncPtr)(void);
//========================================================================
==============================================================

//========================================================================
void Set_Menu_Level (menu_item __flash *NewMenu)
{
if ((void*)NewMenu == (void*)&NULL_ENTRY)
return;

CurrMenuItem = NewMenu;

Out_Menu_Items_Init (); // Так как новый уровень, инициализация переменных.
Out_Menu_Items (); // Вывод названия уровня меню и пунктов меню, курсора.

GO_MENU_FUNC (ENTER_FUNC);
}
//========================================================================

//========================================================================
void Set_Menu_Item (menu_item __flash *NewMenu)
{
if ((void*)NewMenu == (void*)&NULL_ENTRY)
return;

CurrMenuItem = NewMenu;

Out_Menu_Items (); // Вывод названия уровня меню и пунктов меню, курсора.

GO_MENU_FUNC (ENTER_FUNC);
}
//========================================================================

//========================================================================
void MenuFunc (FuncPtr* Function)
{
if ((void*) Function == (void*) NULL_FUNC)
return;

((FuncPtr) Function)();
}
//========================================================================

//========================================================================
bool proc_menu_keys (void)
{
bool a = false;
u08 key;

if (Get_Event (EV_ID_KEY_PRESSED))
{
key = GetKeyCode ();

switch (key)
{
case KEY_ESC_COD:
SET_MENU_LEVEL (PARENT);
a = true;
break;

case KEY_ENTER_COD:
SET_MENU_LEVEL (CHILD);
a = true;
break;

case KEY_NEXT_COD:
inc_pos_y_curs ();
SET_MENU_ITEM (NEXT);
a = true;
break;

case KEY_PREV_COD:
dec_pos_y_curs ();
SET_MENU_ITEM (PREV);
a = true;
break;

default:
break;
}

if (key < 10)
{
GO_MENU_FUNC (NUM_FUNC); // Сервисные меню. Ввод числовых параметров.
a = true;
}
}

if (a) return true;
else return false;
}
//========================================================================

/*
Уровни, пункты, текст - все выводится автоматом.
Так как все переходы по меню расписаны в структуре, то отпадает надобность в запоминании перемещений по меню.
*/

//========================================================================
void Out_Menu_Items_Init (void)
{
quant_items = 1;
pos_y_curs = 1;

// Получение адреса начала массива уровня меню.
BeginCurrMenuLevel = CurrMenuItem;
temp_menu = (menu_item __flash *)(CurrMenuItem->Prev);

while (1)
{
if ((void*)temp_menu == (void*)&NULL_ENTRY)
{
break;
}
else
{
BeginCurrMenuLevel = temp_menu;
temp_menu = (menu_item __flash *)(temp_menu->Prev);
}
}

// Получение количества пунктов меню.
temp_menu = (menu_item __flash *)(BeginCurrMenuLevel->Next);

while (1)
{
if ((void*)temp_menu == (void*)&NULL_ENTRY)
{
break;
}

temp_menu = (menu_item __flash *)(temp_menu->Next);
quant_items++;
}

// Позиция курсора.
if (quant_items > 1)
{
temp_menu = BeginCurrMenuLevel;

while (1)
{
if ((void*)temp_menu == (void*)&NULL_ENTRY)
return;

if (temp_menu == CurrMenuItem)
return;
else
pos_y_curs++;

temp_menu = (menu_item __flash *)(temp_menu->Next);
}
}
}

void Out_Menu_Items (void)
{
clr_dsp_buf ();

out_name_level (); // Вывод названия уровня меню.

make_page_menu (); // Вывод пунктов меню.

set_pos_curs (); // Установка позиции и вывод курсора.
}
//========================================================================

//========================================================================
// Вывод названия уровня меню.
void out_name_level (void)
{
temp_menu = (menu_item __flash *)(CurrMenuItem->Parent); // Считывание названия уровня меню из пункта меню в верхнем уровне.

if ((void*)temp_menu != (void*)&NULL_ENTRY)
{
char __flash *data = temp_menu->Text;

u08 i = count_chars (data); // Подсчет кол-ва символов в строке.

// Выравнивание текста посередине строки дисплея.

u08 a = i;

i = (20 - i); // Дисплей 20x4. Отнимаем от 20 число символов.

i >>= 1; // Делим остаток на 2.

if (a & (1<<0))
i += 2; // Если число нечетное.
else
i++; // Если число четное.

Print_Buf (1, i, temp_menu->Text);
}
}
//========================================================================

//========================================================================
// Подсчет кол-ва символов в строке.
u08 count_chars (char __flash *data)
{
u08 i = 0;

while (data [i])
{
i++;
}
return i;
}
//========================================================================

//========================================================================
void make_page_menu (void)
{
signed char tmp_pos_y_curs;
u08 i; // Счетчик страниц.
u08 j; // Страница меню.

if (quant_items > 1) // Если пунктов меню больше 1, значит есть что выводить.
{
temp_menu = BeginCurrMenuLevel;

if (pos_y_curs > PAGE_MENU)
{
tmp_pos_y_curs = pos_y_curs;

i = 0; // Счетчик страниц.

while (tmp_pos_y_curs > 0)
{
tmp_pos_y_curs -= PAGE_MENU;
i++;
}
tmp_pos_y_curs += PAGE_MENU;

j = PAGE_MENU; // Страница меню.

while (i-- > 1)
{
while (j--)
{
temp_menu = (menu_item __flash *)(temp_menu->Next); // Следующий пункт меню.
}
j = PAGE_MENU; // Страница меню.
}
}

u08 pos_y_text_item = 2; //
j = PAGE_MENU; // Страница меню.

while (j--)
{
Print_Buf (pos_y_text_item, 2, temp_menu->Text); // вывод названия пункта меню.

temp_menu = (menu_item __flash *)(temp_menu->Next); // Следующий пункт меню.

if ((void*)temp_menu == (void*)&NULL_ENTRY) // Если элемент Next
return; // пустой, то выход.
else
pos_y_text_item++;
}
}
}
//========================================================================

//========================================================================
void inc_pos_y_curs (void)
{
if (quant_items > 1)
{
if (pos_y_curs < quant_items) pos_y_curs++;
}
}

void dec_pos_y_curs (void)
{
if (quant_items > 1)
{
if (pos_y_curs > 1) pos_y_curs--;
}
}
//========================================================================

//========================================================================
void set_pos_curs (void)
{
if (quant_items > 1)
{
signed char tmp = pos_y_curs;

while (tmp > 0)
{
tmp -= PAGE_MENU;
}

if (tmp <= 0) tmp += PAGE_MENU;

PrintChar (tmp + 1, 1, ARROW_RIGHT);
}
}
//========================================================================


Пример использования:
Где-то создаем точку входа в меню:
Код
   SET_MENU_LEVEL (YOU_MENU);


Где-то делаем опрос клавиатуры и где-то проверяем, было ли событие клавиатуры:
Код
   if (proc_menu_keys ()) return;


Пример меню:
Код
//         NAME       PARENT      CHILD       NEXT        PREV        NUM_FUNCE  ENTER_FUNC MENU_FUNC   TEXT
MAKE_MENU (ITEM_1,    NULL_ENTRY, NULL_ENTRY, ITEM_2,     NULL_ENTRY, NULL_FUNC, NULL_FUNC, NULL_FUNC, "ПУНКТ 1");
MAKE_MENU (ITEM_2,    NULL_ENTRY, NULL_ENTRY, ITEM_3,     ITEM_1,     NULL_FUNC, NULL_FUNC, NULL_FUNC, "ПУНКТ 2");
MAKE_MENU (ITEM_3,    NULL_ENTRY, NULL_ENTRY, ITEM_4,     ITEM_2,     NULL_FUNC, NULL_FUNC, NULL_FUNC, "ПУНКТ 3");
MAKE_MENU (ITEM_4,    NULL_ENTRY, SUBITEM_1,  NULL_ENTRY, ITEM_3,     NULL_FUNC, NULL_FUNC, NULL_FUNC, "ПУНКТ 4");


MAKE_MENU (SUBITEM_1, ITEM_4, NULL_ENTRY, SUBITEM_2,  NULL_ENTRY, NULL_FUNC, NULL_FUNC, NULL_FUNC, "ПОДПУНКТ 1");
MAKE_MENU (SUBITEM_2, ITEM_4, NULL_ENTRY, SUBITEM_3,  SUBITEM_1,  NULL_FUNC, NULL_FUNC, NULL_FUNC, "ПОДПУНКТ 2");
MAKE_MENU (SUBITEM_3, ITEM_4, NULL_ENTRY, SUBITEM_4,  SUBITEM_2,  NULL_FUNC, NULL_FUNC, NULL_FUNC, "ПОДПУНКТ 3");
MAKE_MENU (SUBITEM_4, ITEM_4, NULL_ENTRY, NULL_ENTRY, SUBITEM_4,  NULL_FUNC, NULL_FUNC, NULL_FUNC, "ПОДПУНКТ 4");


Так как используется структура, нет надобности в запоминании навигации по меню. Вся информация в структуре.

Видео пробного проекта меню
binarick
Здравствуйте уважаемые
Хочу воскресить темку т.к. форум не даёт отправлять личные сообщения ..... почемуто
Очень нужно сделать своё меню, не хочу использовать МикроМеню а тут относительно просто всё и понятно.

Но запустить не получается... Точнее меню ТС запустил, но в конце темы уважаемый demiurg1978 показал чтото очень хорошее ).
Юзаю CodeVisionAVR 3.12
Так вот эта штука у меня не взлетает
1. avrlibtypes.h нашёл такую библиотеку но там используются х64 (long long) я просто закоментил строки в данной библиотеке.
2. SET_MENU_LEVEL (YOU_MENU); что такое YOU_MENU ? как создать ?
3. Любая строка из примера (MAKE_MENU (ITEM_1, NULL_ENTRY, NULL_ENTRY, ITEM_2, NULL_ENTRY, NULL_FUNC, NULL_FUNC, NULL_FUNC, "ПУНКТ 1")wink.gif
даёт непонятную мне ошибку "Error: C:\Project\IntKeyNew\intkey.c(83): storage modifier not allowed in this context"

Не пинайте сильно учусь только ещё

Спасибо за ваше время и ответы
TimurArs
Цитата(ut1wpr @ May 12 2010, 10:21) *
А теперь то же самое, но для четырёх языков, русский, украинский, польский и английский. sm.gif
Во! А мне приходилось наворачивать.... В принципе, ничего особенного, просто добавляются ещё вложения.


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