随着越来越多用户选择i.MX RT系列芯片制作产品,产品的需求以及芯片的用法也越来越多。本文将介绍在i.MX RT平台中,如何创建LVGL项目并将其运行在内部SRAM而非SDRAM上。本文档包含4个部分:通过GUI Guider生成LVGL工程;LVGL工程的 FlexRAM内存配置;修改工程配置使得程序运行;使用局部缓冲区节省SRAM空间。本文档以IAR项目为例。
用GUI Guider生成VGL工程
GUI Guider是恩智浦推出的一款图形用户界面开发工具,在往期的加油站文章中,也有关于它的介绍,在此就不多做描述。读者可以参考相关的文档资料进行了解学习。GUI GUIDER
1. 创建LVGL工程
打开GUI Guider v1.9.1 GA,创建两个GUI Guider项目:一个LVGL v9项目,使用基于MIMXRT1170-EVKB 和 Rasberry Pi LCD显示屏的数字仪表盘模板。一个LVGL v8项目,使用基于MIMXRT1050-EVKB 和 RK043FN66HS LCD显示屏的数字仪表盘模板。以下是LVGL版本选择的屏幕截图。

2. 根据你的设备修改工程配置
对于第一个RT1170-EVKB项目,无需修改GUI Guider中的配置。对于RT1050-EVKB项目,需要使用片内SRAM来保存帧缓冲区。在GUI Guider工具中,点击菜单上的“Project”按钮打开设置页面。窗口中可以看到一些项目设置。将“Frame Buffer Location”修改为SRAM,即可将缓冲区保存到内部RAM中。

FlexRAM配置
FlexRAM是一种高度可配置且灵活的RAM存储器阵列。该存储器阵列包含多个存储器组,每个存储器组可独立配置,以便由不同类型的接口访问,例如I-TCM(指令紧密耦合存储器)、D-TCM(数据紧密耦合存储器)或AX(系统)。存储器组可以用作 ITCM、DTCM或OCRAM存储器。本文档将简要介绍RT1170 和 RT10XX系列的FlexRAM静态配置以及运行时配置。更改IOMUX_GPR_GPR17(在 RT1170 上,也包括 IOMUX_GPR_GPR18)寄存器中定义的FLEXRAM_BANK_CFG字段的值。在设置IOMUX_GPR_GPR17/18的值之前,需要将IOMUXC_GPR_GPR16寄存器的FLEXRAM_BANK_CFG_SEL值设置为1,以启用FlexRAM配置。设置FLEXRAM_BANK_CFG值来配置 RAM 类型:
• 00:RAM 组 n 未使用
• 01:RAM 组 n 为 OCRAM
• 10:RAM 组 n 为 DTCM
• 11:RAM 组 n 为 ITCM
1. i.MX RT芯片FlexRAM配置
不同的i.MX RT芯片具有不同的内存容量,因此每个芯片可配置的FlexRAM大小也不同。下表列出了不同i.MX RT芯片的FlexRAM信息。

对于i.MX RT1170,系统内存映射如下所示。i.MX RT1170 SRAM的默认配置如下表所示:

| ITCM | DTCM | OCRAM |
| 256KB | 256KB | 1.5MB |
2. FlexRAM静态配置
芯片通过POR上电后,i.MX RT会从eFuse中检查FlexRAM组的值,以确定FlexRAM的分配。
OCRAM的最小配置为64KB。这是因为ROM代码的执行至少需要64 KB 的RAM。OCRAM的最小配置要求可能因设备而异。
本文档不使用静态配置方法,因此这里不再详细介绍静态配置。
3. FlexRAM动态配置
FlexRAM也可以通过在运行时更改IOMUXC_GPR寄存器的值来配置。首先,将IOMUXC_GPR_GPR16 寄存器中定义的FLEXRAM_BANK_CFG_SEL位置1。然后,可以通过IOMUXC_GPR_GPR17(在RT1170上,还有IOMUXC_GPR_GPR18)寄存器中FLEXRAM_BANK_CFG的值来配置FlexRAM的大小。运行时配置比静态配置有两个优势:在静态配置中,eFUse只能烧写一次,而FlexRAM配置无法多次调整;eFuse中只有 4/6位配置位,不会穷尽所有FlexRAM Bank组合。
3.1 确定FlexRAM的分配
首先,通过IOMUXC_GPR17(以及 RT1170 中的 IOMUXC_GPR18)寄存器的FLEXRAM_BANK_CFG位,可以自由指定每个Bank的最终形态(ITCM/DTCM/OCRAM)。以下是FLEXRAM_BANK_CFG(i.MX RT1170 芯片)的说明。
FlexRAM bank configuration GPR17 of RT1170

