参考:https://blog.csdn.net/yangluoning/article/details/9313677
在不使用STemwin的情况下,做显示界面,菜单结构将会是很重要的东西。因为如果显示逻辑复杂,当遇上需要修改的界面的时候,复杂、混乱的显示逻辑(没错,就是那种很多switch if语句的结构),将会使人抓狂。在参考了“傻孩子菜单框架”之后自己整理了一个菜单框架,主要应用在如:EC11旋转编码器上。这东西是什么,自己淘宝就知道了了。。。。
编码器提供3种不同的逻辑:左旋转、右旋转、确定。
这点跟我参考的菜单框架有点不同的,他的是4个按键逻辑。所以相应的要对菜单结构体进行改动。
显示逻辑:
(1)当检测到对应按键码时,进入菜单结构体中的按键处理函数,函数根据按键码的不同,要么移动菜单游标切换深度。要么加、减标志位。
所以,这个菜单结构采用的是“消息->消息处理”的机制,按键可以改变标志位数值以及移动菜单游标到不同的结构体成员。实现同深度以及不同深度的结构体成员之间的跳转。
下面代码在Visual Studio 2015下编译运行通过,代码是一个简单的洗衣机菜单的例子。结构如下图:
画面上有2个位置会显示内容,分别是位置1跟位置2。这两个位置显示的内容会根据我们的按键操作进行切换,比方位置一“模式”可以切换成显示“布类”,也可以切换成显示“牛仔”。具体可以运行以下代码:
#include
#include
#include
/************* LCD显示框架相关定义 **************/
#define Null 0
#define u8 unsigned char
#define u16 unsigned short
#define u32 unsigned int
//比较标志位
#define CompareF(Flag) (*Flag == *(Flag+1))
//同化标志位
#define ClearF(Flag) (*(Flag+1) = *Flag)
#define LEFT 1
#define RIGHT 3
#define ENTER 2
void MFunc_MD(u8 key);
void MFunc_TB(u8 key);
//菜单标志位结构体
struct MFlag
{
u16 m1_Main[2];
u16 m2_Mode[2];
u16 m2_Water[2];
u16 m3_type[2];
u16 m3_speed[2];
};
//菜单结构体
struct MenuItem
{
u16 MN; //当前菜单标志位最大值
u16 *MF; //菜单标志位
void (*Func)(u8 key); //菜单功能函数
struct MenuItem *Next;
struct MenuItem *Prev;
};
//声明菜单结构体变量
struct MenuItem m1_Main[1];
struct MenuItem m2_Option[2];
struct MenuItem m3_Mode[2];
struct MenuItem *pMenu;
//声明标志位结构体变量
struct MFlag MenuF =
{
{ 0,0 },
{ 0,0 },
{ 0,0 },
{ 0,0 },
{ 0,0 },
};
/**************** 菜单结构体变量的定义 *****************/
//一级深度菜单结构体
struct MenuItem m1_Main[1] =
{
{2, &MenuF.m1_Main[0] ,MFunc_TB,m2_Option, Null },
};
//二级深度菜单结构体
struct MenuItem m2_Option[2] =
{
{3, &MenuF.m2_Mode[0] , MFunc_MD, &m3_Mode[0], m1_Main },
{5, &MenuF.m2_Water[0], MFunc_TB, Null, m1_Main },
};
//三级深度菜单结构体
struct MenuItem m3_Mode[2] =
{
{5, &MenuF.m3_type[0], MFunc_TB, Null, &m2_Option[0] },
{5, &MenuF.m3_speed[0], MFunc_TB, Null, &m2_Option[0] },
};
/****************** 菜单跳转函数的定义 *******************/
//底部以及顶部菜单结构体成员的按键处理函数
//struct MenuItem *Next; 其中一个为空,使用此菜单函数
//struct MenuItem *Prev;
void MFunc_TB(u8 key)
{
switch (key)
{
case LEFT: if(*pMenu->MF>0) *pMenu->MF -= 1; //当前标志位-1
break;
case RIGHT: if (*pMenu->MF < (pMenu->MN - 1)) *pMenu->MF += 1; //当前标志位+1
break;
case ENTER: if (pMenu->Prev == Null) pMenu = pMenu->Next + *pMenu->MF; //跳转到下一深度
else pMenu = pMenu->Prev; //跳转到上一深度
*(pMenu->MF + 1) += 1; //刷新当前标志位,否则切换深度后不显示当前选项
break;
}
}
// struct MenuItem *Next; 都不为空,使用此菜单功能函数,此函数跳转深度后会将跳转前的菜单成员的标志位清零
// struct MenuItem *Prev;
void MFunc_MD(u8 key)
{
switch (key)
{
case LEFT: if (*pMenu->MF>0) *pMenu->MF -= 1; //当前标志位-1
break;
case RIGHT: if (*pMenu->MF < (pMenu->MN - 1)) *pMenu->MF += 1; //当前标志位+1
break;
case ENTER: if (*pMenu->MF > (pMenu->MN - 2))
{
*pMenu->MF = 0; //清零当前标志位,下一次进入该选项从0开始显示
*(pMenu->MF+1) = 0; //清零当前标志位,下一次进入该选项从0开始显示
pMenu = pMenu->Prev + *pMenu->Prev->MF; //跳转到上一深度
}
else
{
pMenu = pMenu->Next + *pMenu->MF; //跳转到下一深度
}
*(pMenu->MF + 1) += 1;
break;
}
}
/****************** 显示功能函数的定义 *******************/
//同化标志位
void ClearFlag(void)
{
ClearF(MenuF.m1_Main);
ClearF(MenuF.m2_Mode);
ClearF(MenuF.m2_Water);
ClearF(MenuF.m3_speed);
ClearF(MenuF.m3_type);
}
void D_MenuCur(void)
{
printf("菜单游标位置: ");
switch (MenuF.m1_Main[0])
{
case 0: printf("位置1\r\n"); break;
case 1: printf("位置2\r\n"); break;
default: break;
}
}
void D_location1(void)
{
printf("位置1: ");
//比较m2_Mode[0]与m2_Mode[1]是否相等
if(CompareF(MenuF.m2_Mode))
{
switch (MenuF.m2_Mode[0])
{
case 0: printf("布类"); break;
case 1: printf("速度"); break;
case 2: printf("返回上层"); break;
}
}
if (CompareF(MenuF.m3_speed))
{
switch (MenuF.m3_speed[0])
{
case 0: printf("最慢"); break;
case 1: printf("慢速"); break;
case 2: printf("中速"); break;
case 3: printf("快速"); break;
case 4: printf("最快"); break;
}
}
if (CompareF(MenuF.m3_type))
{
switch (MenuF.m3_type[0])
{
case 0: printf("牛仔"); break;
case 1: printf("帆布"); break;
case 2: printf("尼龙"); break;
case 3: printf("涤纶"); break;
case 4: printf("棉布"); break;
}
}
if (CompareF(MenuF.m1_Main))
{
if (MenuF.m1_Main[0] == 0)
{
printf("模式");
}
}
printf("\r\n");
}
void D_location2(void)
{
printf("位置2: ");
if (CompareF(MenuF.m2_Water))
{
switch (MenuF.m2_Water[0])
{
case 0: printf("最低"); break;
case 1: printf("低"); break;
case 2: printf("中等"); break;
case 3: printf("高"); break;
case 4: printf("最高"); break;
}
}
if (CompareF(MenuF.m1_Main))
{
if (MenuF.m1_Main[0] == 1)
{
printf("水量");
}
}
printf("\r\n");
}
//输出画面
void printfGUI(void)
{
printf("****************************\r\n");
printf(" \r\n");
D_MenuCur();
D_location1();
D_location2();
printf(" \r\n");
printf("****************************\r\n\r\n");
}
//打印标志位
void printfLoc(void)
{
printf("m1_Main = {%04x,%04x};\r\n", MenuF.m1_Main[0], MenuF.m1_Main[1]);
printf("m2_Mode = {%04x,%04x};\r\n", MenuF.m2_Mode[0], MenuF.m2_Mode[1]);
printf("m2_Water ={%04x,%04x};\r\n", MenuF.m2_Water[0], MenuF.m2_Water[1]);
printf("m3_type = {%04x,%04x};\r\n", MenuF.m3_type[0], MenuF.m3_type[1]);
printf("m3_speed ={%04x,%04x};\r\n", MenuF.m3_speed[0], MenuF.m3_speed[1]);
printf("\r\n");
}
//画面初始化
void GUI_Init(void)
{
printf("****************************\r\n");
printf(" \r\n");
printf("菜单游标位置: ");
printf("位置1\r\n");
printf("位置1: ");
printf("模式\r\n");
printf("位置2: ");
printf("水量");
printf(" \r\n");
printf("****************************\r\n\r\n");
}
int main(void)
{
u8 temp = 0;
pMenu = &m1_Main;
GUI_Init(); //初始化界面
while (1)
{
temp = _getch();
switch (temp)
{
case '1': (*(pMenu->Func))(LEFT); break;
case '3': (*(pMenu->Func))(RIGHT); break;
case '2': (*(pMenu->Func))(ENTER); break;
default: break;
}
printfGUI();
ClearFlag(); //清零标志位
printfLoc(); //打印标志位
}
}
运行截图:
当按下键盘的‘1’、‘2’、‘3’,会打印出当前的标志位,并且显示位置上面会换成标志位对应的字符。
在实际应用中,把打印字符换成输出对应图片即可。
另外,不要问我为什么菜单游标从“位置2”移到“位置1”,位置2的字符会消失。VS的字符输出功能是,显示你当前输出的内容,你移动到游标位置1,位置2的内容没有改变。。。
真正的LCD显示,你只需要更改你移动的地方,其他不变的地方,进行改动是需要刷图片浪费资源的。。。。如果变换一个标志位需要改动好几个位置的显示,自己修改一下“void D_location1(void)”等的显示功能函数。变动里面的判断逻辑即可。