你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

一种简易菜单的思考  

[复制链接]
wujique 发布时间:2018-6-24 11:52
声明:本处所说的菜单是用在128*64这种小屏幕的菜单,不是彩屏上的GUI。例如下面这种


1.jpg


作为一个底层驱动工程师,驱动写完了,是要写硬件测试程序的。
这个测试程序,一般给测试部/硬件工程师用来测试硬件,
也会给工厂产线测试准成品。

开始的人偷懒,不想一秒就直接上,所有菜单都这样做,一层套一层
  1. void test_main(void)
  2. {
  3.         while(1)
  4.         {
  5.                 get_key(&key);

  6.                 switch(key)
  7.                 {
  8.                         case 1:
  9.                                 test_key();
  10.                                 break;

  11.                         case 2:
  12.                                 test_lcd();
  13.                                 break;

  14.                         ....

  15.                        
  16.                        
  17.                 }
  18.        
  19.         }
  20. }
复制代码
当菜单越来越多,就开始纠结了,,,,
这样写维护不便,看起来也不美,还浪费程序空间。

作为一个天天看《编程之美》的码农,决定改变现状。
酷狗百度一番,找到了两个参考:
《基于二叉树的多层的液晶菜单界面设计》
《--基于节点编号的通用树状菜单设计方法与实现.pdf》
按照他们的设计方法,鼓捣了一个版本,能用,挺好,但是也纠结。
因为他们用了这种数据结构。对于程序运行来说,非常好。
但是对于我来说,菜单代码是一次性的,但是菜单内容,却是会经常改的。
让我用人脑去维护一个几十个上百个菜单的树,不容易。

想来想去,这些菜单到底有什么不好?对于我来说,为什么不好用?
得出下面结论:
1 管得太宽
菜单,你就管菜单切换就行了,到了最低一层,也就是实际的测试功能了,就不要管了。
菜单切换时类似的,实际测试都是不同的。比如在菜单中,1,是进入第一个菜单。但是在测试中,按键1,功能都不一样。
如果菜单连这个也要管,太累。
2 出发点不一样
上面说到的菜单,出发点都是如何设计一个好的菜单数据结构,让程序快速,高效运行。
我想要的却是一个容易维护的菜单结构,至于菜单的代码有多乱多纠结,没关系,
而且,几百上千个菜单,就算用轮询的方法,也不过几百us吧,没关系。

根据需求,设计了一个菜单结构体
  1. /**
  2. * @brief  菜单对象
  3. */
  4. typedef struct _strMenu
  5. {
  6.     MenuLel l;     ///<菜单等级
  7.     char cha[MENU_LANG_BUF_SIZE];   ///中文
  8.     char eng[MENU_LANG_BUF_SIZE];   ///英文
  9.     MenuType type;  ///菜单类型
  10.     s32 (*fun)(void);  ///测试函数

  11. } MENU;
复制代码

是的,就这么简单,每一个菜单都是这个结构体
用这个结构体填充一个列表,就是我们的菜单了

  1. const MENU EMenuListTest[]=
  2. {
  3.         MENU_L_0,//菜单等级
  4.         "测试程序",//中文
  5.         "test",        //英文
  6.         MENU_TYPE_LIST,//菜单类型
  7.         NULL,//菜单函数,功能菜单才会执行,有子菜单的不会执行

  8.                 MENU_L_1,//菜单等级
  9.                 "LCD",//中文
  10.                 "LCD",        //英文
  11.                 MENU_TYPE_LIST,//菜单类型
  12.                 NULL,//菜单函数,功能菜单才会执行,有子菜单的不会执行
  13.                         MENU_L_2,//菜单等级
  14.                         "VSPI OLED",//中文
  15.                         "VSPI OLED",        //英文
  16.                         MENU_TYPE_FUN,//菜单类型
  17.                         test_oled,//菜单函数,功能菜单才会执行,有子菜单的不会执行

  18.                         MENU_L_2,//菜单等级
  19.                         "I2C OLED",//中文
  20.                         "I2C OLED",        //英文
  21.                         MENU_TYPE_FUN,//菜单类型
  22.                         test_i2coled,//菜单函数,功能菜单才会执行,有子菜单的不会执行


  23.                 MENU_L_1,//菜单等级
  24.                 "声音",//中文
  25.                 "sound",        //英文
  26.                 MENU_TYPE_LIST,//菜单类型
  27.                 NULL,//菜单函数,功能菜单才会执行,有子菜单的不会执行
  28.                         MENU_L_2,//菜单等级
  29.                         "蜂鸣器",//中文
  30.                         "buzzer",        //英文
  31.                         MENU_TYPE_FUN,//菜单类型
  32.                         test_test,//菜单函数,功能菜单才会执行,有子菜单的不会执行

  33.                         MENU_L_2,//菜单等级
  34.                         "DAC音乐",//中文
  35.                         "DAC music",        //英文
  36.                         MENU_TYPE_FUN,//菜单类型
  37.                         test_test,//菜单函数,功能菜单才会执行,有子菜单的不会执行

  38.                         MENU_L_2,//菜单等级
  39.                         "收音",//中文
  40.                         "FM",        //英文
  41.                         MENU_TYPE_FUN,//菜单类型
  42.                         test_test,//菜单函数,功能菜单才会执行,有子菜单的不会执行


  43.                 MENU_L_1,//菜单等级
  44.                 "触摸屏",//中文
  45.                 "tp",        //英文
  46.                 MENU_TYPE_LIST,//菜单类型
  47.                 NULL,//菜单函数,功能菜单才会执行,有子菜单的不会执行
  48.                        
  49.                         MENU_L_2,//菜单等级
  50.                         "校准",//中文
  51.                         "calibrate",        //英文
  52.                         MENU_TYPE_FUN,//菜单类型
  53.                         test_cal,//菜单函数,功能菜单才会执行,有子菜单的不会执行

  54.                         MENU_L_2,//菜单等级
  55.                         "测试",//中文
  56.                         "test",        //英文
  57.                         MENU_TYPE_FUN,//菜单类型
  58.                         test_tp,//菜单函数,功能菜单才会执行,有子菜单的不会执行
  59.                        
  60.                 MENU_L_1,//菜单等级
  61.                 "按键",//中文
  62.                 "KEY",        //英文
  63.                 MENU_TYPE_FUN,//菜单类型
  64.                 test_key,//菜单函数,功能菜单才会执行,有子菜单的不会执行

  65.         /*最后的菜单是结束菜单,无意义*/                       
  66.         MENU_L_0,//菜单等级
  67.         "END",//中文
  68.         "END",        //英文
  69.         MENU_TYPE_NULL,//菜单类型
  70.         NULL,//菜单函数,功能菜单才会执行,有子菜单的不会执行
  71. };
