找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 7028|回复: 3
收起左侧

单片机多级菜单编程 用链表实现树状的菜单结构源码介绍

[复制链接]
ID:305925 发表于 2018-4-12 17:20 | 显示全部楼层 |阅读模式
单片机多级菜单编程实现(ZT)建立一个树状的菜单结构,用链表实现
链表中包含:
1、指向同级左右菜单和指向父菜单、子菜单的四个菜单结构体指针;
2、进入该菜单时需要执行的初始化函数指针
3、退出该菜单时需要执行的结束函数指针
4、该菜单内的按键处理函数指针数组的指针操作菜单模块需要的按键操作有:左、右、确
认、退出。
采用这种办法,可以方便的添加或删减菜单。并且只需要在其头文件中修改初始变量就可
以实现,完全无须修改C文件中的任何函数。

具体结构定义
我的定义,做个参考:
#define MENU_HLP_EN //菜单帮助信息使能
typedef struct

void (*pMenuTaskInit)(void); //指向菜单任务初始化函数的指针
void (*pMenuTaskEnd)(void); //指向菜单任务结束函数的指针
}MENU_TASK_TYP;
typedef struct MenuTyp

INT8U *MenuName; //菜单名称字符串
WORK_MOD WorkMod; //工作状态编号
MENU_TASK_TYP *pMenuTask; //指向菜单任务的指针
void (**pTaskKeyDeal)(void); //指向菜单任务按键处理函数数组的指针
#ifdef MENU_HLP_EN
INT8U *MenuHlp; //菜单帮助字符串
#endif
struct MenuTyp *pParent; //指向上层菜单的指针
struct MenuTyp *pChild; //指向子菜单的指针
struct MenuTyp *pRight; //指向右菜单的指针
struct MenuTyp *pLeft; //指向左菜单的指针
}MENU_TYP;

