//呵呵,都在讨论菜单啊?那我也来优化一个! // //大家多提意见哦:-) // // // //--------------------------------------------------------------------------// // // // (c) Copyright 2007-2008 xuwenjun // // All Rights Reserved // // V1.00 // //--------------------------------------------------------------------------// //标 题: xwj优化的通用菜单程序 // //文件名: xwj_menu.c // //版 本: V1.00 // //修改人: xwj E-mail:xuwenjun@21cn.com // //日 期: 07-10-25 // //描 述: xwj优化的通用菜单程序 // //声 明: // // 以下代码仅免费提供给学习用途,但引用或修改后必须在文件中声明出处. // // 如用于商业用途请与作者联系. E-mail:xuwenjun@21cn.com // // 有问题请mailto xuwenjun@21cn.com 欢迎与我交流! // //--------------------------------------------------------------------------// //老版本: 无 老版本文件名: // //创建人: xwj E-mail:xuwenjun@21cn.com // //日 期: 03-11-06 // //--------------------------------------------------------------------------// //描 述: // // xwj优化的通用菜单程序 // // MCU 型号: 任意 // // 开发环境: ANSI C任意平台 // // 开发日期: 2007.10.25 // // 程序编写: XWJ // //--------------------------------------------------------------------------// // 程序特点: // //1、程序和数据的分离,菜单可任意定义,甚至放在RAM里 // //2、定义好链表和菜单条目后,即可自动显示菜单、自动循环、跳转、执行, // // 自动控制程序流向,不需任何额外的判断和switch // //3、保留菜单路径,可原路返回(当然,也可直接跳回去啦:-) ) // //4、可任意定义菜单层数,每层菜单可以任意组织,多一层菜单仅需增加1字节RAM // //5、节省内存,仅需2字节+菜单层数个RAM // //--------------------------------------------------------------------------//
#include <stdio.h> #include “my_displays.h” //my_displays.c中包含char putchar (char)的实体 //和 液晶显示模块(点、线、填充...) //以及printat(x,y,char*),有此足以实现任何界面显示
#define MENU_MAX_LAYER 16 //菜单链最大层数,示例只用到4层 #define MENU_NUM_OF_PAGE 4 //每页可以显示菜单数目
unsigned char Menu_tree[MENU_MAX_LAYER]; //菜单按键树,类似堆栈 unsigned char Menu_tree_ptr; //当前菜单按键树所在层数 unsigned char Menu_item_ptr; //当前菜单项在本层中位置
typedef struct { unsigned char code *Menu_NextTab; //本级菜单菜单链表 char code *Menu_Name; //当前菜单名称 void (*CurrentOperate)(); //当前状态应该执行的功能操作 }Menustruct;
void (*KeyFuncPtr)(); //按键功能指针
void refur_Menu(void); //刷新菜单层,就是依次打印总标题、各个条目, void refur_Menu_item(void); //刷新本层菜单项,就是恢复原来项颜色,反显当前项 //则两个函数和界面布局有关,自己写吧 extern void ScanKey(void); //键盘扫描
void Menu_Key_Func(unsigned char NowKey); //菜单按键功能 { switch(NowKey) { case KEY_UP: if(*(KeyTab[Menu_tree][Menu_tree_ptr]].Menu_NextTab)) //Menu_NextTab[0]不为0,是菜单 // 也可以判断函数指针是否是NULL,写成下面这样: // if(KeyTab[Menu_tree][Menu_tree_ptr]].CurrentOperate == NULL)) //Menu_NextTab[0]不为0,是菜单 { Menu_item_ptr--; //菜单项上移 if(Menu_tree_ptr== -1) //菜单项上移到顶翻转了,修正到最后一项 Menu_item_ptr=sizeof(*(KeyTab[Menu_tree][Menu_tree_ptr]].Menu_NextTab))-1; //菜单循环 refur_Menu_item(); //刷新本层菜单项 } else Error_Beep(); //否则不是菜单,上下移无效,蜂鸣警告 break; case KEY_doWN: if(*(KeyTab[Menu_tree][Menu_tree_ptr].Menu_NextTab)) //Menu_NextTab[0]不为0,是菜单 { Menu_item_ptr++; //菜单项下移 if(Menu_item_ptr==sizeof(*(KeyTab[Menu_tree][Menu_tree_ptr]].Menu_NextTab))-1) //菜单项下移到底 Menu_item_ptr=0; //菜单项下移到底,循环上去 refur_Menu_item(); //刷新本层菜单项 } else Error_Beep(); //否则不是菜单,上下移无效,蜂鸣警告 break; case KEY_OK://KEY_OK, KEY_UP, KEY_ESC, if(*(KeyTab[Menu_tree][Menu_tree_ptr]].Menu_NextTab)) //Menu_NextTab[0]不为0,是菜单 { Menu_tree[Menu_tree_ptr+1]=KeyTab[Menu_tree][Menu_tree_ptr]].Menu_NextTab[Menu_item_ptr]); //下一级菜单压进菜单树,能看懂吗? Menu_tree_ptr++; //菜单又多了一级 refur_Menu(); //刷新菜单层,当前菜单当然是在Menu_tree[Menu_tree_ptr]中啦 } else //不是菜单,执行当前菜单项的操作 { KeyFuncPtr=KeyTab[Menu_tree][Menu_tree_ptr]].CurrentOperate; (*KeyFuncPtr)(); //执行当前菜单项的操作 } break; case KEY_ESC: // if(Menu_tree_ptr) //当前菜单位置不是顶层(主界面) { Menu_tree_ptr--; //则返回上一层 refur_Menu(); //刷新菜单层,当前菜单当然是在Menu_tree[Menu_tree_ptr]中啦 } else Error_Beep(); //否则蜂鸣警告 break; // //case Other: //.... } // }
//************************************************************************ //Menu struct: //菜单示例,共4层19项 //************************************************************************ // First Bmp--------------------->(MenuID_0) // ---+ // |View--------------------->(MenuID_1) // +---+ // | |About Recorde-------->(MenuID_2) // | +-------------- // | |HandSet Clock-------->(MenuID_3) // | +-------------- // |Operation---------------->(MenuID_4) // +---+ // |Set Position--------->(MenuID_5) // +---+ // | |Rd Zj To Flash------->(MenuID_6) // | +-------------- // | |Rd Zl To Ram--------->(MenuID_7) // | +-------------- // | |Del Given Zl--------->(MenuID_8) // | +-------------- // | |Del All Zl----------->(MenuID_9) // | +-------------- // |Test--------------------->(MenuID_10) // +---+ // | |Ram Test------------->(MenuID_11) // | +-------------- // | |Flash Test----------->(MenuID_12) // | +-------------- // |System Set--------------->(MenuID_13) // +---+ // |Debug---------------->(MenuID_14) // +---+ // | |PassWord--------->(MenuID_15) // | +-------------- // | |See Picture------>(MenuID_16) // | +-------------- // |Set Hand Clk--------->(MenuID_17) // +-------------- //************************************************************************
//菜单链表,每项菜单一条,共19项 unsigned char code Tab_First_Bmp[]= {1,4,0x00 }; unsigned char code Tab_View[]= {2,3,0x00 }; unsigned char code Tab_About_Recorde[]= {0x00 }; unsigned char code Tab_HandSet[]= {0x00 }; unsigned char code Tab_Operation[]= {5,10,13,14,17,0x00 }; unsigned char code Tab_Set_Position[]= {6,7,8,9,0x00 }; unsigned char code Tab_Rd_Zj_To_Flash[]= {0x00 }; //... //每项菜单一条,共19项
//菜单结构,每项菜单一条,,共19项 Menustruct MenuTab[]= { //Menu_NextTab, Menu_Name, CurrentOperate //本级菜单菜单链表, 当前菜单名称, 当前状态应该执行的功能操作 {Tab_First_Bmp,"First_Bmp",*First_Bmp}, //实际上菜单功能可以为NULL,由refur_Menu()统一操作; {Tab_View,"View",*View}, //不是菜单则用实际功能函数 {Tab_About_Recorde,"About Recorde",*About_Recorde}, //非菜单 {Tab_HandSet Clock,"HandSet Clock",*HandSet_Clock}, //非菜单 {Tab_Operation,"Operation",*NULL}, //菜单 {Tab_Set_Position,"Set Position",*NULL}, //菜单 {Tab_Rd_Zj_To_Flash,"Rd Zj To Flash",*Rd_Zj_To_Flash}, //非菜单 //... //每项菜单一条,共19项 };
//使用方法: //1、定义好链表和菜单条目,写好每个功能函数 //2、主循环中不停的读按键,然后Menu_Key_Func();即可自动显示菜单、自动循环、跳转、执行 //比如:
void main(void) { Menu_tree_ptr=0; //当前菜单按键树所在层数 Menu_item_ptr=0; //当前菜单项在本层中位置 refur_Menu(); //刷新顶层菜单 refur_Menu_item(); //刷新本层菜单项 while(1) { ScanKey(); //键盘扫描 if(KeyOk) Menu_Key_Func(); //有按键则执行按键功能 } }
//本程序由xwj设计的UltraEdit脚本加亮显示,如需要脚本请访问我的Blog或联系xwjfile@21cn.com
|