裸机点阵液晶自定义菜单,以及菜单回退

//-------------------------------------------堆 栈数据结构-------------------------------
typedef struct TStackSequenceCtrl_t{
	BYTE *const Data;
	BYTE Tail; //StackSequence: 指向 下一个将要存储的索引
	const WORD Align;
	const BYTE Size;
} TStackSequenceCtrl;

#define DEFINE_STACK_SEQUENCE(name, size, align) \
	BYTE name##Buf[align * size];         \
	TStackSequenceCtrl name =        \
	{                                \
			.Data = name##Buf,       \
			.Tail = 0,                \
			.Align = align,			\
			.Size = size,			\
	};

BYTE Lib_StackSequencePush(TStackSequenceCtrl *StackSequence, void *Data)
{
	BYTE Ret = FALSE;

	if(StackSequence->Tail < StackSequence->Size)
	{
		Ret = TRUE;
		memcpy(&StackSequence->Data[StackSequence->Tail * StackSequence->Align], Data, StackSequence->Align);
		StackSequence->Tail++;
	}

	return Ret;
}
BYTE Lib_StackSequenceTailData(TStackSequenceCtrl *StackSequence, void *Data)
{
	BYTE Ret = FALSE;
	
	if(StackSequence->Tail != 0)
	{
		Ret = TRUE;
		memcpy(Data, &StackSequence->Data[(StackSequence->Tail - 1) * StackSequence->Align], StackSequence->Align);
	}

	return Ret;
}
void Lib_StackSequencePop(TStackSequenceCtrl *StackSequence)
{
	if(StackSequence->Tail != 0)
	{
		StackSequence->Tail--;
	}
}

typedef struct TSequenceCtrl_t{
	BYTE *const Data;
	BYTE Head;			//StackSequence: 指向 第一个存储的索引
	BYTE Lenth;			//有效长度

	const WORD Align;
	const BYTE Size;
} TSequenceCtrl;

#define DEFINE_SEQUENCE(name, size, align) \
	BYTE name##Buf[align * size];         \
	TSequenceCtrl name =        \
	{                                \
			.Data = name##Buf,       \
			.Head = 0,                \
			.Lenth = 0,				\
			.Align = align,			\
			.Size = size,			\
	};

BYTE Lib_SequencePush( TSequenceCtrl *Sequence, void *Data)
{
	BYTE Ret = FALSE;

	if(Sequence->Lenth < Sequence->Size)
	{
		Ret = TRUE;

		BYTE Next = (Sequence->Head + Sequence->Lenth) % Sequence->Size;
		memcpy(&Sequence->Data[Next * Sequence->Align], Data, Sequence->Align);
		Sequence->Lenth++;
	}

	return Ret;
}
BYTE Lib_SequenceHeadData(TSequenceCtrl *Sequence, void *Data)
{
	BYTE Ret = FALSE;
	
	if(Sequence->Lenth != 0)
	{
		Ret = TRUE;
		memcpy(Data, &Sequence->Data[(Sequence->Head * Sequence->Align)], Sequence->Align);
	}

	return Ret;
}

void Lib_SequencePop(TSequenceCtrl *Sequence)
{
	if(Sequence->Lenth != 0)
	{
		Sequence->Head++;
		Sequence->Head %= Sequence->Size;
		Sequence->Lenth--;
	}
}


//--------------------------------定时器---------------------------
BYTE Lib_TimerDecrease(void *Timer, BYTE Align, DWORD Init)
{
	BYTE Ret = FALSE;
	DWORD TimerBak;

	TimerBak = 0;
	Align = (Align > sizeof(TimerBak)) ? sizeof(TimerBak) : Align;
	memcpy(&TimerBak, Timer, Align);
	if(TimerBak != 0)
	{
		TimerBak--;
	}
	if(TimerBak == 0)
	{
		TimerBak = Init;
		Ret = TRUE;
	}
	
	memcpy(Timer, &TimerBak, Align);

	return Ret;
}

