搜索
bottom↓
回复: 25

这个“结构体菜单”晕了,还是我自己写个,各位指点一下

  [复制链接]

出0入0汤圆

发表于 2014-5-29 01:53:17 | 显示全部楼层 |阅读模式
如题,看别人的代码,看来看去越看越晕(主要是程序逻辑看不懂),还是自己花了几个小时(我笨),按照自己的理解来写了个程序,采用结构体的菜单,有程序有图有真相,求指点。
功能:
这个菜单程序不会阻塞主循环,运行菜单的同时还可以干别的事情
这个菜单在没有改变内容的时候,是不会刷屏幕的;
只有改变了状态的菜单项才会被刷新,没有被改变的菜单项是不会被刷新的
在进入子菜单之前会保存本级菜单的选择在哪个菜单项上,从子菜单返回的时候还会回到之前的菜单项上
按ESC(伪代码)可以直接返回上级菜单。
按ok键,如果是子菜单,就会进入子菜单,如果不是子菜单,而是要执行的程序,则执行程序,如果都没有,就会返回上级菜单

PS:
代码量相比之前我的土办法真的少了很多,但是内存方面相比我之前的代码却多了很多。。。看来这个结构体菜单不适合大量的菜单。。。尤其是内存吃紧的情况。
就这么简单的几个菜单,竟然吃了我138个字节的内存,我擦!!!!而我之前的代码,一个N个菜单项的菜单(N<4),最少只需要2.1个字节的内存。。。最多255个菜单项,也只需要4.3个字节的内存。

好吧,代码奉上,各位指点一下,那个显示的驱动之类的我就不上传了,见谅!

程序里NULL =0,UINT8=unsigned char



  1. //main.h文件

  2. #ifndef __MAIN_H__
  3. #define __MAIN_H__



  4. #include "keyscan.h"



  5. xdata struct MenuRes
  6. {
  7.         UINT8 MenuItemCount;
  8.         UINT8 *MenuItemString;
  9.         struct MenuRes *ParentMenus;
  10.         struct MenuRes *SubMenus;
  11.         void   (*ExecFunction)();
  12. };

  13. extern xdata struct  MenuRes Menu_MainItems[3];
  14. extern xdata struct  MenuRes Menu_SystemSet[4];
  15. extern xdata struct  MenuRes Menu_TimeSet[3];




  16. void SystemSet1(void);//测试而已
  17. void SystemSet2(void);
  18. void SystemSet3(void);

  19. void TimeSet1(void);
  20. void TimeSet2(void);

  21. void Menu_Item_ChoiceForKey(void);//键盘操作
  22. void Menu_Show_Menu(void);//显示菜单出来

  23. #endif
