前書き
良い一日。EEPROM のパラメータに関する前回の記事は、控えめに言っても少し誤解されていました。どうも私は、目標と解決しようとしている課題を、どこかおかしな形で説明していたようです。今回は自分自身を正そうと試み、解決しようとしている問題の本質をより詳細に説明し、今回は問題の境界を広げます。
つまり、EEPROMに書き込む必要があるパラメータを永続的に保存する方法について話しましょう。
これは非常に具体的な問題であると思われるかもしれませんが、実際には、多くのデバイスがまさにこれを行っています。つまり、常に EEPROM に書き込みを行っています。水道メーター、熱量計、走行距離計、測定履歴を保存するあらゆる種類のユーザー アクション ログとログ、またはその操作の時間を保存するデバイス。
このようなパラメータの特徴は、EEPROM の同じ場所に同じように書き込むことができないことです。EEPROM のすべての書き込みサイクルを使い果たすだけです。たとえば、動作時間を 1 分に 1 回書き込む必要がある場合、1,000,000 回の書き込みサイクルの EEPROM を使用すると、2 年未満で壊れるという計算は簡単です。また、通常の測定装置の検証期間が 3 年または 5 年である場合、2 年とは何ですか。
さらに、すべての EEPROM に 1,000,000 回の書き込みサイクルがあるわけではなく、多くの安価な EEPROM は 100,000 回の書き込みを行う古い技術に従って製造されています. EEPROM は、デバイス動作の最初の 1 年間で最も信頼性の低い要素になる可能性があります。
したがって、この問題を解決して、前の記事のようにパラメーターへのアクセスが簡単になるようにしますが、同時に、EEPROM は 30 年、まあ、または 100 年 (純粋に理論上) で十分です。
そのため、前回の記事では、EEPROM のパラメーターがどこにあるのか、どのようにアクセスするかを考えずに、EEPROM のパラメーターを直感的に操作できるようにする方法をほとんど示しませんでした。
思い出させてください:
ReturnCode returnCode = NvVarList::Init(); // EEPROM
returnCode = myStrData.Set(tString6{ "Hello" }); // Hello EEPOM myStrData.
auto test = myStrData.Get(); //
myFloatData.Set(37.2F); // 37.2 EEPROM.
myUint32Data.Set(0x30313233);
, , HART, FF PF, . , HART - , , , , .. , . 500 - 600, 200.
, @HiSER- , 1 byte, EEPROM. , 200 4 , 1600 EEPROM, 500, 4000.
, 4-20 , 3 , , , BLE . EEPROM . , .
, , , . , , 500 , 1 ( , , ). , 4000 SPI 70 , ( 7 ), , 3 , , .
, . , , , , .
- .
EEPROM,
, . , .
, EEPROM . , 100 000, 1 000 000. , 10 000 000 ? , EEPROM .
, EEPROM . . , EEPROM , , 16, 32 64 . - , EEPROM , , . , . .. , 1 , . - , .
, 1 000 000 , 1 000 000 , . .. , , . 10 , . , 10 , 1.
, , . - .
, . . , , - AntiWearNvData
( ). , , .
// EEPROM
ReturnCode returnCode = NvVarList::Init();
returnCode = myStrData.Set(tString6{ "Hello" }); // Hello EEPROM myStrData.
auto test = myStrData.Get(); //
myFloatData.Set(37.2F); // 37.2 EEPROM.
myUint32Data.Set(0x30313233);
myFloatAntiWearData.Set(10.0F); // 10.0F EEPROM
myFloatAntiWearData.Set(11.0F);
myFloatAntiWearData.Set(12.0F);
myFloatAntiWearData.Set(13.0F);
...
// EEPROM 11 000 000 .
myFloatAntiWearData.Set(11'000'000.0F);
myUint32AntiWearData.Set(10U); // int
myStrAntiWearData.Set(tString6{ "Hello" }); //
:
EEPROM
(), EEPROM. :
, , - , .
() EEPROM
,
, , ,
, runtime, .
EEPROM,
EEPROM I2C SPI, , , .
, .
.
, . , . , .
. , :
AntiWearNvData
, CachedNvData
, . EEPROM, , , . EEPROM , - . uint32_t
30 - 100 000 .
:
, .
EEPROM
CachedNvData
updateTime
. , EEPROM. EEPROM . , :
using tSeconds = std::uint32_t;
constexpr std::uint32_t eepromWriteCycles = 1'000'000U;
constexpr std::uint32_t eepromPageSize = 32U;
// EEPROM 10
constexpr tSeconds eepromLifeTime = 3600U * 24U * 365U * 10U;
updateTime
. . , , . , , , :
template<typename NvList, typename T, const T& defaultValue, tSeconds updateTime, auto& nvDriver>
class AntiWearNvData
{
private:
struct tAntiWear
{
T data = defaultValue;
std::uint32_t index = 0U;
};
inline static tAntiWear nvItem;
public:
// 2 .
//
static constexpr auto recordSize = sizeof(nvItem) * 2U;
// ,
// ,
// ,
// , . .
static_assert(eepromPageSize/recordSize != 0, "Too big parameter");
static constexpr size_t recordCounts = (eepromPageSize/recordSize) *
eepromLifeTime /
(eepromWriteCycles * updateTime);
, , ,
, , . :
, / , .
tAntiWear
. Set(...)
, , , 1.
template<typename NvList, typename T, const T& defaultValue, tSeconds updateTime, auto& nvDriver>
class AntiWearNvData
{
public:
ReturnCode Set(const T& value) const
{
tAntiWear tempData = {.data = value, .index = nvItem.index};
// EEPROM
const auto calculatedAddress = GetCalculatedAdress(nvItem.index);
ReturnCode returnCode = nvDriver.Set(calculatedAddress,
reinterpret_cast<const tNvData*>(&tempData),
sizeof(tAntiWear));
// , ,
// 1,
if (!returnCode)
{
nvItem.data = value;
nvItem.index ++;
}
return returnCode;
}
...
};
:
template<typename NvList, typename T, const T& defaultValue, tSeconds updateTime, auto& nvDriver>
class AntiWearNvData
{
...
private:
static size_t GetCalculatedAdress(std::uint32_t ind)
{
constexpr auto startAddress = GetAddress();
//
// ,
// ,
// - EEPROM.
size_t result = startAddress + recordSize * ((ind % recordCounts));
assert(result < std::size(EEPROM));
return result;
}
constexpr static auto GetAddress()
{
return NvList::template GetAddress<const AntiWearNvData<NvList, T, defaultValue, updateTime, nvDriver>>();
}
};
EEPROM,
Get()
- ,
template<typename NvList, typename T, const T& defaultValue, tSeconds updateTime, auto& nvDriver>
class AntiWearNvData
{
public:
T Get() const
{
return nvItem.data;
}
};
, , . , , , , .
template<typename NvList, typename T, const T& defaultValue, tSeconds updateTime, auto& nvDriver>
class AntiWearNvData
{
public:
static ReturnCode Init()
{
const auto ind = FindLastRecordPosition();
constexpr auto startAddress = GetAddress();
const auto calculatedAddress = startAddress + recordSize * ind;
return nvDriver.Get(calculatedAddress, reinterpret_cast<tNvData*>(&nvItem), sizeof(tAntiWear));
}
...
private:
static std::uint32_t FindLastRecordPosition()
{
// ,
//
// , ,
// 0.
return 0U;
}
};
- , :
template<typename NvList, typename T, const T& defaultValue, tSeconds updateTime, auto& nvDriver>
class AntiWearNvData
{
public:
ReturnCode Set(const T& value) const
{
tAntiWear tempData = {.data = value, .index = nvItem.index};
// 4 .
// 2,
const auto calculatedAddress = GetCalculatedAdress(nvItem.index);
ReturnCode returnCode = nvDriver.Set(calculatedAddress, reinterpret_cast<const tNvData*>(&tempData), sizeof(tAntiWear));
// std::cout << "Write at address: " << calculatedAddress << std::endl;
// , , 1,
if (!returnCode)
{
nvItem.data = value;
// , ,
nvItem.index ++;
}
return returnCode;
}
static ReturnCode Init()
{
const auto ind = FindLastRecordPosition();
constexpr auto startAddress = GetAddress();
const auto calculatedAddress = startAddress + recordSize * ind;
return nvDriver.Get(calculatedAddress, reinterpret_cast<tNvData*>(&nvItem), sizeof(tAntiWear));
}
T Get() const
{
return nvItem.data;
}
static ReturnCode SetToDefault()
{
ReturnCode returnCode = nvDriver.Set(GetCalculatedAdress(nvItem.index), reinterpret_cast<const tNvData*>(&defaultValue), sizeof(T));
return returnCode;
}
private:
static size_t GetCalculatedAdress(std::uint32_t ind)
{
constexpr auto startAddress = GetAddress();
size_t result = startAddress + recordSize * ((ind % recordCounts));
assert(result < std::size(EEPROM));
return result;
}
static std::uint32_t FindLastRecordPosition()
{
// , ,
// 1 - 15 5.
return 1U;
}
constexpr static auto GetAddress()
{
return NvList::template GetAddress<const AntiWearNvData<NvList, T, defaultValue, updateTime, nvDriver>>();
}
struct tAntiWear
{
T data = defaultValue;
std::uint32_t index = 0U;
};
inline static tAntiWear nvItem;
public:
static constexpr auto recordSize = sizeof(nvItem) * 2U;
static_assert(eepromPageSize/recordSize != 0, "Too big parameter");
static constexpr size_t recordCounts = (eepromPageSize/recordSize) * eepromLifeTime / (eepromWriteCycles * updateTime);
};
CachedNvData
, , , CachedNvData
, AntiWearNvData
.
, IAR ++17, , . , SetToDefault
Init
. , , . , .
template<const tNvAddress startAddress, typename ...TNvVars>
struct NvVarListBase
{
static ReturnCode SetToDefault()
{
return ( ... || TNvVars::SetToDefault());
}
static ReturnCode Init()
{
return ( ... || TNvVars::Init());
}
template<typename T>
constexpr static size_t GetAddress()
{
return startAddress + GetAddressOffset<T, TNvVars...>();
}
private:
template <typename QueriedType, typename T, typename ...Ts>
constexpr static size_t GetAddressOffset()
{
auto result = 0;
if constexpr (!std::is_same<T, QueriedType>::value)
{
// , .
result = T::recordSize * T::recordCounts + GetAddressOffset<QueriedType, Ts...>();
}
return result;
}
};
CachedNvData
recordSize
recordCounts = 1
. .
, :
struct NvVarList;
constexpr NvDriver nvDriver;
using tString6 = std::array<char, 6U>;
inline constexpr float myFloatDataDefaultValue = 10.0f;
inline constexpr tString6 myStrDefaultValue = { "Popit" };
inline constexpr std::uint32_t myUint32DefaultValue = 0x30313233;
inline constexpr std::uint16_t myUin16DeafultValue = 0xDEAD;
constexpr CachedNvData<NvVarList, float, myFloatDataDefaultValue, nvDriver> myFloatData;
constexpr CachedNvData<NvVarList, tString6, myStrDefaultValue, nvDriver> myStrData;
constexpr CachedNvData<NvVarList, std::uint32_t, myUint32DefaultValue, nvDriver> myUint32Data;
constexpr AntiWearNvData<NvVarList, std::uint32_t, myUint32DefaultValue, 60U, nvDriver> myUint32AntiWearData;
constexpr AntiWearNvData<NvVarList, float, myFloatDataDefaultValue, 60U, nvDriver> myFloatAntiWearData;
struct SomeSubsystem
{
static constexpr auto test = CachedNvData < NvVarList, std::uint16_t, myUin16DeafultValue, nvDriver>();
};
//*** Register the Shadowed Nv param in the list *****************************
struct NvVarList : public NvVarListBase<0,
decltype(myStrData),
decltype(myFloatData),
decltype(SomeSubsystem::test),
decltype(myUint32Data),
decltype(myFloatAntiWearData),
decltype(myUint32AntiWearData)
>
{
};
, , , , . CachedNvData
.
int main()
{
NvVarList::SetToDefault();
ReturnCode returnCode = NvVarList::Init();
myFloatData.Set(37.2F);
myStrData.Set(tString6{"Hello"});
myFloatAntiWearData.Set(10.0F);
myFloatAntiWearData.Set(11.0F);
myFloatAntiWearData.Set(12.0F);
myFloatAntiWearData.Set(13.0F);
myFloatAntiWearData.Set(14.0F);
myUint32AntiWearData.Set(10U);
myUint32AntiWearData.Set(11U);
myUint32AntiWearData.Set(12U);
myUint32AntiWearData.Set(13U);
myUint32AntiWearData.Set(14U);
myUint32AntiWearData.Set(15U);
return 1;
}
, 10,11,12...15 . , + + . , .
, 15 5 , 10 .
, , 5 15 .
, , , .