使用C实现 类QT/Emwin 框架

使用C实现 类QT/Emwin 框架

  • 作者:MrWang
  • 日期: 2020/6/1
  • 目的:帮助理解QT框架 或 类Emwin框架,可用于单片机断码屏LCD等简易页面开发(已验证)

1.设计 通信消息机制

文件名称: message.h

#ifndef _MESSAGE_H_
#define _MESSAGE_H_
/* Includes ----------------------------------------------------------*/
/* Define ------------------------------------------------------------*/
/* 定义通信事件 --------------------------------------------------------*/
/* 系统通信事件 */
#define WM_NULL					(0x00U)	/* 投递事件结束 */
#define WM_INIT					(0x01U)	/* 事件初始化   */  
#define WM_PAINT 				(0x02U)	/* 事件重绘    */
#define WM_CLEAR				(0x03U)	/* 事件清屏     */
#define WM_DESTROY				(0x04U)	/* 事件销毁     */
#define WM_UPDATE				(0x05U)	/* 事件更新     */
#define WM_TIMER				(0x06U)	/* 事件定时器   */
/* 此处添加系统通信事件 ... ... */

/* 系统按键事件偏移 */												
#define WM_KEY_OFFSET	        (0x20U)
/* 在用户按键中自定义键值在此基础上偏移 */
					
/* 用户自定义消息偏移 */					
#define WM_USER_OFFSET 	        (0x80U)
/* 用户自定义消息在此基础上偏移 ... ... */

/* Typedef ---------------------------------------------------------*/
typedef unsigned char Msg;		/* 自定义事件类型 				*/
typedef void (*MsgPrce)(void);	/* 自定义目标对象执行函数类型 	*/

/* 自定义类 */
typedef struct{
	Msg			m_msg;	      	/* 派发的事件  */
	MsgPrce		m_msgPrce;		/* 事件目标函数 */
	MsgPrce		m_theObject;	/* 当前目标对象 */
}CMsgDC;

/* Enume ----------------------------------------------------------*/
/* Extern ---------------------------------------------------------*/
extern CMsgDC msgDC;
/* 宏定义设置当前目标函数 */
#define CMsgPr_SetMsgObject(X) (msgDC.m_theObject = (MsgPrce)(X)) 
    
/* Declaration ----------------------------------------------------*/
Msg  CMsgPr_GetMessage(void);							/* 获取当前事件后并清空 */
void CMsgPr_PollMessage(Msg aMsg);						/* 向当前对象派发事件  */
void CMsgPr_SendMessage(MsgPrce pmsgPrce, Msg aMsg);	/* 向控件派发事件 */
void CMsgPr_ParentMessage(MsgPrce pmsgPrce, Msg aMsg);	/* 向基类投递事件 */
void CMsgPr_EndMessage(void); 							/* 结束事件投递 	*/
#endif 	// _MESSAGE_H_

  • 文件名称: message.c
  • 作用: 伪框架的通信机制
/* Includes --------------------------------------------------------*/
#include "message.h"
/* Declaration -----------------------------------------------------*/
/* Global variable -----------------------------------=-------------*/
CMsgDC	msgDC;	/* 保存当前执行信息 */

/* 获取当前事件后并清空
** aMsg:事件
**/
Msg CMsgPr_GetMessage(void){
    Msg amsg 	= msgDC.m_msg; /* 缓存事件 */
    msgDC.m_msg = WM_NULL;	   /* 获取当前事件后清空事件 */
    return amsg;               /* 返回当前事件 */
}

/* 向当前对象(自己)派发事件
** aMsg:事件
**/
void CMsgPr_PollMessage(Msg aMsg){
    msgDC.m_msg 	= aMsg;				 /* 保存当前的事件 */
    msgDC.m_msgPrce = msgDC.m_theObject; /* 将当前对象放入执行目标中 */
}

/* 向控件派发事件(核心部分)
** pmsgPrce -> 事件目标   aMsg -> 事件
**/
void CMsgPr_SendMessage(MsgPrce pmsgPrce, Msg aMsg){
	msgDC.m_msg 		= aMsg;	  		 /* 消息事件保存 */
	msgDC.m_msgPrce   	= pmsgPrce;		
	msgDC.m_theObject 	= pmsgPrce;	

	for (;;){ 							/* 调用事件目标函数 */
        /* 事件目标函数/事件 无效则直接退出 */
		if ((WM_NULL == msgDC.m_msgPrce) || (WM_NULL == msgDC.m_msg)){	
			return;
		}
        msgDC.m_msgPrce(); 				/* 执行事件目标函数 */
    }
}

