danstm danstm - 8 months ago 208
C Question

STM32F0 I2C HAL save struct to I2C EEPROM

I need to save a struct:

struct {
uint16_t value;
uint16_t flag;
} status;

into an external I2C EEPROM using

HAL_StatusTypeDef HAL_I2C_Mem_Write (I2C_HandleTypeDef * hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t * pData, uint16_t Size, uint32_t Timeout)


Can you suggest me a right way to do this?

EDIT: integration to the answer

I tried to implement Bence Kaulics solution and it worked perfectly but if you do:

void foo(uint16_t address, status* read)
read = (status*)(&readBuffer);

then, in main, you try to pass the struct by reference:

void main(void)
foo(THE_ADDRESS, &my_struct);

it does populate the struct with junk data. This because of the padding the compiler may introduce to align the data in memory space I suppose.

One solution is to declare the struct with a kind of ATTRIBUTE (compiler dependent) in order to prevent the compiler to introduce the padding but:

1) It is not portable

2) Lots of compiler manuals discourage the use of this kind of attribute (may for the point 1).

So the most portable, effective and safe way I found (yet not the most code compact or fast, maybe) is, instead of,

read = (status*)(&readBuffer)

to use:

memcpy(&read->value, readBuffer + 0, 2);
memcpy(&read->flag, readBuffer + 2, 2);

You have to pay attention to the size of the data and to increment the pointer the size of the data and it works like a charm.

I would like to see if a faster way not using

Answer Source

If you look close all data types store simple bytes, the difference is how the bytes are interpreted.

Basically you need an uint8_t* pData pointer to a buffer and the number of bytes to be transfered (uint16_t Size). So cast the struct pointer to uint8_t* and get the size using sizeof().

Here is a simulation, and please forgive me my poor test environment (the ints and unsigned chars).

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
  int value;
  int flag;
} status;

int main(void) {

    status stat;
    stat.value = 15;
    stat.flag = 1;

    int size = sizeof(status);                            // number of bytes to be sent
    unsigned char* pData;                                 // (uint8_t*) unsigned char* pointer to data

    unsigned char eeprom[size];                           // simulated EEPROM
    unsigned char pDataReceived[size];                    // for read back from EEPROM test
    status* read_back;                                    // for read back from EEPROM test

    printf("%d %d\n", stat.value, stat.flag);             // verify data in original format

    pData = (unsigned char*)(&stat);                      // cast data pointer to (uint8_t*) unsigned char*
    printf("%d %d\n", ((status*)pData)->value, ((status*)pData)->flag);   // verify data after cast

    memcpy(eeprom, pData, size);                          // I2C write bytes simulation
    printf("%d %d\n", ((status*)eeprom)->value, ((status*)eeprom)->flag); // verify data in simulated EEPROM

    memcpy(pDataReceived, eeprom, size);                  // I2C read bytes simulation
    read_back = (status*)(pDataReceived);                 // cast back to struct type
    printf("%d %d\n", read_back->value, read_back->flag); // verify received data in original format

    return 0;

Write example:

struct status stat;
// operations on the struct
uint8_t* addressOfStruct= (uint8_t*)(&stat);
uint16_t sizeOfStruct  = sizeof(status);

HAL_StatusTypeDef HAL_I2C_Mem_Write (&hi2c, DevAddress, MemAddress, MemAddSize, addressOfStruct, sizeOfStruct, 100);

Read example:

struct status* read;
uint16_t sizeOfBuffer = sizeof(status);
uint8_t receiveBuffer[sizeOfBuffer];

HAL_StatusTypeDef HAL_I2C_Mem_Read (&hi2c, DevAddress, MemAddress, MemAddSize, receiveBuffer, sizeOfBuffer, 100);

read = (status*)(receiveBuffer); // or copy or move the data