FlexRAM bank configuration GPR18 of RT1170

3.2 使能动态配置
分配好FlexRAM Bank后,将IOMUXC_GPR16寄存器中的FLEXRAM_BANK_CFG_SET设置为1,FLEXRAM_BANK_CFG指定的配置就会立即生效。
FlexRAM bank configuration GPR16 of RT1170

在片内RAM中运行LVGL工程
现在基于MIMXRT1170-EVKB创建了LVGL工程(2 个全尺寸帧缓冲区),并介绍了配置FlexRAM的方法,下面是使项目在内部RAM而非外部SDRAM上运行的步骤。
1. 在startup文件中配置FlexRAM
本工程中,所有内部RAM均配置为OCRAM,D-TCM和 I-TCM的大小均设置为0。此存储设置仅用于演示如何将所有程序都放入内部 RAM,速度可能并非最快。打开“startup_MIMXRT1176_cm7.s”,并在“SystemInit”之前添加配置代码。参见以下代码:
Reset_Handler CPSID I ; Mask interrupts LDR R0, =0xE000ED08 LDR R1, =__vector_table STR R1, [R0] LDR R2, [R1] MSR MSP, R2 /*FlexRam Configuration*/ //OCRAM = 512KB, DTCM = 0KB ,ITCM = 0KB, ldr R0,=0x400E4040 //gpr16 Enable FLEXRAM_BANK_CFG ldr R1,=0x0000AA07 str R1,[R0] ldr R0,=0x400E4044 //gpr17 0:15 ldr R1,= 0x00005555 str R1,[R0] ldr R0,=0x400E4048 //gpr18 0:15 ldr R1,= 0x00005555 str R1,[R0] LDR R0, =SystemInit BLX R0 CPSIE I ; Unmask interrupts LDR R0, =__iar_program_start BX R0
现在OCRAM大小设置为2MB。保存更改,FlexRAM配置将在项目运行时生效。
2. 修改链接文件
GUI Guider生成的基于i.MX RT芯片的LVGL工程默认存储在Nor Flash 和 SDRAM中。在工程选项中打开Linker File,删除其他data region,只保留1个data region。将整个OCRAM设置为data region。只保留data region配置,删除其他配置。参见以下代码:
/* define symbol m_interrupts_ram_start = 0x20200000; define symbol m_interrupts_ram_end = 0x20200000 + __ram_vector_table_offset__; define symbol m_data_start = m_interrupts_ram_start + __ram_vector_table_size__; define symbol m_data_end = 0x203FFFFF; */ define symbol m_data_start = 0x20240000; define symbol m_data_end = 0x203EFFFF; define symbol m_ncache_start = 0x203F0000; define symbol m_ncache_end = 0x203FFFFF; … define exported symbol __VECTOR_TABLE = m_interrupts_start; //define exported symbol __VECTOR_RAM = isdefinedsymbol(__ram_vector_table__) ? m_interrupts_ram_start : m_interrupts_start; define exported symbol __VECTOR_RAM = m_interrupts_start; define exported symbol __RAM_VECTOR_TABLE_SIZE = 0x0; define memory mem with size = 4G; define region TEXT_region = mem:[from m_interrupts_start to m_interrupts_end] | mem:[from m_text_start to m_text_end]; define region DATA_region = mem:[from m_data_start to m_data_end-__size_cstack__]; define region CSTACK_region = mem:[from m_data_end-__size_cstack__+1 to m_data_end]; define region NCACHE_region = mem:[from m_ncache_start to m_ncache_end]; …3. 减小代码尺寸
在GUI Guider生成的项目中,LVGL内存大小默认设置为2MB。通常项目不需要这么大的内存。因此,此处将LV_MEM_SIZE 改为 80kB,以节省 SRAM。
/*Size of the memory available for `lv_malloc()` in bytes (>= 2kB)*/
#define LV_MEM_SIZE (80U * 1024U)
4. 编译运行工程
保存更改并构建项目,查看“lvgl_guider_cm7.map”文件。内存占用大小显示在底部。
203'063 bytes of readonly code memory
7'351'345 bytes of readonly data memory
1'753'889 bytes of readwrite data memory
将其下载到开发板,工程将成功运行。
局部缓冲区刷新
另一个基于MIMXRT1050-EVKB的 LVGL v8项目使用局部缓冲区刷新来节省SRAM空间。将缓冲区位置配置到SRAM中,生成的项目将在内部SRAM而非外部SDRAM 上运行。
在“lvgl_support.c”文件中,如果启用宏“FB_USE_SRAM”,则会创建两个不同大小的帧缓冲区:一个全尺寸缓冲区和一个部分尺寸缓冲区。
#if FB_USE_SRAM
#define DRAW_BUF_HEIGHT (LCD_HEIGHT / 5)
#define DEMO_DB_SIZE LCD_WIDTH * DRAW_BUF_HEIGHT * LCD_FB_BYTE_PER_PIXEL
SDK_ALIGN(__attribute__((section("FrameBuffer"))) static uint8_t s_frameBuffer[1][DEMO_FB_SIZE], DEMO_FB_ALIGN);
SDK_ALIGN(__attribute__((section("DrawBuffer"))) static uint8_t s_lvglBuffer[DEMO_DB_SIZE], DEMO_FB_ALIGN);
#else
SDK_ALIGN(static uint8_t s_frameBuffer[2][DEMO_FB_SIZE], DEMO_FB_ALIGN);
#endif
缓冲区初始化也不一样。
#if FB_USE_SRAM lv_disp_draw_buf_init(&disp_buf, s_lvglBuffer, NULL, LCD_WIDTH * DRAW_BUF_HEIGHT); #else lv_disp_draw_buf_init(&disp_buf, s_frameBuffer[0], s_frameBuffer[1], LCD_WIDTH * LCD_HEIGHT); #endif … /* Partial refresh */ #if FB_USE_SRAM disp_drv.full_refresh = 0; #else disp_drv.full_refresh = 1; #endif
Flush 方式是更新改变的区域。
#if FB_USE_SRAM
static void DEMO_WaitVsync(lv_disp_drv_t *disp_drv)
{
s_framePending = true;
#if defined(SDK_OS_FREE_RTOS)
if (xSemaphoreTake(s_frameSema, portMAX_DELAY) != pdTRUE)
{
PRINTF("Display flush failed
");
assert(0);
}
#else
while (s_framePending)
{
}
#endif
}
static void copy_area(const lv_area_t *area, lv_color_t *color_p, uint8_t *fb, uint32_t fbStrideBytes)
{
uint32_t y;
uint32_t areaWidth = lv_area_get_width(area);
fb += (area->y1 * fbStrideBytes + area->x1 * sizeof(lv_color_t));
for (y = area->y1; y <= area->y2; y++)
{
lv_memcpy(fb, color_p, areaWidth * sizeof(lv_color_t));
fb += fbStrideBytes;
color_p += areaWidth;
}
}
static void DEMO_FlushDisplay(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
/* Wait VSYNC for each small update. */
DEMO_WaitVsync(disp_drv);
/* Copy data from draw buffer to frame buffer. */
copy_area(area, color_p, (uint8_t*) s_frameBuffer, LCD_WIDTH * LCD_FB_BYTE_PER_PIXEL);
SCB_CleanInvalidateDCache();
lv_disp_flush_ready(disp_drv);
}
#else
static void DEMO_FlushDisplay(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
DCACHE_CleanInvalidateByRange((uint32_t)color_p, DEMO_FB_SIZE);
ELCDIF_SetNextBufferAddr(LCDIF, (uint32_t)color_p);
s_framePending = true;
#if defined(SDK_OS_FREE_RTOS)
if (xSemaphoreTake(s_frameSema, portMAX_DELAY) == pdTRUE)
{
/* IMPORTANT!!!
* Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}
else
{
PRINTF("Display flush failed
");
assert(0);
}
#else
while (s_framePending)
{
}
/* IMPORTANT!!!
* Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
#endif
}
#endif
通过修改链接器文件,我们可以将“DrawBuffer”和“FrameBuffer”放入SRAM中,这些缓冲区的大小将远小于两个全尺寸缓冲区。因此,LVGL项目可以在内部RAM上运行。
小结
本文介绍了如何使基于i.MX RT芯片的LVGL工程在内部RAM上运行而不是外部SDRAM。一种方法是使用FlexRAM扩展OCRAM的大小,以便获得连续的RAM来存储缓冲区。另一种方法是使用部分缓冲区而不是全尺寸缓冲区,这样可以节省SRAM。
全部0条评论
快来发表一下你的评论吧 !