Pierwsza biblioteka opisana/udostępniona na tym blogu - bardzo prosta, wręcz banalna, ale praktyczna.

Biblioteka udostępniona na GitLabie: I2C.

Przeznaczenie biblioteki

Jest to “wrapper” na bibliotekę STHAL ułatwiający pracę z I2C w przypadku kiedy jedna magistrala jest używana przez kilka bibliotek. Zawiera wbudowany mutex dzięki czemu mamy pewność, że tylko jeden task w danej chwili będzie miał dostęp do zasobu I2C.

Najważniejsze wymaganie biblioteki - FreeRTOS w projekcie - biblioteka nie ma żadnej dodatkowej abstrakcji nad funckjami FreeRTOSa.

Użycie - teoria

Konfiguracja biblioteki do wykorzystania w projekcie sprowadza się do uruchomienia funkcji inicjalizacyjnej do której jako prarametr należy przekazać adres wcześniej skonfigurowanego handlera do “I2C_HandleTypeDef”.

Dzięki temu I2C można skonfigurować w CubeMX/CubeIDE - “wyklikać” potrzebną konfigurację, a bibliotekę to totalnie nie interesuje.

I2C_Init(&hi2c1);

Jeśli potrzebne jest dostosowanie timeoutu dla przesyłania pojedynczego znaku - biblioteka oczekuje, że konfiguracja znajdzie się w pliku “board.h”, który będzie znajdował się w ścieżkach includów projektu.

Tak naprawdę moduł udostępnia 5 funkcji (fragment z pliku I2C.h):

bool I2C_Init(I2C_HandleTypeDef *handler);

bool I2C_ReadByte(uint8_t devAddress, uint8_t regAddress, uint8_t *value);

bool I2C_ReadBytes(uint8_t devAddress, uint8_t regAddress, uint8_t *value, uint8_t bytes);

bool I2C_WriteByte(uint8_t devAddress, uint8_t regAddress, uint8_t value);

bool I2C_WriteBytes(uint8_t devAddress, uint8_t regAddress, const uint8_t *values, uint16_t size);

Użycie - w praktyce

Biblioteka została przetestowana w aplikacji, gdzie na jednej magistrali I2C znalazło się 6 urządzeń:

  • OLED 128x64
  • ADP5585
  • LPS331
  • HTS221
  • STMLM75
  • TSL25721

Z punktu projektowania takiego urządzenia należy mieć świadomość mechanizmu “priority inversion” związanego z użyciem mutexów do kontroli dostępu do zasobu i odpowiednio przeliczyć krytyczne czasy wykonania tasków z jego uwzględnieniem.

Jeśli biblioteki do poszczególnych układów są zbudowane tak, że należy im przekazać callbacki do funkcji dostępu do I2C (zasada Dependency Inversion z SOLIDa) - wygląda to mniej więcej tak:

ADP5585_init_s ADP5585_init =
{
    .readI2CBuff = I2C_readBytes,
    .sendI2CBuff = I2C_writeBytes,
};

A - biblioteka jest oczywiście zgodna z punktami z poprzedniego postu: reużywalne biblioteki - jedyny sporny punkt to “zalezność od platformy” - ale ta biblioteka to właśnie wrapper dla STHAL - i dzięki temu, że można ją wykorzystać jak w przykładzie ze strukturą konfiguracją dla ADP5585 można uzyskać niezależność od platformy na wyższych poziomach architektury oprogramowania projektu.