很多嵌入式MCU项目会用到LCD菜单显示和按键配合来实现人机交互。但由于MCU资源有限,通常选用的LCD都是小屏幕,无法完整显示全部的菜单项,出于用户体验以及菜单的整体美观,菜单经常需要设计成多级形式。因此,设计上菜单一般都是使用上、下、选择、退出四个按键进行控制。本文正是介绍一种通用的MCU多级菜单框架设计,主要采用查询式菜单列表,代码几乎可以完美应用于各种MCU LCD菜单应用场合。
一、菜单结构体设计
typedef enum {
CONTENT_STR,
CONTENT_IMG,
CONTENT_CURSOR,
CONTENT_NULL,
}content_type_e;
typedef struct {
void* content;
content_type_e type;
uint8_t revert;
uint16_t x;
uint16_t y;
uint16_t max_width;
uint16_t max_height;
}display_info_t;
typedef enum {
MENU_NO_ACT, /*0 */
MENU_PREVIOUS,
MENU_EXIT,
MENU_ENTER,
MENU_NEXT,
MENU_NEXT_ITEM,
MENU_NEXT_LEVEL,
MENU_SELE_ITEM,
MENU_SELE_LEVEL,
MENU_TOP,
MENU_CURRENT,
MENU_PARAM_NEXT, /*9 */
}menu_act_ret_t;
typedef enum {
MENU_LEVEL_0 = 0,
MENU_LEVEL_1,
MENU_LEVEL_2,
MENU_LEVEL_MAX,
}MENU_LEVEL_E;
/*菜单列表索引列举*/
typedef enum {
/*level-1 menu index*/
MENU_IDX_TOP = 0,
/*level-2 menu index*/
MENU_IDX_SECOND_1,
MENU_IDX_SECOND_2,
MENU_IDX_SECOND_3,
MENU_IDX_SECOND_4,
/*level-3 menu index*/
MENU_IDX_THRID_11,
MENU_IDX_THRID_12,
MENU_IDX_THRID_12,
MENU_IDX_THRID_14,
MENU_IDX_THRID_15,
MENU_IDX_THRID_21,
MENU_IDX_THRID_22,
MENU_IDX_THRID_23,
MENU_IDX_THRID_24,
MENU_IDX_THRID_25,
MENU_IDX_THRID_26,
MENU_IDX_THRID_31,
MENU_IDX_THRID_32,
MENU_IDX_THRID_33,
MENU_IDX_THRID_34,
MENU_IDX_THRID_35,
MENU_IDX_THRID_41,
MENU_IDX_THRID_42,
MENU_IDX_THRID_43,
}MENU_IDX_E;
typedef struct str_menu_table {
MENU_IDX_E table_index;
uint8_t select_index;
uint8_t esc_index;
uint32_t max_key_val;
MENU_LEVEL_E level;
bool has_submenu;
menu_act_ret_t (* act)(uint8_t idx);
display_info_t* (* caption)(prt_menu_ctrl_t *menu, menu_act_ret_t act);
}menu_table_t;
菜单表项结构体(struct str_menu_table)定义如上,成员介绍如下:
1.table_index是各个菜单项的索引,用来跳转选择菜单表项(参见后续代码)
2.select_index、esc_index,分别表示在处在该菜单表项时,按选择键和退出键的跳转菜单索引。注意,只有选择和退出可以跳转菜单表项,上下键只会处理显示菜单内容的更新。
3.max_key_val表示该菜单按键值最大值,比如该菜单只有3个子菜单项,max_key_val就是3。
4.level表示该菜单处在哪一级菜单上,退出时需要跳转到上一级菜单需要用到。
5.has_submenu表示该菜单项有没有子菜单项。
6.act函数,可以作为菜单选项选择某项之后,执行保存菜单参数或设置的回调函数。
7.caption函数响应上下按键,根据按键key值更新显示内容,返回的就是需要显示的内容。
typedef void (*menu_display_t)(display_info_t* info);
typedef struct str_menu_control {
MENU_IDX_E curr_idx;
uint8_t key_val;
uint8_t rollback;
uint8_t menu_level;
uint8_t menu_table_size;
MENU_LANGUAGE_E language;
/*Record item for each menu level*/
uint8_t menu_item[16];
menu_display_t display;
uint8_t (* checkmenu)(void);
uint8_t display_buf[512];
display_info_t display_info;
}menu_ctrl_t;
除了菜单表项结构体,还需要一个菜单控制结构体定义如上,该结构体的作用就是在代码中,掌握全部菜单项的控制、跳转以及菜单内容显示。说明如下:
1.curr_idx记录单前菜单表项索引值,表示目前在哪一个菜单表项上。
2.key_val,表示当前按键菜单的上下键按键值,最大不超过该菜单表项的max_key_val,超过就回到0重新开始。代码可以自己定义按上键key_val加1还是减一。我这里使用的是按上键减1,按下键加1。
3.rollback用来指示key_val值要不要翻转,比如加到max_key_val之后要不要回到0重新开始,或者减到0之后,要不要回到max_key_val-1。相应就是菜单项会不会循环跳转。
4.menu_level表示当前处在哪一级菜单中,当按选择键或者退出键时,需要把当前这级菜单的key_val记录到menu_item[]数组中,以便退出时能正确显示上一级菜单的菜单项。
5.menu_item[]数组如上所