复制代码


  1. //main.c
  2. #include "Keyscan.h"
  3. #include "stc_spi.h"
  4. #include "uc1701x series.h"
  5. #include "Main.h"

  6. xdata struct  MenuRes Menu_MainItems[3]={
  7. {3,"SYSTEM SET",NULL,Menu_SystemSet,NULL},
  8. {3,"TIME SET",NULL,Menu_TimeSet,NULL},
  9. {3,"结构体菜单?FUCK!",Menu_MainItems,NULL,NULL},
  10. };


  11. xdata struct  MenuRes Menu_SystemSet[4]={
  12. {4,"SCREEN SET",Menu_MainItems,NULL,SystemSet1},
  13. {4,"OLED SET",Menu_MainItems,NULL,SystemSet2},
  14. {4,"FUCK SET",Menu_MainItems,NULL,SystemSet3},
  15. {4,"BACK TO MAIN MENU",Menu_MainItems,NULL,NULL},
  16. };

  17. xdata struct  MenuRes Menu_TimeSet[3]={
  18. {3,"DATE SET",Menu_MainItems,NULL,TimeSet1},
  19. {3,"TIME SET1",Menu_MainItems,NULL,TimeSet2},
  20. {3,"BACK TO MAIN MENU",Menu_MainItems,NULL,NULL},
  21. };



  22. xdata struct {
  23.         UINT8 Select;//当前选择
  24.         UINT8 LastSelect;//上次的选择
  25.         UINT8 RePaint;//是否强制重画菜单,=1强制重画,=0自己决定是否该重画菜单
  26.         UINT8 ParentMenuSelect;//上级菜单在进入子菜单之前,选择的哪个菜单
  27.         struct MenuRes *MainPoint;//当前菜单
  28. }ItemSelect={ 0, 0, 1, 0, Menu_MainItems};



  29. void SystemSet1(void)
  30. {
  31.         ST7565_Clear();//清屏
  32.         ST7565_Paint_MixStr(32,24,"SCREEN SET!",0);
  33.         ST7565_Paint_Push_Data();//将缓冲区的数据写入到Lcd

  34.         //按任意键退出,测试而已,就暂时这样吧
  35.         Key_Scan();//扫描键盘
  36.         while(Key_Trg==0)//等待按一个键
  37.         {
  38.                 Key_Scan();//扫描键盘
  39.         }
  40. }

  41. void SystemSet2(void)
  42. {
  43.         ST7565_Clear();//清屏
  44.         ST7565_Paint_MixStr(32,24,"OLED SET!",0);
  45.         ST7565_Paint_Push_Data();//将缓冲区的数据写入到Lcd
  46.         Key_Scan();//扫描键盘
  47.         while(Key_Trg==0)//等待按一个键
  48.         {
  49.                 Key_Scan();//扫描键盘
  50.         }
  51. }

  52. void SystemSet3(void)
  53. {
  54.         ST7565_Clear();//清屏
  55.         ST7565_Paint_MixStr(32,24,"FUCK!FUCK!!",0);
  56.         ST7565_Paint_Push_Data();//将缓冲区的数据写入到Lcd
  57.         Key_Scan();//扫描键盘
  58.         while(Key_Trg==0)//等待按一个键
  59.         {
  60.                 Key_Scan();//扫描键盘
  61.         }
  62. }


  63. void TimeSet1(void)
  64. {
  65.         ST7565_Clear();//清屏
  66.         ST7565_Paint_MixStr(32,24,"DATE SET",0);
  67.         ST7565_Paint_Push_Data();//将缓冲区的数据写入到Lcd
  68.         Key_Scan();//扫描键盘
  69.         while(Key_Trg==0)//等待按一个键
  70.         {
  71.                 Key_Scan();//扫描键盘
  72.         }
  73. }

  74. void TimeSet2(void)
  75. {       
  76.         ST7565_Clear();//清屏
  77.         ST7565_Paint_MixStr(32,24,"TIME SET",0);
  78.         ST7565_Paint_Push_Data();//将缓冲区的数据写入到Lcd
  79.         Key_Scan();//扫描键盘
  80.         while(Key_Trg==0)//等待按一个键
  81.         {
  82.                 Key_Scan();//扫描键盘
  83.         }
  84. }

  85. //--------------------------------------------------------------------------------------------------------------------------------------------------------------
  86. void Menu_Show_Menu(void)
  87. {
  88.         UINT8 xdata ItemCount;//不知道拿来干啥用的

  89.         if( ItemSelect.RePaint == 1)//如果要强制重画菜单
  90.         {
  91.                 ST7565_Clear();//清屏
  92.                 ST7565_Paint_Rectangle( 0, 0, 64, 128, 1);//先画个框框意思一下
  93.                 ST7565_Paint_HLine(13,1,126,1);//横线
  94.                 ST7565_Paint_MixStr(1,4,"我擦这是结构体的菜单",0);//画出标题,12x12新宋体
  95.         }
  96.        
  97.         for(ItemCount = 0; ItemCount < ItemSelect.MainPoint[ ItemSelect.Select ].MenuItemCount; ItemCount++)//遍历所有的菜单,作为测试,这里只考虑4行菜单条目的情况,超过则不会显示
  98.         {
  99.                 if(ItemSelect.Select == ItemCount || ItemSelect.LastSelect == ItemCount || ItemSelect.RePaint == 1)//一次只能选中一个菜单项
  100.                 {
  101.                         ST7565_Paint_MixStr( ((ItemCount + 1) * 12) + 3, 10, ItemSelect.MainPoint[ ItemCount ].MenuItemString, (ItemSelect.Select == ItemCount) ? 1 : 0 );//显示出来菜单
  102.                 }
  103.         }
  104.         //显示缓存写好了,那么就刷吧。。。

  105.         if(( ItemSelect.Select != ItemSelect.LastSelect ) || ( ItemSelect.RePaint == 1 ))//如果有必要刷新,才会刷屏幕
  106.                 ST7565_Paint_Push_Data();//将缓冲区的数据写入到Lcd

  107.         ItemSelect.LastSelect = ItemSelect.Select;//保存上次的选择
  108.         ItemSelect.RePaint = 0;//强制画过了,就转换到自由模式
  109. }
  110. //--------------------------------------------------------------------------------------------------------------------------------------------------------------
  111. void Menu_Item_ChoiceForKey(void)
  112. {
  113.         Key_Scan();//最土的键盘扫描办法

  114.         if(Key_Trg & Key_Cancel)
  115.         {
  116.                 if(ItemSelect.MainPoint[ 0 ].ParentMenus == NULL)//为空则为根菜单
  117.                 {
  118.                         return;//这里就啥也不干好了
  119.                 }
  120.                 else
  121.                 {//不为空,就返回上一级菜单
  122.                         ItemSelect.MainPoint = ItemSelect.MainPoint[ 0 ].ParentMenus;
  123.                         ItemSelect.Select = ItemSelect.ParentMenuSelect;//上级菜单之前选择的那个菜单项
  124.                         ItemSelect.RePaint = 1;//强制刷新一次
  125.                 }       
  126.         }

  127.         if(Key_Trg & Key_plus)//+++
  128.         {        //以下为偷懒之作
  129.                 ( ItemSelect.Select > ( ItemSelect.MainPoint [ 0 ].MenuItemCount - 2) ) ? ItemSelect.Select = 0 : ItemSelect.Select++;
  130.         }

  131.         if(Key_Trg & Key_Minus)//---
  132.         {        //同上
  133.                 ItemSelect.Select < 1 ? ItemSelect.Select = ( ItemSelect.MainPoint [ 0 ].MenuItemCount - 1) : ItemSelect.Select--;
  134.         }

  135.         if(Key_Trg & Key_Ok)//选中某个菜单,按了确认
  136.         {
  137.                 if(ItemSelect.MainPoint [ ItemSelect.Select ].ExecFunction == NULL)//判断是否有执行函数,如果没有执行函数,表示要进入下一级子菜单或者返回上级菜单
  138.                 {
  139.                         if(ItemSelect.MainPoint[ ItemSelect.Select ].SubMenus == NULL)//判断是否有子菜单,如果也没有子菜单,则肯定是返回上级菜单
  140.                         {        //如果连上级菜单的指针也没有,那尼玛设计者就是NC
  141.                                 ItemSelect.MainPoint = ItemSelect.MainPoint = ItemSelect.MainPoint[ ItemSelect.Select ].ParentMenus;//好吧,就算这个是返回,那么返回上级菜单先
  142.                                 ItemSelect.Select = ItemSelect.ParentMenuSelect;//上级菜单之前选择的那个菜单项
  143.                         }
  144.                         else//如果有子菜单,那么进入子菜单
  145.                         {
  146.                                 ItemSelect.MainPoint = ItemSelect.MainPoint = ItemSelect.MainPoint [ ItemSelect.Select ].SubMenus;//进入子菜单
  147.                                 ItemSelect.ParentMenuSelect = ItemSelect.Select;//进入子菜单之前,先保存本级菜单项的选择在哪个菜单项上
  148.                                 ItemSelect.Select = 0;//好吧,下一级菜单的选择项默认是第一个
  149.                         }
  150.                 }
  151.                 else//如果有执行函数,那么就执行这个函数
  152.                 {
  153.                         ( *ItemSelect.MainPoint[ ItemSelect.Select ].ExecFunction )();//执行之
  154.                 }

  155.                 ItemSelect.RePaint = 1;//强制刷新一次
  156.         }


  157. }
  158. //--------------------------------------------------------------------------------------------------------------------------------------------------------------
  159. void main(void)
  160. {
  161.         //目前就只是128 x 64的OLED屏
  162.         //表示俺这个程序,如果没有必要,他是不会玩命刷屏幕的显示的
  163.         ST7565_Init();
  164.         Key_Time0Init();//键盘消抖定时器

  165.         while(1)
  166.         {
  167.                 Menu_Item_ChoiceForKey();//键盘的操作
  168.                 Menu_Show_Menu();//显示菜单出来
  169.                 //其实以上2个函数可以写在一个函数里面,这样就不用搞那个全局的结构体ItemSelect了
  170.                 //非阻塞式,这里还可以干别的事情
  171.         }
  172.        
  173. }
  174. //--------------------------------------------------------------------------------------------------------------------------------------------------------------
