Site Overlay

移植STemwin

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版本较新,参考的火哥的教程,这里主要讲一讲踩过的坑:

路径错误问题

image-20210416090445974

当出现这类报错时,首先确认一下是否配置了对应的路径输出位置,建议使用相对位置而不是绝对位置。注意路径不要有中文

将“$(ProjectDir)Exe\”输入到 输出目录

将“$(ProjectDir)Output\Debug\”输入到 中间目录

目标文件名更改为“GUISimulationDebug” 完成后点“应用”如下图所示:

image-20210513161737352

参考一下这篇文章:

解决VS2013运行emwin模拟器提示“无法启动程序.....找不到指定文件.exe“_quanhengwen123的博客-CSDN博客

库错误问题

image-20210416101119257

问题原因:

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芯片,不开启将无法调用库中函数。

image-20210512105357612

移植emwin文件

首先,我们需要了解移植 STemWin 所需要的配置:

配置系统功能;

配置操作系统接口;

配置内存管理接口;

配置显示驱动接口;

配置触摸屏接口。

STemwin的文件结构如下图所示(图源自野火emwin手册):

image-20210512120442357

其中,在单片机中我们用到的是ConfigincLibOS这四个文件夹中的相关内容。

另外,Lib文件夹中我们实际使用的库只是其中的一个,根据自己的开发环境、硬件支持和操作系统需求进行选择,由于占用空间较大,可以删除其他不需要的库文件。

以我的环境为例做简单的说明:我使用的战舰V2开发板配套显示屏为3.5寸TFT-LCD,型号ILI9341 的颜色格式为 RGB565,IDE环境为Keil5。

STemwin5.44版之后库文件变为了.a格式,使用 wc16 和 wc32 区分不同的编译器,标有 wc16 的可用于 EWARM(IAR for ARM) 7Keil 5,标有 wc32 的可用于 EWARM(IAR for ARM) 8SW4STM32(GCC),因此选择wc16;

在 emWin V5.30 之前的很长一段时间内, 逻辑颜色都只有 ABGR 这一种颜色格式,随硬件发展开始支持ARGB格式。emwin的固定调色板的标识符有些带有字母 M,而另外一些则不 带字母 M。这也是跟之前的逻辑颜色格式有关,含字母 M 的表示 ARGB 颜色格式下使用的调色板,不带M 的表示在 ABGR格式下使用的调色板。ILI9341 的颜色格式为 RGB565,所以可选择 GUICC_M565。

因此,我选择保留的库文件为STemWin_CM3_wc16_ARGB.aSTemWin_CM3_OS_wc16_ARG.a,根据有无使用操作系统选择相应的库文件。

另外要注意一点的是,显示驱动方式有两种,一种是画点,另一种是操作显存画点是万能的,适用于所有屏幕。操作显存适用于RGB屏幕LCDConf_Lin_Template.c 文件是以显存方式进行屏幕显示驱动的底层填充接口,我们的移植不会使用,因此删除.c与.h文件

移植参考

image-20210512155331781

如果看到文件图标含有黄色的钥匙标识无法修改,说明没有相应的文件权限,关闭文件夹的只读即可正常修改文件。

修改emwin文件

移植预修改

由于正点原子的lcd驱动文件与emwin有所重名,因此可将lcd.hlcd.c文件更改为ILI93xx.hILI93xx.c。之后,将所有引用过lcd.h头文件处改为更名后的.h文件名。此外,LCD_Init()函数也需要更改,可修改为TFTLCD_Init()并替换所有声明与调用该函数处的代码。

此外,对于精英版与战舰板,在LCD驱动的.h文件中有如下宏定义:

#define LCD            ((LCD_TypeDef *) LCD_BASE)

需要将该宏进行更名,可改为TFTLCD,并在相应.c文件中替换为对应的更名后宏。

在进行完以上操作后,先编译烧录一遍更改后的文件,确认无误后再进行后续的移植开发工作。

此时可能会出现两种常见的报错:

  1. .a库文件报错Invalid line start
  2. 无法找到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库函数如下:

image-20210513110333130

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中开启中断

image-20210513125132284image-20210513125240054

对于定时器中的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]

安富莱第3版emWin教程

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注