本帖最后由 数码小叶 于 2023-10-15 22:20 编辑
上一篇已经体验了STM32L496 Discovery kit的演示demo,功能很强大,在demo程序里演示了emWin、Embedded Wizard、TouchGFX Lite、TouchGFX Full四个GUI。于是乎会有一个疑问,这四个GUI体验的主界面又是怎么来的呢?
这个就需要通过源码来分析了。
demo的工程文件夹结构如下:
Binary文件夹:放入SD卡的二进制资源文件;
Config文件夹:配置文件,包括FreeRTOS、文件系统、HAL库、LCD、USB等等的配置;
Core文件夹:存储、界面、触摸校准等核心功能;
EWARM文件夹:IAR工程;
MDK-ARM:keil工程;
Modules;各个子功能和GUI加载模块;
STemWin_Addons:STemWin的扩展配置;
SW4STM32:System Workbench for STM32工程;
demo默认支持了三种IDE,选择keil工程打开,
工程结构看起来是文件夹的内的铺展开形式。
下面就来分析资源加载,找到main函数,在main函数最前面,和一般的工程一样,初始化时钟,HAL库,中断等,不同的是,多了RTC的设置以及读取判断,从而区分是系统上电还是软件复位
HAL_PWR_EnableBkUpAccess();
thirdPartyAdress = READ_REG(RTC->BKP30R);
if (thirdPartyAdress != 0)
{
WRITE_REG(RTC->BKP30R, 0);
__set_MSP(*(__IO uint32_t*) thirdPartyAdress);
((pFunc) (*(__IO uint32_t*) (thirdPartyAdress + 4)))();
}
software_reset_flag = READ_REG(RTC->BKP27R);
WRITE_REG(RTC->BKP27R, 1);
if (READ_REG(RTC->BKP31R) == 1)
{
WakeUpFromShutdown = SET;
}
else
{
WakeUpFromShutdown = RESET;
}
接着是创建GUI task
osThreadDef(GUI_Thread, GUIThread, osPriorityNormal, 0, 4096);
osThreadCreate (osThread(GUI_Thread), NULL);
在GUIThread这个task里,首先是触摸校准回调、使能触摸中断、开机图片展示,这个结合了之前的系统起来的原因,决定展示还是不展示。
从调用的接口可以看出,调用的是emWin的GUI 接口,并且这个log显示界面的资源应该不是存储在SD卡上的,界面加载后才是存储初始化
osThreadDef(STORAGE_Thread, StorageThread, osPriorityLow, 0, 128);
osThreadCreate (osThread(STORAGE_Thread), NULL);
osMessageQDef(osqueue, 10, uint16_t);
StorageEvent = osMessageCreate (osMessageQ(osqueue), NULL);
BSP_SD_Init();
BSP_SD_ITConfig();
在存储初始化里,开启了另外一个任务 StorageThread,负责挂载和卸载SD卡。
接着就是主界面的显示了,前面已经看出是emWin了,自然不出意外,同样是emWin函数,ICONVIEW_SetSpace设置间距,ICONVIEW_AddBitmapItem设置图标
ICONVIEW_SetSpace(hIcon, GUI_COORD_Y, 10);
ICONVIEW_SetFrame(hIcon, GUI_COORD_Y, 5);
ICONVIEW_SetSpace(hIcon, GUI_COORD_X, 10);
ICONVIEW_SetFrame(hIcon, GUI_COORD_X, 5);
ICONVIEW_SetBkColor(hIcon, ICONVIEW_CI_BK, GUI_BLACK);
for (i = 0; i < 4; i++)
{
if (ICONVIEW_AddBitmapItem(hIcon,module_prop[i].module->icon, (char *)module_prop[i].module->name)) {
Error_Handler();
}
}
到这可以很肯定,最基础的GUI是emWin,然后在emWin里跳转加载Embedded Wizard、TouchGFX Lite、TouchGFX Full三个GUI。
在GUIThread task主循环里,调用的是GUI_Exec()刷新界面,以及低功耗模式的相关设置处理。
在显示主界面的函数k_InitMenu里,首先是设置了一个回调cbWi,其参数是WM_MESSAGE *指针,通过查看函数调用,可以发现,WM_MESSAGE *贯穿各个模块,都能发现其身影,其包含了目标窗口和源窗口等信息:
typedef struct {
int MsgId;
WM_HWIN hWin;
WM_HWIN hWinSrc;
union {
const void * p;
int v;
GUI_COLOR Color;
void (* pFunc)(void);
} Data;
} WM_MESSAGE;
if (Id == ID_ICONVIEW_MENU)
{
if(sel < 4)
{
module_prop[sel].in_use = 1;
module_prop[sel].module->startup(pMsg->hWin, 0, 0);
sel = 0;
}
}
点击主界面4个图标后,实现载入各自GUI。二对于模块,不论是从程序,还是工程结构,都不是4个,这一点和自己开始猜想的不一样,
不是以GUI分类,而是以应用,demo加载了12个模块。对于每个模块,都是用K_ModuleItem_Typedef类型来定义
typedef struct
{
uint8_t id;
uint8_t *name;
GUI_CONST_STORAGE GUI_BITMAP *icon;
void (*startup) (WM_HWIN , uint16_t, uint16_t );
}
K_ModuleItem_Typedef;
主要的两个参数是GUI_BITMAP *和startup函数。
比如touchGFX,就是这么定义的
GUI_CONST_STORAGE GUI_BITMAP bmtgfx = {
110, // xSize
110, // ySize
220, // BytesPerLine
16, // BitsPerPixel
(unsigned char *)_actgfx, // Pointer to picture data
NULL, // Pointer to palette
GUI_DRAW_BMP565
};
而在void Startup(WM_HWIN hWin, U16 xpos, U16 ypos)函数里,则是主体功能,包括从SD卡载入flash程序和媒体文件。相比其他模块,Embedded Wizard、TouchGFX Lite、TouchGFX Full多了一个载入外部Falsh bin文件的操作。首先是校验Flash里是否是需要体验的demo,不是的话会重新载入,这就是体验demo的时候,touchGFX要重新烧录的原因。
hItem = PROGBAR_CreateEx((40-WM_GetWindowOrgX(hWin)), 110, 150, 20, hWin, WM_CF_SHOW, 0, ID_TGFX_PROGBAR_ID);
PROGBAR_SetMinMax(hItem, 0, f_size(&TGFX_file));
WM_MakeModal(hItem);
k_ResourcesCopy(hItem, &TGFX_file, DEMO_TGFX_LITE_RES_ADDRESS);
程序拷贝的同时,显示一个进度条。k_ResourcesCopy函数实现了Qspi Flash的写入。
资源的校验载入和程序文件是一致的,不同点就是大小和位置。所有都无误后,会跳转到模块执行:
k_JumpToSubDemo(RTC->BKP30R, DEMO_TGFX_LITE_ADDRESS)
至此,工程的大致框架已经了解,后面就是针对一个具体的应用来分析了