复制代码


图片很慢,在编辑,一会上传,手机照的,OLED的小屏幕,很难照清楚,见谅!

阿莫论坛20周年了!感谢大家的支持与爱护!!

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出0入0汤圆

 楼主| 发表于 2014-5-29 02:01:53 | 显示全部楼层
终于传上来了





本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入8汤圆

发表于 2014-5-29 08:15:43 | 显示全部楼层
厉害        

出0入0汤圆

发表于 2014-5-29 08:23:00 | 显示全部楼层
FUCK

出0入0汤圆

发表于 2014-5-29 08:34:20 | 显示全部楼层
FUCK 淫得一个好菜单

出0入0汤圆

发表于 2014-5-29 09:19:37 | 显示全部楼层
自己弱爆了。

出0入0汤圆

发表于 2014-5-29 10:02:22 | 显示全部楼层
这么好的FCUK菜单,顶起。
头像被屏蔽

出0入0汤圆

发表于 2014-5-29 10:13:09 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽

出0入0汤圆

 楼主| 发表于 2014-5-29 12:43:56 | 显示全部楼层
newbie 发表于 2014-5-29 10:13
只是ItemSelect 是ram数据

struct MenuRes 的菜单信息不要放在xdata,放在flash区就节省不少ram了。 ...

明白您的意思了,下午我弄一下试试。。。谢谢

