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; //清零按键键值
}

实际运行效果

单片机多级菜单运行效果

作者:此山无老虎

物联沃分享整理
物联沃-IOTWORD物联网 » STM32F051C8T6 LL库实现的单片机多级菜单框架

发表评论