我根据网上的资料做的一个菜单:
/****************菜单数据结构**********************/
struct KeyTabStruct{
uint8 MenuIndex; //当前状态索引号
uint8 MaxItems; //本级菜单最大条目数
uint8 ShowLevel; //菜单显示内容
uint8 PressOk; //按下"回车"键时转向的状态索引号
uint8 PressEsc; //按下"返回"键时转向的状态索引号
uint8 PressDown; //按下"向下"键时转向的状态索引号
uint8 PressUp; //按下"向上"键时转向的状态索引号
void (*CurrentOperate)(); //当前状态应该执行的功能操作
};
uint8 MenuID; //菜单ID号
uint8 MenuNextID; //下级菜单ID号
//CurMenuID=本菜单ID
//MaxMenuItem=同级菜单最大项数
//OkMenuID=子菜单层所对应的菜单ID,ID=999为菜单已经到底了
//EscMenuID=父菜单层所对应的菜单ID,ID=999为菜单已经到顶了
//DownMenuID=弟菜单层所对应的菜单ID,ID=999为菜单是独生子
//UpMenuID=兄菜单层所对应的菜单ID,ID=999为菜单是独生子
//CurFunction=本菜单所对应的菜单函数指针
const struct KeyTabStruct KeyTab[MAX_KEYTABSTRUCT_NUM]={
//CurMenuID, axMenuItem, MenuShowLevel, OkMenuID, EscMenuID, DownMenuID, UpMenuID,CurFunction{MENU_EDIT, 0, 0, MENU_DATA_VIEW, MENU_NO,MENU_NO, MENU_NO, *MenuEdit},
{MENU_DATA_VIEW, 3, 1, MENU_DATA_VIEW_FIRE,MENU_EDIT, MENU_SYS_EDIT, MENU_PRINT_DATA,*MenuEdit},
{MENU_DATA_VIEW_FIRE, 5, MENU_NO, MENU_NO,MENU_DATA_VIEW, MENU_DATA_VIEW_TROUBLE, MENU_STEP_FOLLOW, *MenuDataViewIn},
{MENU_DATA_VIEW_TROUBLE, 5, MENU_NO, MENU_NO,MENU_DATA_VIEW, MENU_DATA_VIEW_REPEAT, MENU_DATA_VIEW_FIRE, *MenuDataViewIn},
{MENU_DATA_VIEW_REPEAT, 5, MENU_NO,
MENU_NO, MENU_DATA_VIEW, MENU_FACE_CHECK,
MENU_DATA_VIEW_TROUBLE, *MenuDataViewIn},
{MENU_FACE_CHECK, 5, MENU_NO,
MENU_NO, MENU_DATA_VIEW, MENU_STEP_FOLLOW,
MENU_DATA_VIEW_REPEAT, *MenuFaceCheck},
{MENU_STEP_FOLLOW, 5, MENU_NO,
MENU_NO, MENU_DATA_VIEW, MENU_DATA_VIEW_FIRE, MENU_FACE_CHECK,
*MenuStepFollow},
{MENU_SYS_EDIT, 3,
2, MENU_SUM_SET, MENU_EDIT,
MENU_PRINT_DATA, MENU_DATA_VIEW, *MenuEdit},
{MENU_SUM_SET, 6, MENU_NO,
MENU_NO, MENU_SYS_EDIT, MENU_EDIT_INSULATE,
MENU_TIME_SET, *MenuSumSet},
{MENU_EDIT_INSULATE, 6, MENU_NO,
MENU_NO, MENU_SYS_EDIT, MENU_EDIT_HZ, MENU_SUM_SET,
*MenuEditInsulate},
{MENU_EDIT_HZ, 6, MENU_NO,
MENU_NO, MENU_SYS_EDIT, MENU_LD_CONTROL,
MENU_EDIT_INSULATE, *MenuEditHZ},
{MENU_LD_CONTROL, 6,
MENU_NO, MENU_NO, MENU_SYS_EDIT, MENU_LD_DELAY,
MENU_EDIT_HZ, *MenuLDControl},
{MENU_LD_DELAY, 6,
MENU_NO, MENU_NO, MENU_SYS_EDIT, MENU_TIME_SET,
MENU_LD_CONTROL, *MenuLDDelay},
{MENU_TIME_SET, 6, MENU_NO,
MENU_NO, MENU_SYS_EDIT, MENU_SUM_SET, MENU_LD_DELAY,
*MenuTimeSet},
{MENU_PRINT_DATA, 3, 3,
MENU_PRINT_DATA_FIRE, MENU_EDIT, MENU_DATA_VIEW,
MENU_SYS_EDIT, *MenuEdit},
{MENU_PRINT_DATA_FIRE, 4,
MENU_NO, MENU_NO, MENU_PRINT_DATA,
MENU_PRINT_DATA_TROUBLE, MENU_PRINT_SET, *MenuPrintDataIn},
{MENU_PRINT_DATA_TROUBLE, 4, MENU_NO,
MENU_NO, MENU_PRINT_DATA, MENU_PRINTER_CHECK,
MENU_PRINT_DATA_FIRE, *MenuPrintDataIn},
{MENU_PRINTER_CHECK, 4, MENU_NO,
MENU_NO, MENU_PRINT_DATA, MENU_PRINT_SET,
MENU_PRINT_DATA_TROUBLE, *MenuPrintDataIn},
{MENU_PRINT_SET, 4, MENU_NO,
MENU_NO, MENU_PRINT_DATA, MENU_PRINT_DATA_FIRE,
MENU_PRINTER_CHECK, *MenuPrintSet},
};
/**************************************编程菜单显示数据
******************************/
const struct MenuDispData MenuEditShow[][MENU_MAX] = {
{{MENU_NO , 0, 0, "选择: 消音→退出"}, //主菜单
{MENU_DATA_VIEW , 1, 6, "⒈数据查看"},
{MENU_SYS_EDIT , 2, 6, "⒉系统编程"},
{MENU_PRINT_DATA , 3, 6, "⒊数据打印"}},
{{MENU_NO , 0, 0, "数据查看: 消音→退出"}, //数据查

{MENU_DATA_VIEW_FIRE , 1, 4, "⒈火警"},
{MENU_DATA_VIEW_TROUBLE, 2, 4, "⒉故障"},
{MENU_DATA_VIEW_REPEAT , 3, 4, "⒊重码"},
{MENU_FACE_CHECK , 1,12, "⒋面板检测"},
{MENU_STEP_FOLLOW , 2,12, "⒌单步跟踪"}},
{{MENU_NO , 0, 0, "系统编程: 消音→退出"}, //系统编程
{MENU_SUM_SET , 1, 0, "⒈容量设置"},
{MENU_EDIT_INSULATE , 2, 0, "⒉隔离点"},
{MENU_EDIT_HZ , 3, 0, "⒊汉字描述"},
{MENU_LD_CONTROL , 1,12, "⒋联动控制"},
{MENU_LD_DELAY , 2,12, "⒌模块延时"},
{MENU_TIME_SET , 3,12, "⒍时钟调整"}},
{{MENU_NO , 0, 0, "数据打印: 消音→退出"}, //数据打印
{MENU_PRINT_DATA_FIRE , 1, 0, "⒈火警数据"},
{MENU_PRINT_DATA_TROUBLE,2, 0, "⒉故障数据"},
{MENU_PRINTER_CHECK , 3, 0, "⒊打印机自检"},
{MENU_PRINT_SET , 1,12, "⒋打印设置"}},
};
/***********************************等待按键**********************************/
void WaitKey(void)

