Travailler avec des paramètres en EEPROM

introduction

Salut, Habr. Enfin, j'ai du temps libre et je peux partager un peu plus mon expérience, peut-être que cela sera utile à quelqu'un et l'aidera dans son travail, et j'en serai certainement ravi. Bien ...





En regardant comment les étudiants font leurs cours, j'essaie de remarquer les moments qui leur causent des problèmes. L'un de ces points est de travailler avec une EEPROM externe. C'est là que les préférences de l'utilisateur et d'autres informations utiles sont stockées et ne doivent pas être effacées après la mise hors tension. L'exemple le plus simple est le changement d'unités de mesure. L'utilisateur appuie sur le bouton et change les unités de mesure. Eh bien, ou écrit les coefficients d'étalonnage via un protocole externe, tel que Modbas.





Chaque fois qu'un étudiant décide de sauvegarder quelque chose dans l'EEPROM, cela se traduit par de nombreux bugs liés à la fois à l'architecture mal choisie et à un simple facteur humain. En fait, un étudiant va généralement en ligne et trouve quelque chose comme ceci :





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



Et maintenant nous pouvons utiliser nos paramètres n'importe où, très simple et élémentaire :





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



Vous pouvez leur transmettre une référence à n'importe quelle classe, via un constructeur ou un modèle.





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

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



C'est tout. Désormais, les étudiants peuvent travailler avec l'EEPROM de manière plus conviviale et faire moins d'erreurs, car le compilateur effectuera certaines des vérifications à leur place.





Lien vers un exemple de code ici





PS Je voulais aussi vous parler de la façon dont vous pouvez implémenter un pilote pour travailler avec EEPROM via QSPI (les étudiants ont compris comment cela fonctionne trop longtemps), mais le contexte s'est avéré trop hétéroclite, je pense donc le décrire dans un autre article , si c'est bien sûr intéressant.








All Articles