//---------------------------------------------按键扫描-------------------------
static BYTE s_KeyStatus;
void api_KeyScanf(void)
{
	static WORD s_DelayTimer[8];

	s_KeyStatus = 0;
	if(!Up_KeyEnter)
	{
		if(s_DelayTimer[0]>2)
			s_KeyStatus |= Up_Key;
		s_DelayTimer[0] = 0;
	}
	else
	{
		s_DelayTimer[0]++;
	}

	if(!Down_KeyEnter)
	{
		if(s_DelayTimer[1]>2)
			s_KeyStatus |= Down_Key;
		s_DelayTimer[1] = 0;
	}
	else
	{
		s_DelayTimer[1]++;
	}
	if(!Enter_KeyEnter)
	{
		if(s_DelayTimer[2]>2)
			s_KeyStatus |= Enter_Key;
		s_DelayTimer[2] = 0;
	}
	else
	{
		s_DelayTimer[2]++;
	}
	if(!Return_KeyEnter)
	{
		if(s_DelayTimer[3]>2)
			s_KeyStatus |= Return_Key;
		s_DelayTimer[3] = 0;
	}
	else
	{
		s_DelayTimer[3]++;
	}
	if(!Right_KeyEnter)
	{
		if(s_DelayTimer[4]>2)
			s_KeyStatus |= Right_Key;
		s_DelayTimer[4] = 0;
	}
	else
	{
		s_DelayTimer[4]++;
	}
	if(!Left_KeyEnter)
	{
		if(s_DelayTimer[5]>2)
			s_KeyStatus |= Left_Key;
		s_DelayTimer[5] = 0;
	}
	else
	{
		s_DelayTimer[5]++;
	}		
}
// 0 - 9; Range = 10
void MakeUpDigitKey(BYTE KeyStatus, BYTE* Data, BYTE Range)
{
	BYTE Digit = Data[0];

	if( (Range == 0 ) || (Data[0] >= Range))
	{
		Data[0] = 0;
		return;
	}
	if((KeyStatus == Right_Key) || (KeyStatus == Up_Key))
	{
		Data[0]++;
		Digit = (Data[0] >= Range) ? 0 : Data[0];
	}
	if((KeyStatus == Left_Key) || (KeyStatus == Down_Key))
	{
		Digit = (Data[0] == 0) ? Range - 1 : Data[0]-1;
	}

	Data[0] = Digit;
}
void MakeUpDigitASSIC(const BYTE *Src, BYTE *Dst, BYTE Len)
{
	for (BYTE i = 0; i < Len; i++)
	{
		if(Src[i] <= 0x09)
			Dst[i] = Src[i] + '0';
		else
			Dst[i] = (Src[i] - 0x0A) + 'A';
	}
}
//---------------------------------显示逻辑 --------------------------------------
typedef BYTE (*TLCDEventFilter)(WORD CurrKey, WORD CurrSubMenu); //功能实现
struct TLCDMenuTable_t	
{
	const BYTE *HintInfo;								//当前菜单功能信息提示
	BYTE SubMenuNum;									//当前菜单下 菜单个数
	const struct TLCDMenuTable_t *SubMenuTable;			//当前菜单下 子菜单
	TLCDEventFilter LCDEventFilter;						//当前菜单 功能实现

	BYTE BtnCmd; //bitx 有效置位 具体含义: bit0 :确定键:进入下一级目录  bit1:返回键:返回上层目录
};
typedef struct TLCDMenuTable_t TLCDMeunTable;			//菜单目录 


typedef struct TLCDStackTrace_t	//用于菜单的翻页,以及回退
{
	const TLCDMeunTable *CurrMenu;	//当前菜单
	WORD SubMenuNum;			//当前菜单下 子菜单的个数
	WORD FirstSubMenu;			//当前界面第一行 表示的子菜单
	WORD CurrSubMenu;			//当前选中的第一子菜单
}TLCDStackTrace;

