在STM32F407上使用TouchGFX用于软件SPI的0.96寸OLED

一、TouchGFX必要的简单价绍

TouchGFX是一款针对STM32微控制器进行了优化的免费高级图形软件框架。只能用于STM32的芯片,TouchGFX会检测芯片。TouchGFX目前了解的用于STM32G0、STM32F4及以上的处理器。所以常用的STM32F103C8T6不能使用TouchGFX。 实际上STM32CubeMX也不会提供针对C8T6的TouchGFX的功能选项。TouchGFX是使用C++编写的图形框架。

二、全过程使用的开发工具

核心板:STM32F407VET6;显示屏:SPI协议的0.96寸OLED;
软件:STM32CubeMX;Keil5 ARM;TouchGFX Designer;

三、使用STM32CubeMX生成基础工程

1.新建工程选择STM32F407VET6。双击型号,转到工程页面。
2.首先,选择左侧“System Core”下的“SYS”。选择“Debug”为“Serial Wire”。

3.选择左侧“System Core”下的“RCC”。选择“High Speed Clock(HSE)”为“Crystal/Ceramic Resonator”。

4.紧接着,选择蓝色菜单上面一行的“Clock Configuration”,转到时钟树页面。设置外部晶振(HSE左侧的Input frequency)为8MHz,设置HCLK为最大值168MHz。 之后会弹出窗口,选择确定,进行自动配置。

5.选回“Pinout & Configuration”。选择左侧“System Core”下的“GPIO”。点击右侧引脚并设置成GPIO_Output。对应下图命名。

6.选择左侧“Timers”下的任意定时器(这里选择TIM7)。设定如图数值,约为20ms。然后选择其下的“NVIC Setting”选项,开启定时器中断。


7.选择左侧“Middleware and Software Packs”,拖到最下面,选择“X-CUBE_TOUCHGFX”。首先会弹出下图一的界面,进行如下对应设置(如果是灰色的则需要安装)。

对下图右侧红色箭头按从上到下的解释。①启动图形应用,选择打勾。②显示设备选择客制化,即Custom。③0.96OLED屏幕只能二值显示,所以选择黑白。即“BW”。④宽度和高度按显示屏的实际选择。

8.初步的框架完成。然后选择蓝色菜单上面一行的“Project Manager”。在Project页面和Code Generator页面进行如下设置。
下图箭头解释:①生成工程保存的位置;②将要使用的编程工具。

下图箭头解释:①复制所有用到的库到工程文件夹;②外设初始化生成对应的“.c”和“.h”文件;③重新生成代码时,保留用户自定义的代码。

9.点击界面右上角的“GENERATE CODE”生成项目代码。

四、使用TouchGFX Designer设计简单的图像

1.找到上一步STM32CubeMX生成的工程文件夹。具体位置在上一大步的第8小步中有设置过。找到,如:F407_096_SPI_SOFT_TouchGFX(工程文件夹)/TouchGFX/ApplicationTemplate.touchgfx.part。 双击ApplicationTemplate.touchgfx.part文件,就会自动打开TouchGFX Designer软件。正式进入页面设计步骤。第一次打开可能会先置顶展示一个窗口,这个窗口可以直接关闭。打开TouchGFX Designer后,有如下图二界面。


2.如下图所示,选择Shapes中的Box,作为背景。颜色选择为黑色。


3.同理,参考第二小步的图一。选择Shapes中的Shape,产生一个白色菱形,摆放进去使之成为一个特殊图形。如下图所示。

4.这样,一个简单的例子界面就完成了。然后,按界面右下角的,形如“° </>”的代码生成按钮。这样界面就制作完成且生成进之前创建的工程了。

五、使用Keil5 ARM修改相关函数

需要特别注意:在C++的文件(*.cpp)和C语言的文件(*.c)之间相互调用,需要特别的编译标记。
这里主要修改Keil5的右侧工程目录的Application/User/TouchGFX/target下的TouchGFXHAL.cpp文件。
修改TouchGFXHAL.cpp的void TouchGFXHAL::flushFrameBuffer(const touchgfx::Rect& rect)函数。这个函数传递了要显示的内容,在这里把内容显示在屏幕上。
以下的OLED_DrawBMP();函数是定义在oled.c文件的。要在cpp的文件中调用,需要对声明这些函数的“*.h”文件添加如代码段2的内容。
														    //代码段1
														    //注意,以下代码有问题。会把图形压窄,然后在屏幕上重复显示8遍。有驱动程序错误待修改。
														    //在C++中包含用C语言编写的头文件,用以下格式。
															extern "C"{
															#include “oled.h”
															}
															
															void TouchGFXHAL::flushFrameBuffer(const touchgfx::Rect& rect)
															{
																const unsigned char *bitmap = (const unsigned char*) getClientFrameBuffer();
	                                                            OLED_DrawBMP(0,0,127,7,(unsigned char*)bitmap);
															}
														
															//代码段2
															#ifndef __OLED_H
															#define __OLED_H			  	 
															#include "main.h"
															
															/****************需要添加的内容以及对应位置****************/
															//包含的声明的C语言函数会在TouchGFXHAL.cpp文件内调用。
															#ifdef _cplusplus
															extern C {
															#endif
															/*******************************************************/
															
															//一些必要的宏定义
															
															
															//OLED控制用函数
															void OLED_WR_Byte(uint8_t dat,uint8_t cmd); 
															void OLED_Set_Pos(unsigned char x, unsigned char y);
															...
															void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[]);
															
															/****************需要添加的内容以及对应位置****************/
															#ifdef _cplusplus_
															}
															#endif
															/*******************************************************/
															
															#endif
														
然后,之前设置的定时器是用于TouchGFX刷新屏幕计时的。在添加的定时器中断回调函数void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)中需要调用自己编写的void touchgfxTickHandler()函数。
															//在TouchGFXHAL.cpp的最后添加。下面的函数会在C语言的文件中调用。
															//定时器回调函数会调用它。
															
															//包含以下头文件,OSWrappers字段才能使用。
															#include “touchgfx/hal/OSWrappers.hpp”
															    
															//以下在TouchGFXHAL.cpp文件中定义的函数,C文件stm32f4xx_it.c会调用。
															//所以加头extern "C"
															extern "C" void touchgfxTickHandler()
															{
																touchgfx::HAL::getInstance()->vSync();
																touchgfx::OSWrappers::signalVSync();
															}
														
定时器中断回调函数个人习惯放在stm32f4xx_it.c的文件中。代码内容如下。
															extern void touchgfxTickHandler();
															void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
															{
																if(htim->Instance==TIM7)
																{
																	touchgfxTickHandler();
																}
															}
														
最后,在“main.c”文件中开启定时器中断。
															HAL_TIM_Base_Start_IT(&htim7);
														
之后Keil5就可以编译了。

另注:在使用硬件SPI且打开硬件片选信号下,OLED_WR_Byte()函数需要改写成如下形式。
															void OLED_WR_Byte(uint8_t dat,uint8_t cmd)
                                                            {
                                                                if(cmd)
                                                            	  OLED_DC_Set();
                                                            	else 
                                                            	  OLED_DC_Clr();	
                                                            	HAL_SPI_Transmit(&hspi1,&dat,1,0x00ff);
                                                            	OLED_DC_Set();
                                                            } 
														

六、目前代码的实物现象

问题:同一个图像重复显示了8次。需要求改TouchGFXHAL::flushFrameBuffer函数。

完善后,持续更新... ...