CubeMX移植STemwin
在前面的学习中,我们已经实现了LCD的显示与触摸屏的控制驱动的相关移植。但是,光是显示文字,画画简单的圆形矩形等图像,变变颜色等这种基础操作显然不够美观优雅。那么,对于一个美观舒适的GUI界面的需求就自然而然地应运而生了。
现在主流的嵌入式GUI其实很多,像是TouchGFX、EmbeddedWizard、AWTK、Qt、LittlevGL和emWin等等,实际上种类相当丰富,功能也各有千秋。然而,界面看似高端特效炫酷的GUI,实质上都基于高精度的贴图与高速的重绘,由于我所使用的开发板战舰V2性能与资源所限,别说那些基于Linux系统的高端GUI了,光是连性能要求不算很高的TouchGFX都需要F4往上的性能,不是区区F103能跑得动的。因此,LittlevGL和emWin成为了相对靠谱的选择。
STemwin可以简单理解为SEGGER专门为ST公司芯片所提供的emwin。由于STemWin支持裸机,有正点原子与野火的相应开发教程,且相关资料较多,遇到问题比较容易解决。同时,写下这篇文章时我对于操作系统尚无非常深刻的理解,先通过裸机学习GUI相关的内容知识,因此选择了emWin,在之后学习了操作系统之后可能会专门出一期基于RT-Thread的LittlevGL相关开发文章。
本文旨在记录一下自己的STemwin学习经历,如有错误与不足,还望海涵,也请大佬们不吝赐教。
软件仿真
emWin的GUI界面效果可以在Visial Studio上进行仿真和演示,这一部分内容无论是野火还是正点原子的文档中都讲的比较详细,这里就不再重复造车轮了。由于我移植的emwin版本较新,参考的火哥的教程,这里主要讲一讲踩过的坑:
路径错误问题
当出现这类报错时,首先确认一下是否配置了对应的路径输出位置,建议使用相对位置而不是绝对位置。注意路径不要有中文。
将“$(ProjectDir)Exe\”输入到 输出目录
将“$(ProjectDir)Output\Debug\”输入到 中间目录
目标文件名更改为“GUISimulationDebug” 完成后点“应用”如下图所示:
参考一下这篇文章:
解决VS2013运行emwin模拟器提示“无法启动程序.....找不到指定文件.exe“_quanhengwen123的博客-CSDN博客
库错误问题
问题原因:
VS2015中将printf()和scanf()之类的方法改为内联函数。为了兼容用到了之前的printf()和scanf()的程序和库,所以创建了legacy_stdio_definitions.lib。在2015中引用这个库即可。
我的VS版本为2013,对于低于VS2015的版本,legacy_stdio_definitions.lib
库是不需要添加的,直接删除即可。
2020-06-06-c++-vs2015-关于legacy_stdio_definitions库 - 林兴洋的空间站
CubeMX配置
尝试过PC软件仿真后,我们应该已经大致了解了emwin的显示效果。下面我们就可以开始进行移植准备与相关配置了。
开启CRC校验
由于前面已经移植过LCD与触摸屏驱动,因此在CubeMX中我们只需要配置开启一下CRC校验就行。STemWin由SEGGER公司授权给ST(意法半导体),使用ST芯片的用户可以免费使用STemWin。CRC校验这一步骤正是为了判断是否为ST芯片,不开启将无法调用库中函数。
移植emwin文件
首先,我们需要了解移植 STemWin 所需要的配置:
配置系统功能;
配置操作系统接口;
配置内存管理接口;
配置显示驱动接口;
配置触摸屏接口。
STemwin的文件结构如下图所示(图源自野火emwin手册):
其中,在单片机中我们用到的是Config
、inc
、Lib
、OS
这四个文件夹中的相关内容。
另外,Lib
文件夹中我们实际使用的库只是其中的一个,根据自己的开发环境、硬件支持和操作系统需求进行选择,由于占用空间较大,可以删除其他不需要的库文件。
以我的环境为例做简单的说明:我使用的战舰V2开发板配套显示屏为3.5寸TFT-LCD,型号ILI9341 的颜色格式为 RGB565,IDE环境为Keil5。
STemwin5.44版之后库文件变为了.a格式,使用 wc16 和 wc32 区分不同的编译器,标有 wc16
的可用于 EWARM(IAR for ARM) 7
和 Keil 5
,标有 wc32
的可用于 EWARM(IAR for ARM) 8
和 SW4STM32(GCC)
,因此选择wc16
;
在 emWin V5.30 之前的很长一段时间内, 逻辑颜色都只有 ABGR 这一种颜色格式,随硬件发展开始支持ARGB格式。emwin的固定调色板的标识符有些带有字母 M,而另外一些则不 带字母 M。这也是跟之前的逻辑颜色格式有关,含字母 M
的表示 ARGB
颜色格式下使用的调色板,不带M
的表示在 ABGR
格式下使用的调色板。ILI9341 的颜色格式为 RGB565,所以可选择 GUICC_M565。
因此,我选择保留的库文件为STemWin_CM3_wc16_ARGB.a
与STemWin_CM3_OS_wc16_ARG.a
,根据有无使用操作系统选择相应的库文件。
另外要注意一点的是,显示驱动方式有两种,一种是画点,另一种是操作显存。画点是万能的,适用于所有屏幕。操作显存适用于RGB屏幕。LCDConf_Lin_Template.c
文件是以显存方式进行屏幕显示驱动的底层填充接口,我们的移植不会使用,因此删除.c与.h文件
。
移植参考
如果看到文件图标含有黄色的钥匙标识无法修改,说明没有相应的文件权限,关闭文件夹的只读即可正常修改文件。
修改emwin文件
移植预修改
由于正点原子的lcd驱动文件与emwin有所重名,因此可将lcd.h
与lcd.c
文件更改为ILI93xx.h
与ILI93xx.c
。之后,将所有引用过lcd.h头文件处改为更名后的.h文件名。此外,LCD_Init()
函数也需要更改,可修改为TFTLCD_Init()
并替换所有声明与调用该函数处的代码。
此外,对于精英版与战舰板,在LCD驱动的.h文件中有如下宏定义:
#define LCD ((LCD_TypeDef *) LCD_BASE)
需要将该宏进行更名,可改为TFTLCD
,并在相应.c文件中替换为对应的更名后宏。
在进行完以上操作后,先编译烧录一遍更改后的文件,确认无误后再进行后续的移植开发工作。
此时可能会出现两种常见的报错:
- .a库文件报错
Invalid line start
- 无法找到
LCDConf.h
文件
第一个问题的解决方法是,在.a库上点击右键选择options,将文件类型从"File Type" 改至 “Library file”,重新编译。
第二个问题的解决方法是直接新建一个空白文件名为LCDConf.h即可。
修改显示配置文件
GUIConf.c
GUIConf.c文件的主要作用是分配STemWin内部缓存。
如果内存选择使用内部RAM,直接使用GUI_NUMBYTES
改变 emWin 使用的内存块大小即可,正点原子例程中mini板为30*1024
,精英板为100*1024
;如想使用外部SRAM,推荐移植自己板子开发商所提供的内存管理程序进行相应配置。
GUIDRV_Template.c
GUIDRV_Template.c文件里面是自定义的画点填充底层函数,通过画点读点方式,适用于所有的屏幕。
画点有两种做法:使用自定义画点函数,或者使用STemWin自带画点函数(依据MCU屏幕IC)。正点原子的例程使用的是自定义函数,且ST自带的函数效率也并不高,因此直接移植例程函数即可。
由正点原子手册,总共需要修改四个函数:_GetPixelIndex()
返回指定位置颜色的读点函数,
_SetPixelIndex()
设置指定位置颜色的打点函数,_FillRect()
绘制填充颜色的矩形函数以及_DrawBitLine16BPP()
绘制16BPP位图函数。具体配置代码过长就不贴了,去对应开发手册很容易找到。
另外,由于使用了LCD驱动的函数,需要添加头文件#include "ILI93xx.h"
。
此外,还需要定义一下LCD的命令和数据位.
u32 UCGUI_LCD_CMD = 0X6C000000; //地址为0X6C000000;
u32 UCGUI_LCD_DATA = 0X6C000800; //地址为0x6C000800;
LCDConf_FlexColor_Template.c
LCDConf_FlexColor_Template.c文件为屏幕显示驱动底层填充接口,以画点方式实现。前文提过STemwin的默认打点读点函数的效率也不高,因此这部分可直接注释,使用GUIDRV_Template.c文件里面自定义的相关函数与自己的LCD驱动函数。
根据正点原子手册,LcdWriteReg()
、LcdWriteData()
、LcdWriteDataMultiple()
、LcdReadDataMultiple()
四个LCD驱动相关函数直接删去,使用LCD驱动中TFTLCD_Init()
函数即可,保留LCD_X_DisplayDriver()
显示驱动的回调函数,在显示驱动多任务时处理数据与指令;以及LCD_X_Config()
接口配置函数,用于选择自定义接口还是默认接口。
LCD_X_Config()
函数我们保留三行:
void LCD_X_Config(void) {
GUI_DEVICE_CreateAndLink(&GUIDRV_Template_API, GUICC_M565, 0, 0);
//配置显示驱动接口与颜色转换格式,链接至指定图层设备。
LCD_SetSizeEx (0, lcddev.width, lcddev.height);//设置物理屏幕区域
LCD_SetVSizeEx (0, lcddev.width, lcddev.height);//设置虚拟屏幕区域
}
同样,由于用到了LCD驱动中的参数,不要忘了添加头文件#include "ILI93xx.h"
。
至此,我们的LCD移植配置工作就完成了。我们可以去main函数中进行测试:
/* USER CODE BEGIN Includes */
#include "GUI.h"
/* USER CODE END Includes */
/* USER CODE BEGIN 2 */
GUI_Init();
/* USER CODE END 2 */
/* USER CODE BEGIN 3 */
GUI_SetBkColor(GUI_BLUE); //设置背景颜色
GUI_SetColor(GUI_YELLOW); //设置颜色
GUI_Clear(); //清屏
GUI_SetFont(&GUI_Font24_ASCII); //设置字体
GUI_DispStringAt("HELLO WORD!", 0, 0);
/* USER CODE END 3 */
如果出现红蓝反色的情况,那么到GUI_ConfDefaults.h
下将GUI_USE_ARGB
宏定义由0
改为1
。
修改触摸配置文件
在完成显示配置之后,我们需要进行的就是触摸屏相关配置了。
emWin 并没有将触摸设备接口整理为单独的.c 文件,不过提供了触摸相关的库函数, 用户需自行编写触摸接口。emwin库函数如下:
GUI_X_TOUCH_Analog.c
正点原子将前四个函数,即GUI_X_TOUCH_ActivateX()
、ActivateY()
、MeasureX()
和MeasureY()
的配置放在了自定义的GUI_X_TOUCH_Analog.c
文件中,我们新建文件并按例程写入相应函数配置即可。篇幅原因就不展开来写了,具体代码参考相应手册。
GUIConf.h
在GUI配置文件中我们需要确认的是有无将GUI_SUPPORT_TOUCH
设置为1
,即支持触摸屏。
LCDConf_FlexColor_Template.c
正点原子例程中该文件需要定义上下左右四个边界的AD值,以下为例程定义中实际测量参数:
//与触摸屏有关定义,根据实际情况填写
#define TOUCH_AD_TOP 160 //按下触摸屏的顶部,写下 Y 轴模拟输入值。
#define TOUCH_AD_BOTTOM 3990 //按下触摸屏的底部,写下 Y 轴模拟输入值。
#define TOUCH_AD_LEFT 160 //按下触摸屏的左侧,写下 X 轴模拟输入值。
#define TOUCH_AD_RIGHT 3990 //按下触摸屏的右侧,写下 X 轴模拟输入值。
//屏幕大小
#define XSIZE_PHYS 320 //X轴
#define YSIZE_PHYS 240 //Y轴
#define VXSIZE_PHYS 320
#define VYSIZE_PHYS 240
此值可以通过串口或者在主函数中调用emwin提供的函数进行获取,函数名为Mytouch_MainTask()
,具体代码如下:
//触摸屏定位设置
void Mytouch_MainTask(void)
{
GUI_PID_STATE TouchState;
int xPhys;
int yPhys;
GUI_Init();
GUI_SetFont(&GUI_Font20_ASCII);
GUI_CURSOR_Show();/////在这出问题
GUI_CURSOR_Select(&GUI_CursorCrossL);/////
GUI_SetBkColor(GUI_WHITE);
GUI_SetColor(GUI_BLACK);
GUI_Clear();
GUI_DispString("Measurement of\nA/D converter values");
while (1)
{
GUI_TOUCH_GetState(&TouchState); // Get the touch position in pixel
xPhys = GUI_TOUCH_GetxPhys(); // Get the A/D mesurement result in x
yPhys = GUI_TOUCH_GetyPhys(); // Get the A/D mesurement result in y
GUI_SetColor(GUI_BLUE);
GUI_DispStringAt("Analog input:\n", 0, 40);
GUI_GotoY(GUI_GetDispPosY() + 2);
GUI_DispString("x:");
GUI_DispDec(xPhys, 4);
GUI_DispString(", y:");
GUI_DispDec(yPhys, 4);
GUI_SetColor(GUI_RED);
GUI_GotoY(GUI_GetDispPosY() + 4);
GUI_DispString("\nPosition:\n");
GUI_GotoY(GUI_GetDispPosY() + 2);
GUI_DispString("x:");
GUI_DispDec(TouchState.x,4);
GUI_DispString(", y:");
GUI_DispDec(TouchState.y,4);
delay_ms(50);
}
}
另外,LCD_X_Config()
函数在使用触摸屏时也需要进行一定的修改,这一部分篇幅所限就不展示代码了,具体代码参考相应的手册即可。其中涉及到了GUI_TOUCH_Calibrate()
函数在运行时校准屏幕,正点原子例程中已经对自家配套LCD进行了相应的配置,直接移植例程即可。
定时器配置
CubeMX配置
至此,我们的触摸代码移植过程实际上已经完成了,但是GUI界面缺少了最关键的信号心跳,因此我们的触摸函数只会显示0而没有相应的动作反馈。我们需要通过定时器模拟相应的心跳,并通过另一个定时器周期调用GUI_TOUCH_Exec()
函数处理触摸事件。
例程中OS_TimeMS
作为1ms的心跳信号,GUI_TOUCH_Exec()
采用10ms中断处理相应的触摸事件。因此,我们于CubeMX中配置两个定时器,由于我们的HCLK为72Ms,可根据该需求配置为一个定时器预分频71
,计数周期999
,即1ms产生一次中断;另一个定时器预分频719
,计数周期999
,即10ms产生一次中断。F1系列的STM32的Tim2-Tim7均在APB1上,我们任选两个未使用的即可,如例程使用的TIM3与TIM6。我们在CubeMX中进行相应的配置并于NVIC中开启中断
:
对于定时器中的AutoReloadPreload
选项,用于选择是否自动同步影子寄存器,相当于控制是否开启缓存,在本例中影响不大,感兴趣的可以参考下这篇文章:STM32基础分析——PWM配置
对于CubeMX配置定时器不熟悉的,可以参考下这篇文章:【STM32】HAL库 STM32CubeMX教程六----定时器中断_Z小旋-CSDN博客
代码配置
CubeMX中配置好定时器后,我们回到Keil5中修改相应的代码实现功能。首先定义我们用到的心跳参数:
/* USER CODE BEGIN PV */
extern __IO int32_t OS_TimeMS;
/* USER CODE END PV */
然后,我们于Main函数中开启定时器中断并配置相应的中断回调函数:
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim3);
HAL_TIM_Base_Start_IT(&htim6);
/* USER CODE END 2 */
/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim == (&htim3))
{
OS_TimeMS++;
}
else if (htim == (&htim6))
{
GUI_TOUCH_Exec();
}
}
/* USER CODE END 4 */
在主函数中调用触摸调试函数:
/* USER CODE BEGIN 3 */
Mytouch_MainTask();
/* USER CODE END 3 */
完成以上的功能配置后,编译烧录程序,这样我们的触摸屏就能正常显示对应的触摸定位与AD值了。
在触摸定位程序正常运行后,我们的LCD移植就完成了,可以继续进行后续的GUI开发。对于初期学习,一般还是以控件学习为主降低难度,缩短开发周期。
本文为原创个人开发学习过程的记录总结,难免存在不少错误与疏忽,欢迎留言或通过社交媒体联系我指出问题,也欢迎在评论中留下您的建议与见解,或是贴上中意的优质文章链接。
更好的车轮,更远的里程。
参考资料
STM32CubeIDE TFT-LCD移植STemWin,直接线性访问(LIN)驱动器
[STM32]关于移植STemwin所遇到的问题记录(1)_wangyijieonline的博客-CSDN博客
STM32F1 EMWIN开发手册 V2.0
[野火]《emWin应用开发实战指南—基于STM32》
[安富莱_STM32-V7开发板第3版emWin教程(V0.1).pdf]