嵌入式C++ 11代码 - 我需要volatile吗?

sig*_*maN 5 c++ embedded arm stm32 c++11

带Cortex M3 MCU的嵌入式设备(STM32F1).它有嵌入式闪存(64K).MCU固件可以在运行时重新编程闪存扇区; 这是由闪存控制器(FMC)寄存器完成的(所以它不像a = b那么容易).FMC获取缓冲区指针并将数据刻录到某个闪存扇区.

我想将最后一个闪存扇区用于设备配置参数.参数存储在带有数组的压缩结构中,并包含一些自定义类.

可以在运行时更改参数(复制到RAM,使用FMC更改并回显到闪存).

所以有一些问题:

  1. FMC硬件改变参数struct的状态(按位).C++编译器不知道它是否被更改. 这是否意味着我应该将所有struct成员声明为volatile? 我想是的.

  2. 应该在编译时静态初始化Struct(默认参数).Struct应该是POD(TriviallyCopyable并具有标准布局).请记住,那里有一些自定义类,所以我记住这些类也应该是POD.但是有一些问题: cppreference.com

    唯一可复制的类型是标量类型,平凡的可复制类,以及此类类型/类的数组(可能是const限定的, 但不是volatile限定的).

这意味着我不能让我的班级POD和易变? 那么我该如何解决这个问题呢?

在参数struct中只能使用标量类型,但它可能会导致配置处理周围的代码更加干净......

PS 它甚至没有易失性,但我担心有一天,一些聪明的LTO编译器会看到静态初始化,不会改变(通过C++)struct并优化对底层内存地址的一些访问.这意味着不会应用新编程的参数,因为它们是由编译器内联的.

编辑:可以在不使用volatile的情况下解决问题.它似乎更正确.

您需要在单独的转换单元(.cpp文件)中定义config struct变量,并且不要初始化变量以避免在LTO期间替换值.如果不使用LTO - 一切正常,因为优化是一次在一个转换单元中完成的,因此不应优化具有静态存储持续时间和在专用转换单元中定义的外部链接的变量.只有LTO可以抛弃它或者在不发出内存提取的情况下进行值替换.特别是在将变量定义为const时.我认为如果不使用LTO,初始化变量是可以的.

Tho*_*ews 2

根据您的编译器,您有一些选择:

  • 您可以声明一个指向该结构的指针并初始化指向该区域的指针。
  • 告诉编译器变量应该驻留在哪里

指向闪存的指针

声明结构体的指针。
将指针分配给 Flash 中的正确地址。
通过取消引用指针来访问变量。
该指针应该被声明和分配为指向常量数据的常量指针。

告诉编译器变量的地址。

某些编译器允许您将变量放置在特定的内存区域中。第一步是在链接器命令文件中创建一个区域。下一步是告诉编译器该变量位于该区域中。

同样,该变量应声明为“static const”。“静态”是因为只有 1 个实例。“const”是因为闪存在大多数情况下是只读的。

闪存:易失性与常量

在大多数情况下,闪存无论如何编程,都是只读的。事实上,读取闪存中数据的唯一方法就是锁定它,也称为只读。一般情况下,未经程序一致同意,不会更改。

大多数闪存都是由软件编程的。通常,这是您的程序。如果您的程序要重新编程闪存,它就会知道值已更改。这类似于写入 RAM。程序改变值,而不是硬件。因此闪存不是失性的。

我的经验是,Flash 可以通过其他方式进行编程,通常是在程序未运行时。在这种情况下,它仍然不是易失性的,因为您的程序没有运行。Flash 仍然是只读的。

当且仅当您的执行线程处于活动状态时另一个任务或执行线程对闪存进行编程时,闪存才会是易失性的。我仍然不认为这个案子是不稳定的。这将是同步性的一个例子——如果 flash 被修改,那么应该通知一些监听器。

概括

闪存最好被视为只读存储器。尽管某些编译器和链接器允许您在特定的硬编码地址声明变量,但通过指针访问驻留在 Flash 中的变量以获得最佳可移植性。应声明变量,const static以便编译器可以发出代码来直接访问变量,而不是在堆栈上复制。如果闪存由另一个任务或执行线程编程,则这是同步性问题,而不是易失性问题。在极少数情况下,在执行程序时,闪存会由外部源进行编程。

您的程序应该提供校验和或其他方法来确定自上次检查以来内容是否已更改。

不要让编译器从闪存初始化变量。
这并不是真正的便携。更好的方法是让初始化代码从闪存加载变量。让编译器从不同的段加载变量需要对编译器和链接器的内部进行大量工作;不仅仅是初始化指向闪存中地址的指针。