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)


function

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
memcpy
exists.

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