//-----------------------------------LCD 控制框架------------------------------------
#define LCD_MENU_NULL		((TLCDMeunTable*)NULL)		//空菜单
#define LCD_CTRL_FUN_NULL	((TLCDEventFilter)NULL)				//空显示函数
#define SUB_MENU_NUM( type )		(sizeof(type)/sizeof(TLCDMeunTable))
//------------------------------------LCD 子菜单---------------------------------------
BYTE ReadVolRateEvtFilter(WORD CurrKey, WORD CurrSubMenu)
{
	#define ADDR_LEN	12
	static BYTE s_Addr[ADDR_LEN], s_Cursor, s_IsStart = FALSE;
	static WORD s_Delay = 2;

	BYTE Addr[ADDR_LEN + 1];

	//没有开始发送,返回键直接 返回到上一屏
	if( (CurrKey == Return_Key) && (s_IsStart == FALSE))
	{
		return Return_Key;
	}
	//开始发送,返回键,直接暂停
	if( (CurrKey == Return_Key) && (s_IsStart == TRUE))
	{
		s_IsStart = FALSE;
	}

	//1、确定按键状态
	if((CurrKey == Right_Key) || (CurrKey == Left_Key))
	{
		MakeUpDigitKey(CurrKey, &s_Cursor, ADDR_LEN);
	}
	if((CurrKey == Up_Key) || (CurrKey == Down_Key))
	{
		MakeUpDigitKey(CurrKey, &s_Addr[s_Cursor], 0x10);
	}
	if(CurrKey == Enter_Key)
	{
		s_IsStart = TRUE;
	}

	//2、开始发送报文
	if((Lib_TimerDecrease(&s_Delay, sizeof(s_Delay), 2) == TRUE) && (s_IsStart == TRUE))
	{
	  	extern BYTE CommuAddr[6];
		memcpy(CommuAddr, s_Addr, ADDR_LEN);
		MassageSend645();
	}

	//3、主循环调用,非按键翻屏,不要清屏
	if(CurrKey != Refulsh_Key)
	{
		OLED_Clear(OLED_WRBUF);
	}

	//4、显示地址 与 是否发送
	OLED_ShowGBK(0, 0, "通信地址", OLED_WRBUF);
	Addr[ADDR_LEN] = 0x00;//结束字符
	MakeUpDigitASSIC(s_Addr, Addr, ADDR_LEN);
	OLED_ShowGBK(0, 16, (char const *)Addr, OLED_WRBUF);
	OLED_ShowGBK(8*s_Cursor, 16, " ", OLED_WRBUF);
	Addr[0] = Addr[s_Cursor];	Addr[1] = 0x00;
	OLED_ShowGBK(8*s_Cursor, 16, (char const *)Addr, OLED_OPWRBUF);
	
	if(s_IsStart == TRUE)
	{
		OLED_ShowGBK(32, 32, "正在发送", OLED_WRBUF);
	}
	else
	{
		OLED_ShowGBK(32, 32, "        ", OLED_WRBUF);
	}

	return Invaild_Key;
}
static const TLCDMeunTable c_LCDAVolRateMenuTable[1] =
{
	"子菜单1", 0, LCD_MENU_NULL, 	ReadVolRateEvtFilter, BIT1,
};

