STM32F107 + lwIP + FreeRTOS

Ostatnio spędziłem trochę czasu na raczej bezowocnych eksperymentach z RAW API biblioteki lwIP (to oparte na callbackach, przeznaczone do pracy bezpośrednio na krzemie). Wszystko rozbija się o fakt, że to API nie pozwala mi w łatwy sposób pobierać z bufora dowolnej liczby bajtów wtedy, kiedy jest to dla mnie wygodne - kiedy przychodzi nowa paczka powinienem ją jak najszybciej obsłużyć, zwolnić pamięć i poprosić o kolejną. Takie podejście wymagało dość gruntownego przepisania kodu mojej aplikacji, która potem nie działała zbyt sprawnie.

Postanowiłem w końcu spróbować z FreeRTOS-em, aby mieć dostęp do bardziej standardowego API opartego na socketach. Przeklikałem się ponownie przez konfigurację z mojego projektu w STM32CubeMX, dodając FreeRTOS-a. Zmieniłem timebase source z SysTick na Timer1 oraz utworzyłem domyślny task.

Chyba jakiś bug w CubeMX sprawił, że w konfiguracji lwIP pod definicję RTOS_USE_NEWLIB_REENTRANT został podstawiony jakiś dziwnie wyglądający ciąg znaków, co wywoływało komunikat o błedzie. Zamieniłem tę wartość na "1" i wszystko przeszło - byłem w stanie wygenerować kod.

Na początku miałem jakieś problemy ze stabilnością - kod się w pewnym momencie zawieszał i przestawał wykonywać kod umieszczony w pętli głównej tasku. Pomogło jednak zwiększenie rozmiaru przypisanego mu stosu.

Następnie wykonałem podstawowe testy peryferiów, inicjując je i komunikując się z nimi z poziomu tasku. Zadziała zarówno karta SD/FatFS jak i VS1003.

Natomiast trafiłem na jakiś problem z obsługą Ethernetu. Jeśli dobrze rozumiem, w przypadku zastosowania FreeRTOS-a cała obsługa sieci powinna się odbywać w tle, ponieważ sterownik Ethernetu otrzymuje swój własny, prywatny task. Tymczasem po podłączeniu zasilania urządzenie nie pojawia się wcale w routerze, jakby nie został mu przydzielony adres przez DHCP. Co ciekawe jeśli spróbuję włączyć zasilanie przy odłączonym kablu Ethernetowym, to zaraz po starcie mam następujący komunikat:

"Assertion "netif is not up, old style port?" failed at line 727 in /Middlewares/Third_Party/LwIO/src/core/ipv4/dhcp.c"

Czyli jakaś próba uzyskania dostępu do DHCP jest podejmowana, ale failuje (bo kabel jest odłączony). Przy podłączonym kablu nie mam tego komunikatu. Tylko czemu w takim razie płytka nie dostaje adresu i nie zaczyna się komunikować z siecią?

Coś jeszcze trzeba zrobić, żeby uzyskać działający Ethernet na FreeRTOS?

Reply to
Atlantis
Loading thread data ...

Ok, zaczynam powoli podejrzewać, że wina może leżeć gdzieś po stronie ustawień charakterystycznych dla FreeRTOS: priorytety, zarządzanie pamięcią, rozmiar stosu. Na początku zaobserwowałem podobny problem z biblioteką USB - chociaż kod wykonywany w pętli głównego tasku działał, to nie byłem w stanie dostać się do PenDrive'a - FatFS zgłaszał FR_DISK_ERR. Problem minął gdy zwiększyłem rozmiar pamięci przeznaczonej do alokowania stosów tasków oraz sam stos głównego tasku. Pendrive zaczął być wykrywany i mogłem czytać zapisane na nim dane.

Podejrzewam, że z lwIP/Ethernetem sprawa również może leżeć w konfiguracji systemu/pamięci. Niestety nie jestem w stanie zwiększać ilości przydzielonej pamięci w nieskończoność, bo STm32F107 ma jej dość ograniczoną ilość (64kB), która już teraz jest w dość znacznej części zajęta.

Może ktoś jest w stanie zaproponować jakieś optymalne ustawienia? ;)

Reply to
Atlantis

Atlantis snipped-for-privacy@wp.pl napisał(a):

Dlaczego po prostu nie weźmiesz jakiegoś gotowca i zobaczysz jak tam jest zrobione?

Reply to
Grzegorz Niemirowski

Jak pamiętam łączyłem kiedyś MLA z FreeRTOS i faktycznie na prawdę zacnie to pamięci potrzebowało na stos, nie żałuj.

