如题,看别人的代码,看来看去越看越晕(主要是程序逻辑看不懂),还是自己花了几个小时(我笨),按照自己的理解来写了个程序,采用结构体的菜单,有程序有图有真相,求指点。
功能:
这个菜单程序不会阻塞主循环,运行菜单的同时还可以干别的事情
这个菜单在没有改变内容的时候,是不会刷屏幕的;
只有改变了状态的菜单项才会被刷新,没有被改变的菜单项是不会被刷新的
在进入子菜单之前会保存本级菜单的选择在哪个菜单项上,从子菜单返回的时候还会回到之前的菜单项上
按ESC(伪代码)可以直接返回上级菜单。
按ok键,如果是子菜单,就会进入子菜单,如果不是子菜单,而是要执行的程序,则执行程序,如果都没有,就会返回上级菜单
PS:
代码量相比之前我的土办法真的少了很多,但是内存方面相比我之前的代码却多了很多。。。看来这个结构体菜单不适合大量的菜单。。。尤其是内存吃紧的情况。
就这么简单的几个菜单,竟然吃了我138个字节的内存,我擦!!!!而我之前的代码,一个N个菜单项的菜单(N<4),最少只需要2.1个字节的内存。。。最多255个菜单项,也只需要4.3个字节的内存。
好吧,代码奉上,各位指点一下,那个显示的驱动之类的我就不上传了,见谅!
程序里NULL =0,UINT8=unsigned char
- //main.h文件
- #ifndef __MAIN_H__
- #define __MAIN_H__
- #include "keyscan.h"
- xdata struct MenuRes
- {
- UINT8 MenuItemCount;
- UINT8 *MenuItemString;
- struct MenuRes *ParentMenus;
- struct MenuRes *SubMenus;
- void (*ExecFunction)();
- };
- extern xdata struct MenuRes Menu_MainItems[3];
- extern xdata struct MenuRes Menu_SystemSet[4];
- extern xdata struct MenuRes Menu_TimeSet[3];
- void SystemSet1(void);//测试而已
- void SystemSet2(void);
- void SystemSet3(void);
- void TimeSet1(void);
- void TimeSet2(void);
- void Menu_Item_ChoiceForKey(void);//键盘操作
- void Menu_Show_Menu(void);//显示菜单出来
- #endif
复制代码
- //main.c
- #include "Keyscan.h"
- #include "stc_spi.h"
- #include "uc1701x series.h"
- #include "Main.h"
- xdata struct MenuRes Menu_MainItems[3]={
- {3,"SYSTEM SET",NULL,Menu_SystemSet,NULL},
- {3,"TIME SET",NULL,Menu_TimeSet,NULL},
- {3,"结构体菜单?FUCK!",Menu_MainItems,NULL,NULL},
- };
- xdata struct MenuRes Menu_SystemSet[4]={
- {4,"SCREEN SET",Menu_MainItems,NULL,SystemSet1},
- {4,"OLED SET",Menu_MainItems,NULL,SystemSet2},
- {4,"FUCK SET",Menu_MainItems,NULL,SystemSet3},
- {4,"BACK TO MAIN MENU",Menu_MainItems,NULL,NULL},
- };
- xdata struct MenuRes Menu_TimeSet[3]={
- {3,"DATE SET",Menu_MainItems,NULL,TimeSet1},
- {3,"TIME SET1",Menu_MainItems,NULL,TimeSet2},
- {3,"BACK TO MAIN MENU",Menu_MainItems,NULL,NULL},
- };
- xdata struct {
- UINT8 Select;//当前选择
- UINT8 LastSelect;//上次的选择
- UINT8 RePaint;//是否强制重画菜单,=1强制重画,=0自己决定是否该重画菜单
- UINT8 ParentMenuSelect;//上级菜单在进入子菜单之前,选择的哪个菜单
- struct MenuRes *MainPoint;//当前菜单
- }ItemSelect={ 0, 0, 1, 0, Menu_MainItems};
- void SystemSet1(void)
- {
- ST7565_Clear();//清屏
- ST7565_Paint_MixStr(32,24,"SCREEN SET!",0);
- ST7565_Paint_Push_Data();//将缓冲区的数据写入到Lcd
- //按任意键退出,测试而已,就暂时这样吧
- Key_Scan();//扫描键盘
- while(Key_Trg==0)//等待按一个键
- {
- Key_Scan();//扫描键盘
- }
- }
- void SystemSet2(void)
- {
- ST7565_Clear();//清屏
- ST7565_Paint_MixStr(32,24,"OLED SET!",0);
- ST7565_Paint_Push_Data();//将缓冲区的数据写入到Lcd
- Key_Scan();//扫描键盘
- while(Key_Trg==0)//等待按一个键
- {
- Key_Scan();//扫描键盘
- }
- }
- void SystemSet3(void)
- {
- ST7565_Clear();//清屏
- ST7565_Paint_MixStr(32,24,"FUCK!FUCK!!",0);
- ST7565_Paint_Push_Data();//将缓冲区的数据写入到Lcd
- Key_Scan();//扫描键盘
- while(Key_Trg==0)//等待按一个键
- {
- Key_Scan();//扫描键盘
- }
- }
- void TimeSet1(void)
- {
- ST7565_Clear();//清屏
- ST7565_Paint_MixStr(32,24,"DATE SET",0);
- ST7565_Paint_Push_Data();//将缓冲区的数据写入到Lcd
- Key_Scan();//扫描键盘
- while(Key_Trg==0)//等待按一个键
- {
- Key_Scan();//扫描键盘
- }
- }
- void TimeSet2(void)
- {
- ST7565_Clear();//清屏
- ST7565_Paint_MixStr(32,24,"TIME SET",0);
- ST7565_Paint_Push_Data();//将缓冲区的数据写入到Lcd
- Key_Scan();//扫描键盘
- while(Key_Trg==0)//等待按一个键
- {
- Key_Scan();//扫描键盘
- }
- }
- //--------------------------------------------------------------------------------------------------------------------------------------------------------------
- void Menu_Show_Menu(void)
- {
- UINT8 xdata ItemCount;//不知道拿来干啥用的
- if( ItemSelect.RePaint == 1)//如果要强制重画菜单
- {
- ST7565_Clear();//清屏
- ST7565_Paint_Rectangle( 0, 0, 64, 128, 1);//先画个框框意思一下
- ST7565_Paint_HLine(13,1,126,1);//横线
- ST7565_Paint_MixStr(1,4,"我擦这是结构体的菜单",0);//画出标题,12x12新宋体
- }
-
- for(ItemCount = 0; ItemCount < ItemSelect.MainPoint[ ItemSelect.Select ].MenuItemCount; ItemCount++)//遍历所有的菜单,作为测试,这里只考虑4行菜单条目的情况,超过则不会显示
- {
- if(ItemSelect.Select == ItemCount || ItemSelect.LastSelect == ItemCount || ItemSelect.RePaint == 1)//一次只能选中一个菜单项
- {
- ST7565_Paint_MixStr( ((ItemCount + 1) * 12) + 3, 10, ItemSelect.MainPoint[ ItemCount ].MenuItemString, (ItemSelect.Select == ItemCount) ? 1 : 0 );//显示出来菜单
- }
- }
- //显示缓存写好了,那么就刷吧。。。
- if(( ItemSelect.Select != ItemSelect.LastSelect ) || ( ItemSelect.RePaint == 1 ))//如果有必要刷新,才会刷屏幕
- ST7565_Paint_Push_Data();//将缓冲区的数据写入到Lcd
- ItemSelect.LastSelect = ItemSelect.Select;//保存上次的选择
- ItemSelect.RePaint = 0;//强制画过了,就转换到自由模式
- }
- //--------------------------------------------------------------------------------------------------------------------------------------------------------------
- void Menu_Item_ChoiceForKey(void)
- {
- Key_Scan();//最土的键盘扫描办法
- if(Key_Trg & Key_Cancel)
- {
- if(ItemSelect.MainPoint[ 0 ].ParentMenus == NULL)//为空则为根菜单
- {
- return;//这里就啥也不干好了
- }
- else
- {//不为空,就返回上一级菜单
- ItemSelect.MainPoint = ItemSelect.MainPoint[ 0 ].ParentMenus;
- ItemSelect.Select = ItemSelect.ParentMenuSelect;//上级菜单之前选择的那个菜单项
- ItemSelect.RePaint = 1;//强制刷新一次
- }
- }
- if(Key_Trg & Key_plus)//+++
- { //以下为偷懒之作
- ( ItemSelect.Select > ( ItemSelect.MainPoint [ 0 ].MenuItemCount - 2) ) ? ItemSelect.Select = 0 : ItemSelect.Select++;
- }
- if(Key_Trg & Key_Minus)//---
- { //同上
- ItemSelect.Select < 1 ? ItemSelect.Select = ( ItemSelect.MainPoint [ 0 ].MenuItemCount - 1) : ItemSelect.Select--;
- }
- if(Key_Trg & Key_Ok)//选中某个菜单,按了确认
- {
- if(ItemSelect.MainPoint [ ItemSelect.Select ].ExecFunction == NULL)//判断是否有执行函数,如果没有执行函数,表示要进入下一级子菜单或者返回上级菜单
- {
- if(ItemSelect.MainPoint[ ItemSelect.Select ].SubMenus == NULL)//判断是否有子菜单,如果也没有子菜单,则肯定是返回上级菜单
- { //如果连上级菜单的指针也没有,那尼玛设计者就是脑残
- ItemSelect.MainPoint = ItemSelect.MainPoint = ItemSelect.MainPoint[ ItemSelect.Select ].ParentMenus;//好吧,就算这个是返回,那么返回上级菜单先
- ItemSelect.Select = ItemSelect.ParentMenuSelect;//上级菜单之前选择的那个菜单项
- }
- else//如果有子菜单,那么进入子菜单
- {
- ItemSelect.MainPoint = ItemSelect.MainPoint = ItemSelect.MainPoint [ ItemSelect.Select ].SubMenus;//进入子菜单
- ItemSelect.ParentMenuSelect = ItemSelect.Select;//进入子菜单之前,先保存本级菜单项的选择在哪个菜单项上
- ItemSelect.Select = 0;//好吧,下一级菜单的选择项默认是第一个
- }
- }
- else//如果有执行函数,那么就执行这个函数
- {
- ( *ItemSelect.MainPoint[ ItemSelect.Select ].ExecFunction )();//执行之
- }
- ItemSelect.RePaint = 1;//强制刷新一次
- }
- }
- //--------------------------------------------------------------------------------------------------------------------------------------------------------------
- void main(void)
- {
- //目前就只是128 x 64的OLED屏
- //表示俺这个程序,如果没有必要,他是不会玩命刷屏幕的显示的
- ST7565_Init();
- Key_Time0Init();//键盘消抖定时器
- while(1)
- {
- Menu_Item_ChoiceForKey();//键盘的操作
- Menu_Show_Menu();//显示菜单出来
- //其实以上2个函数可以写在一个函数里面,这样就不用搞那个全局的结构体ItemSelect了
- //非阻塞式,这里还可以干别的事情
- }
-
- }
- //--------------------------------------------------------------------------------------------------------------------------------------------------------------
复制代码
图片很慢,在编辑,一会上传,手机照的,OLED的小屏幕,很难照清楚,见谅!
另外按键的处理,使用状态机方式,用状态机 为关键字搜索本坛。
一周热门 更多>