c - STM32创建RAM段

我使用 STM32H7 微 Controller 和 GNU/GCC,在我的代码中,我只使用 DTCM RAM,但我想将一些缓冲区存储在另一个可通过 DMA 访问的内存中。
我完全不熟悉链接器脚本,我需要编辑启动代码吗?
这是我的链接器脚本,我在SECTIONS

中添加了一些代码
/* Memories definition */
MEMORY
{
  DTCMRAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 128K
  ITCMRAM    (xrw)    : ORIGIN = 0x00000000,   LENGTH = 64K
  RAM_D1    (xrw)    : ORIGIN = 0x24000000,   LENGTH = 512K
  RAM_D2    (xrw)    : ORIGIN = 0x30000000,   LENGTH = 288K
  RAM_D3    (xrw)    : ORIGIN = 0x38000000,   LENGTH = 64K
  FLASH    (rx)    : ORIGIN = 0x8000000,   LENGTH = 2048K
}

/* Sections */
SECTIONS
{
   ...
   
   .ram1block 0x24000000 :
  {
    KEEP(*(.ram1section))
  } > RAM_D1

   ...
}

我想使用这个属性:

uint8_t __attribute__(( section(".ram1section") )) ads_buf[READBACK_LENGTH];

我这样做对吗?
你有什么话题或建议吗?我是这些脚本的新手,有点迷茫

编辑:显然有一个更简单的解决方案:
在链接器文件中

SECTIONS
{
   ads_buf=0x24000000;
   ...
}

源码

extern uint8_t ads_buf[READBACK_LENGTH];

谁能确认这个解决方案是有效的?

最佳答案

“更简单”的解决方案意味着您必须手动为该内存部分中的所有对象分配地址,并格外小心以避免重叠。这正是链接器被发明出来要避免的事情,所以我建议不要那样做。

输出段不需要再指定地址,内存范围即可。是否需要修改启动代码取决于是否需要在启动时初始化该部分。

没有初始化

如果你不需要初始化因为你会,例如在读取之前用传入数据填充缓冲区,按如下方式修改链接描述文件:

   .ram1block (NOLOAD) :
  {
    KEEP(*(.ram1section))
  } > RAM_D1

大功告成。

简单的初始化

如果你确实需要简单地初始化它,例如全零,添加地址符号如下:

   .ram1blockBss (NOLOAD) :
  {
    . = ALIGN(4);
    _BeginRam1Bss = .;
    KEEP(*(.ram1sectionBss))
    . = ALIGN(4);
    _EndRam1Bss = .;

  } > RAM_D1

对齐地址使初始化更容易和更快。请注意,我将输入部分重命名为 ram1sectionBss 以表示该部分是零初始化的。在启动代码中添加类似这样的内容以对内存进行零初始化:

    ldr r0, =_BeginRam1Bss
    ldr r1, =_EndRam1Bss
    ldr r2, =0

    b 2f
1:  str r2, [r0], #4
2:  cmp r0, r1
    blo 1b

这有效地用零初始化了整个 block ,因此 C 变量定义为

uint8_t __attribute__(( section(".ram1sectionBss") )) ads_buf[READBACK_LENGTH];

将被零初始化。

值初始化

如果您需要用不同的值初始化内存中的内容,请按如下方式修改链接器部分定义:

   .ram1blockData (NOLOAD) :
  {
    . = ALIGN(4);
    _BeginRam1Data = .;
    KEEP(*(.ram1sectionData))
    . = ALIGN(4);
    _EndRam1Data = .;

  } > RAM_D1 AT> FLASH
  _InitRam1Data = LOADADDR (.ram1blockData);

同时追加一个。 = ALIGN(4); 进入 last 部分 before .ram1blockData 进入 FLASH 以确保闪存中的加载地址也对齐。

然后在启动代码中加入如下内容:

    ldr r0, =_BeginRam1Data
    ldr r1, =_EndRam1Data
    ldr r2, =_InitRam1Data

    b 2f
1:  ldr r3, [r2], #4
    str r3, [r0], #4
2:  cmp r0, r1
    blo 1b

如果您随后将 C 变量定义为

uint8_t __attribute__(( section(".ram1sectionData") )) ads_buf[READBACK_LENGTH] = { 1, 2, 3, 4 };

它将被正确初始化。再次注意重命名的部分(ram1sectionData 表示数据已初始化)。

平凡+值初始化

如果您的内存块中需要零初始化和值初始化的数据,只需将两个段定义放在链接描述文件中,将两个汇编 block 放在启动代码中,即可使 C 定义像这样工作:

uint8_t __attribute__(( section(".ram1sectionBss") )) ads_buf1[READBACK_LENGTH];
uint8_t __attribute__(( section(".ram1sectionData") )) ads_buf2[READBACK_LENGTH] = { 1, 2, 3, 4 };

PS:注意像_BeginRam1Bss这样以下划线+大写字母开头的标识符,是保留的C标识符。这意味着您不会意外地在 C 代码中使用它们,否则会与链接描述文件发生冲突。 Linker-Script + Startup-Code 是实现的一部分,应该为常规 C 代码提供一致的运行时,而不会“占用”应该由 C 代码使用的非保留标识符。

https://stackoverflow.com/questions/65577391/

相关文章:

c++ - 找不到 Glog(缺少 : GLOG_INCLUDE_DIR GLOG_LIBRARY)

deep-learning - 如何通过一次操作合并两个 torch.utils.data 数据加载

c# - 将 RestSharp 用于以下代码的内置替代方法是什么?

python - numpy 中的 3d 矩阵乘法

awk - 将 awk 应用于除第一行以外的所有内容

javascript - 如果 "any"不能在 Nest.js 中使用,模式中的字段类型应该是什么

java - 在 Clojure 中使用 JSoup 解析字符串

powershell - 无法注册 PS 图库

c++ - 如何在 QPixmap 中旋转照片?

c# - 有没有办法在 Switch 表达式中跳过或继续,或者每个分支都需要返回一些东西?