不错哦|多级菜单实现起来就是这么简单!

演示视频

摘要

产品是面向用户的,用户需要的仅仅是功能以及有好的交互界面,多级菜单在其中扮演着重要的角色,有限的按键实现复杂的界面,层层相扣,标志法对于菜单的管理相当费劲,多了自己都会被自己绕晕...下面介绍一种还算不错的多级菜单是实现方式

这个框架本身不复杂,小伙伴们可以直接看源码,有什么问题,可以一起交流

硬件连接

STM32F407ZGT6

ST7789 LCD屏(SPI1,复用开发板NRF24L01接口)

3个按键 PC7、PC8、PC9

代码实现

实现起来比较简单,直接进入代码部分,我使用的是3个按键,功能分别是:上翻(增大数值)、下翻(减小数值)、确认

先来定义一个结构体,结构体中包含了5个成员

1、cur:当前的菜单索引号

2、left:左按键对应的索引号

3、OK:确认件对应的索引号

4、right:右键对应的索引号

5、当前索引对应要执行的菜单界面(或功能)

typedef struct
{
    uint8_t cur;                     //当前索引号
    uint8_t left;                    //left 的索引号
    uint8_t ok;                      //OK 的索引号
    uint8_t right;                   //right 的索引号
    void (*current_operation)(void); //当前执行的函数
} KEY_TABLE;

再定义一个关于按键键值,菜单索引号的结构体:

typedef struct{    uint16_t key_value;    uint8_t key_new[3];    uint8_t key_left;    uint8_t key_right;    uint8_t key_ok;    uint8_t func_index;    uint8_t func_index_last;} Menu_Para;

为每一个子菜单分配上翻、下翻索引值

typedef struct
{
    uint8_t Down_index;
    uint8_t Up_index;

uint8_t Sys_Down_index;
    uint8_t Sys_Up_index;

uint8_t Com_Down_index;
    uint8_t Com_Up_index;

uint8_t Game_Down_index;
    uint8_t Game_Up_index;

uint8_t Sys_DisModeDown_index;
    uint8_t Sys_DisModeUp_index;

uint8_t Sys_NetModeDown_index;
    uint8_t Sys_NetModeUp_index;

} MenuSerch_HandleTypeDef;

利用上面第一个提到的结构体,只需要把对应的索引号填入即可,按键按下根据索引号去查找需要跳转的界面