BYTE AddrMenuEvtFilter(WORD CurrKey, WORD CurrSubMenu)
{
	static BYTE IsLock = FALSE;
	static BYTE s_Addr[ADDR_LEN], s_Cursor;

	BYTE Addr[ADDR_LEN + 1], Ret = CurrKey;

	//当前没有锁定子菜单,返回键直接返回上一级目录
	if((CurrKey == Return_Key) && (IsLock == FALSE))
	{
		return Return_Key;
	}
	//当前锁定子菜单,返回键直接解除锁定
	if((CurrKey == Return_Key) && (IsLock == TRUE))
	{
		IsLock = FALSE;
		Ret = Invaild_Key;
	}

	//1、确定按键状态
	if(CurrKey == Enter_Key)
	{
		IsLock = TRUE;
	}
	//2、锁定当前子菜单,所有按键操作仅局限于当前 菜单,
	if((CurrKey != Refulsh_Key) && (IsLock == TRUE))
	{
		Ret = Invaild_Key;

		if((CurrKey == Right_Key) || (CurrKey == Left_Key))
		{
			MakeUpDigitKey(CurrKey, &s_Cursor, ADDR_LEN);
		}
		if((CurrKey == Up_Key) || (CurrKey == Down_Key))
		{
			MakeUpDigitKey(CurrKey, &s_Addr[s_Cursor], 0x10);
		}
	}

	#define START_ADDR_COLU		2*16
	//4、无论当前是否处理锁定,子菜单选项落到该项时,都应该被刷新显示
	OLED_ShowGBK(START_ADDR_COLU, 0, "            ", OLED_WRBUF);
	Addr[ADDR_LEN] = 0x00;//结束字符
	MakeUpDigitASSIC(s_Addr, Addr, ADDR_LEN);
	OLED_ShowGBK(START_ADDR_COLU, 0, (char const *)Addr, OLED_WRBUF);

	//5、锁定状态下,显示光标位置
	if(IsLock == TRUE)
	{
		OLED_ShowGBK(START_ADDR_COLU + 8*s_Cursor, 0, " ", OLED_WRBUF);
		Addr[0] = Addr[s_Cursor];	Addr[1] = 0x00;
		OLED_ShowGBK(START_ADDR_COLU + 8*s_Cursor, 0, (char const *)Addr, OLED_OPWRBUF);
	}

	return Ret;
}
static const TLCDMeunTable	c_LCDVolRateMenuTable[] =
{
	{"子菜单1",				0,	   		LCD_MENU_NULL,			   
	AddrMenuEvtFilter,		BIT1		},

	{"子菜单2",				0,			c_LCDAVolRateMenuTable,		
	LCD_CTRL_FUN_NULL,		BIT0|BIT1	},

	{"子菜单3", 			0,			LCD_MENU_NULL,				
	LCD_CTRL_FUN_NULL,		BIT1		},	

	{"子菜单4", 			0,			LCD_MENU_NULL,				
	LCD_CTRL_FUN_NULL,		BIT1		},	
};



//-----------------------------------主 菜单目录------------------------------------
static const TLCDMeunTable c_LCDMainMenuTable[] = 
{
	{"菜单1", 		SUB_MENU_NUM(c_LCDVolRateMenuTable),	c_LCDVolRateMenuTable,	LCD_CTRL_FUN_NULL,	BIT0},
	{"菜单2", 		SUB_MENU_NUM(c_LCDVolRateMenuTable),	c_LCDVolRateMenuTable,	LCD_CTRL_FUN_NULL,	BIT0},
};
//---------------------------------LCD 控制框架--建议不做更改------------------------------------
DEFINE_SEQUENCE(g_KeySequence, 30, sizeof(BYTE))
DEFINE_STACK_SEQUENCE(g_LCDStackTrace, 30, sizeof(TLCDStackTrace))

#define LCD_MENU_NUM			3		//一屏 做多显示3行菜单
#define LCD_MENU_ROW_SPACE		16		//行间距

