多级菜单
使用方法
-
以下四个函数分别放进各自按键的回调中,点击一次调用相关函数
void menu_up(); void menu_down(); void menu_confirm(); void menu_back();
-
以下四个函数选择性进行重定义,作用为进入末级功能页面后,此时按键不再归菜单驱动接管,需要用户自行定义按键功能,驱动已经定义了确认按键的作用:进行开启或关闭菜单驱动与按键的关联
__attribute__((weak)) void menu_down_refunc(); __attribute__((weak)) void menu_up_refunc(); __attribute__((weak)) void menu_back_refunc(); __attribute__((weak)) void menu_confirm_refunc();
-
原驱动已包含命令行的菜单指示,用户可重定义以下函数进行转为图形化
__attribute__((weak)) void lcd_display_add(Menu_t *menu) { }
-
开启或关闭菜单与按键的关联
void write_menu_status(MENU_STATUS status) { menuStatus = status; }
-
定义进入功能页面后要显示的页面
void write_function_page(uint8_t index)
#pragma once
#include "main.h"
typedef struct MenuItem {
const char *name;
void (*task)();
struct Menu *submenu;
} MenuItem_t;
typedef struct Menu {
const char *name;
MenuItem_t *items;
int item_count;
struct Menu *parent;
} Menu_t;
typedef enum{
MENU_OPEN,
MENU_CLOSE
}MENU_STATUS;
extern MENU_STATUS menuStatus;
extern Menu_t *current_menu;
//显示菜单页面,引出的原因是在初始化菜单之后需要手动显示第一个页面
void lcd_display();
//打开或者关闭多级菜单功能
//一般在进入末级菜单后会退出多级菜单框架,然后原有的按钮也会赋予不同的功能,
//因为要用这些按键干别的事而不是控制菜单
void write_menu_status(MENU_STATUS status);
MENU_STATUS read_menu_status();
//在退出多级菜单之后,进入功能操作页面要选择第几个页面
void write_function_page(uint8_t index);
uint8_t read_function_page();
//在退出多级菜单之后,进入功能操作页面,要把原来的按键重新给定义
__attribute__((weak)) void menu_down_refunc();
__attribute__((weak)) void menu_up_refunc();
__attribute__((weak)) void menu_back_refunc();
__attribute__((weak)) void menu_confirm_refunc();
__attribute__((weak)) void lcd_display_add(Menu_t *menu);
#include "menu.h"
#include <stdio.h>
Menu_t *current_menu;
int current_item_index = 0;
MENU_STATUS menuStatus = MENU_OPEN;
uint8_t function_page_index = 0;
void write_function_page(uint8_t index) {
function_page_index = index;
}
uint8_t read_function_page() {
return function_page_index;
}
/*
* [ycp250315]:功能菜单页面启用状态
*/
void write_menu_status(MENU_STATUS status) {
menuStatus = status;
}
MENU_STATUS read_menu_status() {
return menuStatus;
}
__attribute__((weak)) void menu_down_refunc() {
}
__attribute__((weak)) void menu_up_refunc() {
}
__attribute__((weak)) void menu_back_refunc() {
}
__attribute__((weak)) void menu_confirm_refunc() {
write_menu_status(MENU_OPEN);
write_function_page(0);
lcd_display();
}
__attribute__((weak)) void lcd_display_add(Menu_t *menu) {
}
void lcd_display() {
printf("\n=== %s ===\n", current_menu->name);
for (int i = 0; i < current_menu->item_count; i++) {
printf("%s %s",
(i == current_item_index) ? ">" : " ",
current_menu->items[i].name);
// 显示子菜单提示
if (current_menu->items[i].submenu) {
printf(" →");
}
printf("\n");
}
printf("-----------------\n");
lcd_display_add(current_menu);
}
void display_menu() {
lcd_display();
}
void menu_up() {
if (read_menu_status() == MENU_OPEN) {
if (current_item_index > 0) {
current_item_index--;
}
display_menu();
} else if (read_menu_status() == MENU_CLOSE) {
menu_up_refunc();
}
}
void menu_down() {
if (read_menu_status() == MENU_OPEN) {
if (current_item_index < current_menu->item_count - 1) {
current_item_index++;
} else {
current_item_index = 0;
}
display_menu();
} else if (read_menu_status() == MENU_CLOSE) {
menu_down_refunc();
}
}
void menu_confirm() {
if (read_menu_status() == MENU_OPEN) {
MenuItem_t *current_item = ¤t_menu->items[current_item_index];
if (current_item->task) {
current_item->task();
}
if (current_item->submenu) {
printf("进入 %s\n", current_item->submenu->name);
current_menu = current_item->submenu;
current_item_index = 0;
}
lcd_display();
} else if (read_menu_status() == MENU_CLOSE) {
//write_menu_status(MENU_OPEN);
menu_confirm_refunc();
}
}
void menu_back() {
if (read_menu_status() == MENU_OPEN) {
if (current_menu->parent) {
printf("返回 %s\n", current_menu->parent->name);
current_menu = current_menu->parent;
current_item_index = 0;
}
lcd_display();
} else if (read_menu_status() == MENU_CLOSE) {
menu_back_refunc();
}
}
使用方法
#include "default_task_menu.h"
#include "main.h"
#include "menu.h"
#include "stdio.h"
#include "fakertos.h"
#include "hal_gpio.h"
//结构: page -> page_items -> son page
/*
* [ycp250315]:页面
* 命名方式:层级_序号
*/
Menu_t root;
Menu_t page1_1;
Menu_t page1_2;
Menu_t page1_3;
Menu_t page1_4;
Menu_t page2_1;
Menu_t page2_2;
void change_baud(){
write_menu_status(MENU_CLOSE);
write_function_page(1);
printf("enter func page\r\n");
}
/*
* [ycp250315]:页面的子页面
* 命名方式:层级_序号
*/
MenuItem_t root_items[4] = {
{"lcd_display_1", NULL, NULL},
{"lcd_display_2", NULL, NULL},
{"lcd_display_3", NULL, NULL},
{"settings", NULL, &page1_4}
};
MenuItem_t page1_4_items[2] = {
{"commu", NULL, &page2_1},
{"testx", change_baud, NULL}
};
MenuItem_t page2_1_items[2] = {
{"change_baud", change_baud, NULL},
{"change_stop", NULL, NULL}
};
void init_menus() {
/*
* [ycp250315]:根页面
*/
root = (Menu_t) {
"root",
root_items,
sizeof(root_items)/sizeof(root_items[0]),
NULL
};
/*
* [ycp250315]:1级页面
*/
page1_1 = (Menu_t) {
"display1",
NULL,
0,
&root
};
page1_2 = (Menu_t) {
"display2",
NULL,
0,
&root
};
page1_3 = (Menu_t) {
"display3",
NULL,
0,
&root
};
page1_4 = (Menu_t) {
"settings",
page1_4_items,
sizeof(page1_4_items)/sizeof(page1_4_items[0]),
&root
};
/*
* [ycp250315]:2级页面
*/
page2_1 = (Menu_t) {
"communication",
page2_1_items,
sizeof(page2_1_items)/sizeof(page2_1_items[0]),
&page1_4
};
page2_2 = (Menu_t) {
"test_xxx",
NULL,
0,
&page1_4
};
}
//放在初始化
void Default_Menu_Ev_Init() {
init_menus();
current_menu = &root;
lcd_display();
}
int blink(){
hal_gpio_trig_level(HAL_GPIOA,5);
return 0;
}
//这里放到循环函数里面,100ms循环一次,
//通过别处调用write_function_page(x),这里的页面就会改变
void Default_menu_Ev_Process() {
switch (read_function_page()) {
case 0:
break;
case 1:
rtosTask_A(blink,1000,millis());
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
}
}
层级结构
License:
CC BY 4.0