我们有一个论文项目正在进行中,这些人正试图让外部RAM为STM32F417 MCU工作。该项目正在尝试一些真正需要资源的东西,而内部RAM是不够的。
问题是如何最好地做到这一点。
目前的方法是将链接脚本(gnu-ld)中的RAM地址替换为外部RAM的地址。
这种方法的问题是,在初始化过程中,由于FSMC尚未初始化,芯片必须在内部RAM上运行。
它似乎有效,但一旦运行pvPortMalloc,我们就会出现硬故障,这可能是由于取消引用伪造的地址,我们可以看到变量在系统初始化时没有正确初始化(我想这是有道理的,因为内部RAM根本没有使用,而它可能应该使用)。
我意识到这是一个模糊的问题,但在Cortex M4 MCU(更具体地说是STM32F4)的外部RAM中运行代码时,一般的方法是什么?
感谢
FreeRTOS定义并使用单个大内存区域进行堆栈和堆管理;这只是一个字节数组,其大小由FreeRTOSConfig.h
中的configTOTAL_HEAP_SIZE
符号指定。FreeRTOS使用其pvPortMalloc
函数在该内存区域中分配任务堆栈,因此这里的主要目标是将FreeRTOS堆区域放置到外部SRAM中。
FreeRTOS堆内存区域在heap_*.c
中定义(除了使用标准库malloc的heap_3.c
,它没有定义任何自定义堆区域),该变量称为ucHeap
。您可以使用编译器扩展来设置它的部分。对于GCC来说,这将类似于:
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ] __attribute__ ((section (".sram_data")));
现在,我们需要配置链接器脚本,将这个自定义部分放置到外部SRAM中。有几种方法可以做到这一点,这再次取决于您使用的工具链。使用GCC,一种方法是为SRAM定义一个存储区域,并为".sram_data"
定义一个部分以附加到SRAM区域,类似于:
MEMORY
{
...
/* Define SRAM region */
sram : ORIGIN = <SRAM_START_ADDR>, LENGTH = <SRAM_SIZE>
}
SECTIONS
{
...
/* Define .sram_data section and place it in sram region */
.sram_data :
{
*(.sram_data)
} >sram
...
}
这将把ucHeap
区域放在外部SRAM中,而所有其他文本和数据部分将放在默认存储区域(内部闪存和ram)中。
几个注意事项:
- 在调用任何FreeRTOS函数(如
xTaskCreate
)之前,请确保初始化SRAM控制器/FFSMC - 一旦启动任务,所有堆栈分配的变量都将放在
ucHeap
(即extRAM)中,但全局变量仍分配在内部RAM中。如果仍然存在内部RAM大小问题,可以使用编译器扩展配置其他全局变量以放置在".sram_section"
中(如ucHeap所示) - 如果您的代码使用动态内存分配,请确保使用
pvPortMalloc/vPortFree
,而不是stdlibmalloc/free
。这是因为只有pvPortMalloc/vPortFree
会使用extRAM中的ucHeap区域(而且它们是线程安全的,这是一个优点) - 如果使用具有不同内存块大小的
pvPortMalloc/vPortFree
进行大量动态任务创建/删除和内存分配,请考虑使用heap_4.c
而不是heap_2.c
。当使用几个不同的块大小时,heap_2.c
存在内存碎片问题,而heap_4.c
能够将相邻的空闲内存块组合成一个大块
另一个(可能更简单)的解决方案是将ucHeap变量定义为指针而不是数组,如下所示:
static uint8_t * const ucHeap = <SRAM_START_ADDR>;
这不需要任何特殊的链接器脚本编辑,所有内容都可以放在默认部分。请注意,使用此解决方案,链接器不会显式为堆保留任何内存,并且会丢失一些潜在的有用信息/错误(如堆区域不适合extRAM)。但只要外部RAM中只有ucHeap
,并且configTOTAL_HEAP_SIZE
小于外部RAM大小,这可能会很好。
当应用程序启动时,它将尝试通过将数据清零或初始化为非零值来初始化数据,具体取决于变量所在的部分。使用正常运行时模型,这将在调用main()之前发生。所以你有这样的东西:
1) 重置矢量调用init代码2) C运行时初始化代码初始化变量3) C运行时初始化代码调用main()
如果使用链接器将变量放置在外部RAM中,则需要确保在初始化之前RAM是可访问的,否则将出现硬故障。因此,您需要有一个引导加载程序来为您设置系统,然后启动您的应用程序。。。。或者更简单地说,只需编辑启动代码即可执行以下操作:
1) 重置矢量调用init代码2)>>C运行时初始化代码配置外部RAM<lt<3) C运行时初始化代码初始化的变量4) C运行时init代码调用main()。
这样,在你尝试访问它之前,RAM是可用的。
然而,如果您只想在外部RAM中拥有FreeRTOS堆,那么您可以不影响init代码,只需使用适当的堆实现——基本上是一个不只是声明大型静态数组的堆实现。例如,如果您使用heap_5,那么您所需要做的就是确保在执行任何分配之前调用heap-init函数,因为heap-init只是描述将哪个RAM用作堆,而不是静态地声明堆。