参考:https://blog.csdn.net/qq_64690561/article/details/135267629
多级菜单的结构
设计多级菜单的目的在于将需要显示的信息分门归类,方便用户筛选。所以在设计菜单时需要根据整个系统的功能和要求来设定菜单的级数,以及各级子菜单的个数。整个多级菜单的拓扑结构为树型结构,主菜单为根节点,子菜单为枝节点,最后一级菜单为叶节点,如图1所示。
循环方式
循环方式的设计思路:预先定义一个包含6个结构元素的结构体、5个字符型和1个指针型。第1个字符变量存放当前界面的索引号;第2个字符变量存放按下“down(向下)”键时需要跳转到的索引号;第3个字符变量存放按下“up(向上)”键时需要跳转到的索引号;第4个字符变量存放按下“enter(进入)”键时需要跳转的索引号;第5个字符变量存放按下“esc(退出)”键时需要跳转的索引号;第6个变量为函数指针变量,存放当前索引号下需要执行的函数的入口地址。
将所有需要显示的界面其所对应的执行函数和按键索引号以结构体的形式列表存储。具体实现如下:
typedef struct{
uchar down_index;
uchar up_index;
uchar enter_index;
uchar esc_index;
void (*operate)();
}Key_index_struct;
假设菜单分3级,共10个界面,则有:
Key_index_struct const Key_tab[10]={
{0, 0, 0, 1, 0,(*main_menu)},
{1, 2, 3, 4, 0,(*sub_menu1)},
{2, 3, 1, 5, 0,(*sub_menu2)},
{3, 1, 2, 7, 0,(*sub_menu3)},
{4, 4, 4, 4, 1,(*sub_menu1_1)},
{5, 6, 6, 5, 2,(*sub_menu2_1)},
{6, 5, 5, 5, 2,(*sub_menu2_2)},
{7, 8, 9, 7, 3,(*sub_menu3_1)},
{8, 9, 7, 8, 3,(*sub_menu3_2)},
{9, 7, 8, 9, 3,(*sub_menu3_3)},
};
void Lcd_display(void){
switch(Key_status){
case enter:
Key_fun=Key_tab[Key_fun].enter_index;
break;
case down:
Key_fun=Key_tab[Key_fun].down_index;
break;
case up:
Key_fun=Key_tab[Key_fun].up_index;
break;
case esc:
Key_fun=Key_tab[Key_fun].esc_index;
break;
default: return;
break;
}
Key_fun_Pt=Key_tab[Key_fun].operate;
(*Key_fun_Pt)();//执行当前按键的操作
}
当微处理器扫描键盘检测到有按键按下时,根据按键按下的类型,返回在当前界面下其所对应的跳转索引号,并执行相应的函数。
由于每个界面的绘制都是由一个独立函数实现的,从循环方式的实现过程中发现,每发生一次按键按下操作都需要重新绘制整个屏幕。如果核心处理器是低速主频的处理器,在界面切换的时候会闪烁。而且,每一个界面都有固定不变的索引号,在增加或删除界面的时候需要重新修改整个列表,降低了程序的可移植性。
#if 1
void (*current_operation_index)(); //执行当前操作函数
uint8_t func_index = 0;
int key_state; //接收当前的按键状态
typedef struct
{
uint8_t current; //当前状态索引号
uint8_t next; //向下按
uint8_t enter; //确定键按
void (*current_operation)(void);//当前状态应该执行的操作
}Menu_table;
//主页面
#define pos_x 24
void meun1_func(void){
OLED_ShowString(pos_x, 0, "1.music", 16);
OLED_ShowString(pos_x, 2, "2.picture", 16);
OLED_ShowString(pos_x, 4, "3.game", 16);
OLED_ShowString(pos_x, 6, "4.back", 16);
}
void meun2_func(void){
OLED_ShowString(pos_x, 0, "0.back", 16);
OLED_ShowString(pos_x, 2, "1.history", 16);
OLED_ShowString(pos_x, 4, "2.dream", 16);
OLED_ShowString(pos_x, 6, "3.love river", 16);
}
void meun3_func(void){
OLED_ShowString(pos_x, 0, "0.back", 16);
OLED_ShowString(pos_x, 2, "1.cat", 16);
OLED_ShowString(pos_x, 4, "2.dog", 16);
OLED_ShowString(pos_x, 6, "3.bird", 16);
}
void meun4_func(void){
OLED_ShowString(pos_x, 0, "0.back", 16);
OLED_ShowString(pos_x, 2, "1.Kings", 16);
OLED_ShowString(pos_x, 4, "2.Yuan God", 16);
OLED_ShowString(pos_x, 6, "3.Play ball", 16);
}
void fun0(void)
{
OLED_Display_128x64(bmp1);
}
void fun1(void)
{
meun1_func();
OLED_ShowString(0, 0, "-> ", 16);
}
void fun2(void)
{
meun1_func();
OLED_ShowString(0, 2, "-> ", 16);
}
void fun3(void)
{
meun1_func();
OLED_ShowString(0, 4, "-> ", 16);
}
void fun4(void)
{
meun1_func();
OLED_ShowString(0, 6, "-> ", 16);
}
void fun5(void)
{
meun2_func();
OLED_ShowString(0, 0, "-> ", 16);
}
void fun6(void)
{
meun2_func();
OLED_ShowString(0, 2, "-> ", 16);
}
void fun7(void)
{
meun2_func();
OLED_ShowString(0, 4, "-> ", 16);
}
void fun8(void)
{
meun2_func();
OLED_ShowString(0, 6, "-> ", 16);
}
void fun9(void)
{
meun3_func();
OLED_ShowString(0, 0, "-> ", 16);
}
void fun10(void)
{
meun3_func();
OLED_ShowString(0, 2, "-> ", 16);
}
void fun11(void)
{
meun3_func();
OLED_ShowString(0, 4, "-> ", 16);
}
void fun12(void)
{
meun3_func();
OLED_ShowString(0, 6, "-> ", 16);
}
void fun13(void)
{
meun4_func();
OLED_ShowString(0, 0, "-> ", 16);
}
void fun14(void)
{
meun4_func();
OLED_ShowString(0, 2, "-> ", 16);
}
void fun15(void)
{
meun4_func();
OLED_ShowString(0, 4, "-> ", 16);
}
void fun16(void)
{
meun4_func();
OLED_ShowString(0, 6, "-> ", 16);
}
Menu_table table[17] =
{
{0, 0, 1, (*fun0)}, //一级界面
{1, 2, 5, (*fun1)}, //二级菜单 1
{2, 3, 9, (*fun2)}, //二级菜单 2
{3, 4, 13, (*fun3)}, //二级菜单 3
{4, 1, 0, (*fun4)}, //二级菜单 Back
{5, 6, 1, (*fun5)}, //三级菜单1 Back
{6, 7, 0, (*fun6)}, //三级菜单 1-1
{7, 8, 0, (*fun7)}, //三级菜单 1-2
{8, 5, 0, (*fun8)}, //三级菜单 1-3
{9, 10, 2, (*fun9)}, //三级菜单2 Back
{10, 11, 0, (*fun10)}, //三级菜单 2-1
{11, 12, 0, (*fun11)}, //三级菜单 2-2
{12, 9, 0, (*fun12)}, //三级菜单 2-3
{13, 14, 3, (*fun13)}, //三级菜单2 Back
{14, 15, 0, (*fun14)}, //三级菜单 3-1
{15, 16, 0, (*fun15)}, //三级菜单 3-2
{16, 13, 0, (*fun16)}, //三级菜单 3-3
};
#endif
//按键事件,根据个人所用芯片不同,自己定义,关键在于按键触发时,及时对状态机进行更新
static void button_timer_handler(uint8_t idx, void const *ctx)
{
ATM_LOG(D, "btn_cnt = %d", btn_cnt);
if(btn_cnt>=2){
ATM_LOG(D, "2 click ");
OLED_Clear();
func_index = table[func_index].enter; //双击表示确定
}
else{
ATM_LOG(D, "1 click ");
OLED_Clear();
func_index = table[func_index].next; //下一个
}
btn_cnt = 0;
printf("func_index:%d\r\n",func_index);
current_operation_index = table[func_index].current_operation;
(*current_operation_index)(); //执行当前操作函数
}