关注、星标公众号,直达精彩内容
来源:网络素材
整理:嵌入式云IOT技术圈
一直很想写关于映射表在MCU开发中的各种应用,在工作中我也经常用,不仅美观,而且写出来的程序可拓展性极高,看到群里已经有网友对映射表的应用做了相应的总结,而且简单易懂,上来就是肝,我们来看看吧:
一、映射表在串口数据解析中的应用
1、数据结构
typedef?struct?
{
????char?CMD[CMDLen];
????unsigned?char?(*cmd_operate)(char?*data);
}Usart_Tab;
2、指令、函数映射表
static?const?Usart_Tab?InstructionList[CMDMax]=
{
????{"PWON",PowOn},
????{"PWOFF",PowOff},
????{"HDCP",HdcpOnOff},
????{"/V",QueryKaVersion},
????{"EDIDUpgrade",UpdataEDID},
????{"Psave",Psave},
????{"Precall",Precall},
????{"Pclear",Pclear},
};
3、串口解析函数实现
unsigned?char?DataAnalysis(char?*buf)
{
??unsigned?char?i,Result;
??char?*NEXT=NULL;
??for(i=0;i<CMDMax;i++)
??{
????NEXT=StrCmp(buf,(char*)InstructionList[i].CMD);
????if(NEXT!=NULL)
????{
??????usartfuncp=InstructionList[i].cmd_operate;
??????Result=(*usartfuncp)(NEXT);
????}
??}
??return?Result;
}
二、映射表在UI设计中的应用
1、数据结构
菜单枚举:
typedef?enum
{
??stage1=0,
??stage2,
??stage3,
??stage4,
??stage5,
??stage6,
??stage7,
??stage8,
??stage9,
}SCENE;
数据结构:
typedef?struct?{
??void?(*current_operate)();?//当前场景的处理函数
??SCENE?Index;???????????????//当前场景的标签
??SCENE?Up;??????????????????//按下Up键跳转的场景
??SCENE?Down;????????????????//按下Down键跳转的场景
??SCENE?Right;???????????????//按下Left键跳转的场景
??SCENE?Left;????????????????//按下Right键跳转的场景
}STAGE_TAB;
2、函数映射表
STAGE_TAB?stage_tab[]={
??#.????operate???????Index????Up?????Down????Left???Right???
??{Stage1_Handler,??stage1,??stage4,??stage7,??stage3,?stage2},
??{Stage2_Handler,??stage2,??stage5,??stage8,??stage1,?stage3},
??{Stage3_Handler,??stage3,??stage6,??stage9,??stage2,?stage1},
??{Stage4_Handler,??stage4,??stage7,??stage1,??stage6,?stage5},
??{Stage5_Handler,??stage5,??stage8,??stage2,?stage4,??stage6},
??{Stage6_Handler,??stage6,??stage9,??stage3,?stage5,??stage4},
??{Stage7_Handler,??stage7,??stage1,??stage4,?stage9,??stage8},
??{Stage8_Handler,??stage8,??stage2,??stage5,?stage7,??stage9},
??{Stage9_Handler,??stage9,??stage3,??stage6,?stage8,??stage7},
};
3、定义两个变量保存当前场景和上一个场景
char?current_stage=stage1;
char?prev_stage=current_stage;
4、按下Up按键 跳转到指定场景current_stage的值根据映射表改变
current_stage?=stage_tab[current_stage].Up;
5、场景改变后 根据映射表执行相应的函数Handler
if(current_stage!=prev_stage)
{
??stage_tab[current_stage].current_operate();
??prev_stage=current_stage;
}
以上文章转载自:
CSDN博客链接:https://blog.csdn.net/appleJanLinux
三、单片机实现屏幕界面,多层菜单(综合版)
1、数据结构
(1)行元素结构体
typedef?struct{
?uint16_t?enterViewIndex;//按下确定键跳转的界面
?char?*?text;????????????//当前行显示的文本
?HandlerFunc?handler;????//显示函数
}RowListTypeDef;
HandlerFunc是函数指针,此函数即可作为行元素的显示函数,又可作为按键处理函数,其类型为:
typedef?void(*HandlerFunc)(uint16_t?index,?char*?p,?uint8_t?key);
三个形参的作用分别是:
@param?index:?指向此函数的RowListTypeDef在数组中的下标
@param?p:?指向当前RowListTypeDef元素的text指针指向的字符串
@param key:?若按下按键的值大于等于6(KEY_ADD),则此形参会是非0值(KEY_NONE);若小于6,则传入0(KEY_NONE)
(2)界面结构体
typedef?struct?{
?const?RowListTypeDef?*?const?list;//指向当前层所指向的行元素
?uint16_t?lengthOfList;????????????//指向的行元素的长度
?uint16_t?parentViewIndex;?????????//本元素所属层的标号
?uint16_t?startRow;????????????????//记录在上一层时的开始行索引
?uint8_t?currRow;??????????????????//记录在上一层时的行索引
}ViewListTypeDef;
定义ViewListTypeDef型数组是可以使用VIEW_MEMBER_FORMAT(x)帮助编写;如:
ViewListTypeDef?menu[]?=?{
?VIEW_MEMBER_FORMAT(rowListHome),
?VIEW_MEMBER_FORMAT(rowListSettingRoot),
?VIEW_MEMBER_FORMAT(rowListView1),
?VIEW_MEMBER_FORMAT(rowListView2),
?VIEW_MEMBER_FORMAT(rowListView3),
VIEW_MEMBER_FORMAT(rowListView1_1),
};
其中VIEW_MEMBER_FORMAT宏定义为
#define?ROW_LENGTH(x)???????((uint16_t)(sizeof(x)/sizeof(RowListTypeDef)))
#define?VIEW_MEMBER_FORMAT(x)???????{x,ROW_LENGTH(x),0,0,0}
(3)游标结构体
//游标,只需要定义一个即可????==>?8字节(byte)
typedef?struct?{
?uint8_t?currRow;???????????//当前指向元素
?uint8_t?keyval;????????????//记录按键
??uint16_t?currViewIndex;???//当前指向层
?uint16_t?startRow;?????????//屏幕第一行显示的行元素索引
?uint16_t?rowNum;???????????//记录当前层的行元素数
}CursorTypeDef;
函数作用
本控件函数很少,只有两个,即:
void?View_Init(ViewListTypeDef?*?v,?CursorTypeDef?*?c)
此函数的作用是初始化界面控件:将用户定义好的ViewListTypeDef数组的地址和CursorTypeDef地址初始化到控件
void?View_Loop(void)
此函数作用是在处理界面数据。注意:需要将此函数放入主循环中,每隔一段时间调用一次。
间隔时间典型值是100ms。
注意:并不是本控件消耗的时间多,而是屏幕驱动程序消耗的时间太多,本人的屏幕驱动是模拟的SPI时序而不是单片机硬件SPI,故屏幕驱动消耗的时间太多。控件每次需要不到1000个机器周期,而驱动程序是其上百倍。
若使用硬件外设驱动屏幕,则可以将间隔时间适当调小一点,同时注意不要低于屏幕刷新周期。
设计界面时只需要定义几个数组即可。
首先定义RowListTypeDef类型数组,根据界面数定义数组个数,根据每个界面包含的行元素数定义每个数组的长度。
然后定义ViewListTypeDef类型数组,定义一个即可,数组长度是界面数决定的。
例如,定义一个实现界面的大数组:
const?RowListTypeDef?rowListHome[]?=?{??
?//{.enterViewIndex??|??.x??|??.text??|??.handler},
???{1,"home",NULL},
};
const?RowListTypeDef?rowListSRoot[]?=?{??
?//{.enterViewIndex??|??.x??|??.text??|??.handler},
?{2,"Row?1",NULL},
?{3,"Row?2",NULL},
?{4,"Row?3",NULL},
};
const?RowListTypeDef?rowListView1[]?=?{??
?//{.enterViewIndex??|??.x??|??.text??|??.handler},
?{5,"Row?1-1",NULL},?
?{VIEW_NONE,"Row?1-2",NULL},
?{VIEW_NONE,"Row?1-3",NULL},
?{VIEW_NONE,"Row?1-4",NULL},
?{VIEW_NONE,"Row?1-5",NULL},
?{VIEW_NONE,"Row?1-6",NULL},
?{VIEW_NONE,"Row?1-7",NULL},
?{VIEW_NONE,"Row?1-8",NULL},
?{VIEW_NONE,"Row?1-9",NULL},
};
const?RowListTypeDef?rowListView2[]?=?{??
?//{.enterViewIndex??|??.x??|??.text??|??.handler},
?{VIEW_NONE,"Row?2-1",NULL},
?{VIEW_NONE,"Row?2-2",NULL},
?{VIEW_NONE,"Row?2-3",NULL},
?{VIEW_NONE,"Row?2-4",NULL},
?{VIEW_NONE,"Row?2-5",NULL},
?{VIEW_NONE,"Row?2-6",NULL},
?{VIEW_NONE,"Row?2-7",NULL},
?{VIEW_NONE,"Row?2-8",NULL},
};
const?RowListTypeDef?rowListView3[]?=?{??
?//{.enterViewIndex??|??.x??|??.text??|??.handler},
?{VIEW_NONE,"Row?3-1",NULL},
?{VIEW_NONE,"Row?3-2",NULL},
?{VIEW_NONE,"Row?3-3",NULL},
?{VIEW_NONE,"Row?3-4",NULL},
?{VIEW_NONE,"Row?3-5",NULL},
?{VIEW_NONE,"Row?3-6",NULL},
?{VIEW_NONE,"Row?3-7",NULL},
?{VIEW_NONE,"Row?3-8",NULL},
?{VIEW_NONE,"Row?3-9",NULL},
?{VIEW_NONE,"Row?3-10",NULL},
?{VIEW_NONE,"Row?3-11",NULL},
?{VIEW_NONE,"Row?3-12",NULL},
?{VIEW_NONE,"Row?3-13",NULL},
?{VIEW_NONE,"Row?3-14",NULL},
?{VIEW_NONE,"Row?3-15",NULL},
};
const?RowListTypeDef?rowListView1_1[]?=?{??
?//{.enterViewIndex??|??.x??|??.text??|??.handler},
?{VIEW_NONE,"Row?1-1-1",NULL},?
?{VIEW_NONE,"Row?1-1-2",NULL},
?{VIEW_NONE,"Row?1-1-3",NULL},
?{VIEW_NONE,"Row?1-1-4",NULL},
?{VIEW_NONE,"Row?1-1-5",NULL},
?{VIEW_NONE,"Row?1-1-6",NULL},
?{VIEW_NONE,"Row?1-1-7",NULL},
?{VIEW_NONE,"Row?1-1-8",NULL},
};
ViewListTypeDef?menu[]?=?{
?//.currIndex?|?.parentViewIndex?|?.list?|?.lengthOfList?|?.display
?VIEW_MEMBER_FORMAT(rowListHome),
?VIEW_MEMBER_FORMAT(rowListSRoot),
?VIEW_MEMBER_FORMAT(rowListView1),
?VIEW_MEMBER_FORMAT(rowListView2),
?VIEW_MEMBER_FORMAT(rowListView3),
?VIEW_MEMBER_FORMAT(rowListView1_1),
};
程序格式
程序需要定义一个全局变量游标CursorTypeDef,如:
CursorTypeDef?cursor;
然后在main函数中调用控件初始化程序View_Init
View_Init(menu,&cursor);
在程序主循环中每隔一段时间调用程序View_Loop.例如每隔100ms调用一次
当有按键按下时,只需要根据按键的不同给cursor.keyval变量赋不同的值即可.例如:
rotaryval?=?ReadRotaryEncoder();
if(rotaryval?==?ROTARY_LEFT)
{
????cursor.keyval?=?KEY_UP;
}else?if(rotaryval?==?ROTARY_RIGHT)
{
????cursor.keyval?=?KEY_DOWN;
}
其中按键值有以下:
#define?KEY_NONE????0??????//没有按下按键
#define?KEY_ENTER???1??????//按下<确定>键
#define?KEY_RETURN??2?????//按下<返回>键(返回上一层)
#define?KEY_HOME????3??????//按下<首页>键
#define?KEY_DOWN????4??????//按下<下>键
#define?KEY_UP??????5??????//按下<上>键
#define?KEY_ADD?????6??????//按下<加>键
#define?KEY_SUB?????7??????//按下<减>键
代码文件
gitee:https://gitee.com/figght/zBitsView.git
GitHub:https://github.com/figght/zBitsView.git
来源整理于网络素材,版权归原作者所有,如有侵权,请联系删除,谢谢。
???????????????? ?END ?????????????????
关注我的微信公众号,回复“加群”按规则加入技术交流群。
欢迎关注我的视频号:
点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。