一、 视频展示:
Oled多级菜单展示。
二、结构体、状态机:
这个结构体包含当前索引号,下一个索引号,以及按下确认键将跳转的索引号,如果需要上一步的操作,则需要再添加一个按键,再添加一个标志位。
下面是对状态机的解读:
以一套流程为例,
{1, 2, 5, (*fun1)}这表示,如果当前的索引号为1,那么向下按则则跳到索引2,并执行索引2状态对应的函数,如果按下确定键,则是进入了该类目下一级菜单,来到索引5,执行索引5的函数。
typedef struct
{
uint8_t current; //当前状态索引号
uint8_t next; //向下按
uint8_t enter; //确定键按
void (*current_operation)(void);//当前状态应该执行的操作
}Menu_table;
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
};
三、完整代码:
关于多级菜单的主要代码就在这,对于OLED代码,和按键初始化的代码,自行匹配。
#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)(); //执行当前操作函数
}
四、功能升级
上面的代码仅有“下一个”和“确认”,增加“上一个”的功能,只需要更新结构体、状态机和添加对应按键的功能。
typedef struct
{
uint8_t current; //当前状态索引号
uint8_t up;
uint8_t next; //向下按
uint8_t enter; //确定键按
void (*current_operation)(void);//当前状态应该执行的操作
}Menu_table;
Menu_table table[17] =
{
{0, 0, 0,1, (*fun0)}, //一级界面
{1,4, 2, 5, (*fun1)}, //二级菜单 1
{2,1, 3, 9, (*fun2)}, //二级菜单 2
{3,2, 4, 13, (*fun3)}, //二级菜单 3
{4,3, 1, 0, (*fun4)}, //二级菜单 Back
{5,8,6, 1, (*fun5)}, //三级菜单1 Back
{6,5, 7, 6, (*fun6)}, //三级菜单 1-1
{7,6, 8, 7, (*fun7)}, //三级菜单 1-2
{8,7, 5, 8, (*fun8)}, //三级菜单 1-3
{9,12,10, 2, (*fun9)}, //三级菜单2 Back
{10,9,11, 10, (*fun10)}, //三级菜单 2-1
{11,10,12, 11, (*fun11)}, //三级菜单 2-2
{12,11,9, 12, (*fun12)}, //三级菜单 2-3
{13,16,14, 3, (*fun13)}, //三级菜单3 Back
{14,13,15, 14, (*fun14)}, //三级菜单 3-1
{15,14,16, 15, (*fun15)}, //三级菜单 3-2
{16,15,13, 16, (*fun16)}, //三级菜单 3-3
};