[GUI] STM32简易多级菜单(数组查表法)

[复制链接]
 楼主| jf101 发表于 2023-12-23 12:42 | 显示全部楼层 |阅读模式
单片机开发中,有时会用到屏幕来显示内容,当需要逐级显示内容时,就需要使用多级菜单的形式了。
一、多级菜单
多级菜单的实现,大体分为两种设计思路:

通过双向链表实现
通过数组查表实现
总体思路都是把菜单的各个界面联系起来,可以从上级菜单跳到下级菜单,也可从下级菜单返回上级菜单。

数组查表的方式比较简单,易于理解,本篇就来使用数组查表发在RVB2601上实现多级菜单的显示。

 楼主| jf101 发表于 2023-12-23 12:45 | 显示全部楼层
二、代码实现
1、数组查表

首先需要定义一个结构体:

  1. typedefstruct
  2. {
  3.         uchar current;
  4.         uchar up;//向上翻索引号
  5.         uchar down;//向下翻索引号
  6.         uchar enter;//确认索引号
  7.         void (*current_operation)();
  8. } key_table;

current:当前页面的索引号

up:按下“向上翻“按钮后要跳转到的页面索引号
down:按下“向下翻“按钮后要跳转到的页面索引号
enter:按下“确认“按钮后要跳转到的页面索引号
current_operation:当前页面的索引号要执行的显示函数,这是一个函数指针
注意:对于菜单显示的操作,用到了3个按键,分别是向下、向下和确认,如果单片机上的IO资源较为紧张,还可以把“向上翻”按钮省去,只通过“向下翻”按钮来实现循环访问,对应的结构体也可以去掉该成员。

然后定义一个表,用来定义各个页面间如何跳转。

  1. key_table table[30]=
  2. {
  3.         //第0层
  4.         {0,0,0,1,(*fun_0)},
  5.        
  6.     //第1层
  7.         {1,4,2, 5,(*fun_a1)},
  8.         {2,1,3, 9,(*fun_b1)},
  9.         {3,2,4,13,(*fun_c1)},               
  10.         {4,3,1, 0,(*fun_d1)},
  11.        
  12.     //第2层
  13.         {5,8,6,17,(*fun_a21)},                                       
  14.         {6,5,7,18,(*fun_a22)},
  15.         {7,6,8,19,(*fun_a23)},                                                                       
  16.         {8,7,5, 1,(*fun_a24)},
  17.        
  18.         { 9,12,10,20,(*fun_b21)},                                       
  19.         {10, 9,11,21,(*fun_b22)},
  20.         {11,10,12,22,(*fun_b23)},                                                                       
  21.         {12,11, 9, 2,(*fun_b24)},
  22.        
  23.         {13,16,14,23,(*fun_c21)},                                       
  24.         {14,13,15,24,(*fun_c22)},                                                       
  25.         {15,14,16,25,(*fun_c23)},                                                       
  26.         {16,15,13, 3,(*fun_c24)},
  27.        
  28.     //第3层
  29.         {17,17,17,5,(*fun_a31)},                                               
  30.         {18,18,18,6,(*fun_a32)},                                       
  31.         {19,19,19,7,(*fun_a33)},
  32.        
  33.         {20,20,20, 9,(*fun_b31)},                                                       
  34.         {21,21,21,10,(*fun_b32)},                                               
  35.         {22,22,22,11,(*fun_b33)},
  36.        
  37.         {23,23,23,13,(*fun_c31)},                                               
  38.         {24,24,24,14,(*fun_c32)},                                               
  39.         {25,25,25,15,(*fun_c33)},                                                               
  40. };

这里解释一下该表是如何工作的:
此表,表示了4级菜单的显示关系(注意第0层其实只是一个欢迎界面)
第一层菜单,只有4个选项,因此这里只列了4行(注意最后一个选项用作返回上一级,无实际内容含义)
第二层菜单,就是对第一层菜单中的3个实际的选项进行进一步的介绍,每种介绍又有4个子项(注意最后一个选项也是用作返回上一级,无实际内容含义),因此,这里的第二层菜单列了3x4=12行
第三层菜单,又是对第二层菜单中的子项进行进一步的介绍(3个分类,每类有3个子项),所以第三层菜单列了9行
注意数组中每一行的第1个数组,是索引号,先列举一个实际的例子进行分析:
1png.png

上图就是一个实际的4级菜单要显示的内容,每个条目前,标记了索引号(0~25),即对应数组在定义的索引号。
比如数组关于第0层和第1层的定义:

  1. //第0层
  2. {0,0,0,1,(*fun_0)},

  3. //第1层
  4. {1,4,2, 5,(*fun_a1)},
  5. {2,1,3, 9,(*fun_b1)},
  6. {3,2,4,13,(*fun_c1)},               
  7. {4,3,1, 0,(*fun_d1)},

先看第一行:索引是0,显示欢迎界面;后面的两个0表示此时按“上翻”和“下翻”无效,继续显示欢迎界面;再后面的1表示按下“确认”按钮后,跳转到索引1处(即显示第1级目录,且指向第1级的第1个子项);最后是此索引要显示的具体内容,fun_0就是控制屏幕显示欢迎界面

再看第二行:索引是1,显示第1级目录,且指向第1级的第1个子项(天气);后面的4表示此时按“上翻”跳转到索引4,即显示第1级目录,且指向第1级的第4个子项(Return);再后面的2表示此时按“下翻”跳转到索引2,即显示第1级目录,且指向第1级的第2个子项(音乐);再后面的5表示按下“确认”按钮后,跳转到索引5处(即显示第2级目录,且指向第2级的第1个子项-杭州);最后是此索引要显示的具体内容,fun_a1就是控制屏幕显示第1级目录,且指向第1级的第1个子项(天气)
其它行的含义与之类似
通过分析,不难发现,这些数组在空间上的关系:
2.png

