【发布时间】:2018-09-10 12:31:37
【问题描述】:
我正在开发一个嵌入式系统,其中一些校准数据存储在闪存中。校准数据存储在一个结构体中,该结构体放置在链接器知道要放置在闪存中的特殊部分中:
struct data_block {
calibration_data mData;
uint16_t mCheckSum;
};
//Define to compile the fixed flash location for image data
const data_block __attribute__((section (".caldata"))) gCalibrationData{};
其中calibration_data 是另一个包含实际值的 POD 结构。
问题是,如果我现在只写以下内容:
const data_block data{gCalibrationData};
if (CheckSum(&(data.mData)) == data.mCheckSum) {
//do stuff
} else {
//Error
}
这总是转到错误分支,即使闪存中的实际校验和是绝对正确的(写这个有点不同使它工作,见下文)。
这当然是可以理解的:编译器看到一个 const 全局对象,它是默认初始化的,所以它知道所有的值,所以我猜它实际上优化了整个 if(如果我通过 debug-printf 数据uint16_t *,我实际上得到了正确的值)。
我认为正确的方式是定义
const volatile data_block __attribute__((section (".caldata"))) gCalibrationData{};
但是,现在我遇到的问题是我无法将 volatile 结构分配给非易失性,即 const data{gCalibrationData}; 无法编译。如果我尝试通过const volatile data_block * 访问也会出现同样的问题。
至少有两种或三种方法可以完成这项工作,但我不喜欢其中任何一种:
- 从
gCalibrationData中删除const(和volatile)限定符。但是,这有点像 hack,因为编译器不够聪明,无法保证我的程序中永远不会触及 gCalibrationData,另一方面,我想保留 const 限定符,因为尝试写入 gCalibrationData通过分配是一个硬错误。 - 通过
const gCalibrationData * volatile pData访问gCalibrationData(是的,volatile 正是我的意思)。通过易变的指针访问会强制编译器实际加载数据。同样,这似乎是一种 hack,因为指针本身当然不是易失性的。 - 给
data_block和calibration_data一个赋值运算符,取const volatile &,并在其中逐个字段地赋值。从语言的角度来看,这似乎是正确的,但是每当calibration_data更改时,我都需要手动编辑赋值运算符。否则会产生难以检测的错误。
我的问题:读取校准数据的正确方法是什么?我的理想标准是:
- 全局对象本身是
const,用于捕获意外写入。 - 没有未定义的行为
- 通过将结构直接分配给另一个结构来访问
- 或者至少这样我就不需要记住在
calibration_data中分配每个原始类型的变量,请参见上面的选项 3。 - 线程安全加分,尽管在我的具体情况下,只有一个线程读取或写入闪存(所有其他“线程”都是中断)。
【问题讨论】:
-
您可能需要注意为什么执行
const data_block data{gCalibrationData};很重要,因为它似乎if (CheckSum(&(gCalibrationData.mData)) == gCalibrationData.mCheckSum) {应该有效。 -
@Dkrueger 为什么你认为这会起作用?无论如何,我认为如果我的代码通过分配给变量作为中间步骤而被破坏,它会非常脆弱。
-
确认一下,
.caldata这个部分肯定在ROM里吗? -
@edking 是的,绝对的,通过多种方式确认。
-
@Timo 在那种情况下,我想知道您是否可以通过抽象结构来解决您的问题?如果您将其设为某个模块的私有并且只允许通过函数进行读取访问,您会充分保护它吗?此外,比较
const结构的两个成员似乎违反直觉,因为由于它们是恒定的,您总是会在事前知道比较的答案?
标签: c++ embedded constants volatile