introduction
Bonne journée. Mon dernier article sur les paramètres dans l'EEPROM était, pour le moins, un peu mal compris. Apparemment, j'ai décrit de manière tordue l'objectif et la tâche qui étaient en train d'être résolus. Cette fois, je vais essayer de me corriger, de décrire plus en détail l'essence du problème à résoudre, et cette fois nous allons élargir les limites du problème.
À savoir, parlons de la façon de stocker les paramètres qui doivent être écrits en permanence dans l'EEPROM.
Il peut sembler à beaucoup qu'il s'agit d'un problème très spécifique, mais en fait, de nombreux appareils font exactement cela - ils écrivent constamment dans l'EEPROM. Un compteur d'eau, un compteur de chaleur, un odomètre, toutes sortes de journaux d'action utilisateur et de journaux qui stockent l'historique des mesures, ou tout simplement n'importe quel appareil qui stocke l'heure de son fonctionnement.
La particularité de tels paramètres est qu'ils ne peuvent pas être écrits comme ça au même endroit dans l'EEPROM, vous utiliserez simplement tous les cycles d'écriture de l'EEPROM. Par exemple, s'il est nécessaire d'écrire le temps de fonctionnement une fois toutes les 1 minute, alors il est facile de calculer qu'avec une EEPROM de 1 000 000 de cycles d'écriture, vous le ruinerez en moins de 2 ans. Et qu'est-ce que 2 ans, si un appareil de mesure ordinaire a un temps de vérification de 3 ou même 5 ans.
De plus, toutes les EEPROM n'ont pas 1 000 000 de cycles d'écriture, de nombreuses EEPROM bon marché sont encore produites selon d'anciennes technologies avec 100 000 écritures. Et si l'on considère que 1 000 000 de cycles ne sont indiqués que dans des conditions idéales, disons à haute température, ce nombre peut être divisé par deux, alors votre EEPROM peut s'avérer être l'élément le moins fiable au cours de la première année de fonctionnement de l'appareil.
Essayons donc de résoudre ce problème, et faisons en sorte que l'accès aux paramètres soit aussi simple que dans l'article précédent, mais en même temps l'EEPROM suffirait pour 30 ans, enfin, ou pour 100 (purement théoriquement).
Ainsi, dans le dernier article, j'ai à peine montré comment faire pour que les paramètres de l'EEPROM puissent être travaillés de manière intuitive, sans penser à où ils se trouvent et comment y accéder.
Laissez-moi vous rappeler:
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 .
, , , .