uint32 time;
time = RTCFlag;
WhichKey = KEY_NONE;
while(!EscFlag){
if(RTCFlag - time >= EDIT_TIME)
EscFlag = TRUE;
if(WhichKey != KEY_NONE){
KeySound(300); //按键音
return;



/*********************************显示多级菜单
**********************************/
void MenuEdit()

uint32 i,j=0;
uint32 oldid;
j = KeyTab[MenuID].ShowLevel;
if(WhichKey == KEY_ESC || WhichKey == KEY_OK){
ClearScreen();
for(i=0;i<KeyTab[MenuNextID].MaxItems+1;i++)
ShowString(MenuEditShow[j][ i].Lin,MenuEditShow[j]
[i ].Column,MenuEditShow[j][ i].Pdata,0); //初始化显示
oldid =
0;
//没有原先选择的项
}else{
if(WhichKey == KEY_UP)
oldid = KeyTab[MenuNextID].PressDown;
else
oldid = KeyTab
[MenuNextID].PressUp;
//指示原先的项

for(i=1;i<KeyTab[MenuNextID].MaxItems+1;i++){
if(MenuEditShow[j][ i].Id == oldid)
ShowString(MenuEditShow[j][ i].Lin,MenuEditShow[j]
[ i].Column,MenuEditShow[j][i ].Pdata,0); //正常显示原先的项
else{
if(MenuEditShow[j][i ].Id == MenuNextID)
ShowString(MenuEditShow[j][i ].Lin,MenuEditShow
[j][i ].Column,MenuEditShow[j][i ].Pdata,1); //反显当前选择的项


WhichKey = KEY_NONE;

/******************************系统编程*******************************/
uint32 Edit(void)

struct KeyTabStruct NowKeyTab; //指示当前的菜单值
uint32 escflag = FALSE;
ResetFlag = FALSE;
ChangeFlag = FALSE;
EscFlag = FALSE;
MenuID = MENU_EDIT;
NowKeyTab = KeyTab[MenuID];
MenuNextID = NowKeyTab.PressOk;
(*NowKeyTab.CurrentOperate)(); //显示主菜单
do{
if(WhichKey == KEY_NONE)
WaitKey(); //等待按键
switch(WhichKey){
case KEY_ESC : if(NowKeyTab.PressEsc != MENU_NO)

MenuID =
NowKeyTab.PressEsc;
MenuNextID =
NowKeyTab.MenuIndex;
NowKeyTab = KeyTab
[MenuID];
NowKeyTab.PressOk =
MenuNextID;
(*NowKeyTab.CurrentOperate)
(); //显示当前菜单
}else
escflag =
TRUE; //退出编程状态
break;
case KEY_OK : if(NowKeyTab.PressOk != MENU_NO)

MenuID =
NowKeyTab.PressOk;
NowKeyTab = KeyTab
[MenuID];
MenuNextID =
NowKeyTab.PressOk;

(*NowKeyTab.CurrentOperate)
(); //执行当前按键的操作
break;
case KEY_UP : if((MenuNextID != MENU_NO) &&
(KeyTab[MenuNextID].PressUp != MENU_NO)){
NowKeyTab.PressOk =
KeyTab[MenuNextID].PressUp;
MenuNextID = KeyTab
[MenuNextID].PressUp;

(*NowKeyTab.CurrentOperate)(); //执行当前按键的操作

break;
case KEY_DOWN: if((MenuNextID != MENU_NO) &&
(KeyTab[MenuNextID].PressDown != MENU_NO)){
NowKeyTab.PressOk =
KeyTab[MenuNextID].PressDown;
MenuNextID = KeyTab
[MenuNextID].PressDown;

(*NowKeyTab.CurrentOperate)(); //执行当前按键的操作

break;
case KEY_RESET: ResetFlag = TRUE;
break;
default : break;

}while(!ResetFlag && !EscFlag&& !escflag);
if(ChangeFlag && !EscFlag && !ResetFlag)
EditDataChange();
if(ResetFlag)
return SYS_RESET;
else{
return 0;


关于这个菜单的说明:
1.我用的是ARM处理器,所以51的时候把const改成code,uint32改成unsigned char。
2.在网上的资料中,结构体数组是存在RAM中的,我把它放在也flash中了,然后再定义一个
结构体变量,就样就可以省很多RAM,比较适合51.
3.在网上资料中,因为保存了原来的选择,当你离开编程状态重新进行后,会发现选择上会
是原来进行的顺序,我改动之后,退出上一级菜单还是你选的那一项,但重新进入后就是第
一个指定项。
4.增加UP和DOWN显示,可以反显最新选定的选项,正常显示原来的选项。

评分

参与人数 2黑币 +80 收起 理由
songhetai + 30
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:387860 发表于 2018-8-31 12:01 | 显示全部楼层
有具体的项目没,分享一下
回复

使用道具 举报

ID:275111 发表于 2020-4-28 17:47 | 显示全部楼层
赞。就是看着格式太累。
回复

使用道具 举报

ID:97678 发表于 2020-4-30 10:44 | 显示全部楼层
高大上的代码。
可以分享一下具体的项目代码吗?
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表