#003 - FreeRTOS - template dla STM32L4 #1
FreeRTOS
Krótka instrukcja jak “recznie” dodać do projektu CubeMX moduł FreeRTOS bez wykorzystania wbudowanego modułu (ponieważ wtedy dodawany jest także CMSIS-OS, jako nakładka na FreeRTOSa, a także wersja z Cube zazwyczaj jest sporo starsza niż aktualnie dostępna w repozytorium jako release), tak przygotowany projekt można wykorzystywać jako template dla kolejnych projektów - ja wykorzystywałem go jako bazę do ćwiczeń mechanizmów FreeRTOSa.
Użyta platforma to nucleo z STM32L476RG.
Za pomocą CubeMX wyklikujemy testową konfiguracje z 3 diodkami - na pinach D2-D4 w notacji arduino (jeśli chodzi o piny STMa będą to kolejno: PA10, PB3 i PB5).
Okazało się, że PB3 nie może być wykorzystany - z powodu alternatywnej funkcji SWO, która może się przydać w przyszłości - więc kolejny to PB4. Czyli diody będą podłączone do PA10, PB5 i PB4
Dalej już tylko wybór ścieżki do projektu i projektu dla Atollica:
- u mnie pobranie paczki z bibliotekami -> 750MB!
Podstawowa struktura plików + testowy blink działa ok.
Pobieramy więc FreeRTOSa z jego głównej strony. Aktualnie jest to wersja 10.3.1.
Początkowe poziomy struktury katalogów wyglądają następująco:
Opisy znajdują się w plikach readme. Nas interesuje katalog FreeRTOS/Source. To go musimy przekopiować do projektu. Znajdują się tam “ogólne” pliki systemu + część “porbable” specyficzna dla platformy. Dla mnie to GCC->ARM_CMF4F. Reszty całkowicie można się pozbyć.
Potrzebujemy pliku FreeRTOSConfig.h - jest to plik specyficzny “per aplikacja” z makrami do uruchamiania poszczególnych funkcjonalności systemu. Weźmy go z katalogu z przykładami
- dla mnie to przykład “CORTEX_M4F_STM32F407ZG-SK”. Należy go wrzucić w katalog z includami w projekcie.
Po przekopiowaniu plików struktura wygląda tak:
FreeRTOS wrzucony w katalog src - to jest akurat dyskusyjne i można to zrobić na mnóstwo lepszych sposobów - o organizacji katalogów zapewne będzie cały wpis kiedyś. Przy takim układzie w configu projektu musimy dodać do ścieżek includów katalogi:
- Src/FreeRTOS/include
- Src/FreeRTOS/portable (chociaż ten można też np pominąć i rozdzielić pliki pomiędzy główny i include - znów kwestia ułożenia)
Po kompilacji okaże się, że musimy jeszcze:
- zdefiniować jaka jest prędkość naszego układu makrem configCPU_CLOCK_HZ
- Z pliku z przerwaniami należy skasować obsługę do:
- SVC_Handler
- PendSV_Handler
- SysTick_Handler
Ale dla SysTicka polecam inne rozwiązanie - systick jest też używany przez HALowskie rzeczy do ikrementacji liczników itp (w Cube można też przeklikać, żeby inny timer przejął to zadanie)- dlatego można odwrócić zależność i w wygenerowane przerwanie przez CubeMX wkleić wywołanie RTOSowe, a zakomentować odpowiednią linijkę w configu OSa do tego.:
#include "FreeRTOS.h"
#include "task.h"
extern void xPortSysTickHandler(void);
/**
* @brief This function handles System tick timer.
*/
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
{
xPortSysTickHandler();
}
/* USER CODE END SysTick_IRQn 1 */
}
Niestety - extern dlatego, że jest to funkcja nie wyprowadzona do żadnego headera i tak to można zostawić jeśli za wszelką cene nie chcemy ingerować w pliki RTOSa. Swoją drogą tak samo robi np CMSIS-os. Można też przepiąć HAL-owski tick na inne przerwanie
- jak kto woli - nie będzie wtedy potrzebna taka poprawka, a basicTimerów mamy pod dostatkiem w STMkach.
Kolejnym krokiem będzie zdefiniowanie funkcji “vApplicationStackOverflowHook” - nazwa sama mówi do czego ona jest. Dajmy ją “pustą” w mainie:
void vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName )
{
while(1);
}
Potrzebujemy jeszcze dorzucić do projektu plik “heap_x.c”, który odpowiada za sposób alokacji pamięci. Tu odsyłam do dokumentacji po szczegóły - ja użyje heap_4 - skopiowałem więc plik z katalogu portable/MemMang do katalogu portable.
I ostatnie to zmiany: configUSE_TICK_HOOK, configUSE_MALLOC_FAILED_HOOK i configUSE_IDLE_HOOK na 0.
Projekt się kompiluje poprawnie.
Szybkie 3 taski z miganiem diodami:
void vTask1( void * pvParameters )
{
for (;;)
{
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
vTaskDelay(1000);
}
}
void vTask2( void * pvParameters )
{
for (;;)
{
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
vTaskDelay(1100);
}
}
void vTask3( void * pvParameters )
{
for (;;)
{
HAL_GPIO_TogglePin(LED3_GPIO_Port, LED3_Pin);
vTaskDelay(1200);
}
}
Dodanie tasków w funkcji main:
xTaskCreate( vTask1, "Task1", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 2, NULL);
xTaskCreate( vTask2, "Task2", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 2, NULL);
xTaskCreate( vTask3, "Task3", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 2, NULL);
vTaskStartScheduler();
Projekt do pobrania tutaj (ale razem z dodanym już SEGGER SystemViewer z innego wpisu): pobierz projekt.