Reply to
Marek

W dniu 26.10.2022 o 08:55, Atlantis pisze:

A zobacz czy czasem MAC nie jest ustawiony na 00:00:00:00:00:00.

Wtedy niby sieć powinna działać, ale z DHCP adresu nie dostaniesz.

Pozdrawiam

Adam Górski

Reply to
Adam Górski

I faktycznie - tutaj leżała przyczyna. Jeśli dobrze rozumiem FreeRTOS (przynajmniej w wersji udostępnianej przez STM32CubeMX) nie alokuje pamięci na stosy tasków ze sterty, ale używana jest do tego statycznie wydzielona przestrzeń w RAM-ie o z góry określonej wielkości. STM32Cube posiada wskaźnik stopnia zajętości tej przestrzeni, najwyraźniej jednak bierze on pod uwagę tylko taski utworzone przez użytkownika. Te wygenerowane automatycznie przez sterowniki nie są brane pod uwagę, co jest mylące.

Wpadłem na to po tym gdy zorientowałem się, że zmniejszenie rozmiaru stosu głównego tasku sprawiło, że sterownik USB nagle zaczął działać. Zwiększyłem więc ilość pamięci na stosy o kilka kolejnych kB i wtedy także Ethernet ruszył. :)

Teraz pozostało jeszcze przepisanie kodu aplikacji w taki sposób, aby w pełni wykorzystać możliwości RTOS-a. Pierwsza rzecz to to oczywiście zaimplementowanie socket API, ale poza tym trzeba będzie usunąć mechanizmy oparte na programowaniu współbieżnym i zastąpić je kilkoma taskami. Powinno się dość dobrze sprawdzić np. podczas przesyłania danych do/z bufora.

Reply to
Atlantis

Ok, jak wspominałem manipulując ustawieniami pamięci i rozmiarem stosu głównego tasku udało mi się uruchomić Ethernet/lwIP. Płytka dostaje adres IP od DHCP, pojawia się na liście na routerze oraz jestem w stanie ją pingować.

Teraz zabrałem się za przepisywanie swojego kodu z użyciem Socket API. Udało mi się uzyskać adres OP docelowego serwera za pomocą DNS-a (co potwierdza, że łączność z siecią działa) jednak w tej chwili rozbijam się przy próbie utworzenia socketu i połączenia z serwerem.

Funkcja lwip_socket zwraca -1, a zmienna errno jest ustawiana na wartość

105 (No buffer space available). Ktoś mógłby podpowiedzieć jakiego bufora mu brakuje? I co powinienem zmienić w konfiguracji?

Generalnie przydałaby się tez informacja skąd mógłbym spróbować odzyskać trochę pamięci, bo w chwili obecnej jej wykorzystanie (szacowane przez STM32CubeIDE po kompilacji) przekroczyło już 86%.

Reply to
Atlantis

I to był chyba główny powód, z którego FreeRTOS u mnie jak dotąd nie znalazł zastosowania....

Reply to
Marek

Ok, w chwili wolnego czasu udało mi się uruchomić część starego kodu na tej płytce, za pomocą FreeRTOS-a. Zarówno lwIP jak i sterownik USB MSD działają idealnie - wszystko czego potrzebowały to dość duże zwiększenie rozmiaru sterty, z której są alokowane stosy dla poszczególnych tasków. Zużycie pamięci RAM doszło w tej chwili do ponad 88%, ale urządzenie jak na razie nie pokazuje oznak niestabilności wynikające z nadpisywania stosu.

Możliwość stosowania normalnych socketów momentalnie wyeliminowała wszystkie problemy. Nawet streamy audio o nieco większym bitrate odtwarzają się perfekcyjnie, a połączenia są stabilne. Odtwarzanie z pendrive'a oraz karty SD również działa poprawnie, a pamieć SPI RAM sprawdza się w roli bufora na odbierane dane.

Teraz pozostało mi do rozwiązania kilka problemów wynikających z zastosowania samego RTOS-a.

