嵌入式软件架构--显示界面架构(工厂流水线模型,HOME界面,命令界面)

显示界面

界面设计

一共设计有8个界面,通过界面号访问

硬件包含8个数码管和12个按键

*号键用来删除

#号键用来确认和命令提示

界面的抽象

c 复制代码
//界面号枚举
typedef enum
{
    HOME_UI,
	  CMD_UI,
	  CALL_UI,
	  OPEN_UI,
	  ERROR_UI,
	  SUCCEED_UI,
	  PASSWORD_UI,
	  SET_PW_UI
}UI_E;
c 复制代码
//界面消息结构
typedef struct{
	
	MSG_QUEUE_S *pQueue;//字符消息队列由右侧12个按键产生
	MSG_QUEUE_S *pOpenQueue;//开门消息队列用左侧四个控制按键产生
	ASBUF_S echo;//存储用户输入的按键消息用于回显
	UI_E 		newUi;//新界面号
	UI_E    	oldUi;//旧界面号 
}DIS_MSG;

typedef struct{
	void (*init)(void);//界面初始化函数
	UI_E (*proc)(DIS_MSG );//界面消息轮询函数
}UI_S;

用户输入存储缓冲

c 复制代码
#define ABUF_LEN	8
#define WIN0	0
#define WIN1    1
typedef struct{
	INT8 dat[ABUF_LEN];//存储用户输入
	UINT8 idx;//当前存储个数
}ABUF_S;
//函数实现放在文章末尾
void initAbuf(ABUF_S *pBuf);
void saveAbuf(ABUF_S *pBuf,INT8 dat);
BOOL isABufFull(ABUF_S *pBuf);
UINT8 AbufCnt(ABUF_S *pBuf);
void delAbuf(ABUF_S *pBuf);
BOOL abufCmp(ABUF_S  *p1, ABUF_S *p2);

工厂流水线模型

流水线就是上一个工位的产出流入下一个工位的输入,上下工位通过加工品沟通。

事件传递流程

界面的切换原理
将界面封装成结构体里面包含每个界面的初始化函数和消息循环函数,初始化函数只需要调用一次,消息循环函数轮询等待消息,它的返回值是界面号,如果返回的界面号与上次存储的界面号不一致我们就切换界面。当需要切面界面时,就调用新界面的初始化函数初始化界面并且不断调用新界面的消息循环函数等待新消息,直到下一次界面切换。如果返回的界面号与上次存储的界面号一致不切换界面。

根据上面的原理搭建界面架构

1,路由按键消息

c 复制代码
//initkey函数中
	 initMsgQueue(&Queue0);
	 initMsgQueue(&Queue1);
	 p->route[KEY_CHAR_EVENT]=&Queue0;//将12个字符按键消息路由到QUEUE0
	 p->route[KEY_DOWN_EVENT]=&Queue1;//将4个控制按键消息路由到QUEUE1
c 复制代码
//路由消息
void keyMain(void *pMsg)
{
	KEY_S *p = pMsg;
	//keyScan(p,p->pf());
	if(p->keyF){//按键单击事件
		createRouteMsg(p,&p->keyF,KEY_CHAR_EVENT);
	}
}

2,界面初始化与切换

c 复制代码
#include "display.h"
DIS_MSG DisMsg;//显示消息变量
/*
每添加一个界面就要将其界面初始化函数和消息轮询函数添加到数组中
并且添加的界面顺序一定要与.h中的界面号能对应上,因为我们是通过
界面号来访问这些函数
*/
UI_S const Ui[]={

		{home_init,home_proc},
		{cmd_init,cmd_proc}

};
void displayInit(void){
	DisMsg.newUi=HOME_UI;
	DisMsg.oldUi=HOME_UI;
	cmd_init();
	DisMsg.pOpenQueue=Key.route[1];
	DisMsg.pQueue=Key.route[0];
}
/*
函数功能:界面切换函数
参数说明:
	参数1:
返回值:void
*/
void displayMain(void * pMsg){
	DisMsg.newUi=Ui[DisMsg.oldUi].proc(&DisMsg);//不断调用对应界面的消息轮询函数
	if(DisMsg.newUi!=DisMsg.oldUi){//如果返回的界面号不同则切换界面
		DisMsg.oldUi=DisMsg.newUi;
		Ui[DisMsg.newUi].init();//调用新界面的初始化函数
	}
}

