Skocz do zawartości

Czym są: RTOS, wielozadaniowość, taski?


SOYER

Pomocna odpowiedź

(edytowany)
23 minuty temu, ethanak napisał:

Ale przed czym ma ta sekcja krytyczna chronić?

Na przykład esp32 rozpoczyna transfer na magistrali. 1wire jest dość restrykcyjny w kwestii zarządzania czasem bitów. Dlatego podczas transferu nie może dojść do zakłócenia transmisji, to jest powód użycia sekcji krytycznej - tak myślę.

Edytowano przez _LM_
Link do komentarza
Share on other sites

1 minutę temu, ethanak napisał:

To potrzebujesz blokady przerwań na tym rdzeniu a nie sekcji krytycznej

Noo 🙂 o to mi chodziło. Dobrze, sprawdzę jak to zrobić, jak nie będę wiedział - wrócę. Dziękuję @ethanak !

Link do komentarza
Share on other sites

(edytowany)

Uff jakoś to dzisiaj umęczyłem, problemy sprzętowe - taki urok budowania prototypów od zera, mniejsza o to. W każdym razie, wyniki testów potwierdziły co pisał @ethanak najmniej błędów CRC uzyskałem kiedy użyłem blokowania przerwań na rdzeniu gdzie jest podpięty task 1Wire. 

Zastosowałem critical section:

W (6362) _LVGL: ds temp 38.38
E (8152) zh_ds18b20: DS18B20 read fail. Invalid CRC.
W (8152) _LVGL: ERROR READ TEMP
E (9952) zh_ds18b20: DS18B20 read fail. Invalid CRC.
W (9952) _LVGL: ERROR READ TEMP
W (11752) _LVGL: ds temp 38.19
W (13552) _LVGL: ds temp 38.19
W (15352) _LVGL: ds temp 38.12
W (17142) _LVGL: ds temp 38.12
W (18932) _LVGL: ds temp 38.12
W (20732) _LVGL: ds temp 38.12
E (22532) zh_ds18b20: DS18B20 read fail. Invalid CRC.
W (22532) _LVGL: ERROR READ TEMP
E (24332) zh_ds18b20: DS18B20 read fail. Invalid CRC.
W (24332) _LVGL: ERROR READ TEMP
W (26132) _LVGL: ds temp 38.12
E (27932) zh_ds18b20: DS18B20 read fail. Invalid CRC.
W (27932) _LVGL: ERROR READ TEMP
E (29732) zh_ds18b20: DS18B20 read fail. Invalid CRC.

Najwięcej błędów kiedy nie było żadnej blokady:

I (932) LVGL: Starting LVGL task
W (932) _LVGL: END init
I (962) gpio: GPIO[13]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
W (1152) _LVGL: START
W (2792) _LVGL: ds temp 37.56
E (4592) zh_ds18b20: DS18B20 read fail. Invalid CRC.
W (4592) _LVGL: ERROR READ TEMP
E (6392) zh_ds18b20: DS18B20 read fail. Invalid CRC.
W (6392) _LVGL: ERROR READ TEMP
E (8192) zh_ds18b20: DS18B20 read fail. Invalid CRC.
W (8202) _LVGL: ERROR READ TEMP
E (9992) zh_ds18b20: DS18B20 read fail. Invalid CRC.
W (9992) _LVGL: ERROR READ TEMP
E (11792) zh_ds18b20: DS18B20 read fail. Invalid CRC.
W (11792) _LVGL: ERROR READ TEMP
E (13592) zh_ds18b20: DS18B20 read fail. Invalid CRC.
W (13592) _LVGL: ERROR READ TEMP

A tutaj kiedy zastosowałem blokowanie przerwań:

W (1152) _LVGL: START
W (2782) _LVGL: ds temp 37.62
W (4572) _LVGL: ds temp 37.56
W (6362) _LVGL: ds temp 37.56
E (8152) zh_ds18b20: DS18B20 read fail. Invalid CRC.
W (8152) _LVGL: ERROR READ TEMP
W (9932) _LVGL: ds temp 37.56
W (11722) _LVGL: ds temp 37.50
W (13512) _LVGL: ds temp 37.50
W (15302) _LVGL: ds temp 37.44
E (17092) zh_ds18b20: DS18B20 read fail. Invalid CRC.
W (17092) _LVGL: ERROR READ TEMP

W czasie prób miałem podłączony analizator stanów logicznych aby na żywo było widać co dzieje się na magistrali, niestety nie zrobiłem screenshot. Aby ułatwić sobie wybór pomiędzy spinlockiem a port_XXXX_INTERRUPTS(); przygotowałem proste definicje. 

#define PORT_MUX 0
#define PORT_ISR 1

static const char *TAG = "zh_onewire";

#if PORT_MUX == 1
// Statically allocate and initialize the spinlock
static portMUX_TYPE my_spinlock = portMUX_INITIALIZER_UNLOCKED; 
#endif

Które później w kodzie wykorzystałem na przykład w taki sposób

