Как-то так:
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");
Так как используется структура, нет надобности в запоминании навигации по меню. Вся информация в структуре.
Видео пробного проекта меню
Сообщение отредактировал IgorKossak - Dec 5 2014, 19:29
Причина редактирования: [codebox] для длинного кода, [code] - для короткого!!!