HOME界面

c 复制代码
/*
函数功能:HOME界面初始化函数
参数说明:
	参数1:
返回值:void
*/
void home_init(void){
	setWinCmd(&Dleds,WIN0 ,LED_TEXT,0,0,0,NULL);
	setWinNum(&Dleds,1);
	setWinSize(&Dleds,WIN0,0,8);
	writeLed(&Dleds,WIN0,"%-8s","L-71");
}
/*
函数功能:HOME界面消息轮询函数
参数说明:
	参数1:
返回值:void
*/
UI_E home_proc(DIS_MSG *pMsg){
	initMsgQueue(pMsg->pOpenQueue);
	if(msgQueueNum(pMsg->pQueue)){
		return CMD_UI;
	}
	return HOME_UI;
}

命令界面

1,把用户输入的信息先存储到echo,然后显示

2,对用户输入的命令进行解析

输入#,(判断是1#或者0#)

输入超时,101

输入*,删除一个字符

输入0-9放入echo,再解析

0,中间全局变量

c 复制代码
DIS_MSG DisMsg;//按键消息
UINT32 Clk;//时间戳用来判断超时
UINT8 MatchCode;//保存开门键

1,定义命令解析结构体

c 复制代码
//命令匹配结构体
typedef struct{
	UINT8 len;//命令长度
	INT8 dat[4];
	UI_E  ui;//返回界面号
	UINT8 match;//匹配码 开门键
}CMD_S;

预设命令

c 复制代码
CMD_S  const ImmCmd[] = 
{
    {2,"0#",PASSWORD_UI},//0#  进入密码界面
		{2,"1#",SET_PW_UI},//1#  进入密码设置界面
};
CMD_S const TimeOutCmd[]=
{
    {3,"101",CALL_UI,VK_ENTER},//命令长度,门牌号,返回界面号,开门键
		{3,"201",CALL_UI,VK_SHIFT},
		{3,"102",CALL_UI,VK_CONTROL},
		{3,"202",CALL_UI,VK_ALT},
};

2,初始化命令界面

c 复制代码
/*
函数功能:命令界面初始化函数
参数说明:
	参数1:
返回值:void

*/
void cmd_init(void){
	initAbuf(&DisMsg.echo);//回显缓冲区初始化
	setWinCmd(&Dleds,WIN0 ,LED_TEXT,0,0,0,NULL);//普通文本显示
	setWinNum(&Dleds,1);
	setWinSize(&Dleds,WIN0,0,8);//设置显存起始位置长度
	writeLed(&Dleds,WIN0,"%-8s",DisMsg.echo.dat);	//输入回显  刚开始为空
}

3,命令消息循环函数

具体流程:

a,检查队列是否有消息,读取消息,对消息按键码进行分类解析。

如果是*表示要删除一个用户输入的字符,回显,界面不切换

如果是#表示进行命令匹配看到底是要进入密码界面还是密码设置界面

如果都不是说明为0-9即用户在输入门牌号

b,超时判断:每次进来都会进行输入超时判断,将用户输入与预设的超时命令

进行匹配,如果匹配正确则保留开门键匹配码,匹配不上则返回ERROR_UI到错误界面。

c 复制代码
/*
函数功能:命令循环解析函数
参数说明:
	参数1:消息
返回值:界面号
*/
UI_E cmd_proc(DIS_MSG *pMsg){
	MSG_S msg;
	BOOL status;
	CMD_S tempCmd;
	if(msgQueueNum(pMsg->pQueue)){//有消息
		getMsgQueue(pMsg->pQueue,&msg);
		Clk=msg.time;
		switch(msg.code){
			case '*'://删除一个字符并回显
			{
				delAbuf(&pMsg->echo);
				writeLed(&Dleds,WIN0,"%-8s",DisMsg.echo.dat);
				return	CMD_UI;
			}
			case '#':
			{
				saveAbuf(&pMsg->echo,msg.code);//保存字符
				writeLed(&Dleds,WIN0,"%-8s",DisMsg.echo.dat);//回显
				//匹配命令
				status=testCmd(&pMsg->echo,&tempCmd,ImmCmd,sizeof(ImmCmd)/sizeof(CMD_S));
				if(status == TRUE ){
					return tempCmd.ui;//两种情况密码界面还是设置界面
				}
				return ERROR_UI;	
			}
			default://0-9字符
			{
				saveAbuf(&pMsg->echo,msg.code);//保存字符
				writeLed(&Dleds,WIN0,"%-8s",DisMsg.echo.dat);//回显
				return CMD_UI;
			}
		}
	}
	if(timeOut(Clk,2000)==TRUE){//超时判断
		status=testCmd(&pMsg->echo,&tempCmd,TimeOutCmd,sizeof(TimeOutCmd)/sizeof(CMD_S));
			if(status == TRUE ){
				return tempCmd.ui;
				MatchCode=tempCmd.match;//存储开门键
			}
			return ERROR_UI;		
	}	
	
	return CMD_UI;
}