/* 向基类投递事件
** pmsgPrce -> 事件目标   aMsg -> 事件
**/
void CMsgPr_ParentMessage(MsgPrce pmsgPrce, Msg aMsg){
    msgDC.m_msg 	= aMsg;	
    msgDC.m_msgPrce = pmsgPrce;
}

/* 结束事件投递 */
void CMsgPr_EndMessage(void){
	msgDC.m_msg     = WM_NULL;
    msgDC.m_msgPrce = WM_NULL;
}

2.设计 页面框架

2.1 页面框架.h文件

  • 数据类型说明:
    • typedef unsigned char uint8_t;
    • typedef unsigned short int uint16_t;
    • typedef unsigned int uint32_t;
#ifndef _MENU_H_
#define _MENU_H_
/* Includes ----------------------------------------------------------*/
/* 系统库 头文件 ... ... */
#include "bsp_key.h"  	/* 底层按键驱动 */	 
#include "bsp_lcd.h"  	/* 底层LCD驱动 */
/* 其它库 头文件 ... ... */

/* Define ------------------------------------------------------------*/
/* 用于页面文件的调试打印 0:close 1:open ---------------------------------*/
#if 0
	#define Mprint printf
#else
	#define Mprint(...)
#endif

/* 系统参数设置 单位:根据定时器巡检时基(此处为10ms) -------------------------*/
/* 定时时间到后会执行相应的操作 */
#define MENU_EVENT_DURATION                 (25U)  	 	/* 事件定时: 250ms 	*/
#define MENU_BACKLIGH_DURATION              (2500U) 	/* 背光定时: 25S 	*/
#define MENU_EVENT_OFF		                (0x00U) 	/* 事件开*/
#define MENU_EVENT_OPEN	                   	(0x01U) 	/* 事件关 */

/* 系列菜单ID定义 */
#define MENU_FUNTION_1                 		(0x00U) 
/* 此处添加菜单ID 每一个page都有一个ID,以作区分 ... ... */

/* Typedef ----------------------------------------------------------*/
typedef void (*EXEFUN)(void);			/* 自定义回调函数类型 */
typedef void (*BACKLIGHT_FUN)(void); 	/* 自定义背光操作(开/关)函数类型 */

/* 菜单结构体 */
typedef struct{
    EXEFUN		g_pfun;			/* 函数指针 */  
    MsgPrce 	g_CurrentFocus;	/* 当前焦点 */
    uint8_t 	g_menuIndex;	/* 菜单索引 */
    	
    struct {
      uint32_t  g_LightTimer;  	/* 背光定时器 */
      uint32_t  g_EventTimer;  	/* 事件定时器 */
    }MenuTimer;					/* 定时器变量 */

    struct {
      uint8_t b_LightTM :1;		/* 定时背光标志 */
      uint8_t b_EventTM :1; 	/* 定时事件标志 */
    }MenuTMFlag;         		/* 定时器标志位 */
    
	BACKLIGHT_FUN g_pOnBackLight;	/* 开启背光灯函数指针 */
	BACKLIGHT_FUN g_pOffBackLight;	/* 关闭背光灯函数指针 */
}g_GlobalMenu;

/* Enume ----------------------------------------------------------*/
/* Extern ---------------------------------------------------------*/
extern g_GlobalMenu g_MenuVar; /* 外部声明菜单结构变量 */

/* Declaration ----------------------------------------------------*/
/* 初始化菜单 执行之前调用 */
void R_MenuListInit(BACKLIGHT_FUN onBackLight, BACKLIGHT_FUN OffBackLight);
/* 用于定时器扫描计时,在定时器中断里调用   */
void R_MenuTimerScan(void);
/* Menu获取按键事件,在主函数里 循环调用   */
void R_AppMenu_KeyEvent(uint8_t (*GetKeyEvent)(void), uint8_t KeyNoPress);
/*  Menu定时器事件巡检,在主函数里循环调用 */
void R_MenuTimerEvent(void);												
#endif  /* _MENU_H_ */

2.2 页面框架.c文件

2.2.1 定义全局菜单变量

作用: 记录操作

g_GlobalMenu g_MenuVar;		/* 菜单结构变量 */

