跳转到主要内容

内存使用

引言

在本节中,我们将讨论TouchGFX应用的存储空间使用量。 典型的TouchGFX应用使用4类存储器,但这取决于使用的硬件:

存储器类型使用
内部RAM内部RAM用于存储配置数据,如所有Widget的坐标和色彩。 这里存储了当前屏幕的几个对象。
包含UI任务运行时间栈的操作系统所需内存也位于内部RAM中。 其他软件组件的所有数据(如文件系统和显示屏驱动)同样位于内部RAM中。
内部闪存内部闪存用于存储应用程序代码、TouchGFX库和使用的其他库。
外部RAM外部RAM通常用于提供帧缓冲,也可以是位图缓存。
外部Flash外部闪存用于存储图像、字体和文本。

静态存储分配

TouchGFX只使用静态存储分配。 这意味着所有存储空间都是预先分配的。 TouchGFX在运行时不从存储空间中获取分配。 如果应用可以一开始就分配好存储空间,则可确保永远不会使用内存时产生越界。

Screen和Widget

在TouchGFX中,通过开发许多C++类来创建用户界面。 TouchGFX Designer在您设计界面时创建类。 对于在TouchGFX Designer中设计的每个界面,您会自动获得许多类(MVP架构)。
在显示屏上显示界面时,TouchGFX会在内部RAM中自动分配类的对象。

当从一个界面切换到另一个界面时,不再使用为上一个界面分配的对象,而只使用新界面的对象。 因此,将在内部RAM中旧对象所在的位置分配新对象(旧对象被覆盖)。 在一个时间点,内部RAM只保存一个界面的对象。

根据定义的类,C++编译器能够计算最大界面类的大小,并为这些类预留存储空间。

因此,内部RAM中的存储空间使用量不取决于应用中界面的数量,而是取决于最大界面的大小。

为这些对象留出的存储空间称为FrontendHeap。

TouchGFX

应用代码

应用代码通常存储在内部闪存中。 应用代码包括您编写的程序代码、TouchGFX Designer生成的代码、来自TouchGFX Designer库和您使用的其他库的代码。

随着您编写的代码和添加到应用的界面越来越多,应用代码的数量必然增加。 在您首次使用某个功能时,从库中取出的代码的量随之增加。 例如,在您首次将Button添加到界面时,TouchGFX库中的Button代码就会包含在您的应用中,导致代码量增加。 在您第二次为同一个或另一个界面添加Button时,不会再次从TouchGFX库获取代码,应用中增加的只有您编写的或TouchGFX 设计器生成的代码。

资源

诸如图像、文本和字体之类的资源产会被转换为C++文件并链接到应用中。 资源的数据通常存储在外部闪存中,但也可存储在内部闪存中。 这是通过链接器脚本来控制的。

在添加图像时,应用大小的增量与图像大小成正比。

在添加文本时,文本中的每个字符会导致应用大小增加两个字节。 如果同一个字符串使用了两次,应用大小只增加一次。

只有应用中使用的字符,才会从字符文件中获取。 这意味着如果应用中只使用大写字母A-Z,则应用中不包含字体中的小写字母a-z。 如果后续添加使用这些字母的文本,则应用中字体数据的量会增加。

闪存中字符的大小取决于选择的字体大小。 如果字体增大,应用大小也会增加。

检查存储空间使用量

通过检查链接器生成的映射文件,可以找到特定应用的存储空间使用量。

下面我们来检查IAR Embedded Workbench生成的映射文件。 其他编译器也会生成类似的映射文件。

首先在TouchGFX Designer中为STM32F746Discovery评估套件创建一个空项目:

具有一个Box和一个Button的STM32F746项目

在IAR中打开项目后,查看关于IAR生成.MAP文件的属性:

生成链接器映射文件

在IAR中进行编译后,可以检查链接器映射文件STM32F746G_DISCO.map,该文件位于EWARM/STM32F746G_DISCO/List文件夹。

