pengantar
Hai, Habr. Akhirnya, saya punya waktu luang dan saya dapat berbagi sedikit pengalaman saya, mungkin itu akan berguna bagi seseorang dan membantu dalam pekerjaannya, dan saya pasti akan senang dengan ini. Yah ...
Dengan melihat bagaimana siswa mengerjakan tugas kuliahnya, saya mencoba memperhatikan saat-saat yang menyebabkan mereka bermasalah. Salah satu poin ini bekerja dengan EEPROM eksternal. Di sinilah preferensi pengguna dan informasi berguna lainnya disimpan dan tidak boleh dihapus setelah mematikan daya. Contoh paling sederhana adalah mengubah satuan pengukuran. Pengguna menekan tombol dan mengubah satuan pengukuran. Nah, atau menuliskan koefisien kalibrasi melalui beberapa protokol eksternal, seperti Modbas.
Setiap kali seorang siswa memutuskan untuk menyimpan sesuatu di EEPROM, ini menghasilkan banyak bug yang terkait dengan arsitektur yang dipilih secara salah dan hanya faktor manusia. Sebenarnya, seorang siswa biasanya online dan menemukan sesuatu seperti ini:
int address = 0;
float val1 = 123.456f;
byte val2 = 64;
char name[10] = "Arduino";
EEPROM.put(address, val1);
address += sizeof(val1); //+4
EEPROM.put(address, val2);
address += sizeof(val2); //+1
EEPROM.put(address, name);
address += sizeof(name); //+10
, 100 EEPROM , , . , - .
, , , EEPROM , . EEPROM, , .
:
EEPROM .
EepromManager
, EEPROM , , , EEPROM.
: EEPROM, .
: , - , Modbus , , - , , . , . , , , . . , - , , .
- .
, . EEPROM , .
: , , - , EEPROM.
, , , 5 , EEPROM , . , , EEPROM, , , , ( .. ) 5 10 , .
, , , , , , , :
// 10.0F EEPROM , myEEPROMData
myEEPROMData = 10.0F;
, , EEPROM . , - :
// EEPROM 5 myStrData
auto returnStatus = myStrData.Set(tStr6{"Hello"});
if (!returnStatus)
{
std::cout << "Ok"
}
// EEPROM float myFloatData
returnStatus = myFloatData.Set(37.2F);
, , .
, . :
() EEPROM
, , , ,
EEPROM,
EEPROM I2C SPI. , , .
, EEPROM, - .
EEPROM, EEPROM, , , , , .
:)
, : CahedNvData
CachedNvData
, :
Init()
EEPROM .
, . data
, - , Get()
.
, EEPROM nvDriver
. nvDriver, , Set()
Get()
. , .
NvDriver
@gleb_l , EEPROM, , , , , .
, , . , , , EEPROM - . .
, 3 :
// 6
constexpr CachedNvData<NvVarList, tString6, myStrDefaultValue, nvDriver> myStrData;
// 4
constexpr CachedNvData<NvVarList, float, myFloatDataDefaultValue, nvDriver> myFloatData;
// 4
constexpr CachedNvData<NvVarList, std::uint32_t, myUint32DefaultValue, nvDriver> myUint32Data;
- :
NvVarList<100U, myStrData, myFloatData, myUint32Data>
myStrData
100, myFloatData
- 106, myUint32Data
- 110. .
, EEPROM. GetAdress()
, .
, , . , , , .
, NvVarListBase:
NvVarListBase
.
- . ,
CahedNvData
template<typename NvList, typename T, const T& defaultValue, const auto& nvDriver>
class CahedNvData
{
public:
ReturnCode Set(T value) const
{
// EEPROM
constexpr auto address =
NvList::template GetAddress<NvList,T,defaultValue,nvDriver>();
// EEPROM
ReturnCode returnCode = nvDriver.Set(
address,
reinterpret_cast<const tNvData*>(&value), sizeof(T));
// ,
if (!returnCode)
{
memcpy((void*)&data, (void*)&value, sizeof(T));
}
return returnCode;
}
ReturnCode Init() const
{
constexpr auto address =
NvList::template GetAddress<NvList,T,defaultValue,nvDriver>();
// EEPROM
ReturnCode returnCode = nvDriver.Get(
address,
reinterpret_cast<tNvData*>(&data), sizeof(T));
// EEPROM,
if (returnCode)
{
data = defaultValue;
}
return returnCode;
}
T Get() const
{
return data;
}
using Type = T;
private:
inline static T data = defaultValue;
};
template<const tNvAddress startAddress, const auto& ...nvVars>
struct NvVarListBase
{
template<typename NvList, typename T, const T& defaultValue, const auto& nvDriver>
constexpr static size_t GetAddress()
{
// EEPROM
//CahedNvData<NvList, T, defaultValue, nvDriver>
using tQueriedType = CahedNvData<NvList, T, defaultValue, nvDriver>;
return startAddress +
GetAddressOffset<tQueriedType>(NvVarListBase<startAddress,nvVars...>());
}
private:
template <typename QueriedType, const auto& arg, const auto&... args>
constexpr static size_t GetAddressOffset(NvVarListBase<startAddress, arg, args...>)
{
// ,
//
auto test = arg;
// ,
if constexpr (std::is_same<decltype(test), QueriedType>::value)
{
return 0U;
} else
{
//
// .
return sizeof(typename decltype(test)::Type) +
GetAddressOffset<QueriedType>(NvVarListBase<startAddress, args...>());
}
}
};
.
:
using tString6 = std::array<char, 6U>;
inline constexpr float myFloatDataDefaultValue = 10.0f;
inline constexpr tString6 myStrDefaultValue = {"Habr "};
inline constexpr std::uint32_t myUint32DefaultValue = 0x30313233;
:
// , .
// forward declaration
struct NvVarList;
constexpr NvDriver nvDriver;
// NvVarList EEPROM
constexpr CahedNvData<NvVarList, float, myFloatDataDefaultValue, nvDriver> myFloatData;
constexpr CahedNvData<NvVarList, tString6, myStrDefaultValue, nvDriver> myStrData;
constexpr CahedNvData<NvVarList, uint32_t, myUint32DefaultValue, nvDriver> myUint32Data;
. , EEPROM . NvVarListBase, .
struct NvVarList : public NvVarListBase<0, myStrData, myFloatData, myUint32Data>
{
};
Dan sekarang kita dapat menggunakan parameter kita di mana saja, sangat sederhana dan mendasar:
struct NvVarList;
constexpr NvDriver nvDriver;
using tString6 = std::array<char, 6U>;
inline constexpr float myFloatDataDefaultValue = 10.0f;
inline constexpr tString6 myStrDefaultValue = {"Habr "};
inline constexpr uint32_t myUint32DefaultValue = 0x30313233;
constexpr CahedNvData<NvVarList, float, myFloatDataDefaultValue, nvDriver> myFloatData;
constexpr CahedNvData<NvVarList, tString6, myStrDefaultValue, nvDriver> myStrData;
constexpr CahedNvData<NvVarList, uint32_t, myUint32DefaultValue, nvDriver> myUint32Data;
struct NvVarList : public NvVarListBase<0, myStrData, myFloatData, myUint32Data>
{
};
int main()
{
myStrData.Init();
myFloatData.Init();
myUint32Data.Init()
myStrData.Get();
returnCode = myStrData.Set(tString6{"Hello"});
if (!returnCode)
{
std::cout << "Hello has been written" << std::endl;
}
myStrData.Get();
myFloatData.Set(37.2F);
myUint32Data.Set(0x30313233);
return 1;
}
Anda dapat meneruskan referensi ke mereka ke kelas mana pun, melalui konstruktor atau templat.
template<const auto& param>
struct SuperSubsystem
{
void SomeMethod()
{
std::cout << "SuperSubsystem read param" << param.Get() << std::endl;
}
};
int main()
{
SuperSubsystem<myFloatData> superSystem;
superSystem.SomeMethod();
}
Itu saja. Sekarang siswa dapat bekerja dengan EEPROM lebih ramah pengguna dan membuat lebih sedikit kesalahan, karena kompilator akan melakukan beberapa pemeriksaan untuk mereka.
PS Saya juga ingin memberi tahu Anda tentang bagaimana Anda dapat mengimplementasikan driver untuk bekerja dengan EEPROM melalui QSPI (siswa memahami cara kerjanya terlalu lama), tetapi konteksnya ternyata terlalu beragam, jadi saya pikir untuk menggambarkannya di artikel lain , jika itu tentu saja menarik.