2.2.2 设置当前执行目标函数

  • 名称: R_MenuSetFocus
  • 作用: 保存当前执行的目标函数,并设置事件对象(只在该文件下有效,对外不提供接口)
  • 参数: aFocus -> 目标执行函数
static void R_MenuSetFocus(MsgPrce aFocus){ 
    g_MenuVar.g_CurrentFocus = aFocus; 	/* 缓存当前输入焦点 	 */
    CMsgPr_SetMsgObject(aFocus);   		/* 设置消息栈的消息对象	*/
}

2.2.3 初始化菜单 (外部接口)

  • 名称: R_MenuListInit
  • 作用: 初始化菜单,在底层驱动初始化后调用,且必须在 while(1){} 执行之前调用
  • 参数: OnBackLight : 开启lcd背光函数 OffBackLight: 关闭lcd背光函数
  • 说明: 该函数指示初始化菜单变量,并跳转到第一级执行函数,该函数名称为 void R_MenuObjectProc(void);
void R_MenuListInit(BACKLIGHT_FUN onBackLight, BACKLIGHT_FUN OffBackLight){	
    /* 关闭背光 及 事件 标志 */
	g_MenuVar.MenuTMFlag.b_EventTM    = MENU_EVENT_OFF;
	g_MenuVar.MenuTMFlag.b_LightTM    = MENU_EVENT_OFF;

    /* 清空背光计时 及 事件计时 */
    g_MenuVar.MenuTimer.g_EventTimer  = 0; 
    g_MenuVar.MenuTimer.g_LightTimer  = 0;
	
	/* 指定背光开启/关闭函数 */
	g_MenuVar.g_pOnBackLight  = onBackLight;
	g_MenuVar.g_pOffBackLight = OffBackLight;
	
    /* 设置投递事件:操作完成后自动跳转到 设置目标函数 中执行初始化操作 */
    R_MenuSetFocus(R_MenuObjectProc);
    /* 向控件派发 初始化事件 */
    CMsgPr_SendMessage(g_MenuVar.g_CurrentFocus, WM_INIT); 
    /* 投递结束事件 就转到执行目标操作函数 进行初始化 */
    CMsgPr_EndMessage();  									
}	

2.2.4 定时器扫描事件函数 (外部接口)

  • 名称: R_MenuTimerScan
  • 作用: 用于定时器扫描计时,在定时器中断里调用
  • 说明: 当前定时器中断时间:10ms
void R_MenuTimerScan(void){
	/* 事件定时器开启后计时 */ 
	if(g_MenuVar.MenuTMFlag.b_EventTM)
		g_MenuVar.MenuTimer.g_EventTimer++;
	
	/* 背光定时器开启后计时 */
	if(g_MenuVar.MenuTMFlag.b_LightTM)
		g_MenuVar.MenuTimer.g_LightTimer++;
}

2.2.5 获取按键事件函数 (外部接口)

  • 名称: R_AppMenu_KeyEvent
  • 作用: Menu获取按键事件,在主函数里 循环调用
  • 参数: GetKeyEvent -> 获取按键的键值函数指针 KeyNoPress -> 无按键按下的键值
void R_AppMenu_KeyEvent(uint8_t (*GetKeyEvent)(void), uint8_t KeyNoPress){
	/* 获取按键键值 */
    uint8_t g_key_event = GetKeyEvent();      	
	
	/* 有按键按下 */
	if(KeyNoPress != g_key_event){
        /* 这样做的目的为 有按键事件发生时 重新计时背光 */
		g_MenuVar.MenuTimer.g_LightTimer = 0; 
        /* 像当前目标焦点 派发按键事件 */
		CMsgPr_SendMessage(g_MenuVar.g_CurrentFocus, g_key_event); 	
	}
}

2.2.6 Menu定时器事件检测 (外部接口)

  • 名称: R_MenuTimerEvent
  • 作用: Menu定时器事件巡检,在主函数里循环调用
  • 说明: 检测当前 定时器事件 是否到来,到了执行相应的操作