void zh_onewire_send_byte(uint8_t byte)
{
    
 #if PORT_MUX == 1
 portENTER_CRITICAL(&my_spinlock);
 #endif

 #if PORT_ISR ==1
   portDISABLE_INTERRUPTS(); 
 #endif

    for (uint8_t i = 0; i < 8; ++i)
    {
        _send_bit(byte & 1);
        byte >>= 1;
    }

 #if PORT_MUX == 1
 portEXIT_CRITICAL(&my_spinlock);
 #endif
 #if PORT_ISR == 1
 portENABLE_INTERRUPTS();
    #endif
}

także dało mi to solidną dawkę wiedzy na temat działania freeRTOSa 

Edytowano przez _LM_
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

(edytowany)

Tym razem rozważanie - kolejka czy wskaźnik: w jednym z tasków pobieram wartości z touchpadów, pomyślałem że po co do takiego zadania angażować kolejkę być może wystarczy wskaźnik na wartości i semafor
 

         if(xSemaphoreTake(xTpSemaphore,0)){
        for(int i = 0; i <TOUCHPAD_USED ;i++)
            {
            touch_pad_read_filtered(tp[i].tpnum,&tp[i].value);
            global_tp[i].value = tp[i].value;
            }
        xSemaphoreGive(xTpSemaphore);   

kopiuję wartości do tablicy global_tp.value a w innym tasku odczytuję wartości tablicy gdzie wcześniej przekazałem wskaźnik na nią 

const hw_touchpad_value_t * touchpad_get_value(void)
{
    return &global_tp[0];
} 

i żeby nie doszło do odczytu wartości kiedy ten pierwszy akurat zapisuje również obudowałem to semaforem

         if(xSemaphoreTake(xTpSemaphore,0))
     {
      lv_label_set_text_fmt(objects.lbl_tp1,"_PL %d",tval[0].value);
      lv_label_set_text_fmt(objects.lbl_tp2,"_MN %d",tval[1].value);
      lv_label_set_text_fmt(objects.lbl_tp3,"_OK %d",tval[2].value);
      xSemaphoreGive(xTpSemaphore);
     }    

to działa elegancko, nie wiem tylko czy zastosowanie tutaj MUTEXa jest uzasadnione, czy jednak poprawne byłoby użycie kolejki?

Edytowano przez _LM_
Link do komentarza
Share on other sites

Nie wiem czy tak elegancko - użycie 0 jako drugiego parametru xSemaphoreTake może doprowadzić do sytuacji, kiedy task działający na jednym rdzeniu nie może się dostać do semafora bo na drugim chodzi sobie jakaś pętla co co chwila wywołuje Take. Np. task czytający touchpady nie może wejść do kodu umożliwiającego mu ten odczyt bo drugi task z uporem maniaka wypisuje wartości na displayu i oczywiście do tego musi mieć zamknięty semafor.

Nie wiem też co robisz z tymi danymi o touchpadach, bo wygląda na to że w tasku odbierającym chcesz zrobić z nimi jakieś machloje (przykładowo zamianę surowej wartości na zerojedynkowy stan). Jeśli to jest tak jak myślę to przekształcenie powinno być po stronie odczytującej touchpady (w nadajnik), a odbiornik powinien dostać np. tylko informację o stanie touchpadów (przy czym wystarczy tu uint8_t). W takiej sytuacji do kolejki pchasz dane tylko wtedy jeśli stan się zmienił. Możesz zastosować powiadomienie, ale to więcej kombinowania chociaż warto (eSetBits w xTaskNotify i odpowiednia wartość ulBitsToClearOnExit przy xTaskNotifyWait).

Jeśli koniecznie musisz mieć surowe dane i oba taski latają jak głupie w pętli to lepiej zastosować globalną zmienną pomocniczą z dostępem chronionym z obu stron sekcją krytyczną.

W każdym razie na pewno nie polling na semaforze.

Link do komentarza
Share on other sites

1 godzinę temu, ethanak napisał:

Nie wiem czy tak elegancko - użycie 0 jako drugiego parametru xSemaphoreTake może doprowadzić do sytuacji, kiedy task działający na jednym rdzeniu nie może się dostać do semafora bo na drugim chodzi sobie jakaś pętla co co chwila wywołuje Take

Touchpady są odczytywane co 50ms, wyświetlacz akutualizowany co 10ms

 

1 godzinę temu, ethanak napisał:

Nie wiem też co robisz z tymi danymi o touchpadach, bo wygląda na to że w tasku odbierającym chcesz zrobić z nimi jakieś machloje (przykładowo zamianę surowej wartości na zerojedynkowy stan)

wartości są przetwarzane oraz wołane jest zdarzenie ok kliknięcia, przytrzymania, opuszczenia tp. Wartości "surowe" wyświetlam jako  info że system działa oraz w celach debugowych.

1 godzinę temu, ethanak napisał:

Jeśli koniecznie musisz mieć surowe dane i oba taski latają jak głupie w pętli to lepiej zastosować globalną zmienną pomocniczą z dostępem chronionym z obu stron sekcją krytyczną.

próbowałem ale to generowało więcej błędów CRC podczas odczytu magistrali 1Wire  

Link do komentarza
Share on other sites

4 minuty temu, ethanak napisał:

A co się stanie jeśli jako drugi argument w take dasz 1 a nie 0?

Chyba nie do końca tego rozumiem? Ta jedynka, zero lub inna wartość mówi ile task może czekać na odblokowanie semafora? 

Link do komentarza
Share on other sites

Zero znaczy nie czeka. Każda inna wartość powoduje ustawienie stanu blocked na tasku, wrzucenie tasku do kolejki semafora. Jeśli semafor zostanie podniesiony to ten task będzie pierwszym do odblokowania. Np. W tym wolniejszym tasku nawet jak trafi na zamknięty semafor to ruszy po jego podniesieniu.

No ale przy 10 msec rastrze wyświetlania dałbym jednak 1000 Hz zamiast domyślnych 100Hz. Mam tak ustawione w arduino board i działa bardzo dobrze.

A tak przy okazji nie da się użyć RMT do gadania po 1wire? Bo UART jakoś mi e wynikach gógla przemknął, a bit banging to chyba ostateczność...

  • Pomogłeś! 1
Link do komentarza
Share on other sites

3 minuty temu, ethanak napisał:

A tak przy okazji nie da się użyć RMT do gadania po 1wire?

Tak, jest taki komponent do IDFa. Korzystając z gotowca niewiele bym się nauczył przy następnych projektach to już będzie sprzętowy odczyt, DSa miało nie być bo chciałem odczytywać temperaturę bezpośrednio z ESP (tymczasem wroom32 niema wbudowanego czujnika heh).

Link do komentarza
Share on other sites

10 minut temu, ethanak napisał:

Np. W tym wolniejszym tasku nawet jak trafi na zamknięty semafor to ruszy po jego podniesieniu

A to nie jest tak że scheduler wpierw odblokuje task który czekał najdłużej? 

Link do komentarza
Share on other sites

Kto napisze powyżej jakiego progu skomplikowania, lub jakie inne krytyczne czynniki na to wpływają, że już opłaca się/trzeba wchodzić w RTOS a nie wystarczy pojedynczy wątek robiący ileś tam operacji na sekundę.

Takie przykłady z życia amatora, który zrozumie sedno. 

Jedno co mi przychodzi do głowy to jakieś procesy które zajmują dużo czasu, ale nawet takie pewnie można podzielić tak żeby nie blokowały wątku.

Więc dlaczego RTOS w diy, skoro dla zastosowań amatorskich chyba wydajności esp32 wystarczają.

Jeszcze jedno, czy taki dwurdzeniowy esp32, obrabiający np. mój projekt ekranu, pracuje tylko na 1 rdzeniu? Drugi się nudzi?

Link do komentarza
Share on other sites

19 minut temu, SOYER napisał:

Jeszcze jedno, czy taki dwurdzeniowy esp32, obrabiający np. mój projekt ekranu, pracuje tylko na 1 rdzeniu? Drugi się nudzi?

Wcale się nie nudzi. Masz tam jakieś WiFi czy innego BT? No to właśnie one sobie działają na drugim rdzeniu.

Poza tym to, że nie używasz jawnie w swoim kodzie żadnych funkcji RTOS nie znaczy, że jakieś biblioteki tego nie używają (choćby AsyncWebServer). A przy okazji warto wiedzieć jak to wszystko działa. Przykładowa funkcja delay() (na którą wszyscy psioczą bo to modne) tu działa zupełnie inaczej.

A czy coś wystarczy czy nie wystarczy... spróbujmy czegoś takiego (raczej jako eksperyment myślowy).

Załóżmy istnienie funkcji createArduinoTask() (w rzeczywistości funkcja miałaby parę linijek na krzyż):

void createArduinoTask(void (*setup)(), void (*loop)());

I taki kawałek kodu:


void setup1()
{
  pinMode(20, OUTPUT);
}

void loop1()
{
  digitalWrite(20, HIGH);
  delay(25);
  digitalWrite(20, LOW);
  delay(90);
}



void setup()
{
  pinMode(19, OUTPUT);
  createArduinoTask(setup1, loop1);
}

void loop()
{
  digitalWrite(19, HIGH);
  delay(20);
  digitalWrite(19, LOW);
  delay(50);
}

Patrząc na ten kod powinieneś się od razu zorientować co to robi (taki multi-blink na dwóch ledach).

No i teraz spróbuj ocenić, ile kodu będziesz potrzebować aby zrobić to bez użycia RTOS-a (czyli na swoich millisach zapewne) i porównaj czytelność i objętość  kodu w obu wersjach (czyli mojej na RTOS i Twojej na millisach).

Mam nadzieję, że jest to odpowiedź na Twoje pytania.

 

  • Lubię! 1
Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.