Opis kolejnej biblioteki dostępnej na GitLabie.

Do pobrania: https://gitlab.com/embedownik/UART

Przeznaczenie biblioteki

Jest to biblioteka z dodatkową warstwą abstrakcji nad ST-HAL.

Nie ma w niej konfiguracji peryferium - założeniem jest, że wykonuje się ją np poprzez CubeIDE w pluginie CubeMX a następnie przekazuje pointer do obiektu “UART_HandleTypeDef” z którego będzie korzystać biblioteka.

Udostępnione opcje/cechy biblioteki:

  • możliwość przypisania callbacków użytkownika przy odbiorze pojedynczego znaku
  • możliwość “dekodowania linii” - uruchamianie callbacka dla użytkownika po odebraniu linii
  • możliwość uruchomienia wielu instancji ten biblioteki dla różnych uartów
  • odbiór oparty o przerwania - nie wymaga dodatkowej konfiguracji - biblioteka nadpisuje funkcje HAL_UART_TxCpltCallback i HAL_UART_RxCpltCallback z ST-HALa
  • odebrane dane z poziomu przerwań przekazywane są kolejką do taska odbiorczego

Bibioteka wymaga używania FreeRTOSa w projekcie.

Biblioteka została wykorzystana w projekcie, gdzie STM używał 3 niezależnych uartów do komunikacji z:

  • GPS (tylko odbiór)
  • ZigBee (moduł ETRX357)
  • PC (komendy AT + odpowiedzi)

Użycie biblioteki

Biblioteka zakłada wykorzystanie pliku konfiguracyjnego “board.h” w projekcie. Należy w nim umieścić zdefiniowany symbol “UART_MAX_INSTANCES” - gdzie należy podać ilość używanych instancji biblioteki UART (wybrano taką metodę, aby uniknąć dynamicznej alokacji).

Bibliotekę należy inicjalizować za pomocą funkcji:

bool UART_Init(UART_config_s *config);

do której należy przekazać wskaźnik na strukturę konfiguracyjną. Jest ona dość rozbudowana:

/* Callback for "byteReceived" event */
typedef void (*UART_receiveByteUserParser_t)(uint8_t data);

/*
 * Callback for "lineReceived" event.
 * End of line replaced with '\0'.
 */
typedef void (*UART_receiveLineCallback_t)(uint8_t *line);

typedef struct
{
    char    *taskName;
    uint8_t  priority;
    uint16_t stackSize;
}UART_config_RTOSTask_s;

typedef struct
{
    UART_HandleTypeDef *handler;

    UART_config_RTOSTask_s taskConfig;

    bool receiverEnabled;      /* enable receiver on selected UART */
    bool receiverLinesMode;    /* fires user received data in case of full line receive. "line end" sign is "\n" (10dec) */

    uint8_t *lineBuffer;       /* buffer for "reveiveLine" mode */
    uint8_t lineBufferLength;  /* length of lineBuffer - given by user */

    uint8_t receivedQueueSize; /* size of internal receive queue */

    UART_receiveByteUserParser_t receiveByteCallback; /* callback to call after received data event */
    UART_receiveLineCallback_t receiveLineCallback;   /* callback to call after received line event */


    /* Internal module parameters - this way to avoid dynamic allocation */
    uint8_t receiveBuff; /* TODO - better buff - right now just testing */

    uint8_t receivedToRead; /* todo better implementation */

    bool receivedFlag;
    bool initialized;

    bool volatile transmitterBusyFlag;

    uint16_t lineBufferPosition;

    QueueHandle_t receivedQueue;
}UART_config_s;

Najważniejsze rzeczy do przekazania to:

  • wskaźnik na handler skonfigurowanego uartu - dzięki temu biblioteka nie zajmuje się częścią konfiguracyjną i można ją “wyklikać” w CubeIDE
  • konfiguracja taska odbiorczego - tzn. rozmiar stosu/priorytet i nazwę do tracowania; dla STM32F3 i braku optymalizacji rozmiar stosu dla funkcji odbiorczej to ~432B
  • callback użytkownika jest wywoływany z poziomu taska odbiorczego - więc proponowanym rozwiązaniem jest przekazanie go np. kolejną kolejką do taska aplikacyjnego
  • konfigurację, czy tryb odbiorczy jest aktywny
  • konfigurację, czy ma zostać uruchomiony tryb parsowania linii
  • zaalokowany przez użytkownika bufor na dane robocze (dzięki temu biblioteka się tym nie zajmuje) - długość tego bufora powinna być dopasowana do najdłuższej możliwej wiadomości jakiej spodziewa się urządzenie
  • długość tego bufora
  • rozmiar “wewnętrzenego” bufora odbiorczego w bibliotece
  • callbacki od odbioru znaku/linii

Reszta parametrów w strukturze jest “wewnętrzna” - zostanie to w późniejszym okresie przerobione na mechanizm związany z “opaque pointer” (trzeba sobie jakoś radziś w C).

Czyli odbioór danych z wykorzystaniem tej biblioteki zawsze odbywa się poprzez callbacki.

W przypadku transmisji danych - biblioteka pełni rolę “wrapera” na biblitotekę ST-HAL udostępniając funkcje:

/**
 * Transmit text - required '/0' at the end.
 * Warning - blocking implementation!
 * @param config UART instance pointer
 * @param buff buff to send.
 */
void UART_TransmitText(UART_config_s *config, const char *buff);

/**
 * Transmit buffer with data.
 * Warning - blocking implementation!
 * @param config UART instance pointer
 * @param buff buff to send
 * @param len length of data to send
 */
void UART_TransmitBuff(UART_config_s *config, const uint8_t *buff, uint16_t len);

Przykłady praktyczne

Biblioteka będzie wykorzystywana w kilku projektach, które będą tutaj opisane - będą tu wtedy stopniowo uzupełniane linki do nich.