开篇前来一首歌放松一下,推荐简弘亦的新歌:忽如一面
,妥妥的歌唱实力派,可是不知道为啥感觉一直不温不火,我是听了他的不染
喜欢上的,好了,进入正题。
现在市面上有很多成熟的GUI,老早前就听说过STemWin了,一直想学习怎么用,但一直又觉得没地方可用,现在手上平台很多(人送外号:最不缺钱且烧钱的玩家),只要有时间,还是要把它搞起来!毕竟多掌握一门技能那肯定有好处的。
关于GUI的移植,之前也在小熊派上分享过一些别的GUI的教程,文章链接如下:
废话不多说,先上效果图:
该Demo用GUIBuilder工具画了一个Listview
的控件以及三个Text
控件以及一个Image控件,最后保存生成代码拷贝到Keil MDK后编译下载到小熊派上运行。
1、STemWin简介
STemWin是SEGGER
公司授权给意法半导体(ST)
公司,使用ST芯片的用户可免费使用STemWin,关于STemWin的显示效果,有兴趣的可自行百度搜索查看。
1.1 下载并熟悉官网最新版本的STemWin
解压后得到以下目录,移植STemWin最主要关心的是Libraies
目录下的与STemWin相关的库文件,其它的多数是ST官网的一些Demo例程,暂时用不着,如下图所示:
简单介绍下以下几个目录:
2、小熊派移植STemWin
2.1 移植STemWin到小熊派
2.1.1 添加STemWin软件包到带LCD的基础工程
首先需要一个源码工程,这个直接从小熊派源码包拿就行了,不需要重新去写,我们本次移植的是不带操作系统的。
解压,然后在工程目录下创建两个和STemWin相关的目录:
将1.1下载的STemWin软件包整个拷贝到STemWin这个目录下。
2.1.2 添加STemWin相关库及文件到Keil MDK
以下文件添加模板参考了正点原子的STM32F429 EMWIN开发手册:
这个app.c用来存放GUIBuilder
软件生成的代码,最后通过调用STemWin的CreateWindow
函数来展示界面。
注意:正点原子的STemWin版本可能比较老,当前该版本所有的GUI读点、画点等函数配置只需要在LCDConf_FlexColor_Template.c
下就可以完成了,不需要GUIDRV_Template.c
这个文件,正点原子的教程和我现在下的最新版本移植还有些区别,仅供参考。
2.1.3 修改STemWin所需要的内存空间
位于GUIConf.c
文件:
#define GUI_NUMBYTES (16*1024)//0x200000 //设置EMWIN内存大小
注意:如果该参数设置太大的话Keil会报空间不够的错误,这里我们根据需求,将其设置为16*1024
即可,它分配的其实是一块静态连续的内存空间:
2.1.4 配置LCD及STemWin相关
位于LCDConf_FlexColor_Template.c
文件:
在这个文件中,我们需要包含LCD驱动文件,小熊派已经实现好了,我们直接拿过来用就行了。
#inlcude "lcd.h"
注意lcd.h中并没有写命令和写数据的函数声明,这里我们需要将lcd.c修改一下,然后在lcd.h导出这两个函数,才能给LCDConf_FlexColor_Template.c
使用。
(1)修改LCD显示分辨率
//更改显示分辨率 #define XSIZE_PHYS 240 // To be adapted to x-screen size #define YSIZE_PHYS 240 // To be adapted to y-screen size
(2)实现写指令、写数据、写多个数据、读多个数据接口
注意,官方给的模板默认是一次2个字节的写入,而小熊派LCD是SPI OLED(ST7789)
,实现的接口是一次只写1个字节的数据,所以我们要将接口改成1个字节的写入,否则显示会有问题。
/******************************************************************** * * LcdWriteReg 写寄存器 * * Function description: * Sets display register */ static void LcdWriteReg(U8 Data) { // ... TBD by user LCD_Write_Cmd(Data); } /******************************************************************** * * LcdWriteData 写数据 * * Function description: * Writes a value to a display register */ static void LcdWriteData(U8 Data) { // ... TBD by user LCD_Write_Data(Data); } /******************************************************************** * * LcdWriteDataMultiple 写多个数据 * * Function description: * Writes multiple values to a display register. */ static void LcdWriteDataMultiple(U8 * pData, int NumItems) { while (NumItems--) { // ... TBD by user LCD_Write_Data(*pData++); } } /******************************************************************** * * LcdReadDataMultiple 读多个点 * * Function description: * Reads multiple values from a display register. */ static void LcdReadDataMultiple(U8 * pData, int NumItems) { while (NumItems--) { // ... TBD by user //没有读点函数,留空 } }
(3)修改LCD_X_Config函数
void LCD_X_Config(void) { GUI_DEVICE * pDevice; CONFIG_FLEXCOLOR Config = {0}; GUI_PORT_API PortAPI = {0}; // Set display driver and color conversion pDevice = GUI_DEVICE_CreateAndLink(GUIDRV_FLEXCOLOR, GUICC_M565, 0, 0); // Display driver configuration, required for Lin-driver LCD_SetSizeEx (0, XSIZE_PHYS, YSIZE_PHYS); LCD_SetVSizeEx(0, VXSIZE_PHYS, VYSIZE_PHYS); // Orientation //Config.Orientation = GUI_SWAP_XY | GUI_MIRROR_Y; Config.Orientation = 0 ; Config.NumDummyReads = 2; GUIDRV_FlexColor_Config(pDevice, &Config); // Set controller and operation mode PortAPI.pfWrite8_A0 = LcdWriteReg; PortAPI.pfWrite8_A1 = LcdWriteData; PortAPI.pfWriteM8_A1 = LcdWriteDataMultiple; PortAPI.pfReadM8_A1 = LcdReadDataMultiple; //ST7789为GUIDRV_FLEXCOLOR_F66709 GUIDRV_FlexColor_SetFunc(pDevice, &PortAPI, GUIDRV_FLEXCOLOR_F66709, GUIDRV_FLEXCOLOR_M16C0B8); }
那么这些参数具体是怎么适配的呢?详情请看STemWin
手册参数配置部分的说明,手册讲得相当详细,这里就不再多解释了。
(4)添加LCD初始化函数
int LCD_X_DisplayDriver(unsigned LayerIndex, unsigned Cmd, void * pData) { int r; (void) LayerIndex; (void) pData; switch (Cmd) { case LCD_X_INITCONTROLLER: { // // Called during the initialization process in order to set up the // display controller and put it into operation. If the display // controller is not initialized by any external routine this needs // to be adapted by the customer... // // ... //调用LCD初始化 LCD_Init(); return 0; } default: r = -1; } return r; }
(5)添加时基触发
这里我们直接将该变量添加到STM32自带的SysTick
回调函数里就可以了,如下所示:
位于:stm32l4xx_it.c
/** * @brief This function handles System tick timer. */ void SysTick_Handler(void) { /* USER CODE BEGIN SysTick_IRQn 0 */ #include "GUI.h" extern volatile GUI_TIMER_TIME OS_TimeMS; ++OS_TimeMS ; /* USER CODE END SysTick_IRQn 0 */ HAL_IncTick(); HAL_SYSTICK_IRQHandler(); /* USER CODE BEGIN SysTick_IRQn 1 */ /* USER CODE END SysTick_IRQn 1 */ }
添加完时基以后,需要在主循环中循环调用GUI_Delay()函数,为什么呢?官方文档有相应的描述:
到这里,STemWin在小熊派上的移植工作就基本结束了,接下来直接编写例程。
3、编写Demo验证
3.1 使用GUIBuilder画界面
这个工具在使用的时候需要注意,在执行该软件前先把配置文件删除。
为什么呢?我们打开这个配置文件看看就清楚了:
注意:这个配置文件里填的是Software目录的绝对路径,但这个软件没那么智能,换个地方打开GUIBuilder它还不更新!!!这会导致软件没法用!!!所以用这个工具前记得把它删了,反正重新打开它还会继续生成,这时候的路径才是正确的。
打开软件后,绘制界面如下:
绘制完毕后Ctrl+S
保存,这时候会自动生成代码逻辑,就在当前路径下的WindowDLG.c
中。
3.2 添加软件生成的代码到工程中
接下来我们打开这个程序,将所有代码复制到app.c中。
在.h文件中导出CreateWindow
函数,该函数就是刚刚画的那个页面的实现流程,详情请分析app.c里的源代码。
3.3 在主函数中添加代码逻辑
包含头文件:
#include "GUI.h" #include "app.h"
主函数:
int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration----------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_SPI2_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ //打开CRC __HAL_RCC_CRC_CLK_ENABLE(); //初始化STemWin GUI_Init(); //调用CreateWindow CreateWindow(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ GUI_Delay(1); } /* USER CODE END 3 */ }
编译下载后运行结果:
关于STemWin要学习的东西还有很多,就安富莱和正点原子的学习手册几千页就已经够学习很久了,我也打算以它为主要学习目标,深入学习,后续做出一些好看的样例再继续分享。
4、案例下载
公众号后台回复:STemWin 即可获取本节案例的下载链接。