对于菜单的最底层,因为没有上翻和下翻的功能需求,因此每行的前3个数字都是当前的索引号:

  1. //第3层
  2. {17,17,17,5,(*fun_a31)},                                               
  3. {18,18,18,6,(*fun_a32)},                                       
  4. {19,19,19,7,(*fun_a33)},

  5. {20,20,20, 9,(*fun_b31)},                                                       
  6. {21,21,21,10,(*fun_b32)},                                               
  7. {22,22,22,11,(*fun_b33)},

  8. {23,23,23,13,(*fun_c31)},                                               
  9. {24,24,24,14,(*fun_c32)},                                               
  10. {25,25,25,15,(*fun_c33)},


 楼主| jf101 发表于 2023-12-23 12:46 | 显示全部楼层
2、具体的显示函数
对于函数要显示的具体内容,根据自己的实现需要显示即可。

这里我使用的是OLED屏幕,借助U8g2图形库进行内容显示,以下是部分显示示例:

  1. /*********第1层***********/
  2. void fun_a1()   
  3. {       
  4.         u8g2_DrawStr(&u8g2,0,16,">");
  5.         u8g2_DrawStr(&u8g2,16,16,"[1]Weather");
  6.         u8g2_DrawStr(&u8g2,16,32,"[2]Music");
  7.         u8g2_DrawStr(&u8g2,16,48,"[3]Device Info");
  8.         u8g2_DrawStr(&u8g2,16,64,"<--");                                                                                                                                                                       
  9. }

  10. void fun_b1()   
  11. {       
  12.         u8g2_DrawStr(&u8g2,0,32,">");
  13.         u8g2_DrawStr(&u8g2,16,16,"[1]Weather");
  14.         u8g2_DrawStr(&u8g2,16,32,"[2]Music");
  15.         u8g2_DrawStr(&u8g2,16,48,"[3]Device Info");
  16.         u8g2_DrawStr(&u8g2,16,64,"<--");                                                                                                                                                                               
  17. }

  18. void fun_c1()     
  19. {       
  20.         u8g2_DrawStr(&u8g2,0,48,">");
  21.         u8g2_DrawStr(&u8g2,16,16,"[1]Weather");
  22.         u8g2_DrawStr(&u8g2,16,32,"[2]Music");
  23.         u8g2_DrawStr(&u8g2,16,48,"[3]Device Info");
  24.         u8g2_DrawStr(&u8g2,16,64,"<--");                                                                                                                                                                       
  25. }

  26. void fun_d1()     
  27. {       
  28.         u8g2_DrawStr(&u8g2,0,64,">");
  29.         u8g2_DrawStr(&u8g2,16,16,"[1]Weather");
  30.         u8g2_DrawStr(&u8g2,16,32,"[2]Music");
  31.         u8g2_DrawStr(&u8g2,16,48,"[3]Device Info");
  32.         u8g2_DrawStr(&u8g2,16,64,"<--");                                                                                                                                                                                       
  33. }

  34. /*********第2层***********/
  35. void fun_a21()     
  36. {       
  37.         u8g2_DrawStr(&u8g2,0,16,">");
  38.         u8g2_DrawStr(&u8g2,16,16,"* HangZhou");
  39.         u8g2_DrawStr(&u8g2,16,32,"* BeiJing");
  40.         u8g2_DrawStr(&u8g2,16,48,"* ShangHai");
  41.         u8g2_DrawStr(&u8g2,16,64,"<--");                                                                                                                                                                               
  42. }
  43. //省略...


 楼主| jf101 发表于 2023-12-23 12:46 | 显示全部楼层
3、按键切换页面

页面的切换,这里里简单的按钮轮询为例,比如初始显示欢迎界面的状态下,按下不同按键后,通过数组查表,确定要跳转到的索引号,然后根据索引号,通过函数指针执行索引号对应的显示函数,即实现了一次页面切换。

然后,就是在新的页面状态,收到下一个按钮指令,再切换到下一个显示状态。

  1. void (*current_operation_index)(); //定义一个函数指针

  2. //...
  3. while(1)
  4. {
  5.     if((KEY1==0)||(KEY2==0)||(KEY3==0))
  6.     {
  7.         delay_ms(10);//消抖
  8.         if(KEY1==0)
  9.         {
  10.             func_index = table[func_index].up;    //向上翻
  11.             while(!KEY1);//松手检测
  12.         }
  13.         if(KEY2==0)
  14.         {
  15.             func_index = table[func_index].down;    //向下翻
  16.             while(!KEY2);
  17.         }
  18.         if(KEY3==0)
  19.         {
  20.             func_index = table[func_index].enter;    //确认
  21.             while(!KEY3);
  22.         }
  23.     }       

  24.     if (func_index != last_index)
  25.     {
  26.         current_operation_index = table[func_index].current_operation;

  27.         u8g2_ClearBuffer(&u8g2);
  28.         (*current_operation_index)();//执行当前操作函数
  29.         u8g2_SendBuffer(&u8g2);

  30.         last_index = func_index;
  31.     }
  32. }

中国龙芯CDX 发表于 2023-12-28 10:42 | 显示全部楼层
STM32简易多级菜单讲解的很详细
您需要登录后才可以回帖 登录 | 注册

本版积分规则

255

主题

1848

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部

255

主题

1848

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部