
#013 - LIB03 - UART
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.