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

2020-01-20 19:11发布

如题,看别人的代码,看来看去越看越晕(主要是程序逻辑看不懂),还是自己花了几个小时(我笨),按照自己的理解来写了个程序,采用结构体的菜单,有程序有图有真相,求指点。
功能:
这个菜单程序不会阻塞主循环,运行菜单的同时还可以干别的事情
这个菜单在没有改变内容的时候,是不会刷屏幕的;
只有改变了状态的菜单项才会被刷新,没有被改变的菜单项是不会被刷新的
在进入子菜单之前会保存本级菜单的选择在哪个菜单项上,从子菜单返回的时候还会回到之前的菜单项上
按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.                         {        //如果连上级菜单的指针也没有,那尼玛设计者就是脑残
  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的小屏幕,很难照清楚,见谅!
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
24条回答
wxty
1楼-- · 2020-01-23 03:47
 精彩回答 2  元偷偷看……
mafeimafei
2楼-- · 2020-01-23 08:49
学习了!!
一棵葱
3楼-- · 2020-01-23 09:23
mark,收藏备用
star_tale
4楼-- · 2020-01-23 10:26
不错阿。                  
阿文
5楼-- · 2020-01-23 12:27
学习啦,收藏
clesun
6楼-- · 2020-01-23 15:06
 精彩回答 2  元偷偷看……

一周热门 更多>