命令比较函数

c 复制代码
/*
函数功能:命令比较函数
参数说明:
	参数1:用户输入的按键字符信息
	参数2:返回匹配成功命令
	参数3:预设命令
	参数4:命令结构数组长度即包含多少命令
返回值:
		TRUE:命令匹配成功
		FALSE:命令匹配失败
*/
//testCmd(&pMsg->echo,&tempCmd,ImmCmd,sizeof(ImmCmd)/sizeof(CMD_S));
BOOL testCmd(ABUF_S *p,CMD_S * pTemp,CMD_S const * pCmd,UINT8 len){
	UINT8 i;
	for(i=0;i<len;i++){
		if(p->idx == pCmd[i].len){//先拿长度过滤一下
			if(!strcmp(p->dat,pCmd[i].dat)){
				*pTemp =pCmd[i];//这是匹配成功要返回的命令结构
				return TRUE;
			}
		}
	}
	return FALSE;
}

超时判断函数

c 复制代码
//TimeStamp是UINT32一个全局时间戳,在systick_handler函数中每1ms自增
BOOL timeOut(UINT32 clk,UINT32 delayTime)
{
    if(TimeStamp - clk >= delayTime){
		    return TRUE;
		}
		else{
		    return FALSE;
		}
}

TimeStamp从0不断自增到0xffffffff,当超出最大值又从0开始增加,当cmd_proc中记录的clk大于TimeStamp时,TimeStamp-clk的结果还是正确的。

以下是ABUF_S 相关函数的实现。

c 复制代码
void initAbuf(ABUF_S *pBuf){
	UINT8 i;
	pBuf->idx=0x00;
	for(i=0;i<ABUF_LEN;i++){
		pBuf->dat[i]=0;
	}
}
void saveAbuf(ABUF_S *pBuf,INT8 dat){
	if(pBuf->idx<=ABUF_LEN){
		pBuf->dat[pBuf->idx]=dat;
		pBuf->idx++;
	}
}
BOOL isABufFull(ABUF_S *pBuf){
	return (pBuf->idx <=ABUF_LEN)? TRUE:FALSE;
}

UINT8 AbufCnt(ABUF_S *pBuf){
	return pBuf->idx;
}
void delAbuf(ABUF_S *pBuf){
	if(pBuf->idx){
		pBuf->idx--;
		pBuf->dat[pBuf->idx]=0x00;
	}
}
BOOL abufCmp(ABUF_S  *p1, ABUF_S *p2){
	return (p1->idx==p2->idx && !strcmp(p1->dat,p2->dat))?TRUE:FALSE;

}
相关推荐
无心水4 小时前
Java主流锁全解析:从分类到实践
java·面试·架构
爱倒腾的老唐5 小时前
07、点亮第一个LED
单片机·51单片机
优信电子5 小时前
ESP32 I2S音频总线学习笔记(二):I2S读取INMP441音频数据
单片机·嵌入式
Lenz's law5 小时前
四足机器狗整机关节控制-CAN/FD总线架构及分析
架构·1024程序员节
第二层皮-合肥6 小时前
坦电容做电源滤波,放在陶瓷电容的前面还是后面好
单片机·嵌入式硬件
后端小张6 小时前
【JAVA 进阶】SpringBoot集成Sa-Token权限校验框架深度解析
java·spring boot·spring·架构·sa-token·springboot·权限框架
知识充实人生11 小时前
时序收敛方法一:控制集优化
stm32·单片机·fpga开发
逆小舟14 小时前
【STM32】学习准备、初识
stm32·嵌入式硬件
周杰伦_Jay17 小时前
【MCP开发部署流程表格分析】MCP架构解析、开发流程、部署方案、安全性分析
人工智能·深度学习·opencv·机器学习·架构·transformer