C语言----基于旋转编码器按键的菜单结构
参考:
在不使用的情况下,做显示界面,菜单结构将会是很重要的东西。因为如果显示逻辑复杂,当遇上需要修改的界面的时候,复杂、混乱的显示逻辑(没错,就是那种很多语句的结构),将会使人抓狂。在参考了“傻孩子菜单框架”之后自己整理了一个菜单框架,主要应用在如:EC11旋转编码器上。这东西是什么,自己淘宝就知道了了。。。。
编码器提供3种不同的逻辑:左旋转、右旋转、确定。
这点跟我参考的菜单框架有点不同的,他的是4个按键逻辑。所以相应的要对菜单结构体进行改动。
显示逻辑:
(1)当检测到对应按键码时,进入菜单结构体中的按键处理函数,函数根据按键码的不同,要么移动菜单游标切换深度。要么加、减标志位。
(2)标志位为一个16bit数组如([2];),具有2个元素。元素0为当前的标志位状态,程序主要运用的是元素0。元素1用来保存上一次标志位的状态。如按键处理函数改变了“[0]”的值,只要将它与“[1]”对比,如果不同则代表这个标志位被程序改变了,将会触发此标志位管理的显示功能。这一过程类似于单片机的中断。检测到标志位改变则触发相应的中断事件。
(3) 界面深度处理:在最高深度时,按下“ENTER”就会进入下一深度,最低深度则会进入上一深度。那么中间的深度呢?我进行了一个处理,就是在中间的深度,如果当前的标志位达到最大值,则会跳转到上一深度。如果不是,则跳转下一深度。在VS上运行下面的代码,跳转到“布类、速度、返回”的位置就很明了。当显示“返回”时,“”这个标志位达到最大值“2”, 此时按下“”会跳转到第一深度,如果是“布类”、“速度”,则会跳转到对应的下一深度。
所以,这个菜单结构采用的是“消息->消息处理”的机制,按键可以改变标志位数值以及移动菜单游标到不同的结构体成员。实现同深度以及不同深度的结构体成员之间的跳转。
下面代码在 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 2void 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; //当前标志位-1break;case RIGHT: if (*pMenu->MF < (pMenu->MN - 1)) *pMenu->MF += 1; //当前标志位+1break;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; //当前标志位-1break;case RIGHT: if (*pMenu->MF < (pMenu->MN - 1)) *pMenu->MF += 1; //当前标志位+1break;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 = {x,x};\r\n", MenuF.m1_Main[0], MenuF.m1_Main[1]);printf("m2_Mode = {x,x};\r\n", MenuF.m2_Mode[0], MenuF.m2_Mode[1]);printf("m2_Water ={x,x};\r\n", MenuF.m2_Water[0], MenuF.m2_Water[1]);printf("m3_type = {x,x};\r\n", MenuF.m3_type[0], MenuF.m3_type[1]);printf("m3_speed ={x,x};\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 (void)”等的显示功能函数。变动里面的判断逻辑即可。