static void ProcInitLCD(void)
{
	TLCDStackTrace MainMenu;

	MainMenu.CurrMenu = &c_LCDMainMenuTable[0];
	MainMenu.SubMenuNum = SUB_MENU_NUM(c_LCDMainMenuTable);
	MainMenu.FirstSubMenu = 0;
	MainMenu.CurrSubMenu = 0;

	Lib_StackSequencePush(&g_LCDStackTrace, &MainMenu);
}
static void DoLCDBulidSceen(const TLCDMeunTable *Menu, WORD SubMenuNum, WORD FirstSubMenu, WORD CurrSubMenu)
{
	const TLCDMeunTable *MainMenu = Menu;
	WORD MenuMax = FirstSubMenu + LCD_MENU_NUM;

	//|---------|
	//|	....	|	第一步的作用就是 将子菜单的信息显示
	//|---------|
	//|	....	|	第二步的作用就是 执行每一个菜单中 特殊操作
	//|---------|
	//|	....	|
	//|---------|

	//1、一屏最多显示 LCD_MENU_NUM屏 子菜单
	MenuMax = (MenuMax > SubMenuNum) ? SubMenuNum : MenuMax;
	for (WORD i = 0; i < (MenuMax - FirstSubMenu); i++)
	{
		Menu = &MainMenu[FirstSubMenu + i];
		if( (FirstSubMenu + i) == CurrSubMenu)
		{
			OLED_ShowGBK( 0, LCD_MENU_ROW_SPACE * i, (char const *)Menu->HintInfo, OLED_OPWRBUF );
		}
		else
		{
			OLED_ShowGBK( 0, LCD_MENU_ROW_SPACE * i, (char const *)Menu->HintInfo, OLED_WRBUF );
		}

		TLCDEventFilter LCDEventFilter = Menu->LCDEventFilter;
		if(LCDEventFilter != LCD_CTRL_FUN_NULL)
		{
			LCDEventFilter(Refulsh_Key, FirstSubMenu + i);
		}
	}

	//2、之所以放到最后,以用户的逻辑为主,
	if(SubMenuNum == 0)
	{
		TLCDEventFilter LCDEventFilter = MainMenu[0].LCDEventFilter;
		if(LCDEventFilter != LCD_CTRL_FUN_NULL)
		{
			LCDEventFilter(Refulsh_Key, 0);
		}
	}
	OLED_ShowGBK( 0, 48, "确定", OLED_WRBUF );	
	OLED_ShowGBK( 96, 48, "返回", OLED_WRBUF );
}

