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

时间:2015-07-09 16:00:38

标签: c++ c++11 arm embedded stm32

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

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

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

所以有一些问题:

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

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

      

    唯一可复制的类型是标量类型,可以轻易复制   类和类型/类的数组(可能是const限定的,   但不是挥发性合格的)。

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

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

    P.S。 它甚至可以在没有volatile的情况下工作,但是恐怕有一天,一些聪明的LTO编译器会看到静态初始化,不会改变(通过C ++)struct并优化对底层内存地址的一些访问。这意味着不会应用新编程的参数,因为它们是由编译器内联的。

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

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

2 个答案:

答案 0 :(得分:2)

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

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

指向Flash的指针

声明结构的指针 将指针指定给Flash中的正确地址 通过取消引用指针来访问变量 应该声明指针并将其指定为常量数据的常量指针。

告诉变量的编译器地址。

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

同样,变量应声明为“static const”。 “静态”因为只有一个实例。 “const”因为Flash存储器在大多数时间是只读的。

闪存:易失性与常量

在大多数情况下,闪存(无论如何编程)都是只读的。事实上,你可以在Flash中读取数据的唯一方法是锁定它,a.k.a。使它成为只读。一般而言,如果没有一致的程序,它将不会改变。

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

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

Flash将是易失性的,当且仅当执行的另一个任务或执行线程在您的执行线程处于活动状态时对闪存进行编程时。我仍然不会将此情况视为 volatile 。这将是同步性的一种情况 - 如果闪存被修改,那么应该通知一些听众。

摘要

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

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

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

答案 1 :(得分:2)

通过重新编程闪存,您正在更改基础对象的表示。 volatile限定符是适合的解决方案 确保数据变化不被优化的情况。

您希望声明为:const volatile Settings settings;

缺点是volatile会阻止对象的静态初始化。这会阻止您使用链接器将初始化对象放入适当的内存地址。

您希望定义为:const Settings settings = { ... };

幸运的是,您可以初始化const对象并将其作为const volatile进行访问。

// Header file
struct Settings { ... };
extern const volatile Settings& settings;
// Source file
static const Settings init_settings = { ... };
const volatile Settings& settings = init_settings;

init_settings对象是静态初始化的,但通过settings引用的所有访问都被视为volatile。

请注意,修改定义为const的对象是未定义的行为。