Bekerja dengan parameter di EEPROM, bagaimana tidak menghabiskan memori

pengantar

Selamat siang. Artikel terakhir saya tentang parameter di EEPROM adalah, secara halus, sedikit disalahpahami. Rupanya, saya entah bagaimana menggambarkan tujuan dan tugas yang sedang diselesaikan. Kali ini saya akan mencoba mengoreksi diri, menjelaskan lebih detail inti permasalahan yang sedang diselesaikan, dan kali ini kita akan memperluas batasan permasalahan.





Yaitu, mari kita bicara tentang cara menyimpan parameter yang perlu ditulis ke EEPROM secara permanen.





Tampaknya bagi banyak orang bahwa ini adalah masalah yang sangat spesifik, tetapi pada kenyataannya, banyak perangkat yang melakukan hal ini - mereka terus-menerus menulis ke EEPROM. Pengukur air, pengukur panas, odometer, semua jenis log tindakan pengguna dan log yang menyimpan riwayat pengukuran, atau perangkat apa pun yang menyimpan waktu pengoperasiannya.





Keunikan parameter tersebut adalah bahwa mereka tidak dapat ditulis begitu saja ke tempat yang sama di EEPROM, Anda hanya akan menggunakan semua siklus penulisan EEPROM. Misalnya, jika perlu menulis waktu pengoperasian setiap 1 menit sekali, maka mudah untuk menghitung bahwa dengan EEPROM 1.000.000 siklus tulis, Anda akan merusaknya dalam waktu kurang dari 2 tahun. Dan apa itu 2 tahun, jika alat ukur biasa memiliki waktu verifikasi 3 atau bahkan 5 tahun.





Selain itu, tidak semua EEPROM memiliki 1.000.000 siklus tulis, banyak EEPROM murah masih diproduksi sesuai dengan teknologi lama dengan 100.000 tulis. Dan jika kita menganggap bahwa 1.000.000 siklus diindikasikan hanya dalam kondisi ideal, katakanlah pada suhu tinggi jumlah ini dapat dibagi dua, maka EEPROM Anda mungkin menjadi elemen yang paling tidak dapat diandalkan di tahun pertama pengoperasian perangkat.





Oleh karena itu, mari kita coba memecahkan masalah ini, dan membuatnya sehingga mengakses parameter sesederhana di artikel sebelumnya, tetapi pada saat yang sama EEPROM akan cukup untuk 30 tahun, baik, atau untuk 100 (murni secara teoritis).





Jadi, di artikel terakhir, saya hampir tidak menunjukkan bagaimana melakukannya sehingga parameter di EEPROM dapat bekerja secara intuitif, tanpa memikirkan di mana mereka berada dan bagaimana mengaksesnya.





Biarkan saya mengingatkan Anda:





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);
      
      



, , . @Andy_Big @HiSER .





, , 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 .





, , , .





.








All Articles