static void ProcLCDBulidSceen(void)
{
	BYTE KeyStatus = Invaild_Key;
	TLCDStackTrace CurrMenuTrace;
	const TLCDMeunTable *CurrMenu;		//当前菜单--数组起始地址
	const TLCDMeunTable *SubMenu;			//当前菜单下 选中的子菜--非数组

	//1、获取当前菜单,以及当前菜单下的子菜单选择
	CurrMenu = &c_LCDMainMenuTable[0];	//意外情况
	CurrMenuTrace.CurrMenu = CurrMenu;
	CurrMenuTrace.SubMenuNum = SUB_MENU_NUM(c_LCDMainMenuTable);
	CurrMenuTrace.FirstSubMenu = 0;
	CurrMenuTrace.CurrSubMenu = 0;
	if(Lib_StackSequenceTailData(&g_LCDStackTrace, &CurrMenuTrace) == TRUE)
	{
		CurrMenu = CurrMenuTrace.CurrMenu;
	}
	SubMenu = &CurrMenu[CurrMenuTrace.CurrSubMenu];
	Lib_StackSequencePop(&g_LCDStackTrace);

	OLED_Clear(OLED_WRBUF);

	//2、按键动作
	while(Lib_SequenceHeadData(&g_KeySequence, &KeyStatus) == TRUE)
	{
		Lib_SequencePop(&g_KeySequence);

		//事件滤波,根据当前子菜单下的 滤波器优先执行 用户自定义动作,并根据后续反馈,确定是否执行下一步动作
		TLCDEventFilter LCDEvnetFilterFun = SubMenu->LCDEventFilter;
		if(LCDEvnetFilterFun != LCD_CTRL_FUN_NULL)
		{
			KeyStatus = LCDEvnetFilterFun(KeyStatus, CurrMenuTrace.CurrSubMenu);
		}

		//根据按键做出相应的动作
		switch(KeyStatus)
		{
			case Invaild_Key:
				break;
			case Enter_Key:	//进入子菜单,重新确定当前菜单也 以及子菜单
				if(SubMenu->BtnCmd & BIT0)
				{
					Lib_StackSequencePush(&g_LCDStackTrace, &CurrMenuTrace);
					if(CurrMenu[CurrMenuTrace.CurrSubMenu].SubMenuTable != LCD_MENU_NULL)
					{
						CurrMenu = SubMenu->SubMenuTable;
						CurrMenuTrace.CurrMenu = CurrMenu;
						CurrMenuTrace.SubMenuNum = SubMenu->SubMenuNum;
						CurrMenuTrace.FirstSubMenu = 0;
						CurrMenuTrace.CurrSubMenu = 0;
						SubMenu = &CurrMenu[CurrMenuTrace.CurrSubMenu];
					}
				}
				break;
			case Return_Key: //返回上一级菜单,恢复原 选中子菜单
				if(SubMenu->BtnCmd & BIT1)
				{
					if(Lib_StackSequenceTailData(&g_LCDStackTrace, &CurrMenuTrace) == TRUE)
					{
						Lib_StackSequencePop(&g_LCDStackTrace);
						CurrMenu = CurrMenuTrace.CurrMenu;
						SubMenu = &CurrMenu[CurrMenuTrace.CurrSubMenu];
					}
				}
				break;
			default:		//菜单栏暂时不支持 左右按键
				if(KeyStatus == Up_Key)	//子菜单上下 移动选中
				{
					MakeUpDigitKey(Down_Key, (BYTE *)&CurrMenuTrace.CurrSubMenu, CurrMenuTrace.SubMenuNum);
				}
				if(KeyStatus == Down_Key)
				{
					MakeUpDigitKey(Up_Key, (BYTE *)&CurrMenuTrace.CurrSubMenu, CurrMenuTrace.SubMenuNum);
				}
				CurrMenuTrace.FirstSubMenu = (CurrMenuTrace.CurrSubMenu / LCD_MENU_NUM) * LCD_MENU_NUM;
				SubMenu = &CurrMenu[CurrMenuTrace.CurrSubMenu];
				break;
		}
	}

	/*KeyStatus = Refulsh_Key;
	TLCDEventFilter LCDEventFilter = SubMenu->LCDEventFilter;
	if(LCDEventFilter != LCD_CTRL_FUN_NULL)
	{
		KeyStatus = LCDEventFilter(Refulsh_Key, CurrMenuTrace.CurrSubMenu);
	}
	if(KeyStatus == Invaild_Key)
	{
		DoLCDBulidSceen(LCD_MENU_NULL, 0, 0, 0);
	}
	else //if(KeyCmd == 0xFE)*/
	{
		DoLCDBulidSceen(CurrMenuTrace.CurrMenu, CurrMenuTrace.SubMenuNum, CurrMenuTrace.FirstSubMenu, CurrMenuTrace.CurrSubMenu);
	}

	Lib_StackSequencePush(&g_LCDStackTrace, &CurrMenuTrace);

	OLED_Refresh();
}
inline static void LCD100MsTask(void)
{
	ProcLCDBulidSceen();
}


void api_LCDTask_1(void)
{
	static BYTE s_100msTime;

	if(api_GetTaskFlag(eTASK_EVENTS_ID, eFLAG_10_MS) == TRUE)
	{
		api_ClrTaskFlag(eTASK_EVENTS_ID, eFLAG_10_MS);

		//按键队列,按键扫描快,刷新屏幕慢,暂存按键状态
		api_KeyScanf();
		for (BYTE i = 0; i < 8;i++)
		{
			BYTE KeyStatus = (1U << i);
			if(s_KeyStatus & KeyStatus)
			{
				Lib_SequencePush(&g_KeySequence, &KeyStatus);
			}
		}

		//100ms 刷屏任务
		if (Lib_TimerDecrease(&s_100msTime, sizeof(s_100msTime), 20) == TRUE)
		{
			LCD100MsTask();
		}
	}
}

1、ProcLCDBulidSceen()利用栈结构,存储当前菜单信息。

        确定键按下,当前菜单信息压栈,进入下一级目录菜单;

        返回键按下,弹出菜单信息作为当前菜单信息,实现按键返回;

        上下翻屏键,当前菜单下,选中子菜单,高亮显示;

2、点阵液晶驱动显示,OLED_ShowGBK()自行实现

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值