Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

STM32F107 + lwIP + FreeRTOS

76 views
Skip to first unread message

Atlantis

unread,
Oct 25, 2022, 4:48:00 AM10/25/22
to
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?


Atlantis

unread,
Oct 26, 2022, 2:55:20 AM10/26/22
to
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? ;)

Grzegorz Niemirowski

unread,
Oct 26, 2022, 4:02:12 AM10/26/22
to
Atlantis <marekw19...@wp.pl> napisał(a):
> Może ktoś jest w stanie zaproponować jakieś optymalne ustawienia? ;)

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

--
Grzegorz Niemirowski
https://www.grzegorz.net/

Marek

unread,
Oct 26, 2022, 7:19:03 AM10/26/22
to
On Wed, 26 Oct 2022 08:55:17 +0200, Atlantis <marekw19...@wp.pl>
wrote:
> Ok, zaczynam powoli podejrzewać, że wina może leżeć gdzieś po
> stronie
> ustawień charakterystycznych dla FreeRTOS: priorytety, zarządzanie
> pamięcią, rozmiar stosu.

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

--
Marek

Adam Górski

unread,
Oct 26, 2022, 7:24:19 AM10/26/22
to
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

Atlantis

unread,
Oct 26, 2022, 3:32:06 PM10/26/22
to
On 26.10.2022 13:18, Marek wrote:

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

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.


Atlantis

unread,
Oct 27, 2022, 2:01:02 PM10/27/22
to
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%.

Marek

unread,
Oct 28, 2022, 2:44:59 AM10/28/22
to
On Thu, 27 Oct 2022 20:00:58 +0200, Atlantis <marekw19...@wp.pl>
wrote:
> 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%.

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

--
Marek

Atlantis

unread,
Nov 2, 2022, 4:17:01 PM11/2/22
to
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()?

heby

unread,
Nov 2, 2022, 5:04:20 PM11/2/22
to
On 02/11/2022 21:16, Atlantis wrote:
> 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()?

FreeRTOS wspiera muteksy.


JDX

unread,
Nov 3, 2022, 2:22:51 AM11/3/22
to
On 02.11.2022 21:16, Atlantis wrote:
[...]
> 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()?
>
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).

Atlantis

unread,
Nov 3, 2022, 3:33:55 AM11/3/22
to
On 03.11.2022 07:22, JDX 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.

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.

heby

unread,
Nov 3, 2022, 4:07:42 AM11/3/22
to
On 03/11/2022 08:33, Atlantis wrote:
> Na chwilę obecną przeszedł mi do głowy jeszcze pomysł, żeby zastosować
> kolejkę wiadomości.

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.

JDX

unread,
Nov 3, 2022, 10:02:40 AM11/3/22
to
On 03.11.2022 08:33, Atlantis wrote:
[...]
> 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.
>
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.

> 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.
Właśnie wynalazłeś przekazywanie wiadomości i kolejkę komunikatów. :-)
https://en.wikipedia.org/wiki/Message_passing
https://en.wikipedia.org/wiki/Message_queue

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.

Marek

unread,
Nov 3, 2022, 3:57:30 PM11/3/22
to
On Thu, 3 Nov 2022 09:07:39 +0100, heby <he...@poczta.onet.pl> wrote:
> Pewne rzeczy łatwiej zrobic w cooperative, ale do tego wymagana
> jest
> higiena pisania kodu. Za to znikają problemy z synchronizacjami
> między
> wątkami.

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? :)

--
Marek
0 new messages