const KEY_TABLE k_table[] =    {        {0, 2, 3, 1, (*MainMenuTask)}, //0---开机主菜单

        {1, 2, 3, 1, (*MenuSearchDown)}, //1---1级菜单下翻        {2, 2, 3, 1, (*MenuSearchUP)},   //2---1级菜单上翻        {3, 0, 3, 3, (*MenuSelect)},     //3---1级菜单确认

        {4, 0, 4, 4, (*Psd_Auth_Process)}, //4---2级密码认证菜单

        {5, 7, 8, 6, (*SysMenu_Process)},   //5---2级系统设置菜单,未使用        {6, 7, 8, 6, (*Sys_SearchDown)},    //6---2级系统设置下翻        {7, 7, 8, 6, (*Sys_SearchUP)},      //7---2级系统设置上翻        {8, 7, 8, 6, (*SubMenu_SysSelect)}, //8---2级系统设置菜单确认

        {9, 11, 12, 10, (*ComMenu_Process)},   //9---1级通讯菜单明细显示,未使用        {10, 11, 12, 10, (*Com_SearchDown)},   //10---2级通讯设置下翻        {11, 11, 12, 10, (*Com_SearchUP)},     //11---2级通讯设置上翻        {12, 3, 12, 12, (*SubMenu_ComSelect)}, //12---1级通讯菜单选择确认

        {13, 15, 3, 14, (*GamesMenu_Process)},   //13---2级系统设置菜单,未使用        {14, 15, 3, 14, (*Game_SearchDown)},    //14---2级系统设置下翻        {15, 15, 3, 14, (*Game_SearchUP)},      //15---2级系统设置上翻        {16, 3, 8, 6, (*SubMenu_GameSelect)}, //16---2级系统设置菜单确认

        {17, 18, 5, 17, (*Current_RangeDown)}, //17--3级系统设置电流范围设置--减小电流范围        {18, 18, 5, 17, (*Current_RangeUP)},   //18---3级系统设置电流范围设置--增大电流范围

        {19, 20, 5, 19, (*Voltage_RangeDown)}, //19--3级系统设置电流范围设置--减小电流范围        {20, 20, 5, 19, (*Voltage_RangeUP)},   //20---3级系统设置电流范围设置--增大电流范围

        {21, 22, 5, 21, (*DisMode_SearchDown)}, //21--3级系统设置灯光模式下翻        {22, 22, 5, 21, (*DisMode_SearchUP)},   //22---3级系统设置灯光模式上翻

        {23, 24, 5, 23, (*Lightness_RangeDown)}, //23--3级系统设置灯光亮度设置--增大亮度        {24, 24, 5, 23, (*Lightness_RangeUP)},   //24---3级系统设置灯光亮度设置--减小亮度

        {25, 26, 5, 25, (*Temperature_RangeDown)}, //25--3级系统设置温度设置--增大温度        {26, 26, 5, 25, (*Temperature_RangeUP)},   //26---3级系统设置温度设置--减小温度

        {27, 28, 5, 27, (*NetMode_SerchDown)},   //27--3级系统设置网络模式选择        {28, 28, 5, 27, (*NetMode_SerchUP)},     //28---3级系统设置网络模式选择};

菜单要显示的内容,我们定义为指针数组形式

const uint8_t *MenuItem[MainItemsCount] = {
    'Psd Authen',
    'Sys Settings',
    'Com Settings',
    'User Games',
    'About Me',
    'About Version',
};

const uint8_t *Com_SubItem[Sys_SubMenuItems] = {
    'Slave Addr:',
    'BoundRate:',
    'reserved',
    'reserved',
    'reserved',
    'reserved',
};

const uint8_t *Game_SubItem[Game_SubMenuItems] = {
    'Greedy snake ',
    'Super Marie ',
    'Sokoban',
    'Double Dragon',
    'Metal Slug ',
    'Contra Force',
};

...

按键部分:

#define KEYPINS GPIOC->IDR

KEY_DATA key_LOR[3];Menu_Para Menu_Parameter;

unsigned int key_read(void){    unsigned char i;

    Menu_Parameter.key_value = KEYPINS;    Menu_Parameter.key_value >>= 6;    Menu_Parameter.key_value &= 0x000000E;    for (i = 0; i < 3; i++)    {        if ((Menu_Parameter.key_value & (0x02 << i)) == 0) //有按键按下        {             key_LOR[i].T_press++;

            if (key_LOR[i].T_press > 600)  //长按键            {

            }        }        else        {            if (key_LOR[i].T_press > 100)            {                key_LOR[i].press = 1; //short press                Menu_Parameter.key_new[i] = 1;                key_to_lcd();            }

            key_LOR[i].T_press = 0;            key_LOR[i].L_press = 0;            key_LOR[i].press = 0;        }    }    return 1;}

然后是查询按键按下对应的索引值处理:

