1

我想在stm32中保存一块闪存来存储我自己的配置信息。

为此,我想将闪存的第二个扇区保存在 STM32F2/STM32F4 上(16kb 存储在 0x08004000-0x08007FFF)

检查互联网和 stackoverflow,你有 4 种方法可以做到这一点

1)

#pragma location=0x08004000 __no_init const char ReservedArea[16*1024];

2)

__no_init const char ReservedArea[16*1024] @0x08004000;

3) 创建一个部分 + #pragma location=

项目icf:

place at address mem: 0x08004000 { readonly section ConfigSection };

c文件:

#pragma location="ConfigSection" __no_init const char ReservedArea[16*1024];

4)

在项目 .icf 文件中定义一个部分IAR 定义自定义数据的内存区域

发现错误或问题

方法 1 到 3工作正常。链接器为我的变量包含一个空间区域。您可以检查使用十六进制编辑器生成的 .bin 文件,或者仅调试并查看该变量为 @ 0x08004000。

使用这些方法发现的问题是 iar 链接器在 0x08000800 - 0x08003FFF 之间留下了超过 12kbytes 的未使用闪存。验证这一点的最佳方法是删除 var,编译,在注释中写入 bin 文件的大小,然后添加变量。如果你这样做,你会注意到新的 bin 文件大小大于 16kb,而它必须是 16kb。

如果您将地址从 0x08004000 移动到 0x0800C000 而不进行任何其他更改,则文件大小将再增加 32kbytes,并且所有先前的区域都设置为 0x00,并且在 bin 文件中未使用。这对我们的项目来说是一个大问题,因为我使用 bin 文件中剩余的未使用区域来允许固件更新。

检查地图文件,您会发现保留区之前的区域也未使用。

我尝试了几种方法来解决这个问题,例如定义 2 个带地址的变量、玩几个小时、检查链接器选项、优化、使用其他 #pragma 选项等。

关于第四种方法,它将变量存储在系统中,但它没有得到我想要的地址。问题可能是两个区域共享地址空间。

.icf 文件

   define region LANGUAGE_region   = mem:[from 0x08004000 to 0x08007FFF];
   define region ROM_region      = mem:[from __ICFEDIT_region_ROM_start__   to __ICFEDIT_region_ROM_end__];
   define region RAM_region      = mem:[from __ICFEDIT_region_RAM_start__   to __ICFEDIT_region_RAM_end__];

   "LANGUAGE_PLACE":place at start of LANGUAGE_region  { section .LANGUAGE_PLACE.noinit };

c代码

extern const char ReservedArea[16*1024] @".LANGUAGE_PLACE.noinit";

const char ReservedArea[16*1024];

是我的问题吗?这是一个错误吗?欢迎任何提示。

提前致谢。

4

3 回答 3

3

这对我来说听起来不像是一个错误,而是一个你需要处理的问题。.bin 文件是一个原始内存文件,其中文件的每个字节都映射到内存中的一个字节。您如何期望 .bin 文件表示位于偏移量 0x4000 或 0xC0000 的字节,而不表示之前的所有字节?.bin 文件必须包含两个内存部分之间的所有未使用字节,以保持后续部分的相对偏移量。

您是否担心未使用的字节是 0x00,因此如果不先擦除它们就无法编程?如果是这样,那么您可能可以将链接器(或您用于创建 .bin 文件的任何程序)配置为对所有未使用的字节使用 0xFF 而不是 0x00。检查链接器(或命令行)选项。

或者您是否担心 .bin 文件包含大量未使用的内存,下载和重新编程需要更长的时间?还是 .bin 文件现在太大而无法放入您为固件更新保留的内存区域?无论哪种情况,解决方案都是将固件更新分成两个单独的部分。例如,第一部分仅包含从 0x08000000 开始并在代码结束处结束的代码。第二部分包含从 0x08004000 开始的数据。两个分割的 .bin 文件的大小将比组合的 .bin 文件小得多,因为它们不需要包含所有未使用的内存。您的固件更新程序需要足够智能,以识别每个部分并将它们编程到正确的内存地址。

如果您不想处理单独的 .bin 文件,那么您可以考虑下载 .hex 文件而不是 .bin 文件。.hex 文件不是内存字节的一对一映射,它包含允许跳过未使用的内存区域的编码信息。但是,您的嵌入式固件更新例程必须足够智能,才能在对闪存进行编程之前解码 hex 文件。

于 2016-05-11T14:53:27.210 回答
1

我试图将一些常量放入一个已知的 FLASH 地址中,但是使用上面处理的那些方法我没有得到结果。我尝试了编译指示位置,但我没有得到所有结果,还有@,但 IAR 抱怨这一点。我要存储的变量是固件的版本,所以它有一个 12 字节的值,我将它声明为一个全局值(在我想说的函数之外,所以它可以从 .c 的所有函数中访问) ):

#pragma location=0x00001FF0
__no_init const uint8_t version[12] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B};

我还检查了 IAR 文档,例如: 技术说明 27498

如果有帮助,我正在使用 IAR 6.5(因为我注意到有些方法需要 6.70 更新!

编辑:

好吧,现在它可以执行以下操作:

在 .icf 文件中:

/* Now I have a read only section in the ROM address 0x00001FF4 */
"ROM":
place at address mem:0x00001FF4 { readonly section .version };

在 .c 源文件中:

#pragma location=".version"
__root const uint8_t version[12] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B};

最好的祝福,

伊万

于 2016-07-20T07:12:48.240 回答
0

好的,我终于明白了它是如何工作的。链接器搜索最长的未使用空间以包含那里的代码。

我个人认为这不是最好的方法,如果函数或 const 变量有足够的空间可以容纳,我希望链接器使用第一个未使用的区域。

为了限制这一点,我刚刚添加了下一个代码(stm32F4 的示例)

const char unusedarea[128*3*1024] @0x08020000 ;
#pragma required=unusedarea

这将使用 0x08020000 和 0x0807FFFF 之间的空间,并强制链接器使用其他区域。它有效地发挥了作用。

这允许我保留空间,但留下所需的空间空闲和未使用。我什至可以从 bin 文件中删除最后的 384 kb,只上传前 128kb。

已编辑。将这些变量设置为 __no_init bin 文件仍然很小,并且在使用 jtag 时不会覆盖保留区域

于 2016-05-12T07:53:05.793 回答