IAR链接器映射文件包含一个极佳的总结。 查找“模块总结”

*******************************************************************************
*** MODULE SUMMARY
***

Module ro code ro data rw data
------ ------- ------- -------
command line/config:
------------------------------------------------------------------
Total:

C:\TouchGFXProjectsDocumentation\STM32F746MemoryUsage\EWARM\STM32F746G_DISCO\Obj: [1]
ApplicationFontProvider.o 20
BitmapDatabase.o 12 40
Blue_Buttons_Round_Edge_small.o 40'800
Blue_Buttons_Round_Edge_small_pressed.o 40'800
Font_verdana_10_4bpp_0.o 24
Font_verdana_20_4bpp_0.o 72
Font_verdana_40_4bpp_0.o 280
FrontendApplication.o 46 60
FrontendApplicationBase.o 706 816
GeneratedFont.o 84 84
Kerning_verdana_10_4bpp.o 4
Kerning_verdana_20_4bpp.o 4
Kerning_verdana_40_4bpp.o 4
Model.o 10
OSWrappers.o 156 1 9
STM32DMA.o 898 176
STM32TouchController.o 162 24 4
...
heap_4.o 444 32'792
...
touchgfx_core.a: [7]
AbstractButton.o 136
AbstractPartition.o 8
Application.o 2'218 290 28
Bitmap.o 1'064 604 36
Box.o 108 104
Button.o 276 308
ConstFont.o 62
Container.o 510 396
DMA.o 558 252
DisplayTransformation.o 192
Drawable.o 418
FontManager.o 12 4
Gestures.o 364 60
HAL.o 1'758 544 18
LCD24bpp.o 2'732 1'604 80
Screen.o 1'924 124
TouchCalibration.o 252 76
TypedText.o 14
------------------------------------------------------------------
Total: 12'728 4'286 256

Gaps 4 3
Linker created 36 2'560
----------------------------------------------------------------------
Grand Total: 38'676 88'973 42'731

此表包含三列数字, 其中的ro代码ro数据为只读,位于闪存中, rw数据为非常量读写变量,位于RAM中。

表中的行划分为7块。 第一块为项目中的所有.cpp文件。 后面的六块为项目中使用的库(.a文件)。 最后一个是TouchGFX库。

我们可以看到TouchGFX库(“touchgfx_core.a: [7]”部分)为应用增加了12,728字节的代码(和4,286字节的常量数据)。

内部RAM

为了找到内部RAM的总使用量,我们来看模块总结表底部的“总计”一行。 第三列是内部RAM。 这表示项目使用了内部RAM的42,731字节空间。 我们来看TouchGFX库的总值,可以看到TouchGFX库[7]使用了256字节。 heap_4.o使用了32,792字节。 这是为FREERTOS预留的动态内存堆。 32Kb是默认值,但堆的大小可在STM32CubeMX中配置。 典型的TouchGFX程序使用此堆中几Kb的空间,主要用来为用户界面任务分配栈。

通过搜索FrontendHeap,我们可以找到界面对象的大小:

FrontendHeap::getInstance()::instance
0x2000'95d0 0x240 Data Gb TouchGFXConfiguration.o [1]

用户界面所需的对象占用了0x240字节 = 576字节的空间。

内部闪存

“总计”一行中可以看到,此应用使用了38,676字节代码 + 88,973字节数据。 其中只有一部分是内部闪存。 至少两张Button图像位于外部闪存中。

为了找出会有多少代码和数据存储在内部闪存中,我们首先检查“存储总结”(删除了一些细节):

*******************************************************************************
*** PLACEMENT SUMMARY
***

"A0": place at address 0x800'0000 { ro section .intvec };
"P1": place in [from 0x800'0000 to 0x80f'ffff] { ro };
"P2": place in [from 0x2000'0000 to 0x2004'ffff] { rw };
"P3": place in [from 0x9000'0000 to 0x90ff'ffff] {
section ExtFlashSection, section FontFlashSection,
section TextFlashSection };