void key_to_lcd(void)
{
    Menu_Parameter.key_left = Menu_Parameter.key_new[0];
    Menu_Parameter.key_ok = Menu_Parameter.key_new[1];
    Menu_Parameter.key_right = Menu_Parameter.key_new[2];

memset(Menu_Parameter.key_new, 0, 3);

if (Menu_Parameter.key_left == 1)
    {
        Menu_Parameter.func_index = k_table[Menu_Parameter.func_index].left;
    }
    if (Menu_Parameter.key_right == 1)
    {
        Menu_Parameter.func_index = k_table[Menu_Parameter.func_index].right;
    }
    if (Menu_Parameter.key_ok == 1)
    {
        Menu_Parameter.func_index = k_table[Menu_Parameter.func_index].ok;
    }
    if (Menu_Parameter.func_index_last == 1 ||
        Menu_Parameter.func_index_last == 2 ||
        Menu_Parameter.func_index_last == 6 ||
        Menu_Parameter.func_index_last == 7 ||
        Menu_Parameter.func_index_last == 10 ||
        Menu_Parameter.func_index_last == 11 ||
      Menu_Parameter.func_index_last == 14 ||
        Menu_Parameter.func_index_last == 15 ||
        Menu_Parameter.func_index_last == 17 ||
        Menu_Parameter.func_index_last == 18 ||
        Menu_Parameter.func_index_last == 19 ||
        Menu_Parameter.func_index_last == 20 ||
        Menu_Parameter.func_index_last == 21 ||
        Menu_Parameter.func_index_last == 22 ||
        Menu_Parameter.func_index_last == 23 ||
        Menu_Parameter.func_index_last == 24 ||
        Menu_Parameter.func_index_last == 25 ||
        Menu_Parameter.func_index_last == 26 ||
        Menu_Parameter.func_index_last == 27 ||
        Menu_Parameter.func_index_last == 28

)
    {
        current_operation_index = k_table[Menu_Parameter.func_index].current_operation;
        (*current_operation_index)();

Menu_Parameter.func_index_last = Menu_Parameter.func_index;
    }
    else if (Menu_Parameter.func_index != Menu_Parameter.func_index_last)
    {
        current_operation_index = k_table[Menu_Parameter.func_index].current_operation;
        (*current_operation_index)();

Menu_Parameter.func_index_last = Menu_Parameter.func_index;
    }

//UI_printf('\r\n func_index_last is:%d', Menu_Parameter.func_index_last);
    //UI_printf('\r\n Down_index is:%d', MenuSerchPara.Down_index);
}

有个需要注意的地方,我们一般使用的时候,在一个仅仅是显示信息,即不做其他操作,显示一些版本信息的时候,我们按除了返回键是不想让他响应的,否则,按一下就会刷新一次,体验很不好,所以需要做一下处理,简单来说就是,两次的索引值相同,就不再响应

但是一些情况下,我们是一直处于同一个界面,比如上翻、下翻,始终是处于同一个父界面,这时候需要响应以完成子项选择

    if (Menu_Parameter.func_index_last == 1 ||        Menu_Parameter.func_index_last == 2 ||        Menu_Parameter.func_index_last == 6 ||        Menu_Parameter.func_index_last == 7 ||        Menu_Parameter.func_index_last == 10 ||        Menu_Parameter.func_index_last == 11 ||      Menu_Parameter.func_index_last == 14 ||        Menu_Parameter.func_index_last == 15 ||        Menu_Parameter.func_index_last == 17 ||        Menu_Parameter.func_index_last == 18 ||        Menu_Parameter.func_index_last == 19 ||        Menu_Parameter.func_index_last == 20 ||        Menu_Parameter.func_index_last == 21 ||        Menu_Parameter.func_index_last == 22 ||        Menu_Parameter.func_index_last == 23 ||        Menu_Parameter.func_index_last == 24 ||        Menu_Parameter.func_index_last == 25 ||        Menu_Parameter.func_index_last == 26 ||        Menu_Parameter.func_index_last == 27 ||        Menu_Parameter.func_index_last == 28

    )    {        current_operation_index = k_table[Menu_Parameter.func_index].current_operation;        (*current_operation_index)();

        Menu_Parameter.func_index_last = Menu_Parameter.func_index;    }

其实整个框架就这么多,很简单,编写程序主要是理清思绪,界面的切换、转换索引相连,就可以很轻松的编写多级菜单了

(0)

相关推荐