EEVblog Electronics Community Forum

Products => Computers => Programming => Topic started by: 741 on June 12, 2020, 10:52:33 am

Title: Initialising a byte array by overlaying union-style (without a union instance)
Post by: 741 on June 12, 2020, 10:52:33 am
I have an existing code base which assumes an variable
   uint8_t gEEPersistentData[_EEPROMSIZE];
That is, logically speaking, EEPROM is treated like a "byte array".

Mindful, of course of packing, alignment and other "gotchas" I could do this kind of thing

typedef union
{
      Tpwm     selectedPwm;             //A|B
      uint8_t gEEPersistentData[_EEPROMSIZE];
}tEE_Config;

tEE_Config eeConfig;

But that means editing a generic code base to work with eeConfig.gEEPersistentData. Existing projects would have to declare a dummy union just so they can compile.

For the simple array I can do this
   uint8_t gEEPersistentData[_EEPROMSIZE] = {1,2, 0x20, 9, 65}; //etc

Is there a syntax that allows me to cast a union as an array, and use that for intiialising? Something like this

gEEPersistentData[_EEPROMSIZE] = (cast of some type)
(
   Tpwm
   {
      .period = 40,
      .duty = 20,
      .phase = 20
   }
);
Perhaps, if I am prepared to ammend existing projects, I could start by typedef-ing the array, which could give me a type to cast "into"?
Title: Re: Initialising a byte array by overlaying union-style (without a union instance)
Post by: Kalvin on June 12, 2020, 12:37:45 pm
Typically one would typedef a new data structure, and then  assign a const pointer to point to that specific memory location (eg. the pointer to point to the address of gEEPersistentData, so that the two variables are forming effectively an union). Alternatively, one would typedef a new data structure and then create a new variable with an absolute address (ie. the address of gEEPersistentData).
Title: Re: Initialising a byte array by overlaying union-style (without a union instance)
Post by: 741 on June 12, 2020, 12:56:01 pm
That sounds workable  :)

I could require user code to define the pointer
    pEEPersistentData

Then the user can initialise it with the address of their arbitrary data structure.
Title: Re: Initialising a byte array by overlaying union-style (without a union instance)
Post by: Nominal Animal on June 12, 2020, 05:41:36 pm
You cannot cast an intialization expression to another type in a way that would achieve what you want, but you can use an union and initialize a variable of that union type using whichever member you want.  Then, you can use the preprocessor to mangle the name seen by userspace, into a reference of a member of that union.

So, if you have an existing codebase that accesses the EEPROM data through gEEPersistentData but you want to embed it in an union or a structure (or a combination):
Code: [Select]
union {
    Tpwm     selectedPwm;
    uint8_t data[_EEPROMSIZE];
} eeConfig = { .selectedPwm = { .period = 40, .duty = 20, .phase = 20 } };
#define gEEPersistentData  (eeConfig.data)
Here, sizeof gEEPersistentData == _EEPROMSIZE, and the type of gEEPersistentata is an array of uint8_t.  It is perfectly okay to grab its address, for example.

If the structure is declared and initialized in user code, but accessed in your/library code, you can put in a header file used by both user and library code:
Code: [Select]
typedef union {
    Tpwm        selectedPwm;
    uint8_t     data[_EEPROMSIZE];
} TEEConfig;

#define  DECLARE_EEPROM_PWM(spec...) \
    TEEConfig eeConfig = { .selectedPwm = { spec } };

#define  DECLARE_EEPROM_DATA(spec...) \
    TEEConfig eeConfig = { .data = { spec } };

#define  gEEPersistentData (eeConfig.data)

extern TEEConfig eeConfig;
and library code can happily access the _EEPROMSIZE bytes in array gEEPersistentData, as long as one of the source objects declares the EEPROM, using e.g.
Code: [Select]
DECLARE_EEPROM_PWM(.period=40, .duty=20, .phase=20);
or (equivalently if those Tpwm fields are all just bytes in that order)
Code: [Select]
DECLARE_EEPROM_DATA(40, 20, 20);
This is pure ANSI C/ISO C89, and should work even with slightly limited or wonky C compilers just fine.  In case you wonder, yes, it is legal to have extern Type Var; and then define Type Var = ...; you do not need to have separate header files for "user code" and "library code".