EEPROM 内のパラメータの操作

前書き

こんにちは、Habrです。最後に、私には自由な時間があり、私の経験をもう少し共有できます.誰かの役に立ち、彼の仕事に役立つかもしれません. 上手 ...





生徒の授業の様子を観察することで、問題が発生する瞬間に気付くようにしています。これらのポイントの 1 つは、外部 EEPROM の使用です。これは、ユーザー設定やその他の有用な情報が保存されている場所であり、電源を切った後は消去しないでください。最も単純な例は、測定単位の変更です。ユーザーはボタンを押して、測定単位を変更します。まあ、またはModbasなどの外部プロトコルを介してキャリブレーション係数を書き留めます。





学生が EEPROM に何かを保存することを決定するたびに、誤って選択されたアーキテクチャと単なる人的要因の両方に関連する多くのバグが発生します。実際、学生は通常、オンラインで次のようなものを見つけます。





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>
{
};
      
      



これで、パラメータをどこでも使用できるようになりました。非常にシンプルで初歩的です。





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



コンストラクターまたはテンプレートを介して、それらへの参照を任意のクラスに渡すことができます。





template<const auto& param>
struct SuperSubsystem
{
  void SomeMethod()
  {
    std::cout << "SuperSubsystem read param" << param.Get() << std::endl; 
  }
};

int main()
{  
  SuperSubsystem<myFloatData> superSystem;
  superSystem.SomeMethod();
}
      
      



それで全部です。コンパイラがいくつかのチェックを行うため、学生は EEPROM をより使いやすく、ミスを減らすことができます。





サンプルコードへのリンクはこちら





PS QSPI を介して EEPROM を操作するためのドライバーを実装する方法についてもお話したいと思います (学生は、それがどのように機能するかをあまりにも長く理解していました)。 、もちろん面白いなら。








All Articles