一般用单片机写液晶菜单都是通过大量标志位配合if else语句,或是switch case语句来完成当前液晶显示位置记录以及跳转的情况,这种写法往往会出现标志位多,代码冗余度高,程序中套用大量的条件判断语句,使得程序难以维护和升级。本文介绍了一种通过函数指针的方法来实现多级菜单的写法,程序通俗易懂,且易于维护。
extern void (*KeyFuncPtr)(void);
typedef struct
{
unsigned int KeyIndex; //当前所在的位置,类似于索引号,目的是为了告诉用户当前位置下需要执行的函数
unsigned int KeyUp; //当前按下了向上按键,按下后需要跳转到下一幅菜单的KeyIndex的值
unsigned int KeyDown; //当前按下了向下按键,按下后需要跳转到下一幅菜单的KeyIndex的值
unsigned int KeyLeft; //当前按下了向左按键,按下后需要跳转到下一幅菜单的KeyIndex的值
unsigned int KeyRight; //当前按下了向右按键,按下后需要跳转到下一幅菜单的KeyIndex的值
unsigned int KeyEnter; //当前按下了确定按键,按下后需要跳转到下一幅菜单的KeyIndex的值
unsigned int KeyEsc; //当前按下了取消按键,按下后需要跳转到下一幅菜单的KeyIndex的值
void (*CurrentOperate)(void); //当前位置下需要执行的函数
}MeunTabStruct;
首先定义了结构体用来记录菜单的位置,以及按键该按键后要跳转的函数位置,KeyIndex变量主要做唯一索引用,用来给每一幅液晶画面一个唯一标识,上下左右确定取消按键目的是为了记录在当前KeyIndex下,按下这些按键会跳转到的下一个KeyIndex的索引值是多少,CurrentOperate函数指针是记录当前KeyIndex索引下需要执行的函数。
大致代码如下:
Menu.h
#ifndef _MENU_H_
#define _MENU_H_
extern void (*KeyFuncPtr)(void);
typedef struct
{
unsigned int KeyState; //当前状态索引号
unsigned int KeyEnter; //按下"确认"键时转向的状态索引号
unsigned int KeyEsc; //按下"取消"键时转向的状态索引号
unsigned int KeyUp; //按下"向上"键时转向的状态索引号
unsigned int KeyDown; //按下"向下"键时转向的状态索引号
unsigned int KeyLeft; //按下向左键时转向的状态索引号
unsigned int KeyRight; //按下向右键时转向的状态索引号
void (*CurrentOperate)(void); //当前状态应该执行的功能操作
}MeunTabStruct;
void DispFirstSectionMenu(void);
void DispSecondSection1Menu(void);
void DispSecondSection2Menu(void);
#endif
Menu.c
#include "Menu.h"
onst MeunTabStruct KeyTab[]=
{
// |-----------> Index
// | Enter
// | | Esc
// | | | up
// | | | | down
// | | | | | left
// | | | | | | right
// | | | | | | | |--->功能函数
// | | | | | | | |
/**************************一级主菜单***************************************/
{0, 5, 0, 4, 1, 0, 0, (*DispFirstSectionMenu)}, //一级菜单功能1
{1, 6, 0, 0, 2, 1, 1, (*DispFirstSectionMenu)}, //一级菜单功能2
{2, 7, 0, 1, 3, 2, 2, (*DispFirstSectionMenu)}, //一级菜单功能3
{3, 8, 0, 2, 4, 3, 3, (*DispFirstSectionMenu)}, //一级菜单功能4
{4, 9, 0, 3, 0, 4, 4, (*DispFirstSectionMenu)}, //一级菜单功能5
/*************************功能1二级菜单*************************/
{5, 5, 0, 5, 5, 5, 5, (*DispSecondSection1Menu)},//功能1二级菜单
/*************************功能2二级菜单*******************************/
{6, 6, 1, 6, 6, 6, 6, (*DispSecondSection2Menu)}, //功能2二级菜单
void DispFirstSectionMenu(void)
{
//液晶屏幕显示处理
}
void DispSecondSection1Menu(void)
{//液晶屏幕显示处理
}
void DispSecondSection2Menu(void)
{//液晶屏幕显示处理
}
void MenuProc(unsigned int KeyValue)
{
case _KEY_NO:
break;
case _ESC_KEY:
LastKeyFuncIndex = KeyFuncIndex; //存取上一次函数的索引
KeyFuncIndex = KeyTab[KeyFuncIndex].KeyEsc;
KeyFuncPtr = KeyTab[KeyFuncIndex].CurrentOperate;//按下取消键后,
(*KeyFuncPtr)();
break;
case _ENT_KEY:
LastKeyFuncIndex = KeyFuncIndex; //存取上一次函数的索引
KeyFuncIndex = KeyTab[KeyFuncIndex].KeyEnter;
KeyFuncPtr = KeyTab[KeyFuncIndex].CurrentOperate;//按下确认键后,
(*KeyFuncPtr)();
break;
case _UP_KEY:
BK = 1;
BKLightCount = 0;
LastKeyFuncIndex = KeyFuncIndex; //存取上一次函数的索引
KeyFuncIndex = KeyTab[KeyFuncIndex].KeyUp;
KeyFuncPtr = KeyTab[KeyFuncIndex].CurrentOperate;//按下向上键后,
(*KeyFuncPtr)();
break;
case _LEFT_KEY:
LastKeyFuncIndex = KeyFuncIndex; //存取上一次函数的索引
KeyFuncIndex = KeyTab[KeyFuncIndex].KeyLeft;
KeyFuncPtr = KeyTab[KeyFuncIndex].CurrentOperate;//按下确认键后,
(*KeyFuncPtr)();
break;
case _DOWN_KEY:
LastKeyFuncIndex = KeyFuncIndex; //存取上一次函数的索引
KeyFuncIndex = KeyTab[KeyFuncIndex].KeyDown;
KeyFuncPtr = KeyTab[KeyFuncIndex].CurrentOperate;//按下向下键后,
(*KeyFuncPtr)();
break;
case _RIGHT_KEY:
LastKeyFuncIndex = KeyFuncIndex; //存取上一次函数的索引
KeyFuncIndex = KeyTab[KeyFuncIndex].KeyRight;
KeyFuncPtr = KeyTab[KeyFuncIndex].CurrentOperate;//按下确认键后,
(*KeyFuncPtr)();
break;
}
main.c
int main()
{
while(1)
{
MenuProc(GetKeyValue());//这里的GetKeyValue是获取按键值的意思,可以参考我的个人博客中关于按键函数的编写
}
}