Po pierwsze terowanie: Bez systemu operacyjnego wszystko było proste - funkcje reagujące na naciśnięcia przycisków czy kręcenie enkoderem były wykonywane w tej samej pętli głównej. Miałem wiec absolutną pewność, że jeśli wykona się funkcja wywołana naciśnięciem przycisku "stop" albo "next" to maszyna stanów odpowiedzialna za odtwarzanie na chwilę obecną zakończyła wykonywanie swoich operacji i mogę po prostu zmienić jej stan, zamknąć połączenie i zainicjować następne albo przełączyć na odtwarzanie kolejnego pliku. Teraz sprawdzanie przycisków ma miejsce w innym tasku niż obsługa maszyny stanów. Domyślam się, że nie mogę tak po prostu w dowolnym momencie z innego tasku zamknąć połączenia, sprawdzić albo zmienić stanu maszyny stanów obsługującej odtwarzanie. Jaki jest najlepszy sposób na upewnienie się, że obsługa przycisków nie wywoła żadnej waznej funkcji zanim w innym tasku nie zakończy się wykonywać VS1003_handle()?

Reply to
Atlantis

FreeRTOS wspiera muteksy.

Reply to
heby

On 02.11.2022 21:16, Atlantis wrote: [...]

Poczytaj o mechanizmach synchronizacji w systemach wielowątkowych – nie ma znaczenia czy to FreeRTOS, Linux, Windows czy coś innego – podstawy są takie same, a API podobne. Na początek sprawdź hasła mutex (aka binary semaphore), semafor, condition(al) varialble, bariera (np. pthread_barrier, nie mylić z instrukcjami typu "memory barrier" dostępnymi w niektórych CPU/MCU bo to co innego, chociaż jedno z drugom jest powiązane). No i najważniejsze, to co leży u podstaw w/w pojęć – dowiedz się co to jest operacja atomowa; chociaż w przypadku programowania bare metal może być to temat trochę zaawansowany, to poczytaj też o typach _Atomic (C) i std::atomic (C++), a także popatrz jak ja się realizuje operacje atomowe na różnych platformach. Temat do dalszych czytanek to lock-free programming (aka non-blocking programming) – dosyć gorący temat ze względu na to, że wielordzeniowość zeszła już na poziom MCU (Raspberry Pi Pico, ESP32).

Reply to
JDX

Inaczej, to nie jest tak, że te pojęcia są dla mnie czymś zupełnie nowym. Używałem RTOS-a na ESP8266/ESP32 (tam jest on właściwie zintegrowany z SDK) oraz na Raspberry Pi Pico i jakieś proste zadania synchronizowałem za pomocą semaforów, np. upewniając się, że zadanie w jednym tasku nie zostanie wykonanie do momentu, aż nie pozwoli na to pojawienie się przerwania. W pierwszym momencie tutaj chciałem użyć podobnego mechanizmu z binary semaphoirami, ale najwyraźniej robię coś źle, bo po jego dodaniu mam dość mocny spadek wydajności - w tym przypadku słyszalny.

Na chwilę obecną przeszedł mi do głowy jeszcze pomysł, żeby zastosować kolejkę wiadomości. Na zasadzie:

Task I/O:

- Sprawdza stan kolejnych przycisków

- Jeśli wykryte zostanie naciśnięcie przycisku na kolejkę wysyłana jest wiadomość z kodem operacji do wykonania

Główny task:

- Cyklicznie wywołuje funkcję obsługującą maszynę stanów urządzenia

- Następnie pobiera wiadomość z kolejki i wykonuje odpowiadającą jej operację, modyfikującą stan maszyny stanów.

- W kolejnym przebiegu maszyna będzie już obsługiwana w swoim nowym stanie.

Dzięki temu task I/O nigdy nie będzie wywoływał funkcji modyfikujących stan maszyny działającej w drugim tasku.

Reply to
Atlantis

A w zasadzie jakiego multitaskingu używasz w tym FreeRTOS? Preemptive czy cooperative?

Pewne rzeczy łatwiej zrobic w cooperative, ale do tego wymagana jest higiena pisania kodu. Za to znikają problemy z synchronizacjami między wątkami.

Reply to
heby

On 03.11.2022 08:33, Atlantis wrote: [...]

Zwróć uwagę na to, że „sekcje krytyczne” obudowane muteksem/semaforem powinny być możliwie krótkie, aby wątki nie blokowały się na zbyt długo na tych muteksach/semaforach.

Właśnie wynalazłeś przekazywanie wiadomości i kolejkę komunikatów. :-)

formatting link
To niezły pomysł, ale wydaje mi się, że czytanie kilku klawiszy i enkodera da się załatwić kilkoma współdzielonymi (globalnymi) zmiennymi obudowanymi muteksem bądź semaforem. Tak czy inaczej, synchronizacja musi być jakoś zapewniona.

Reply to
JDX

Heh no właśnie. Nie wspominając, że kooperatywie mniej pamięci zużyje niż w FTOS. Idąc dalej.. to po co mu ftos? :)

Reply to
Marek

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.