新闻中心

EEPW首页>嵌入式系统>设计应用> 单片机多级菜单编程实现

单片机多级菜单编程实现

作者: 时间:2016-11-27 来源:网络 收藏
建立一个树状的菜单结构,用链表实现

链表中包含:

本文引用地址://m.amcfsurvey.com/article/201611/322426.htm

1、指向同级左右菜单和指向父菜单、子菜单的四个菜单结构体指针;

2、进入该菜单时需要执行的初始化函数指针

3、退出该菜单时需要执行的结束函数指针

4、该菜单内的按键处理函数指针数组的指针操作菜单模块需要的按键操作有:左、右、确

认、退出。

采用这种办法,可以方便的添加或删减菜单。并且只需要在其头文件中修改初始变量就可

以实现,完全无须修改C文件中的任何函数。

具体结构定义

我的定义,做个参考:

#defineMENU_HLP_EN//菜单帮助信息使能

typedef struct

{

void (*pMenuTaskInit)(void);//指向菜单任务初始化函数的指针

void (*pMenuTaskEnd)(void);//指向菜单任务结束函数的指针

}MENU_TASK_TYP;

typedef struct MenuTyp

{

INT8U*MenuName;//菜单名称字符串

WORK_MODWorkMod;//工作状态编号

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{

uint8MenuIndex;//当前状态索引号

uint8MaxItems;//本级菜单最大条目数

uint8ShowLevel;//菜单显示内容

uint8PressOk;//按下"回车"键时转向的状态索引号

uint8PressEsc;//按下"返回"键时转向的状态索引号

uint8PressDown;//按下"向下"键时转向的状态索引号

uint8PressUp;//按下"向上"键时转向的状态索引号

void(*CurrentOperate)();//当前状态应该执行的功能操作

};

uint8MenuID;//菜单ID号

uint8MenuNextID;//下级菜单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 MenuDispDataMenuEditShow[][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

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

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)

returnSYS_RESET;

else{

return0;

}

}

关于这个菜单的说明:

1.我用的是ARM处理器,所以51的时候把const改成code,uint32改成unsigned char。

2.在网上的资料中,结构体数组是存在RAM中的,我把它放在也flash中了,然后再定义一个

结构体变量,就样就可以省很多RAM,比较适合51.

3.在网上资料中,因为保存了原来的选择,当你离开编程状态重新进行后,会发现选择上会

是原来进行的顺序,我改动之后,退出上一级菜单还是你选的那一项,但重新进入后就是第

一个指定项。

4.增加UP和DOWN显示,可以反显最新选定的选项,正常显示原来的选项。



评论


技术专区

关闭