在单片机中如果需要一个可以便于维护的菜单程序,那么设计一个便于封装的菜单数据结构就是必不可少的了。 最近观看B站UP主有手也不会发布的视频后,发现其写的菜单数据结构尤为好用,这里用于记录,有误之处还望大家指正! 按键采用Multibutton开源框架有兴趣可去GitHub上搜索,也可私信我,我发源码。
struct MenuItem
{
unsigned char menu_cnt;
unsigned char * display_str;
void (*subs)();
struct MenuItem *children_menus;
struct MenuItem *parent_menus;
};
从头到尾依次是:本级菜单所拥有的项目总数,当前项目所需要显示的菜单信息,所需要执行对应的菜单项目的回调函数,当前菜单的子菜单也就是下一级菜单,当前菜单的父菜单也就是上一级菜单。
在这本例移植中我是用了OLED12864进行显示 菜单数据,在此例子中共有两级菜单,每级菜单有三个项目
struct MenuItem parent_menu[] = {
{3, (unsigned char *) "meat", NULL, NULL, NULL},
{3, (unsigned char *) "vegetable", NULL, NULL, NULL},
{3, (unsigned char *) "drinks", NULL, NULL, NULL},
};
struct MenuItem childeren_menu1[] = {
{3, (unsigned char *) "pork", fun1, NULL, parent_menu},
{3, (unsigned char *) "beef", fun2, NULL, parent_menu},
{3, (unsigned char *) "mutton", fun3, NULL, parent_menu},
};
struct MenuItem childeren_menu2[] = {
{3, (unsigned char *) "cabbage", fun4, NULL, parent_menu},
{3, (unsigned char *) "tomato", fun5, NULL, parent_menu},
{3, (unsigned char *) "potato", fun6, NULL, parent_menu},
};
struct MenuItem childeren_menu3[] = {
{3, (unsigned char *) "milk", fun7, NULL, parent_menu},
{3, (unsigned char *) "cola", fun8, NULL, parent_menu},
{3, (unsigned char *) "orange juice", fun9, NULL, parent_menu},
};
菜单显示函数
select_menu
select_item_num
void menu_display(void)
{
unsigned char i;
OLED_SetScreen(0);
for (i = 0;i < select_menu->menu_cnt;i++) {
OLED_ShowString(32, i*12, (select_menu+i)->display_str, FONT_SIZE);
if (i == select_item_num) OLED_ShowString(16, i*12, "->", FONT_SIZE);
}
OLED_RefreshGram();
}
while(1)
{
if (allow_switch) {
allow_switch = 0;
menu_display();
}
}
按键1单击表示本级菜单向下移动,双击表示返回上一级菜单 按键2单击表示本级菜单向上移动,双击表示进入下一级菜单
按键1处理回调函数
void button1_callback_fun(void *btn, uint8_t event)
{
unsigned char i;
if ((event & SINGLE_CLICK) == SINGLE_CLICK) {
LED1_TOGGLE;
allow_switch = 1;
select_item_num++;
if (select_item_num >= select_menu->menu_cnt) select_item_num = 0;
} else if ((event & DOUBLE_CLICK) == DOUBLE_CLICK) {
LED2_TOGGLE;
for (i = 0;i < select_menu->menu_cnt;i++) {
if (i == select_item_num) {
allow_switch = 1;
if ((select_menu->parent_menus != NULL) && (recode != 1)) {
select_menu = (select_menu+select_item_num)->parent_menus;
select_item_num = 0;
}
else if (recode == 1) {
recode = 0;
}
}
}
delay_ms(500);
}
}
按键2处理回调函数
void button2_callback_fun(void *btn, uint8_t event)
{
unsigned char i;
if ((event & SINGLE_CLICK) == SINGLE_CLICK) {
LED2_TOGGLE;
allow_switch = 1;
select_item_num--;
if (select_item_num < 0) select_item_num = select_menu->menu_cnt-1;
} else if ((event & DOUBLE_CLICK) == DOUBLE_CLICK) {
LED3_TOGGLE;
for (i = 0;i < select_menu->menu_cnt;i++) {
if (i == select_item_num) {
allow_switch = 1;
if (select_menu->children_menus != NULL) select_menu = (select_menu+select_item_num)->children_menus;
else if (select_menu->subs != NULL) (select_menu+select_item_num)->subs();
select_item_num = 0;
}
}
delay_ms(500);
}
}
|