void R_MenuTimerEvent(void){
	/* 事件定时器打开 定时发送定时消息 */ 
	if(g_MenuVar.MenuTMFlag.b_EventTM){
        /* 事件定时到 */
		if(g_MenuVar.MenuTimer.g_EventTimer >= MENU_EVENT_DURATION){
            /* 向控件派发 定时器事件 */
            CMsgPr_SendMessage(g_MenuVar.g_CurrentFocus, WM_TIMER);
            /* 投递结束事件 就转到执行目标操作函数 进行定时器事件操作 */
    		CMsgPr_EndMessage();  									
        }
    }
    
	/* 背光定时器打开 开启背光 */
	if(g_MenuVar.MenuTMFlag.b_LightTM){
        /* 背光定时器 25s时间到 */
		if(g_MenuVar.MenuTimer.g_LightTimer >= MENU_BACKLIGH_DURATION){
            /* 关闭OLED背光 */
			g_MenuVar.g_pOffBackLight(); 
            /* 关闭背光标志位 防止溢出 */
			g_MenuVar.MenuTMFlag.b_LightTM    = MENU_EVENT_OFF;
            
            /* 设置投递事件:操作完成后自动跳转到 设置目标函数 中执行初始化操作 */
            /* 设置目标函数 */
    		R_MenuSetFocus(R_MenuObjectProc);
            /* 向控件派发 初始化事件 */
    		CMsgPr_SendMessage(g_MenuVar.g_CurrentFocus, WM_INIT); 
            /* 投递结束事件 就转到执行目标操作函数 进行初始化 */
    		CMsgPr_EndMessage();  									
		}
	}	
}

2.2.7 标准用户操作框架

  • 名称: R_MenuObjectProc
  • 作用: 用户自定义目标函数, 这里只是标准框架,用户需要多级页面的话,需要编辑多个
  • 说明: 程序中的 KEY_1_UP 以及 KEY_1_DOWN 是底层提供的事件, 该事件必须在 WM_KEY_OFFSET(message.h中)基础上偏移
void R_MenuObjectProc(void)
{ 
	switch(CMsgPr_GetMessage()){ /* 获取当前事件 */ 
		
		case WM_INIT: /* 初始化事件 */
			/* 初始化事件发生后 执行的操作 ... ... */
            /* 开启事件标志 */
			g_MenuVar.MenuTMFlag.b_EventTM  = MENU_EVENT_OPEN; 
            /* 当前菜单索引 */
			g_MenuVar.g_menuIndex = MENU_FUNTION_1; 
            /* 默认消息结束 */
			CMsgPr_EndMessage(); 								
			break;

		case KEY_1_UP:	/* 按键1抬起时 的事件操作 */
            /* 打开LCD背光灯 */
			g_MenuVar.g_pOnBackLight(); 
            /* 复位背光计时 重新开始计时 */
			g_MenuVar.MenuTimer.g_LightTimer = 0; 
            /* 开启背光标志 开始在定时器中断里计时 */
			g_MenuVar.MenuTMFlag.b_LightTM   = MENU_EVENT_OPEN; 
			break;

            case KEY_1_DOWN: 	/* 按键1按下时 的事件操作 */
            /* 指向下一个执行函数 用户自定义 */
			g_MenuVar.g_pfun = /* xxxxx */; 
             /* 设置输入焦点 */
			R_MenuSetFocus(g_MenuVar.g_pfun); 
            /* 派发下一菜单的初始化事件 */
			CMsgPr_PollMessage(WM_INIT);  	        
			break;

		case WM_TIMER:   /* 定时器事件 */ 
			/* ... 定时器事件发生后 执行的操作 ... */
			g_MenuVar.MenuTimer.g_EventTimer = 0;	/* 复位定时器计时 */
			break;
					
		default: 
			CMsgPr_EndMessage(); /* 默认消息结束 */
			break;
	}
}

3. 主函数中调用框架

int main(){	
    /* 硬件设备初始化 ....。。。。。.......................*/
	/* 菜单初始化 LcdDisplayOn-> 开启LCD背光操作函数 
		LcdDisplayOFF -> 关闭LCD背光操作函数 */	
	R_MenuListInit(LcdDisplayOn, LcdDisplayOFF);
	
	for(;;){
		R_MenuTimerEvent(); 			   /* 定时器事件触发 */  
        /* 菜单控制按键事件处理: 参数:获取按键键值函数, 未按下键值 */		   
 		R_AppMenu_KeyEvent(bsp_GetKey, KEY_NONE); 	
		/* 其它任务在此处添加 ...........................*/
	}
}

4. 说明

  • 设计初始目的: 断码屏幕 涉及 自动灭屏和光标闪烁等(光标闪烁已删减, 可用户添加), 本来页面就简便,为方便开发,所以设计此应用
  • 原创性说明: 此处不是原创,原帖在阿莫论坛中;吸收学习后改造为以上程序
  • 此贴类型: 学习笔记

你可能感兴趣的:(MCU)