出0入0汤圆

 楼主| 发表于 2014-5-29 13:11:48 | 显示全部楼层
本帖最后由 lswhome 于 2014-5-29 13:22 编辑

测试了下,把xdata 改成code,这样只占用了8个字节。。。结构体7字节,一个变量,共计8个字节。。。不错,如果这样算是一个结构体的菜单的话,也就是说,我也知道怎么弄了?
剩下的工作就是要弄成无论多少个菜单项和多少个子菜单,都无需改变程序,只需改变结构体的内容,就都可以自动适应,还有就是菜单界面的美化以及图标啊标题啊动画啊之类的上面下功夫了。。。
我认为同样功能的一个产品,友好的漂亮的操作方便的人机界面会比不好的要更加吸引人。

编译结果:

Program Size: data=43.3 xdata=1032 const=1971 code=3505

菜单使用的内存都在xdata里,显示缓存占用了1024BYTE,1032-1024=8,所以只占用了8个字节,相比我之前的土办法,果然节省了内存以及代码空间。。。非常不错!

顺便求一个键盘的扫描算法,我用的太土了,貌似还是N年前的算法。无需贴程序也可以,告诉我思路就行,当然有代码更好。

出0入10汤圆

发表于 2014-5-29 16:05:42 | 显示全部楼层
不错,mark一下
菜单的我都收藏一下

出0入0汤圆

发表于 2014-5-29 16:16:33 | 显示全部楼层
呵呵,这菜单有意思。

出0入0汤圆

发表于 2014-5-29 16:22:42 | 显示全部楼层
mark。。。。学习一下

出0入264汤圆

发表于 2014-5-29 16:41:05 | 显示全部楼层
恭喜迈出第一步,真正的麻烦还在后面,就是具体的设置界面的处理。
另外按键的处理,使用状态机方式,用状态机 为关键字搜索本坛。

出0入0汤圆

发表于 2014-5-29 16:51:48 | 显示全部楼层
while(Key_Trg==0)//等待按一个键
        {
                Key_Scan();//扫描键盘
        }

这里就阻塞了吧,你main的while里面还怎么加任务,如果是在某个菜单项不按按键,就一直在这里等着咯?那后面的任务咋执行。
我在一个学术文章上看到的一个菜单结构跟你这个类似,不过他的结构体里面有菜单的ID,我自己照这个思路弄了一个,感觉维护起来简单多了。
不过我的按键扫描都是中断触发。keyscan函数里面是用RTX操作系统的阻塞等待事件来做的。然后菜单就是根据当前的menuID一直不停的刷,不过我的是数码管,哈哈。。。

出0入0汤圆

 楼主| 发表于 2014-5-29 17:02:46 | 显示全部楼层
jzkn 发表于 2014-5-29 16:51
while(Key_Trg==0)//等待按一个键
        {
                Key_Scan();//扫描键盘

哦,这个只是在一个执行函数里面的,按任意键返回那个函数,操作菜单的时候怎么会执行到这里呢???
您仔细看一下

出0入0汤圆

 楼主| 发表于 2014-5-29 17:03:35 | 显示全部楼层
mcu_lover 发表于 2014-5-29 16:41
恭喜迈出第一步,真正的麻烦还在后面,就是具体的设置界面的处理。
另外按键的处理,使用状态机方式,用状 ...

OK了。。。谢谢

出0入0汤圆

发表于 2014-5-31 12:46:14 来自手机 | 显示全部楼层
我表示    这样的教学印象深刻   好玩

出0入0汤圆

发表于 2014-5-31 12:54:04 | 显示全部楼层

出0入0汤圆

发表于 2014-5-31 13:00:14 | 显示全部楼层
mcu_lover 发表于 2014-5-29 16:41
恭喜迈出第一步,真正的麻烦还在后面,就是具体的设置界面的处理。
另外按键的处理,使用状态机方式,用状 ...

后面的麻烦,第一步还没走好。

出0入0汤圆

发表于 2014-11-9 21:12:40 | 显示全部楼层
感谢分享,收藏、学习。

出0入0汤圆

发表于 2014-11-9 21:41:44 | 显示全部楼层
学习了!!

出0入0汤圆

发表于 2014-11-9 21:43:06 | 显示全部楼层
mark,收藏备用

出0入0汤圆

发表于 2014-11-9 21:53:38 | 显示全部楼层
不错阿。                  

出0入4汤圆

发表于 2014-11-10 10:09:30 | 显示全部楼层
学习啦,收藏

出0入0汤圆

发表于 2015-2-7 22:57:26 | 显示全部楼层
学习了,不错,谢谢分享
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-7-23 10:37

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表