Nazwałem to zachowanie "zaskakującym" bo nic innego z cenzuralnych określeń nie przychodziło mi do głowy.
Już tłumaczę gdzie ta ciekawostka mieszka - obsługa SPI jest w pliku stm32l4xx_hal_spi.c. Znajdziemy w nim funkcję o wdzięcznej nazwie SPI_WaitFifoStateUntilTimeout.
Kod: Zaznacz cały
static HAL_StatusTypeDef SPI_WaitFifoStateUntilTimeout(SPI_HandleTypeDef *hspi, uint32_t Fifo, uint32_t State,
uint32_t Timeout, uint32_t Tickstart);
Sama funkcja to niby nic złego, jest używana do czekania na opróżnienie bufora FIFO w module SPI. Natomiast jej użycie jest już mocno zaskakujące - używana jest mnóstwo razy, ja ją spotkałem w SPI_EndRxTxTransaction, czyli funkcji wywoływanej na zakończenie transmisji danych przy użyciu HAL_SPI_Transmit_IT.
Okazuje się, że taki kod ma dwie wady. Pierwsza, nazwijmy to "optymistyczna" to blokowanie wykonywania kod - udało mi się wygenerować przerwanie, którego obsługa zajmuje 150us, czyli dokładnie 7208 cykli zegara. Przy transmisji 16-bitowych danych i preskalerze /256 tyle zajmuje czekanie aż ten bufor FIFO się opróżni.
W sumie jest jeszcze jedna funkcja z aktywnym czekaniem - SPI_WaitFlagStateUntilTimeout, to co podawałem dotyczy obu "w sumie".
Natomiast druga wada jest znacznie poważniejsza. Koniec nazwy funkcji czyli "UntilTimeout" prawdopodobnie oznacza, że nawet jak coś pójdzie źle, to funkcja nie powinna blokować przerwań w nieskończoność. Niestety autorzy biblioteki pokazali co potrafią... po pierwsze ten "Timeout" jest stały i wynosi 100ms. Nie można go zmienić bez zmiany biblioteki HAL, bo ktoś sobie stałą SPI_DEFAULT_TIMEOUT zdefiniował lokalnie, prywatnie...
Ale w rzeczywistości jest jeszcze gorzej. Ten timeout bazuje na wywołaniu funkcji HAL_GetTick, tylko że przy domyślnych priorytetach przerwań to raczej nie zadziała.
Więc pierwsza nauczka jest taka, że priorytety przerwań właściwie trzeba ustawiać, nawet jeśli nie ma to teoretycznie sensu. No a druga nauczka - to chyba lepiej ostrożnie podchodzić do biblioteki HAL, albo może i nie podchodzić wcale...