复制代码
这个菜单列表有什么特点和要求呢?
1 需要一个根节点和结束节点
2 子节点必须跟父节点,类似下面结构
-----------------------------------------------
根节点
        第1个1级菜单
                       第1个子菜单
                       第2个子菜单
                       第3个子菜单
        第2个1级菜单
                       第1个子菜单
                                     第1个孙菜单
                                     第2个孙菜单
                       第2个子菜单
                       第3个子菜单
        第3个1级菜单
        第4个1级菜单
        第5个1级菜单
结束节点
------------------------------------------------
第2个1级菜单有3个子菜单,子菜单是2级菜单,其中第1个子菜单下面又有2个孙菜单(3级菜单)。

维护菜单,就是维护这个列表,添加删除修改,非常容易。
那菜单程序怎么样呢?管他呢。
定义好菜单后,通过下面函数运行菜单,
  1. emenu_run(WJQTestLcd, (MENU *)&WJQTestList[0], sizeof(WJQTestList)/sizeof(MENU), FONT_SONGTI_1616, 2);       
复制代码
-第1个参数是在哪个LCD上显示菜单,
-第2个是菜单列表,
-第3个是菜单长度,
-第4个四字体,
-第5则是行间距


注意:
运行这个菜单需要有rtos,因为菜单代码是while(1)的,陷进去就不出来了。
需要有其他线程(TASK)维护系统,例如按键扫描。

代码托管在github:http://github.com/wujique/stm32f407/tree/sw_arch
相关文件:emenu.c/emenu.h/emenu_test.c

当前代码:
1实现了双列菜单,用数字键选择进入下一层。每页最多显示8个菜单(4*4键盘用1-8键)
2 实现了单列菜单,通过上下翻查看菜单,确认键进入菜单。
3 天顶菜单未实现,谁有兴趣可以加上。
3 基于LCD驱动架构,这个简易菜单自适应于多种LCD。

效果如下,有需要的尽管拿去,不用谢。

OLED效果
3.jpg

4.jpg

1.44 128*128 tftlcd 效果
1.jpg

2.jpg

2.8 320*240 tftlcd 效果

5.jpg

6.jpg

路过点个赞呗!


评分

参与人数 3 ST金币 +15 收起 理由
五哥1 + 5 对于常用嵌套编程的人来说这个有新的思路.
toofree + 5 很给力!
子曰好人 + 5 很给力!

查看全部评分

收藏 3 评论19 发布时间:2018-6-24 11:52

举报

19个回答
wujique 回答时间:2019-2-23 23:32:03
l297915958 发表于 2019-2-23 11:12
楼主哥哥,新手32学习,您这个菜单的实验程序有吗,可以分享一下吗,我加你联系方式 ...

在github上有最新的
勿忘心安110 回答时间:2018-6-26 08:41:31
huangxuejia-292 发表于 2018-6-25 09:14
都在玩GUI,这么简陋的东西,不知道还有没有人用。。。。。

大把的人用 很实用 感谢楼主分享
wujique 回答时间:2018-6-25 13:22:55
wolfgang2015 发表于 2018-6-25 11:00
麻烦的东东,继续优化;
潜在问题:返回上级菜单

版主,你是说返回上级菜单有问题吗?
wujique 回答时间:2018-6-25 09:14:02
都在玩GUI,这么简陋的东西,不知道还有没有人用。。。。。
子曰好人 回答时间:2018-6-25 09:38:40
虽然我目前用不上GUI,但是楼主给的思路有一定的启发作用,感谢分享
Bowen 回答时间:2018-6-25 10:01:06
我们产品上就有菜单,和楼主思路类似
wujique 回答时间:2018-6-25 10:03:32
strang 发表于 2018-6-25 10:01
我们产品上就有菜单,和楼主思路类似

有什么好设计提醒提醒我,我优化优化
Bowen 回答时间:2018-6-25 10:07:15
用函数指针做界面,每个函数里都有while(1)
wolfgang 回答时间:2018-6-25 11:00:47
麻烦的东东,继续优化;
潜在问题:返回上级菜单
程序员 回答时间:2018-6-25 11:26:30
很赞!
GKoSon 回答时间:2018-6-25 16:09:28
双向链表 基本都是菜单的路子
l297915958 回答时间:2019-2-23 11:12:59
楼主哥哥,新手32学习,您这个菜单的实验程序有吗,可以分享一下吗,我加你联系方式
西点钟灵毓秀 回答时间:2019-2-23 19:31:56
这个要点赞。
路痴痴路 回答时间:2019-5-30 19:46:03
谢谢楼主收藏
12下一页

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版