Elektroda.pl
Elektroda.pl
X
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

[C++11][Cortex-M3/M4] - distortos - obiektowy RTOS dla mikrokontrolerów w C++

Freddie Chopin 07 Mar 2015 10:24 36873 255
Automation24
  • #1
    Freddie Chopin
    MCUs specialist
    Witam!

    Postanowiłem w końcu się pochwalić projektem, który rozpocząłem troszkę ponad 7 miesięcy temu. Na razie można go określić jako "alpha", w ostateczności jako wczesną "betę", jednakże - wbrew dosłownej wymowie tych określeń - to co jest w nim zrobione - a jest tego całkiem sporo - działa tak jak powinno i jest przetestowane. Jedyną wspieraną obecnie architekturą jest (cóż za zaskoczenie) ARM Cortex-M3 lub ARM Cortex-M4(F) (bez włączonego FPU EDIT 19.03.2015: obecnie można już używać FPU wielowątkowo), a jedynym wspieranym obecnie układem jest STM32F407VG (układ na pierwszej płytce STM32F4DISCOVERY).

    Całość stworzona została w C++11, nie ma API dla języka C. Takie API oczywiście jest planowane i będzie to pthread oraz być może jeszcze jedno, bardziej "natywne", pozwalające wygodniej korzystać z "ficzerów" specyficznych dla tego projektu. Niemniej jednak API dostępne w C++11 jest w zasadzie 100% odpowiednikiem wymagań POSIXa. Warto podkreślić, że kod nie używa pamięci alokowanej dynamicznie, wiec wszystko (przede wszystkim wątki wraz z ich stosami) można stworzyć jako zmienne lokalne (na stosie), globalne czy jak kto woli.

    Drugą istotną sprawą wartą podkreślenia jest to, że projekt ten nie ma ambicji bycia "najmniejszym" albo/i "najszybszym" RTOSem. Wraz z upływem czasu (i wzrostem popularności oraz spadkiem cen szybkich układów z dużą ilością pamięci <; ) stwierdzam, że ekwilibrystyka wyczyniana czasem aby zaoszczędzić kilka taktów czy 10 bajtów nie jest uzasadniona. Nie znaczy to oczywiście że całość działa żałośnie wolno i zajmuje całą pamięć (; Po prostu nie skupiam się przesadnie na tych sprawach - skupiam się na nich w stopniu który uznałem za "wystarczający".

    Nie ma co powielać opisów, więc podaję linka do oficjalnej strony projektu, linka do artykułu na mojej stronce w którym opisałem ten projekt troszkę bardziej oraz linka do repozytorium.

    http://distortos.org/
    http://www.freddiechopin.info/pl/artykuly/34-news/94-distortos-7-miesiecy-i-0x3ff-commitow
    https://github.com/DISTORTEC/distortos

    Jeszcze jeden link z małym teaserem - http://www.freddiechopin.info/pl/artykuly/34-news/93-work-in-progress
  • Automation24
  • #2
    grko
    Level 33  
    Część,

    projekt mam na oku od jakiegoś czasu. Mam kilka pytań związanych z nim:

    1. Czy distortos jest konfigurowalny ? Ile potrzbuje minimalnie/maksymalnie pamięci w zależności od wybranych funkcjonalności ? Może forma jakiejś tabeli na stronie projektu.
    2. Jaki toolchain polecasz? BET nie ma pod Linuxa i przy kompilacji toolchainem z pakietów debiana dostaje błąd kompilacji.
    3. Od której wersji GCC wspierane są ficzery C++11 które użyłeś w projekcie?
    3. Czy planujesz wsparcie tylko dla architektury cortex-mX ?
    4. Możesz wyjaśnić dlaczego wybrałeś licencje MPL ? :)
    5. Dlaczego zwracasz wartości z nagłówka errno.h bez znaku minus (btw ja też tak robie :)). W kernelu zwracaja z minusem kody błędów. Nie znalazłem info w internecie czy to kwestia konwencji czy powinien być minus.
  • #3
    Freddie Chopin
    MCUs specialist
    GrzegorzKostka wrote:
    projekt mam na oku od jakiegoś czasu.

    No i się wyjaśniło jakiego masz nicka na githubie (;

    Quote:
    1. Czy distortos jest konfigurowalny ? Ile potrzbuje minimalnie/maksymalnie pamięci w zależności od wybranych funkcjonalności ? Może forma jakiejś tabeli na stronie projektu.

    Na chwilę obecną nie jest specjalnie konfigurowalny. Są trzy powody takiej sytuacji:
    - planuje to dodać nieco później, gdy całość się nieco "ustabilizuje",
    - z racji swojej budowy, wielka i rozbudowana konfiguracja nie jest specjalnie potrzebna - rzeczy których nie używasz zostaną wywalone przez linker,
    - doświadczenia z innymi projektami pokazują, ze nadmiar konfiguracji powoduje sporo problemów.

    W każdym razie jest to planowane.

    Co do wymagań pamięci i tabelki, to autentycznie ostatnio o tym myślałem i naprawdę nie mam pomysłu jak takie liczby uzyskać (; Np. w projekcie ChibiOS/RT są "raporty pamięci", ale jak te liczby zostały uzyskane to nie mam pojęcia, a naprawdę szukałem. Masz może jakiś pomysł jak to ocenić? No bo rozmiar obiektów można sprawdzić łatwo, ale jak dowiedzieć się ile zajmuje scheduler? Przecież nie będę sumował rozmiaru funkcji z pliku .map, bo bym oszalał <:

    Dodatkowym "problemem" jest tutaj użycie w wielu miejscach template'ów (wątki, kolejki), co sprawia że finalny rozmiar jeszcze bardziej zależny jest od konkretnego programu.

    Quote:
    2. Jaki toolchain polecasz? BET nie ma pod Linuxa i przy kompilacji toolchainem z pakietów debiana dostaje błąd kompilacji.

    Toolchain od ARMa ("linaro" - ten z launchpada) też na razie jest OK i nie wciąga wyjątków. Sam używam linuxa i używam bleeding-edge-toolchaina (; Może chodzi Ci o to, że nie ma wersji 32-bitowej, a takiej właśnie potrzebujesz? Możesz podać wersję toolchaina z debiana oraz wkleić błędy które dostajesz?

    Quote:
    3. Od której wersji GCC wspierane są ficzery C++11 które użyłeś w projekcie?

    Ciężko stwierdzić... To jest taka "materia", że naprawdę dobrze mieć najnowszą wersję. Generalnie zacząłem tworzyć projekt na GCC 4.8, teraz używam GCC 4.9, ale na 4.8 czasem sprawdzam czy wciąż się kompiluje (ostatnio sprawdzałem to z miesiąc temu - dobrze że przypomniałeś, to sprawdzę jak to teraz wygląda). W GCC 4.8 wyłączone jest (przez #ifdef ... #endif) wsparcie dla funkcji emplace() dla kolejek - to działa dopiero od GCC 4.9. Dało by się to w sumie zrobić w GCC 4.8, ale nie widzę takiej potrzeby na tym etapie - wymagałoby to odrobiny "szalonych" template'ów, które zresztą zostały ustandaryzowane w C++14.

    Quote:
    3. Czy planujesz wsparcie tylko dla architektury cortex-mX ?

    Nie, choć nie da się ukryć, że na innych specjalnie się nie znam (; Masz jakąś konkretną architekturę na myśli? Od razu mówię, że na AtMedze raczej to nie pójdzie (; Za mało RAMu na takie cuda.

    Quote:
    4. Możesz wyjaśnić dlaczego wybrałeś licencje MPL ? :)

    Wydaje mi się ona takim "złotym środkiem" pomiędzy GPLem a BSD. Gdyby chcieć stosować GPL, to w takim projekcie musiałoby to być "GPL z wyjątkiem", a to rodzi pewne problemy. Gdyby zaś użyć BSD, to licencja ta pozwala komuś na praktycznie wszystko. MPL jest "pomiędzy". Od razu jest jakby "GPLem z wyjątkiem" - można kod na tej licencji łączyć z czymkolwiek i jakkolwiek, licencja MPL dotyczy tylko i wyłącznie pojedynczych źródeł nią objętych, nie przechodzi na inne części projektu. Dodatkowo zmiany w kodzie objętym MPLem muszą zostać upublicznione - to wymaganie mi się podoba, ponieważ zachęca do współpracy i dzielenia się zmianami, zamiast "tajnych zmian na własny użytek", jak to ma miejsce w przypadku BSD.

    A przynajmniej tak to widzę (; Masz jakieś zdanie w tej sprawie, czy to tylko ciekawość?

    Quote:
    5. Dlaczego zwracasz wartości z nagłówka errno.h bez znaku minus (btw ja też tak robie :)). W kernelu zwracaja z minusem kody błędów. Nie znalazłem info w internecie czy to kwestia konwencji czy powinien być minus.

    To raczej tylko konwencja. Zwróć uwagę na to, że wszystkie funkcje pthread zwracają błędy bez minusa - choćby pierwsza lepsza http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_create.html . Zwracanie z minusem niby pozwoliłoby na zwracanie błędu lub wyniku w jednej zmiennej, ale osobiście nie lubię tego rozwiązania, w C++ można bez problemu (oraz optymalnie) zwrócić dowolnie duży obiekt ("move semantics" i "return value optimization"), więc jak chcę zwrócić dwie czy trzy rzeczy, to tak po prostu robię - std::pair<int, ...> albo struktury ze stosownymi danymi.

    EDIT:
    Sprawdziłem profilaktycznie czy projekt wciąż kompiluje się w GCC 4.8 i wszystko jest OK, przynajmniej przy użyciu gcc-arm-none-eabi-4_8-141002 (bleeding-edge-toolchain). Za to ostatnie zmiany przestały się kompilować w toolchainie ARMa z launchpada (gcc-arm-none-eabi-4_9-2014q4), więc coś z tym zaraz zrobię (;

    EDIT2:
    No dobra, problem z toolchainem od ARMa rozwiązany, choć przyznam że na obecną chwilę bleeding-edge-toolchain jest _BARDZO_ zalecany. Toolchain z launchpada używa "domyślnej" - czyli dużej - konfiguracji newlibowej struktury _reent, która w tym wariancie zajmuje trochę ponad 1kB. Ponieważ każdy wątek wymaga osobnej struktury _reent, to w przypadku toolchaina ARMowego, każdy obiekt wątku na dzień dobry zajmuje - właśnie z tego powodu - ponad 1kB RAMu. W bleeding-edge-toolchain używany jest mały wariant struktury _reent, który zajmuje - dla porównania - około 100 bajtów.

    4\/3!!
  • Automation24
  • #4
    EBC41
    Level 28  
    A jak wygląda zarządzanie pamięcią w tym RTOSie? Czy można bez przeszkód używać dynamicznej alokacji nie obawiając się HardFault co kilka minut?
  • #6
    Freddie Chopin
    MCUs specialist
    EBC41 wrote:
    A jak wygląda zarządzanie pamięcią w tym RTOSie? Czy można bez przeszkód używać dynamicznej alokacji nie obawiając się HardFault co kilka minut?

    Na chwilę obecną szansę na HardFault masz sporą, ponieważ muszę dodać w końcu implementacją funkcji __malloc_lock() i __malloc_unlock() (jest to na razie konieczne zanim nastąpi "pełna" integracja z newlibem). Jeśli chciałbyś potestować, to mogę dodać te dwie funkcje dziś. Generalnie to będą "jednolinijkowce" - lock() i unlock() rekursywnego mutexa i tyle. No ewentualnie można dodać też alternatywną implementację wyłączającą przerwania, która pozwoli na korzystanie z dynamicznej alokacji w przerwaniach - jeśli kogoś oczywiście interesuje tak szalone rozwiązanie...

    uzi18 wrote:
    - wsparcie dla cortex-m0?

    Na razie wciąż nieobecne [; Kiedyś pewnie się pojawi, ale akurat nie mogę obiecać aby to było bardzo szybko... O ile dodanie samego wsparcia dla architektury nie jest aż takim problemem (to tylko kilka funkcji, większość identycznych jak dla Cortex-M3), to rozchodzi się też o sposób konfiguracji i kompilacji całości, tak aby odbywało się to w miarę automatycznie, a nie wymagało od użytkownika grzebania się w Makefile'ach/Tupfile'ach.

    Quote:
    - przykładowy template z mryganiem diodami w watkach?

    Demo projekt z czymś takim jest wysoce "board-specific", bo nie każda płytka ma diodki... Pozatym miganie diodą w wątku to bezsens - można to przecież zrobić w timerze programowym [; Wg moich założeń distortos pod względem kompilacji i konfiguracji nie będzie podobny do FreeRTOSa czy innych "małych" RTOSów, gdzie uruchomienie całości to (za pierwszym razem) prawdziwa męka. Bardziej planuję uczynić go podobnym do nuttxa. Z tego powodu "template" ma mało sensu, bo w tym szablonie byłoby 10 linijek kodu - cała konfiguracja i sposób kompilacji są "załatwione" przez distortos, a użytkownik nie powinien tego zmieniać. Nie ma potrzeby dodawania stoich startupów, skryptów linkera czy tablic wektorów, bo to wszystko już jest w projekcie distortos.

    Mówię to całkiem serio... Oto taki szablon, w najmniej udziwnionej, czyli najprostszej, formie:

    Code: cpp
    Log in, to see the code


    Powyższe to absolutnie wszystko co jest potrzebne. "Normalny" bieg programu, ten który zaczyna się w startupie a kończy w main(), konwertowany jest po drodze w normalny wątek, wiec nawet nie ma potrzeby tworzyć kolejnego. Powyższe zajmuje:

       text	   data	    bss	    dec	    hex	filename
       4924	    100	   4840	   9864	   2688	./output/distortos.elf


    Przy czym 2kB to stos dla main(), a 2kB to stos dla przerwań (w powyższym przykładzie są to oczywiście wartości przesadzone, można spokojnie zmniejszyć)

    Jeśli ma być koniecznie z dodatkową funkcją i dodatkowym wątkiem, to może to wyglądać np. tak:

    Code: cpp
    Log in, to see the code


    Rozmiar stosu (512 bajtów) i priorytet wątku (1) są liczbami "z kapelusza", ale na 99% zadziała (chyba że Twoje funkcje konfiguracji i migania diodą będą używały kosmicznych ilości stosu). Priorytet oczywiście można sobie zmieniać do woli w zakresie 0-255, choć 0 jest mało sensowne, bo to priorytet wątku "idle".

       text	   data	    bss	    dec	    hex	filename
       6084	   1144	   4880	  12108	   2f4c	./output/distortos.elf

    Większość zużytego RAMu (czyli praktycznie wszystko ponad 2kB na jeden i 2kB na drugi stos oraz kilkaset bajtów dla innych części systemu) zużyte jest przez moduł z malloc() oraz free() z newliba, który niestety jest wciągany do finalnego kodu (przez free()), choć w takim kodzie jest absolutnie nieużywany.

    Do powyższych wystarczy wstawić Twój kod (; Jak wstawisz jeden z powyższych przykładów do pliku test/main.cpp, to wszystko się skompiluje i będzie działać.

    4\/3!!
  • #7
    uzi18
    Level 24  
    Myslalem aby taki prosty przyklad dodac do katalogu examples, zasadnosc migania diodami oczywiscie jest znikoma ale chodzi o sam obrazowy przyklad ktory ktos sobie pozniej rozszerzy.

    Może pora ... rownolegle pisac tutorial o wykorzystaniu poszczegolnych elementow distortos?
  • #8
    Freddie Chopin
    MCUs specialist
    uzi18 wrote:
    Myslalem aby taki prosty przyklad dodac do katalogu examples, zasadnosc migania diodami oczywiscie jest znikoma ale chodzi o sam obrazowy przyklad ktory ktos sobie pozniej rozszerzy.

    Nie no, ja nie mówię, że nigdy takiego przykładu nie będzie i nigdy tego nie zrobię. Po prostu fajnie by było, gdyby w jakiś prosty sposób można było jednak ten przykład skonfigurować i skompilować na wybraną płytkę, a następnie uruchomić. Do tego zaś potrzebny jest właśnie "system" konfiguracji całości w jakiś rozsądny sposób...

    Ciężko się wzorować na innych projektach, bo albo w nich czegoś takiego nie ma wcale, albo jest bardzo skomplikowane (np. w nuttxie).

    uzi18 wrote:
    Może pora ... rownolegle pisac tutorial o wykorzystaniu poszczegolnych elementow distortos?

    Tylko skąd wziąć czas (; Taki tutorial to by była raczej "teoria systemów operacyjnych czasu rzeczywistego", no bo tutaj mutexa używa się jak gdziekolwiek indziej (; No ale wiem o co Ci chodzi - o jakiś przystępny tekst "co i jak". Wszystko jednak rozbija się o ilość wolnego czasu...

    4\/3!!
  • #9
    gaskoin
    Level 38  
    Można zawsze zastosować dźwignię czasu i poprosić o kolaborację :D
  • #10
    Freddie Chopin
    MCUs specialist
    Przecież cały czas piszę, że jestem otwarty na współpracę - ostatni akapit w zalinkowanym newsie z mojej stronki (; Nie będę każdemu wysyłał PMa <:

    Tak więc raz jeszcze - zainteresowanych zapraszam do współpracy, projekt jest open-source, więc generalnie o to chodzi.

    4\/3!!
  • #11
    Smashing
    Level 20  
    Witam,
    Wkleiłem projekt do Eclipse.... kompilacja
    Make.... 50sekund, tup... 22sekundy (i7 2,9GHz)

    ustawiam pod STM32F427
    zmieniłem
    #define CONFIG_CHIP_STM32F427

    Nie wiem gdzie ustalasz zegar (chcę go puścić na 180Mhz)

    Pozdrawiam
    Smashing
  • #12
    Freddie Chopin
    MCUs specialist
    Smashing wrote:
    Wkleiłem projekt do Eclipse.... kompilacja
    Make.... 50sekund, tup... 22sekundy (i7 2,9GHz)

    Jak włączysz w opcjach projektu kompilację wielowątkową, to w make skompiluje się z podobną prędkością jak w tup. Prędkość tup można w Eclipse nieco poprawić - przynajmniej u mnie różnica była znaczna - https://groups.google.com/d/msg/tup-users/7qNxtgh9Z-s/kJInk6LyK7sJ Choć to w sumie zależy też od tego, czy masz włączony indekser - jeśli jest wyłączony, to te optymalizacje i tak nic nie dadzą.

    Tak czy siak tup to świetne narzędzie (;

    Smashing wrote:
    ustawiam pod STM32F427
    zmieniłem
    #define CONFIG_CHIP_STM32F427

    Profilaktycznie zajrzyj też do skryptu linkera i popraw rozmiary i/lub adresy pamięci. Raczej nie ma to specjalnego znaczenia, bo w Twoim układzie pamięci pewnie jest więcej niż w używanym przeze mnie STM32F407VG, ale może akurat nie <;

    Smashing wrote:
    Nie wiem gdzie ustalasz zegar (chcę go puścić na 180Mhz)

    Zacząłbym oczywiście od uruchomienia tego bez mieszania z zegarem, czyli na 16MHz. Tak profilaktycznie (;

    Zegar układu, a w zasadzie częstotliwość z jaką pracuje timer odliczający "ticki" systemowe, ustalona jest w tym samym pliku w którym wybrałeś układ, czyli distortos/include/distortos/distortosConfiguration.h i jest to:

    #define CONFIG_TICK_CLOCK 16000000

    Ponieważ w funkcji distortos::architecture::startScheduling() dla tej architektury (distortos/source/architecture/ARM/ARMv7-M/ARMv7-M-startScheduling.cpp) _WYŁĄCZAM_ dzielenie zegara dla SysTick przez 8, to powyższa wartość powinna być równa częstotliwości rdzenia.

    Swoją drogą - te wszystkie prefixy CONFIG_ są tam dlatego, że do konfiguracji całości planuję użyć narzędzia Kconfig, znanego pewnie niektórym z konfiguracji kernela Linuxa. Narzędzie to użyte jest też w nuttxie i generalnie jest ono w miarę przyjemne.

    W razie problemów/pytań/sugestii/... pisz!

    4\/3!!
  • #13
    Smashing
    Level 20  
    Witam,
    skryp linkera już zmieniłem, uruchomienie na 16Mhz mam za sobą (STM32F427)
    Chciałem się tym "pobawić" ale na zewnętrznym ramie. (STM32F429I-DISCO)
    Mam rozumieć ze nie włączasz żadnych peryferii?
    i CONFIG_TICK_CLOCK 16000000 używasz tylko do SysTick.
    Czyli jak jestem w "main" wtedy wszystko sobie uruchamiam? (dla mnie to ma sens ale chce się upewnić )
    Jak zmienię Clocka na 180Mhz (ale dopiero w main)to mogę wpisać do kompilacji CONFIG_TICK_CLOCK 180000000 i mam co 1ms SysTick?.
    Wolał bym ustawić malloc na zewnętrzny ram
    Problem zewnętrznym ramem jest taki że robię jego init w main....
    to jak wystartuje mi system?
    Czy taki zapis zadziała:
    Code: cpp
    Log in, to see the code

    oznacza że mój Stack używa tej sekcji i to może być sdRam?

    Pozdrawiam
    Smashing
  • #14
    Freddie Chopin
    MCUs specialist
    Smashing wrote:
    Mam rozumieć ze nie włączasz żadnych peryferii?
    i CONFIG_TICK_CLOCK 16000000 używasz tylko do SysTick.
    Czyli jak jestem w "main" wtedy wszystko sobie uruchamiam? (dla mnie to ma sens ale chce się upewnić )

    3x "tak". Na chwilę obecną żadne układy peryferyjne (poza SysTick) nie są przeze mnie "dotykane". W przyszłości pewnie się to nieco zmieni, ale na razie tak właśnie jest.

    Smashing wrote:
    Jak zmienię Clocka na 180Mhz (ale dopiero w main)to mogę wpisać do kompilacji CONFIG_TICK_CLOCK 180000000 i mam co 1ms SysTick?.

    Tak będzie, pod warunkiem że masz też ustawione:

    #define CONFIG_TICK_RATE_HZ 1000

    Wartość wpisywana do SysTick->LOAD to - jak łatwo się domyślić - wynik dzielenia jednego przez drugie:

    	SysTick->LOAD = CONFIG_TICK_CLOCK / CONFIG_TICK_RATE_HZ - 1;


    Jak już jesteśmy przy tym wywodzie, to przypominam, że funkcja main() jest już wątkiem, tzn. można (i należy!) jej użyć do jakiegoś zadania w projektowanym firmware. Nie należy robić tutaj czegoś takiego jak we FreeRTOS, że funkcja ta będzie służyła do uruchomienia wszystkiego, a następnie uruchomienia systemu (tutaj w ogóle nie ma czegoś takiego - system już działa) i więcej się do niej nie wróci. W przeciwnym wypadku cały stos przeznaczony na main() zostanie zmarnowany.

    Smashing wrote:
    Wolał bym ustawić malloc na zewnętrzny ram
    Problem zewnętrznym ramem jest taki że robię jego init w main....
    to jak wystartuje mi system?

    System w obecnej postaci _NIE_ używa nawet jednego bajta pamięci alokowanej dynamicznie, więc to gdzie robisz inicjalizację pamięci zewnętrznej jest - z punktu widzenia tego systemu - zupełnie nieistotne. Byle tylko stosy dla main i idle były w pamięci wewnętrznej, tak jak i obiekty systemowe (obiekty klas Thread / StaticThread, mutexy, semafory, kolejki, ...) które są globalne (bo ich konstruktory uruchamiają się przed wejściem do main()). Jeśli i coś z tego chcesz mieć w zewnętrznym RAM, to możesz użyć funkcji lowLevelInitialization0(), która wywoływana jest ze startupa _PRZED_ inicjalizacją czegokolwiek. Da się też te obiekty inicjalizować ręcznie - przez "placement new".

    Smashing wrote:
    Czy taki zapis zadziała:
    Code: cpp
    Log in, to see the code

    oznacza że mój Stack używa tej sekcji i to może być sdRam?


    Niestety nie (; Jeśli w następnych zdaniach opisuję coś co dobrze wiesz, to nie obrażaj się - profilaktycznie warto to wytłumaczyć dla innych. Zacznijmy od tego, że "constexpr" to po prostu "compile time constant" - w przybliżeniu można uznać, że to taki #define. Tak więc stała ...StackSize to po prostu ładnie opisany rozmiar stosu. Nie chcesz więc umieszczać "rozmiaru" w innej pamięci, bo to bez sensu. Opcji masz kilka, choć pierwsza jest znacząco bardziej przystępna.

    1. Zamiast używać klasy StaticThread używasz klasy Thread, do której podajesz "ręcznie" bufor na stos. Tenże bufor na stos znajdować się będzie właśnie w pamięci zewnętrznej. Wyglądałoby to mniej więcej tak:

    Code: cpp
    Log in, to see the code


    Sam wątek nie może być zmienną globalną, ponieważ jego konstruktor nie zadziała przed skonfigurowaniem pamięci w której jest stos.

    Generalnie da się to zrobić też bez tej funkcji "makeThread()" i bez "auto", ale wtedy trzeba podawać "explicite" parametry dla template'a - szczególnie "typ funkcji" i (opcjonalnie) typ argumentów dla niej. Jakbyś bardzo chciał tak zrobić, to pisz - podam taki przykład (jeśli oczywiście nie wiesz jak by to miało wyglądać). O ile dla "zwykłej" funkcji jest to stosunkowo proste, to w przypadku "member functions" czy funktorów robi się nieco koślawe, a w przypadku lambdy nie jest w ogóle możliwe. Stąd prościej użyć zawsze makeThread() / makeStaticThread().

    Stos oparłem o zmienne uint64_t, ponieważ dla ARM stos musi być wyrównany do 8 bajtów. Jeśli dla wątku podasz stos który nie jest odpowiednio wyrównany, to zostanie on nieco "przycięty", tak aby uzyskać wymagane wyrównanie. Używając zmiennej uint64_t po prostu masz gwarancję, że nie będzie żadnego przycinania. To samo można oczywiście uzyskać na inne sposoby, np. używając atrybutu, ale prościej użyć uint64_t.

    2. Cały obiekt StaticThread alokujesz dynamicznie w pamięci zewnętrznej.

    W tej wersji w pamięci dynamicznej (czyli w zewnętrznej) zostanie umieszczony zarówno stos wątku jak i "dane sterujące" wątku (np. wewnętrzny obiekt ThreadControlBlock, semafor służący do robienia join() itd.) - nie jest to oczywiście żaden problem, chyba że ze względów wydajnościowych chciałbyś te dane mieć jednak w szybszej wewnętrznej pamięci.

    3. Deklarujesz "miejsce na obiekt" (storage) w wybranej sekcji i tworzysz obiekt "ręcznie" przy użyciu "placement new".

    ---

    Zrobienie punktu 2 i 3 w obecnej postaci wymaga właśnie podawania "explicite" parametrów dla template'a, więc na razie daruję sobie opis jak to zrobić. Jeśli to jest ścieżka którą chciałbyś pójść, to daj znać - opiszę "co i jak".

    W sumie najprościej byłoby jednak zrobić inicjalizację tej pamięci przed konstruktorami, czyli - na chwilę obecną - najprościej będzie zdefiniować funkcję lowLevelInitialization0() w której ta pamięć zostanie zainicjalizowana - w tej funkcji zmienne globalne _NIE_ są wyzerowane/zainicjalizowane. Możesz też "wpiąć" swoją inicjalizację pamięci zewnętrznej do funkcji lowLevelInitialization1(), z tą uwagą, że funkcja ta "u mnie" już jest przemianowana na co innego, bo właśnie zmieniam ten fragment kodu.

    Dać znać czy jest to dla Ciebie pilne, ponieważ - jak już wyżej wspomniałem - pracuję właśnie nad nieco innym sposobem inicjalizacji całości i do tego sposobu łatwo byłoby dodawać dodatkowe funkcje. Mogę wysłać to co już mam (7 rewizji, więc nie są to jakieś drastyczne zmiany) na githuba. Planowałem te zmiany zakończyć i dopiero je publikować, ale mogę je też wrzucić od razu. Zmiany mają na celu umożliwienie wielowątkowego korzystania z malloc() - pamiętaj że teraz nie ma jeszcze szeregowania dostępu do tej funkcji mutexem, więc użycie jej z wielu wątków to gwarancja problemów.

    4\/3!!
  • #15
    Smashing
    Level 20  
    Witam
    Freddie Chopin wrote:

    Warto podkreślić, że kod nie używa pamięci alokowanej dynamicznie, wiec wszystko (przede wszystkim wątki wraz z ich stosami) można stworzyć jako zmienne lokalne (na stosie), globalne czy jak kto woli.

    To było w Twoim pierwszym poście i jakoś mi to umknęło...
    Mam w sumie teraz 192K+64K ramu... na testy wystarczy.
    Chciałem zewnętrzny ram dla malloc'a... ale distortos go nie potrzebuje... ja na razie też.
    Wieczorem będę patrzył na przerwania:
    1. Priority mogę ustawić jak chcę?
    Jeszcze nie wiem jak działają kolejki przerwaniach, ale jak czegoś nie ogarnę to napisze..

    Pozdrawiam
    Smashing
  • #16
    Freddie Chopin
    MCUs specialist
    Smashing wrote:
    1. Priority mogę ustawić jak chcę?

    Generalnie tak. Zastosowałem mechanizm znany z innych RTOSów, czyli część priorytetów przerwań (niższych) może korzystać z funkcji systemu, a część priorytetów (wyższych) nie może. Ta druga część nigdy nie jest blokowana, ta pierwsza zaś jest blokowana w czasie większości operacji typu operacje na mutexach, kolejkach, wewnętrznych strukturach danych, .... W związku z tym w pierwszej grupie można się spodziewać - jak w każdym RTOSie - pewnego opóźnienia pomiędzy zgłoszeniem przerwania a jego obsłużeniem, natomiast w drugiej grupie nie ma tego problemu.

    Parametr który to konfiguruje znów znajduje się w pliku distortosConfiguration.h i jest to:

    #define CONFIG_ARCHITECTURE_ARMV7_M_KERNEL_BASEPRI 8

    W przypadku STM32, ktory ma 16 priorytetów przerwań, "niższa" połowa jest więc blokowana (8-15), a "wyższa" połowa nie (0-7).

    Smashing wrote:
    Jeszcze nie wiem jak działają kolejki przerwaniach, ale jak czegoś nie ogarnę to napisze..

    Działają "po prostu" (; Oczywiście nie można używać operacji blokujących, czyli w przerwaniach mogą być użyte tylko i wyłącznie funkcje:
    X::tryEmplace(),
    X::tryPop(),
    X::tryPush(),
    gdzie X to typ kolejki (MessageQueue, FifoQueue, ... - wszystkie 8 wariantów działa tutaj tak samo).

    4\/3!!
  • #17
    michalko12
    MCUs specialist
    @Freddie Chopin A możesz tak w kilku punktach opisać co gdzie zmienić/dodać, żeby przeportować na inny uC np LPC1788?
  • #18
    Freddie Chopin
    MCUs specialist
    Zależy czy chcesz to zrobić "ładnie", czy "brutalnie". Największą przeszkodą w zrobieniu tego pierwszego jest właśnie brak jakiegoś systemu konfiguracji całości - że ustawiasz sobie gdzieś "LPC1788" i kod dla tego układu się kompiluje, a inne - dla innych układów - nie...

    Zrobienie tego brutalnie jest stosunkowo proste - LPC1788 jest przecież praktycznie identyczny jak STM32, wiec trzeba by po prostu dodać wszystko to co jest w ścieżce distortos/source/chip/STMicroelectronics/STM32F4 tyle że dla układu który wybrałeś. Za dużo tam na razie nie ma - fragment skryptu linkera, fragment tablicy wektorów i nagłówki producenta. Po prostu każdy z tych plików trzeba dostosować do układu który wybrałeś, a na koniec zrobić tak żeby się całość skompilowała, a kod dla STM32F4 wyłączyć z kompilacji (albo wywalić) (;

    Jeśli interesuje Cię taki port, to mogę spróbować to również wkrótce zrobić (albo Cię w tym wspomóc, może chcesz sam spróbować) - trzeba by w końcu ten system konfiguracji wymyślić...

    Jest jakaś bardzo popularna i stosunkowo tania płytka z układem którego chcesz użyć, tak abym mógł ją sobie kupić? Z tą ceną to tak specjalnie mi nie zależy żeby była bardzo niska, po prostu zwykle takie płytki, które są naprawdę tanie, są bardzo popularne, a jak płytka kosztuje np 5x tyle co STM32F4DISCOVERY, to już raczej specjalnie popularna nie będzie... Nie mam niczego z tym układem, mam za to makietę z propoxu z LPC1769, ale ona raczej nie jest specjalnie popularna czy tania. Chodzi mi o odpowiednik płytek STM32...DISCOVERY. Chodzi mi o to, żeby w przyszłości mieć zestaw gotowych konfiguracji na najpopularniejsze płytki.

    4\/3!!
  • #19
    michalko12
    MCUs specialist
    Freddie Chopin wrote:
    Jeśli interesuje Cię taki port, to mogę spróbować to również wkrótce zrobić (albo Cię w tym wspomóc, może chcesz sam spróbować)

    Wolę sam to zrobić, bo wtedy będę wiedział z czym mam do czynienia. Kopiuj wklej jak najbardziej mnie nie interesuje.
    Freddie Chopin wrote:
    trzeba by w końcu ten system konfiguracji wymyślić...

    Na razie usunięcie niepotrzebnych portów jest dla mnie zadawalającym rozwiązaniem.
    Freddie Chopin wrote:

    Jest jakaś bardzo popularna i stosunkowo tania płytka z układem którego chcesz użyć, tak abym mógł ją sobie kupić?

    Tego to nie wiem, nie szukałem. Mam na stanie Link, o tyle dobre rozwiązanie, że jak mi zachce się LPC4357 to podmieniam tylko OEM Board.

    Nie wiem tez co to znaczy tanio, ale ciekawą opcją jest np LPC4357-EVB, szkoda tylko, że nie pomyśleli nad ethernetem. Jeszcze taniej wychodzi LPC-Link2 na którym jest LPC4370 (CM4F+CM0+CM0), coś własnego do testów uC można na tym uruchomić.

    Freddie Chopin wrote:
    ...nie jest specjalnie popularna czy tania. Chodzi mi o odpowiednik płytek STM32...DISCOVERY. Chodzi mi o to, żeby w przyszłości mieć zestaw gotowych konfiguracji na najpopularniejsze płytki.

    Ja nie widzę sensu kupowania tych zestawów DISCOVERY. To jest tylko wypełniacz do szuflady i raczej traktuję te zestawy jako gadżet reklamowy, które w większości przypadków do niczego nie będą wykorzystane.
  • #21
    Freddie Chopin
    MCUs specialist
    michalko12 wrote:
    Wolę sam to zrobić, bo wtedy będę wiedział z czym mam do czynienia. Kopiuj wklej jak najbardziej mnie nie interesuje.

    No i OK, jakby co to pytaj (;

    michalko12 wrote:
    Tego to nie wiem, nie szukałem. Mam na stanie Link, o tyle dobre rozwiązanie, że jak mi zachce się LPC4357 to podmieniam tylko OEM Board.

    Nie wiem tez co to znaczy tanio, ale ciekawą opcją jest np LPC4357-EVB, szkoda tylko, że nie pomyśleli nad ethernetem. Jeszcze taniej wychodzi LPC-Link2 na którym jest LPC4370 (CM4F+CM0+CM0), coś własnego do testów uC można na tym uruchomić.

    Troche drogawe (; Nie sądzę więc, aby były szczególnie rozpowszechnione... Na tym drugim mi zależy najbardziej, a to pierwsze zwykle wpływa wprost na to drugie.

    michalko12 wrote:
    Ja nie widzę sensu kupowania tych zestawów DISCOVERY. To jest tylko wypełniacz do szuflady i raczej traktuję te zestawy jako gadżet reklamowy, które w większości przypadków do niczego nie będą wykorzystane.

    Trochę tak jest, jednak czasem się przydają - ot np. żeby na szybko sprawdzić czy RTOS miga diodą i zapoznać się z projektem (;

    tymon_x wrote:
    1. Może oprzeć API wątkowe dla C o C11 Thread support library

    To API, podobnie jak standardowe API C++11 dla wątków, ma sporo "dziur", które sprawiłyby, że jego używanie na mikrokontrolerach byłoby problematyczne. Choćby np takie, że nie da się ustawić rozmiaru stosu dla wątku, a to najważniejsza sprawa dla mikrokontrolera. To API jest dobre do systemu z wirtualną pamiecią, bo tam gdy system wykryje przepełnienie stosu, to alokuje więcej pamieci, kopiuje jeden stos do drugiego i po problemie. Na mikrokontrolerze takich cudów niestety nie da się zrobić.

    Z tego względu trzeba by tworzyć wątek w dwóch etapach - najpierw jakąś funkcją skonfigurować rozmiar stosu dla wątku który zaraz zostanie stworzony, a potem dopiero stworzyć wątek. Rodzi to standardowy problem z wywłaszczaniem wątków - te dwa etapy musiałyby być w sekcji krytycznej... Same problemy...

    W każdym razie nic nie stoi na przeszkodzie, żeby takie API _TEŻ_ było dostępne. Bazowe będzie jednak raczej (bo decyzji jeszcze nie podjąłem) API pthread, gdyż jest ono nieco bardziej rozbudowane.

    tymon_x wrote:
    2. Może CMake do budowania modułowego projektu?

    Nigdy nie byłem w stanie przekonać się do CMake... Samo budowanie projektu to nie problem, make i tup radzą sobie z tym doskonale. Problemem jest konfiguracja, a w tej kwestii CMake nie ma raczej nic specjalnego do zaoferowania. Przyznam jednak, że moja wiedza o CMake jest powierzchowna, więc chętnie poznam Twoją propozycję jeśli uważasz, że będzie dobra. Tylko musi być lepsza od narzędzia Kconfig, które jest naprawdę fajne (;

    4\/3!!
  • #22
    Smashing
    Level 20  
    Witam,
    Jestem w trakcie przenoszenia jednego mojego projektu pod Twój Rtos... (na testy).
    Powiem tak jedyny problem jaki mam (pewnie dla Ciebie jest blachy) to żeby używać np include stm32f429xx.h lub core_cm4.h to dodałem je jeszcze raz (skopiowałem z CMSIS do folderu gdzie ich używam ).
    To mi się tylko nie podoba bo mam teraz dwa razy te same hadery w projekcie, a nie wiem gdzie dodać patch
    a jak piszę w pliku bezpośrednio ścieżkę to nie działa.
    Jak wpiszę w Rules.mk : SUBDIRECTORIES += source\chip\STMicroelectronics\STM32F4\external\CMSIS
    to też coś nie chce działać.
    Ogólnie chodzi oto ja dodam nowy Folder Task1 w projekcie w nim mam:
    plik Task1.cpp i pliki Rules.mk i Tupfile.lua co należy dodać w Rules.mk żebym widział core_cm4.h i stm32fxxx.h (ten który rzeczywiście jest dodany)
    Jak używałem makefile do wpisywałem INC_DIRS i SRCS_DIRS i działało.


    Pozdrawiam
    Smashing
  • #23
    Freddie Chopin
    MCUs specialist
    Smashing wrote:
    Powiem tak jedyny problem jaki mam (pewnie dla Ciebie jest blachy) to żeby używać np include stm32f429xx.h lub core_cm4.h to dodałem je jeszcze raz (skopiowałem z CMSIS do folderu gdzie ich używam ).
    To mi się tylko nie podoba bo mam teraz dwa razy te same hadery w projekcie, a nie wiem gdzie dodać patch
    a jak piszę w pliku bezpośrednio ścieżkę to nie działa.

    Plan "na przyszłość" jest taki, że aplikacje powinny być "warstwowe" - będzie warstwa "board" i warstwa "application". Warstwa aplikacji powinna się odwoływać jedynie do HAL (którego oczywiście jeszcze nie ma, ale w przyszłości pewnie będzie) oraz do warstwy "board". Warstwa "board" zaś może odwoływać się zarówno do HAL jak i bezpośrednio do sprzętu. Ale to na razie jeszcze "pieśń przyszłości"...

    Smashing wrote:
    Jak wpiszę w Rules.mk : SUBDIRECTORIES += source\chip\STMicroelectronics\STM32F4\external\CMSIS
    to też coś nie chce działać.

    Zmienna SUBDIRECTORIES służy tylko do tego, żeby dany folder został "zbudowany" - tzn. w takim folderze musi być plik Rules.mk w którym będą opisane jakieś operacje do zrealizowania (albo choć kolejna modyfikacja zmiennej SUBDIRECTORIES). Innymi słowy - wszystkie foldery w których są pliki Rules.mk (i pliki źródłowe) muszą być połączone ze sobą w "drzewo" za pomocą zmiennej SUBDIRECTORIES. Jeśli dodajesz nowy folder z nowymi plikami źródłowymi, to musi się w nim znaleźć plik Rules.mk (jeśli używasz make, a nie tup). Następnie folder ten musi zostać dodany do zmiennej SUBDIRECTORIES pliku Rules.mk który znajduje się "wyżej" w hierarchii projektu.

    Smashing wrote:
    Ogólnie chodzi oto ja dodam nowy Folder Task1 w projekcie w nim mam:
    plik Task1.cpp i pliki Rules.mk i Tupfile.lua co należy dodać w Rules.mk żebym widział core_cm4.h i stm32fxxx.h (ten który rzeczywiście jest dodany)

    Zacznijmy od tego, że jeśli nie używasz tup, to plik Tupfile.lua możesz sobie darować, jeśli zaś używasz tup, a nie używasz make, to możesz sobie darować plik Rules.mk.

    Jeśli używasz make, to w pliku Rules.mk w folderze z którego chciałbyś dodawać te nagłówki powinno się znaleźć coś mniej więcej takiego:

    Code: bash
    Log in, to see the code


    Pierwszą linijkę musisz dopasować do układu jaki masz - jest to definicja wymagana przez nagłówek distortos/source/chip/STMicroelectronics/STM32F4/external/CMSIS/stm32f4xx.h

    Drugą linijkę pewnie już w tym pliku masz.

    Powyższe oczywiście jest dla kodu w C++. Jeśli chcesz to samo zrobić dla kodu w C, to wszelkie "CXXFLAGS" należy zastąpić w powyższym przez "CFLAGS".

    Powyższe jest oparte na pliku distortos/source/architecture/ARM/ARMv7-M/Rules.mk - jeśli chciałbyś coś takiego zrobić dla tup, to po prostu zobacz jak to jest zrobione w pliku "obok", czyli distortos/source/architecture/ARM/ARMv7-M/Tupfile.lua

    Jakby coś wciąż nie działało to daj znać - nie sprawdziłem "na żywo" tego co napisałem powyżej, więc gdzieś mogłem się pomylić.

    Smashing wrote:
    Jak używałem makefile do wpisywałem INC_DIRS i SRCS_DIRS i działało.

    Makefile z "przykłado-szablonów" raczej by się tu nie nadał, choć oczywiście można go użyć jakby się uprzeć, ale byłoby to naprawdę ciężkie (; Taka "rozproszona" konfiguracja make'a ("non-recursive makefile") jest całkiem wygodna, choć trzeba się przyzwyczaić do paru "dziwactw", np. używania tylko ":=" i tej magicznej zmiennej "$(d)". Generalnie w tup to wszystko jest w standardzie, więc kolejny powód dla którego tup jest fajniejszy niż make (;

    4\/3!!
  • #24
    Smashing
    Level 20  
    Witam
    Freddie Chopin wrote:
    Zacznijmy od tego, że jeśli nie używasz tup, to plik Tupfile.lua możesz sobie darować, jeśli zaś używasz tup, a nie używasz make, to możesz sobie darować plik Rules.mk.

    aha............... :)
    Dla potomnych... dodałem do Tupfile takie ścieżki i jest ok
    Code: cpp
    Log in, to see the code

    Ok czyli headery z CMSIS są "widoczne"
    Pozdrawiam
  • #25
    Freddie Chopin
    MCUs specialist
    Poniższe dwie linijki nie są (zwykle) potrzebne dla aplikacji:

    CXXFLAGS += "-I" .. TOP .. "/source/chip/STMicroelectronics/STM32F4/include"
    CXXFLAGS += "-I" .. TOP .. "/source/architecture/ARM/ARMv7-M/include

    Ta pierwsza daje dostęp jedynie do pliku CMSIS-proxy.h, który stanowi swoistą abstrakcję potrzebną dla źródeł "architektury". Nagłówki CMSIS od ARM wymagają nagłówków CMSIS od producenta układu - chodzi o to, że używanie funkcji typu NVIC_SetPriority() czy __set_BASEPRI() (które w zasadzie są niezależne od użytego układu) nie jest możliwe inaczej niż przez dołączenie - na przykład - stm32f4xx.h. Stąd plik CMSIS-proxy.h ma być abstrakcją typu "dołącz to co trzeba dla wybranego aktualnie układu".

    Druga linijka również potrzebna jest tylko dla wewnętrznych części systemu - dokładnie jest ona wykorzystywana w implementacji klasy architecture::Stack i w implementacji klasy architecture::InterruptMaskingLock. W normalnych zastosowaniach na 100% nie będzie potrzebna Ci ta pierwsza, a ta druga tylko okazjonalnie i w bardziej specyficznych sytuacjach. Moje założenie jest takie, że aplikacja nie powinna w ogóle dotykać niczego ze ścieżki "architecture" i niczego z namespace'a o tej samej nazwie.

    4\/3!!
  • #26
    Smashing
    Level 20  
    Witam,
    Mam 4 osobne wątki, startuje je z Main:
    Code: cpp
    Log in, to see the code

    Po co jest xxxx.join(); ?
    Bez tego wątki się przełączają a jak dam w main.
    Code: cpp
    Log in, to see the code

    to nie wracam już do main.
    Chciałem zrobić tak jak we FreeRtos, czyli wystartować wszystkimi wątkami z main
    i zakończyć wątek main.. bo pisałeś że main to jest zwykły wątek.
    Czyli żeby go zakończyć wystarczy zrobić xxxx.join(); z byle jakiego wątku?

    Pozdrawiam
  • #27
    Freddie Chopin
    MCUs specialist
    Smashing wrote:
    Po co jest xxxx.join(); ?

    ThreadBase::join() to odpowiednik funkcji pthread_join() z POSIXa ( http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_join.html ) czy thread::join() z C++11 ( http://en.cppreference.com/w/cpp/thread/thread/join ). Jeśli chcesz się zsynchronizować z zakończeniem wątku, to robisz to przez tą właśnie funkcję - wątek który wywołał tą funkcję zostanie wstrzymany dopóki "wskazany" wątek się nie zakończy.

    Smashing wrote:
    Bez tego wątki się przełączają a jak dam w main.
    thread1.join();
    to nie wracam już do main.

    Bo Twój wątek thread1 się nie kończy (;

    Smashing wrote:
    Chciałem zrobić tak jak we FreeRtos, czyli wystartować wszystkimi wątkami z main
    i zakończyć wątek main.. bo pisałeś że main to jest zwykły wątek.
    Czyli żeby go zakończyć wystarczy zrobić xxxx.join(); z byle jakiego wątku?

    Zakończenie wątku main() nie jest możliwe. Próba zrobienia czegoś takiego zaowocuje trwałym wstrzymaniem wątku który próbował tego dokonać.

    Generalnie sposób w jaki robione jest to we FreeRTOS jest dosyć głupi i ja osobiście polecałbym użycie main() do czegoś sensownego. W przeciwnym wypadku po prostu stos przeznaczony na main() zostanie zmarnowany (we FreeRTOS stos "główny" był po uruchomieniu schedulera przeznaczany dla przerwań)... Z takich "popularnych" RTOSów na mikrokontrolery np w ChibiOS/RT również main() jest normalnym wątkiem i nie da się go "zakończyć".

    W kodzie testowym nie używam wątku main() tylko osobnego testThread, bo... brakowało mi wygodnego sposobu konfiguracji rozmiaru wątku main() poprzez opcje projektu, a nie tylko przez skrypt linkera (; Jak już taka konfiguracja będzie gotowa, to wtedy osobny wątek przestanie być potrzebny.

    W ostateczności Twój kod powinien wyglądać tak:

    Code: cpp
    Log in, to see the code


    Nie działało Ci, bo pewnie dałeś to "na przemian".

    Ta funkcja jest bardzo istotna jeśli wątek (obiekt klasy ThreadBase lub klas z niej dziedziczących) jest obiektem lokalnym (na stosie). Powrót z funkcji przed fizycznym zakończeniem wątku z tego obiektu (czyli wywołanie destruktora zanim wątek zostanie zakończony) to idealny sposób na totalną masakrę wszystkiego - destruktor nie kończy wątku, on sobie będzie działał dalej i operował na pamięci która została zwolniona oraz używał obiektów które zostały usunięte.

    4\/3!!
  • #28
    Smashing
    Level 20  
    Witam,
    Wszystko to rozumie.
    Tylko że jak dam tak
    Code: cpp
    Log in, to see the code

    A ja robię tak wyłączam wątkek 2, (nie było thread2.join())
    wyłączam wątkek 3, (nie było thread3.join()), wyłączam wątkek 4, (nie było thread4.join())
    Wątek thread1 pracuje cały czas, a wątki 2,3,4 już nie pracują nie było na nich join.
    Sorki że tak drążę ale wolę się upewnić, bo tego nie mogę zrozumieć

    Pozdrawiam
  • #29
    Freddie Chopin
    MCUs specialist
    Smashing wrote:
    A ja robię tak wyłączam wątkek 2, (nie było thread2.join())

    Co rozumiesz przez "wyłączam wątek"?

    Domyślam się, że pytasz o to, czy można wywołać ThreadBase::join() po tym jak wątek się już skończył wykonywać. Oczywiście - w takim wypadku funkcja ta wykona się od razu, bez przełączania kontekstu i bez żadnego oczekiwania (bo nie ma na co czekać). Jeśli startujesz kilka wątków i potem chcesz na nie wszystkie poczekać, to kolejność w jakiej wywołasz funkcje ThreadBase::join() dla tych obiektów jest bez znaczenia. To że np. thread2, thread3 i thread4 skończyły się wykonywać gdy Ty wciąż czekałeś na thread1 nie ma znaczenia. Byle by dla każdego wątku wywołać ThreadBase::join() przed wywołaniem destruktora powiązanego obiektu.

    W przyszłości - gdy dodam wątki dynamiczne - będzie można (jak w POSIXie czy w C++11) stworzyć wątek w stanie "detached" albo wywołać na wątku funkcje ...::detach(). Po takim "odłączeniu" wątku wywołanie funkcji ThreadBase::join() już nie będzie potrzebne, a powiązany obiekt może zostać spokojnie zniszczony kiedykolwiek - nawet zanim wątek zakończy się wykonywać.

    W przypadku mojego kodu (jak i API wątków w C++11) należy mieć świadomość, że zakończenie wykonywania wątku to zupełnie co innego niż zniszczenie obiektu wątku (czyli wywołanie destruktora na obiekcie klasy ThreadBase, Thread lub StaticThread). To pierwsze _MUSI_ nastąpić przed tym drugim, w przeciwnym wypadku można napisać tylko "undefined behavior"... Jeśli przyjąć, że są 3 istotne punkty:
    - zakończenie wykonywania wątku - [Z]
    - wywołanie na tym wątku funkcji ThreadBase::join - [J]
    - zniszczenie obiektu tego wątku - [D]

    To istotne jest tylko i wyłącznie, żeby [Z] było przed [D]. Tak jest OK:
    [Z] -> [J] -> [D]
    Tak też jest OK:
    [J] -> [Z] -> [D]
    W zasadzie tak też jest OK (przynajmniej na razie):
    [Z] -> [D]
    Wywołanie ThreadBase::join() (na dzień dzisiejszy) nie jest obowiązkowe, tyle że bez tego ciężko mieć PEWNOŚĆ, że wątek się zakończył. Jeśli ją masz, to nic nie stoi na przeszkodzie żeby to zignorować. Jeśli destruktor tego wątku nigdy się nie wykona (bo np. obiekt jest stworzony przez operator new i nigdzie nie ma odpowiadającego mu operatora delete), również możesz to zignorować. Niemniej jednak wyjście z main() powoduje wywołanie destruktorów wszystkich obiektów globalnych i wszystkich obiektów które utworzyłeś lokalnie na stosie w main(). Tak więc dobrze po prostu "z zasady" wywoływać zawsze gdzieś tą funkcję dla tworzonych wątków.

    Należy mieć jeszcze świadomość, że opuszczenie funkcji wątku nie jest tożsame z jego zakończeniem, wiec "synchronizacja" przez współdzielone zmienne nie zadziała. Wątek jest "zakończony" gdy funkcja wątku zostanie opuszczona _ORAZ_ gdy zostanie on całkowicie usunięty ze schedulera (co oczywiście zwykle następuje zaraz po tym pierwszym).

    FreeRTOS jest dosyć... ograniczający...

    Quote:
    Sorki że tak drążę ale wolę się upewnić, bo tego nie mogę zrozumieć

    Bardzo dobrze że drążysz - o to właśnie chodzi!

    4\/3!!
  • #30
    Smashing
    Level 20  
    Witam
    Freddie Chopin wrote:
    Co rozumiesz przez "wyłączam wątek"?

    wyłączam... kończę czyli przestaje się wykonywać pętle for lub while. (robię to tylko na testy)
    Obecnie uruchomiłem sobie tego rtos'a w ramie, jak na razie wszystko działa,
    jak czegoś nie będę pewien .... to na pewno zapytam

    Pozdrawiam