低成本硬件上的TouchGFX
本节讨论如何在RAM和Flash大小有限、无加速且与外部Flash和显示屏的SPI连接速度慢的低成本硬件上使用TouchGFX。
我们将尝试提供一些关于为指定硬件编写最佳应用的建议。
本节将以STM32G071 nucleo板和X-Nucleo-GFX01M1扩展板为例,描述如何使用应用模板。 扩展板具有16位显示屏和串行Flash。
硬件概述
该套件中的硬件配置包括STM32G071 MCU、SPI NOR flash、SPI显示屏和摇杆按钮。
组件 | |
---|---|
MCU | STM32G071RB |
MCU RAM | 32 Kb |
MCU Flash | 128 Kb |
显示屏 | Displaytech DT022CTFT |
显示屏分辨率 | 240 x 320 |
显示格式 | 16位RGB565 |
显示屏控制器 | ILI9341V |
显示屏连接 | SPI |
显示屏连接速度 | 32 MHz |
NOR Flash | Macronix MX25L6433F |
NOR Flash大小 | 64 Mbit |
NOR Flash连接速度 | 32 MHz |
显示屏连接到SPI1外设,而闪存连接到SPI2外设。 因此,MCU能够在向显示屏发送数据的同时从闪存读取数据。
GPIO分配
信号 | GPIO引脚 |
---|---|
显示屏CS | PB5 |
显示屏DCX | PB3 |
显示屏SCK | PA5 |
显示屏MOSI | PA7 |
显示屏TE | PA0 |
Flash CS | PB9 |
Flash SCK | PB13 |
Flash MOSI | PC3 |
Flash MISO | PC2 |
上表列出了闪存和显示屏的信号GPIO分配。 这些信号可通过示波器或逻辑分析仪进行监测。 在诸如性能等问题的调试中,这一点非常有用。
启动项目
在TouchGFX Designer中,可以轻松地为STM32G071RB Nucleo评估套件启动一个项目。 点击“Create New”按钮,然后搜索STM32G071 Nucleo。 此模板专为Nucleo-G071RB套件和X-Nucleo-GFX01M1显示板卡而开发。
该应用模板支持NOR flash、显示屏和按钮。 显示屏有竖屏和横屏两种使用模式。
可以在TouchGFX Designer中的 配置 -> 显示屏部分修改显示屏方向:
X-Nucleo-GFX01M1 Shield上的显示屏原本是竖屏(高度大于宽度),但很容易改为横屏。
显示屏更新
如上文所述,显示屏分辨率为240 x 320像素。 总共76,800像素或153,600字节。 MCU与显示屏之间的SPI连接的运行频率为32 MHz。 因此,传输速度可以达到4 MB/s或2M像素/s。
显示屏的刷新频率为76.1 Hz,因此帧间隔为13.14 ms。
这意味着有最多13 ms的时间来发送下一帧的数据。 在此期间,我们可以发送2.000.000像素/秒/76 fps=26.280像素/帧或34%的全屏。 实际上,由于协议开销,我们无法维持SPI总线上的传输速度,因此不能发送超过一个完整帧的30%左右。
如果应用更新的像素数超过此值,硬件将无法在帧时以内完成发送。 结果是显示屏将在全部更新结束前开始显示更新的帧。 因此,用户有时会看到原来的帧与新的帧混合在一起的画面。
对于一些动画而言,用户注意不到这一点,但在其他情况下可能不可接受。
因此,我们建议将更新率维持在30%以下。 如 通过渐进式地分步更新帧。
因此,一般来说最好在屏幕上扩展项目,而不是移动项目。
当星形移动到右侧时,必须更新星形覆盖的所有像素。 如果只扩展星形,则只需更新新像素。 在上一帧更新的像素保持不变。
绘制速度
向显示屏发送数据的频率为最高32 MHz。
串行闪存能以与显示屏发送相同的速度运行。 这意味着串行闪存的速度足够快,能以最高速度向显示屏供应数据。
这只有在闪存中的图像像素格式为RGB565时才能实现。 这种情况下,从闪存读取的两个字节等于1个像素,也就是显示屏上的两个字节。 如果闪存中的像素格式为ARGB8888,我们需要从闪存中读取两倍的数据量才能在显示屏上产生像素,而串行Flash将无法跟上显示屏的速度。
当发生这种情况时,不再继续向显示屏发送数据,并且不可能在一帧时间内更新显示屏30%的帧数据。 一种办法是将图像移动到内部闪存,另一种办法当然是更改像素格式。
其他控件不受闪存速度的制约。 如 Box控件,用于绘制彩色矩形。 此控件速度很快,比显示屏数据发送速度要快许多。 其他控件(如Line和Circle控件)需要使用更多CPU资源。 这些控件生成像素的速度比不上将它们发送给显示屏时的速度。 对于使用这些控件的应用,不能期望在每帧时间内更新显示屏30%区域。
点击这里了解像素渲染的复杂性
使用串行闪存时TouchGFX的限制
应用程序员必须认识到,在STM32G0平台使用串行闪存时,使用TouchGFX有少许限制。
纹理映射器
纹理映射器控件(TextureMapper、AnimationTextureMapper和 ScalableImage)不能绘制存储在外部SPI闪存中的图像。 原因在于,用存储在串行闪存中的图像无法获得可接受的性能,如图像旋转。
在Texture Mapper控件中使用图像时,必须将图像保存在内部闪存或RAM中。 可通过TouchGFX Designer修改图像配置,轻松地将图像保存在内部闪存中。
转至Images选项卡并选中图像。 在窗口的右侧,将“Section”属性更改为“IntFlashSection”。
Texture Mapper的代码量过大,不能包含在所有项目中。 因此,对于STM32G0项目,默认禁用Texture Mapper。 这意味着您必须先全能Texture Mapper,然后才能在STM32G0项目中使用它。
转至“Config”选项卡,选择“Framework Features”,并点击相关的一个或一组Texture Mapper。
还可以使用Bitmap Cache将图像暂时移动到RAM
位图绘制器
可通过图像为直线、圆和DynamicGraph上色。 这不适用于存储在SPI闪存中的图像。 使用这些控件的图像必须放在内部闪存或RAM中。
L8调色板
L8格式的图像可以用在具有串行闪存的硬件上。 限制条件是调色板数据必须放在内部闪存中(也是出于性能考虑)。
通过在TouchGFX Designer中将“Extra Section”修改为“IntFlashSection”,可以将调色板移动到内部闪存。
驱动
应用模板是使用TouchGFX Generator创建的。 点击 此处阅读TouchGFX Generator的更多相关内容。 TouchGFX Generator生成HAL层,它连接TouchGFX框架与一组底层驱动程序(已在该应用模板中实现)。 该应用模板的底层驱动位于项目的Core/Src
文件夹中。
驱动程序位于3个文件中:
驱动 | 文件 |
---|---|
显示屏 | Core/Src/MB1642BDisplayDriver.c |
Flash | Core/Src/MB1642BDataReader.c |
按钮 | Core/Src/MB1642BButtonController.cpp |
显示屏
显示屏使用标准SPI协议。 可以通过写寄存器来配置显示屏。 在数据发送到显示屏时生成片选信号。 使用额外的GPIO(DCX)将指令字节与数据字节区分开来。
驱动使用DMA通道发送像素数据给显示屏。 这样就可以在MCU计算像素的同时进行发送。 DMA完成中断被用来释放已发送数据的存储空间以便在将来绘制时重复使用,以及在有新数据可用时重新开始发送数据。
由于CS和CDX引脚必须在小配置封装之间切换并位于其内部,因此不通过DMA发送配置数据。
在发送配置数据时,驱动使用8位模式的SPI,但在发送像素数据时改为16位模式。 其中的原因在于,MCU存储器以小端模式读取。 RGB565格式的像素存储在RAM中,首先是低字节(G和B),然后是高字节(R和G)。 当使用8位SPI发送内存数据时,此顺序维持不变。 当SPI为16位模式时,按16位RGB565从存储器读取数据,并按显示屏的正确顺序发送数据。
对于不使用16位DMA的驱动,必须在发送前交换像素中的字节序。
初始化
显示屏初始化在函数MB1642BDisplayDriver_DisplayInit(void)
中完成
驱动按照推荐的上电顺序向显示屏发送6个指令:
- 退出睡眠模式(11h)
- 进入正常模式(13h)
- 设置存储器存取控制(36h),MX和BGR位置位
- 设置像素格式(3Ah)为16位格式
- 撕裂行功能开启(35h)
- 设置撕裂扫描行(44h)使其 = 0
驱动会在这些指令之间休眠100 ms。
撕裂效应
来自显示屏的撕裂效应(TE)信号非常重要。 从而允许应用程序将显示内存的更新与显示刷新率正确同步。 这有助于应用避免显示屏上的撕裂效应。 显示屏在开始更新周期时生成信号。 MCU使用此信号向显示屏发送数据。
TE信号连接到MCU的外部中断输入。 CubeMx生成并配置该引脚上的中断。
驱动中的回调指示TouchGFX开始绘制:
MB1642BDisplayDriver.c
void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)
{
...
touchgfxSignalVSync();
}
外部闪存
显示板卡上的SPI NOR flash是标准SPI flash。 该驱动比显示屏驱动简单。 无需专门初始化即可从Flash读取数据。
该驱动可使用轮询SPI(忙碌等待所有字节)或使用DMA读取数据。 开始DMA接收的时间接近于以轮询模式读取20个字节所需的时间,因此短读通常较慢。 另一方面,DMA在中断(即sysTick或应用中断)期间继续运行,并且可以在MCU忙于渲染像素时在后台运行。 出于这个原因两种方法都将会使用。
Flash驱动使用与显示屏驱动不同的DMA通道,因此新数据接收和已绘制像素发送可以同时运行。
链接脚本
链接器控制应用中各种数据所在的位置。 这是在链接脚本中指定的。 下面是gcc编译器的链接器脚本的第一部分:
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 36K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 128K
SPI_FLASH (r) : ORIGIN = 0x90000000, LENGTH = 8M
}
它声明NOR flash从地址0x90000000开始。 目标应用程序中的Flash驱动器从SPI Flash读取0x90000000地址中的数据(使用较低的24位作为Flash中的地址)。
STM32CubeProgramer中使用的外部Flash加载器可以将此范围内的烧录到SPI Flash(见下文)。
第二部分将图像(ExtFlashSection)和字体(FontFlashSection)数据放在SPI闪存中。
ExtFlashSection :
{
*(ExtFlashSection ExtFlashSection.*)
*(.gnu.linkonce.r.*)
. = ALIGN(0x4);
} >SPI_FLASH
FontFlashSection :
{
*(FontFlashSection FontFlashSection.*)
*(.gnu.linkonce.r.*)
. = ALIGN(0x4);
} >SPI_FLASH
通过向链接器脚本中添加相似分区,可将其他数据放入SPI闪存。
外部闪存下载
G071 TouchGFX应用模板包含STM32CubeProgrammer要使用到的flash loader算法, 用于将数据写入SPI NOR flash。
flash loader文件为gcc/S25FL032P_STM32G071B-NUCLEO.stldr
STM32CubeIDE项目可从CubeIDE直接烧录,但使用IAR或Keil工具时,必须用STM32CubeProgrammer来烧录。
最初时flashloader是不可用的,必须将stldr档复制到STM32CubeProgrammer的安装文件夹进行安装才可使用。
现在,可以正常地在STM32CubeProgrammer中选择flash loader:
Tip
如果在自定义硬件上对串行闪存使用其他GPIO配置,则必须相应地修改flash loader。
按钮
按钮驱动十分简单。 它对MB1642B上的摇杆和Nucleo板上的蓝色用户按钮使用的5个GPIO的状态进行采样。
此按钮驱动作为BottonController安装在TouchGFX中。 这意味着在TouchGFX Designer中可以直接将按钮点击用在交互的配置中。 还可以在用户代码中使用它们,例如:
void Screen1View::handleKeyEvent(uint8_t key)
{
if (key == '6')
{
application().gotoScreen2Screen();
}
}
使用的键码:
按键 | 键值编码 |
---|---|
左 | '4' |
右 | '6' |
上 | '8' |
下 | '2' |
中 | '5' |
蓝色用户按钮 | '0' |
当您使用键盘numpad运行模拟器(在TouchGFX设计器中点击F5
)时,这些键也可以使用。