STM32F051C8T6 LL库实现的单片机多级菜单框架
实现原理:
用链表的形式,在结构体中链接上级菜单和下级菜单。
菜单模块的定义:
菜单结构体:
struct _sMenuList{
uint16_t num; //当前菜单功能项总数
uint8_t title[MENU_TITLE_MAX_SIZE]; //标题名称
uint8_t label[MENU_LABEL_MAX_SIZE]; //项目名称
uint8_t type; //功能项类型
void (*fun)(void); //功能项函数
struct _sMenuList *next; //下一级菜单
struct _sMenuList *prev; //上一级菜单
};
菜单运行结构体:
struct _Menu{
struct _sMenuList *current; //当前菜单
uint16_t index[MENU_LEVEL_NUM]; //各级索引,最大10级
uint16_t level; //当前级数
};
菜单页面显示:
//刷新页面
void MenuDisplay(struct _sMenuList *buf, uint16_t index)
{
uint16_t menu_num = (buf+index)->num; //获取当前菜单项目数量
uint16_t i=0;
uint16_t limit=3; //当前显示行数
//index在最后一页
if((index/MENU_PAGE_LIST_SIZE) == ((menu_num-1)/MENU_PAGE_LIST_SIZE))
{
if(menu_num > MENU_PAGE_LIST_SIZE)
limit = menu_num%MENU_PAGE_LIST_SIZE;
else
limit = menu_num;
}
else //其他情况
{
limit = MENU_PAGE_LIST_SIZE;
}
LcdClearScreen(); //清屏
LcdDisplayCGROM(LcdLineAddrBuf[0],(buf+index)->title); //显示标题
for(i=0; i<limit; i++) //显示项目
{
LcdDisplayCGROM(LcdLineAddrBuf[i+1]+1,\
(buf+(index/MENU_PAGE_LIST_SIZE*MENU_PAGE_LIST_SIZE)+i)->label);
}
MenuLineDisplay(index); //显示光标
}
制作实际菜单:
菜单页面数组:
//****menu 1****
const struct _sMenuList Menu1_Main[TEST_MENU1_MAIN_NUM]={
{TEST_MENU1_MAIN_NUM,"-= 一级菜单 =-","1.文件",MENU_TYPE_LIST,0,(struct _sMenuList*)Menu2_File,0,},
{TEST_MENU1_MAIN_NUM,"-= 一级菜单 =-","2.编辑",MENU_TYPE_LIST,0,(struct _sMenuList*)Menu2_Write,0},
{TEST_MENU1_MAIN_NUM,"-= 一级菜单 =-","3.视图",MENU_TYPE_LIST,0,(struct _sMenuList*)Menu2_View,0},
{TEST_MENU1_MAIN_NUM,"-= 一级菜单 =-","4.帮助",MENU_TYPE_LIST,0,(struct _sMenuList*)Menu2_Help,0},
{TEST_MENU1_MAIN_NUM,"-= 一级菜单 =-","5.工程",MENU_TYPE_FUN,0,0,0,},
{TEST_MENU1_MAIN_NUM,"-= 一级菜单 =-","6.调试",MENU_TYPE_FUN,0,0,0},
{TEST_MENU1_MAIN_NUM,"-= 一级菜单 =-","7.工具",MENU_TYPE_FUN,0,0,0},
{TEST_MENU1_MAIN_NUM,"-= 一级菜单 =-","8.内存",MENU_TYPE_FUN,0,0,0},
};
//****menu 2****
const struct _sMenuList Menu2_File[TEST_MENU2_FLIE_NUM]={
{TEST_MENU2_FLIE_NUM,"-= 二级文件 =-","1.打开",MENU_TYPE_LIST,0,(struct _sMenuList*)Menu3_Open,(struct _sMenuList*)Menu1_Main},
{TEST_MENU2_FLIE_NUM,"-= 二级文件 =-","2.关闭",MENU_TYPE_FUN,0,0,(struct _sMenuList*)Menu1_Main},
{TEST_MENU2_FLIE_NUM,"-= 二级文件 =-","3.新建",MENU_TYPE_FUN,0,0,(struct _sMenuList*)Menu1_Main},
{TEST_MENU2_FLIE_NUM,"-= 二级文件 =-","4.保存",MENU_TYPE_FUN,0,0,(struct _sMenuList*)Menu1_Main},
{TEST_MENU2_FLIE_NUM,"-= 二级文件 =-","5.退出",MENU_TYPE_FUN,0,0,(struct _sMenuList*)Menu1_Main},
};
const struct _sMenuList Menu2_Write[TEST_MENU2_WRITE_NUM]={
{TEST_MENU2_WRITE_NUM,"-= 二级编辑 =-","1.修改",MENU_TYPE_FUN,0,0,(struct _sMenuList*)Menu1_Main},
{TEST_MENU2_WRITE_NUM,"-= 二级编辑 =-","2.设置",MENU_TYPE_FUN,0,0,(struct _sMenuList*)Menu1_Main},
};
const struct _sMenuList Menu2_View[TEST_MENU2_VIEW_NUM]={
{TEST_MENU2_VIEW_NUM,"-= 二级视图 =-","1.全屏",MENU_TYPE_FUN,0,0,(struct _sMenuList*)Menu1_Main},
{TEST_MENU2_VIEW_NUM,"-= 二级视图 =-","2.窗口",MENU_TYPE_FUN,0,0,(struct _sMenuList*)Menu1_Main},
{TEST_MENU2_VIEW_NUM,"-= 二级视图 =-","3.最小化",MENU_TYPE_FUN,0,0,(struct _sMenuList*)Menu1_Main},
};
const struct _sMenuList Menu2_Help[TEST_MENU2_HELP_NUM]={
{TEST_MENU2_HELP_NUM,"-= 二级帮助 =-","1.说明",MENU_TYPE_FUN,0,0,(struct _sMenuList*)Menu1_Main},
};
//****menu 3****
const struct _sMenuList Menu3_Open[TEST_MENU3_OPEN_NUM]={
{TEST_MENU3_OPEN_NUM,"-= 三级打开 =-","1.云端",MENU_TYPE_FUN,0,0,(struct _sMenuList*)Menu2_File},
{TEST_MENU3_OPEN_NUM,"-= 三级打开 =-","2.本地",MENU_TYPE_FUN,0,0,(struct _sMenuList*)Menu2_File},
};
按键与菜单操作:
void TestMenuMain(void)
{
uint16_t lastIndex = TestMenu.index[TestMenu.level]; //记录当前索引
if(Key.Key == KEY_NULL) //没有按键,返回
return;
switch (Key.Key)
{
case KEY_UP:
if(--TestMenu.index[TestMenu.level] == 0xffff) //超出上限
{
//设置索引到菜单列表最后
TestMenu.index[TestMenu.level] = TestMenu.current->num-1;
}
//索引超出当前页面,刷新菜单列表
if((TestMenu.index[TestMenu.level]/MENU_PAGE_LIST_SIZE) != (lastIndex/MENU_PAGE_LIST_SIZE))
{
MenuDisplay(TestMenu.current,TestMenu.index[TestMenu.level]);
}
else //索引没有超出当前页面,只更新光标位置
{
MenuLineDisplay(TestMenu.index[TestMenu.level]);
}
break;
case KEY_DOWN:
//索引超出下限
if(++TestMenu.index[TestMenu.level] >= TestMenu.current->num)
{
//设置索引到菜单列表头部
TestMenu.index[TestMenu.level] = 0;
}
//索引超出当前页面,刷新菜单列表
if((TestMenu.index[TestMenu.level]/MENU_PAGE_LIST_SIZE) != (lastIndex/MENU_PAGE_LIST_SIZE))
{
MenuDisplay(TestMenu.current,TestMenu.index[TestMenu.level]);
}
else //索引没有超出当前页面,只更新光标位置
{
MenuLineDisplay(TestMenu.index[TestMenu.level]);
}
break;
case KEY_ESC:
//1级菜单,没有上级菜单,直接返回
if(TestMenu.level == 0)
break;
//将运行指针设置上级菜单
TestMenu.current = (TestMenu.current+TestMenu.index[TestMenu.level])->prev;
TestMenu.level--; //菜单级别--
MenuDisplay(TestMenu.current,TestMenu.index[TestMenu.level]);
break;
case KEY_OK:
//当前菜单类型为列表类型,进入下级菜单
if((TestMenu.current+TestMenu.index[TestMenu.level])->type == MENU_TYPE_LIST)
{
//将运行指针设置下级菜单
TestMenu.current = (TestMenu.current+TestMenu.index[TestMenu.level])->next;
TestMenu.level++; //菜单级别++
TestMenu.index[TestMenu.level]=0; //清零下级菜单索引
MenuDisplay(TestMenu.current,TestMenu.index[TestMenu.level]);
break;
}
//添加菜单类型为功能类型 处理
break;
default:
break;
}
Key.Key = KEY_NULL; //清零按键键值
}
实际运行效果
单片机多级菜单运行效果
作者:此山无老虎