内部闪存的起始地址为0x08000000。 它有两个区域“A0”和”P1”。

继续看映射文件,可以看到这些区域中的内容:

  Section                Kind         Address      Size  Object
------- ---- ------- ---- ------
"A0": 0x1c8
.intvec ro code 0x800'0000 0x1c8 startup_stm32f746xx.o [1]
- 0x800'01c8 0x1c8

"P1": 0xb05d
.text ro code 0x800'01c8 0x9b8 main.o [1]
.text ro code 0x800'0b80 0x14 memset.o [5]
...
.text ro code 0x800'b17a 0x2 AbstractButton.o [7]
.rodata const 0x800'b17c 0x1 unwind_debug.o [6]
.rodata const 0x800'b17d 0x0 zero_init3.o [5]
.rodata const 0x800'b17d 0x0 lz77_init_single.o [5]
Initializer bytes const 0x800'b17d 0xa8 <for P2-1>
- 0x800'b225 0xb05d

这表示”A0”使用了0x1c8字节 = 456字节,”P1”使用了0xb05d字节 = 45,149字节。 内部闪存的总使用量为45,605字节。

外部Flash

外部闪存为”P3”区域(起始地址为0x90000000)。 下面是该区域的内容:

"P3":                                          0x1'4076
ExtFlashSection const 0x9000'0000 0x9f60 Blue_Buttons_Round_Edge_small.o [1]
ExtFlashSection const 0x9000'9f60 0x9f60 Blue_Buttons_Round_Edge_small_pressed.o [1]
FontFlashSection const 0x9001'3ec0 0x118 Font_verdana_40_4bpp_0.o [1]
FontFlashSection const 0x9001'3fd8 0x48 Font_verdana_20_4bpp_0.o [1]
FontFlashSection const 0x9001'4020 0x18 Font_verdana_10_4bpp_0.o [1]
FontFlashSection const 0x9001'4038 0x10 Table_verdana_10_4bpp.o [1]
FontFlashSection const 0x9001'4048 0x10 Table_verdana_20_4bpp.o [1]
FontFlashSection const 0x9001'4058 0x10 Table_verdana_40_4bpp.o [1]
FontFlashSection const 0x9001'4068 0x4 Kerning_verdana_10_4bpp.o [1]
FontFlashSection const 0x9001'406c 0x4 Kerning_verdana_20_4bpp.o [1]
FontFlashSection const 0x9001'4070 0x4 Kerning_verdana_40_4bpp.o [1]
TextFlashSection const 0x9001'4074 0x2 Texts.o [1]
- 0x9001'4076 0x1'4076

可以看到外部闪存的总使用量为0x14076字节 = 82,038字节。 两张Button图像(2 x 0x9f60字节 = 40,800字节)使用了其中的大部分。 其余空间被3种字体使用。 在本例中,由于不使用任何文本,只包含“?”字符,因此它们用不了多少空间。

总结

外部RAM中只有帧缓冲。 由于它们未被定义为应用中的变量,因此在链接器脚本中找不到它们。 分辨率为480x272像素(24位)。 有两个帧缓冲,总使用量为480 * 272 * 3 * 2 = 786,360字节。

存储器类型使用
内部RAM42731 字节
TouchGFX界面对象576字节
内部闪存45605 字节
TouchGFX框架12,728字节代码
外部RAM786,360 字节
外部Flash82,028 字节

Demo 1

下面是另一个示例:TouchGFX Designer中可以找到的TouchGFX Demo1的数据量。 它包含5个界面和100多张图像:

STM32F746 Demo 1

总结

存储器类型使用
内部RAM51,387 字节
TouchGFX界面对象10,772 字节
内部闪存187,768 字节
TouchGFX框架代码85,174字节代码
外部RAM786,360 字节
外部Flash5,281,812字节