基于恩智浦i.MX RT芯片内部RAM运行LVGL工程

描述

随着越来越多用户选择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。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分