C語(yǔ)言映射表在嵌入式串口解析、UI設(shè)計(jì)中的應(yīng)用
關(guān)注、星標(biāo)公眾號(hào),直達(dá)精彩內(nèi)容
來(lái)源:網(wǎng)絡(luò)素材
整理:嵌入式云IOT技術(shù)圈
一直很想寫(xiě)關(guān)于映射表在MCU開(kāi)發(fā)中的各種應(yīng)用,在工作中我也經(jīng)常用,不僅美觀,而且寫(xiě)出來(lái)的程序可拓展性極高,看到群里已經(jīng)有網(wǎng)友對(duì)映射表的應(yīng)用做了相應(yīng)的總結(jié),而且簡(jiǎn)單易懂,上來(lái)就是肝,我們來(lái)看看吧:
一、映射表在串口數(shù)據(jù)解析中的應(yīng)用
1、數(shù)據(jù)結(jié)構(gòu)
typedef struct
{
char CMD[CMDLen];
unsigned char (*cmd_operate)(char *data);
}Usart_Tab;
2、指令、函數(shù)映射表
static const Usart_Tab InstructionList[CMDMax]=
{
{"PWON",PowOn},
{"PWOFF",PowOff},
{"HDCP",HdcpOnOff},
{"/V",QueryKaVersion},
{"EDIDUpgrade",UpdataEDID},
{"Psave",Psave},
{"Precall",Precall},
{"Pclear",Pclear},
};
3、串口解析函數(shù)實(shí)現(xiàn)
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設(shè)計(jì)中的應(yīng)用
1、數(shù)據(jù)結(jié)構(gòu)
菜單枚舉:
typedef enum
{
stage1=0,
stage2,
stage3,
stage4,
stage5,
stage6,
stage7,
stage8,
stage9,
}SCENE;
數(shù)據(jù)結(jié)構(gòu):
typedef struct {
void (*current_operate)(); //當(dāng)前場(chǎng)景的處理函數(shù)
SCENE Index; //當(dāng)前場(chǎng)景的標(biāo)簽
SCENE Up; //按下Up鍵跳轉(zhuǎn)的場(chǎng)景
SCENE Down; //按下Down鍵跳轉(zhuǎn)的場(chǎng)景
SCENE Right; //按下Left鍵跳轉(zhuǎn)的場(chǎng)景
SCENE Left; //按下Right鍵跳轉(zhuǎn)的場(chǎng)景
}STAGE_TAB;
2、函數(shù)映射表
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、定義兩個(gè)變量保存當(dāng)前場(chǎng)景和上一個(gè)場(chǎng)景
char current_stage=stage1;
char prev_stage=current_stage;
4、按下Up按鍵 跳轉(zhuǎn)到指定場(chǎng)景current_stage的值根據(jù)映射表改變
current_stage =stage_tab[current_stage].Up;
5、場(chǎng)景改變后 根據(jù)映射表執(zhí)行相應(yīng)的函數(shù)Handler
if(current_stage!=prev_stage)
{
stage_tab[current_stage].current_operate();
prev_stage=current_stage;
}
以上文章轉(zhuǎn)載自:
CSDN博客鏈接:https://blog.csdn.net/appleJanLinux三、單片機(jī)實(shí)現(xiàn)屏幕界面,多層菜單(綜合版)
1、數(shù)據(jù)結(jié)構(gòu)
(1)行元素結(jié)構(gòu)體
typedef struct{
uint16_t enterViewIndex;//按下確定鍵跳轉(zhuǎn)的界面
char * text; //當(dāng)前行顯示的文本
HandlerFunc handler; //顯示函數(shù)
}RowListTypeDef;HandlerFunc是函數(shù)指針,此函數(shù)即可作為行元素的顯示函數(shù),又可作為按鍵處理函數(shù),其類(lèi)型為:
typedef void(*HandlerFunc)(uint16_t index, char* p, uint8_t key);
三個(gè)形參的作用分別是:
@param index: 指向此函數(shù)的RowListTypeDef在數(shù)組中的下標(biāo)
@param p: 指向當(dāng)前RowListTypeDef元素的text指針指向的字符串
@param key: 若按下按鍵的值大于等于6(KEY_ADD),則此形參會(huì)是非0值(KEY_NONE);若小于6,則傳入0(KEY_NONE)
(2)界面結(jié)構(gòu)體
typedef struct {
const RowListTypeDef * const list;//指向當(dāng)前層所指向的行元素
uint16_t lengthOfList; //指向的行元素的長(zhǎng)度
uint16_t parentViewIndex; //本元素所屬層的標(biāo)號(hào)
uint16_t startRow; //記錄在上一層時(shí)的開(kāi)始行索引
uint8_t currRow; //記錄在上一層時(shí)的行索引
}ViewListTypeDef;定義ViewListTypeDef型數(shù)組是可以使用VIEW_MEMBER_FORMAT(x)幫助編寫(xiě);如:
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)游標(biāo)結(jié)構(gòu)體
//游標(biāo),只需要定義一個(gè)即可 ==> 8字節(jié)(byte)
typedef struct {
uint8_t currRow; //當(dāng)前指向元素
uint8_t keyval; //記錄按鍵
uint16_t currViewIndex; //當(dāng)前指向?qū)?br style="box-sizing: border-box;"> uint16_t startRow; //屏幕第一行顯示的行元素索引
uint16_t rowNum; //記錄當(dāng)前層的行元素?cái)?shù)
}CursorTypeDef;
函數(shù)作用
本控件函數(shù)很少,只有兩個(gè),即:
void View_Init(ViewListTypeDef * v, CursorTypeDef * c)
此函數(shù)的作用是初始化界面控件:將用戶(hù)定義好的ViewListTypeDef數(shù)組的地址和CursorTypeDef地址初始化到控件
void View_Loop(void)
此函數(shù)作用是在處理界面數(shù)據(jù)。注意:需要將此函數(shù)放入主循環(huán)中,每隔一段時(shí)間調(diào)用一次。
間隔時(shí)間典型值是100ms。
注意:并不是本控件消耗的時(shí)間多,而是屏幕驅(qū)動(dòng)程序消耗的時(shí)間太多,本人的屏幕驅(qū)動(dòng)是模擬的SPI時(shí)序而不是單片機(jī)硬件SPI,故屏幕驅(qū)動(dòng)消耗的時(shí)間太多??丶看涡枰坏?000個(gè)機(jī)器周期,而驅(qū)動(dòng)程序是其上百倍。
若使用硬件外設(shè)驅(qū)動(dòng)屏幕,則可以將間隔時(shí)間適當(dāng)調(diào)小一點(diǎn),同時(shí)注意不要低于屏幕刷新周期。
設(shè)計(jì)界面時(shí)只需要定義幾個(gè)數(shù)組即可。
首先定義RowListTypeDef類(lèi)型數(shù)組,根據(jù)界面數(shù)定義數(shù)組個(gè)數(shù),根據(jù)每個(gè)界面包含的行元素?cái)?shù)定義每個(gè)數(shù)組的長(zhǎng)度。
然后定義ViewListTypeDef類(lèi)型數(shù)組,定義一個(gè)即可,數(shù)組長(zhǎng)度是界面數(shù)決定的。
例如,定義一個(gè)實(shí)現(xiàn)界面的大數(shù)組:
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),
};
程序格式
程序需要定義一個(gè)全局變量游標(biāo)CursorTypeDef,如:
CursorTypeDef cursor;
然后在main函數(shù)中調(diào)用控件初始化程序View_Init
View_Init(menu,&cursor);
在程序主循環(huán)中每隔一段時(shí)間調(diào)用程序View_Loop.例如每隔100ms調(diào)用一次
當(dāng)有按鍵按下時(shí),只需要根據(jù)按鍵的不同給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 //沒(méi)有按下按鍵
#define KEY_ENTER 1 //按下<確定>鍵
#define KEY_RETURN 2 //按下<返回>鍵(返回上一層)
#define KEY_HOME 3 //按下<首頁(yè)>鍵
#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
來(lái)源整理于網(wǎng)絡(luò)素材,版權(quán)歸原作者所有,如有侵權(quán),請(qǐng)聯(lián)系刪除,謝謝。
???????????????? END ???????????????? 關(guān)注我的微信公眾號(hào),回復(fù)“加群”按規(guī)則加入技術(shù)交流群。
點(diǎn)擊“閱讀原文”查看更多分享,歡迎點(diǎn)分享、收藏、點(diǎn)贊、在看。
