Elektroda.pl
Elektroda.pl
X
CControls
Proszę, dodaj wyjątek www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

[FreeRtos] Obsługa przerwań

janiewiem 06 Cze 2010 17:04 3606 22
  • #1 06 Cze 2010 17:04
    janiewiem
    Poziom 11  

    Witam,

    Mam problem z obsługą przerwań we Freertosie. Do tej pory byłem przyzwyczajony do obsługi przerwań bez systemu operacyjnego, tworzyłem funkcję przerwania, ustawiałem odpowiednio AIC, i wszystko działało.

    Przebuszowałem całą stronę FreeRTOS'a już setki razy, przeszukałem internet, z demo programu niestety niczego konkretnego nie udało mi się wyłuskać. Chcę po prostu zrobić przerwanie, aby po przycisnięciu guzika dioda się wyłączała lub włączała. Oto prosty kod:

    ISR:

    Code:

    void ChangeFlashRate()
    {
       vParTestToggleLED( 0 );
    }


    Inicjalizacja przerwania:
    Code:

    void initLeds()
    {
       AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_PIOA, 5, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL,ChangeFlashRate);
       AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_PIOA);
       AT91C_BASE_PIOA->PIO_IER = AT91C_PIO_PA19; // B1
    }


    I start tasku:
    Code:

    void vStartLEDFlashTasks()
    {
       initLeds();
       xTaskCreate( vLEDBlink, ( signed char * ) "LEDtaskC", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 2, NULL );

    }


    Kod ten znajduje się w pliku leds.c, który kompiluję w trybie ARM.

    0 22
  • CControls
  • #2 06 Cze 2010 22:13
    _Robak_
    Poziom 33  

    Przerwania we Freertosie obsługuje się poprzez semaphory. Generalnie w taskach przerwaniowych są spore restrykcje czego można używać czego nie, polecam kupić sobie booka, wszystko jest pięknie wytłumaczone.

    0
  • #3 06 Cze 2010 23:40
    janiewiem
    Poziom 11  

    No właśnie, przeszperałem całe internety już w ciągu tego tygodnia i doszedłem do wniosku, że zakup Practical Guide jest niezbędny jeśli chcesz poważnie się zająć FreeRTOS'em a nie jesteś superbiegły w interpretowaniu demonstracyjnego kodu. Więc jutro sobię go zaserwuję na śniadanie.

    0
  • #4 07 Cze 2010 12:10
    arrevalk
    Poziom 25  

    Jeżeli dobrze zrozumiałem, odpalasz taska vLEDBlink() który jest w taskach demonstracyjnych razem ze źródłami freeRTOSA. A do tego w przerwaniu uruchamiasz funkcje vParTestToggleLED() która też pochodzi z przykładów.

    I tu jest moim zdaniem problem. Task mryga sobie diodkami, a jak wcisniesz przycisk to zmieniony zostanie stan led0 na przeciwny, po wyjsciu z przerwania wroci task i znowu to przelaczy.
    Całość najprawdopodobniej działa tylko tyle że tego nie widzisz na diodkach.

    Żeby całość działała najprościej było by nie uruchamiać żadnego taska.

    Albo tak jak wspomniał _Robak_ wykorzystać semafory. Tylko pamiętaj że z przerwania można jedynie "dać" semafor oraz korzystać z funkcji kończących się "*FromISR" (np.: xSemaphoreGiveFromISR).

    0
  • CControls
  • #5 07 Cze 2010 14:22
    nenpa8lo
    Poziom 17  

    To ja podepne tutaj pytanko. Jezeli mam na przyklad 1000-10000 przerwan na sekunde, to wolanie FromISR zamiast obslugiwanie w przerwaniu, jest dla mnie przerostem formy nad trescia.
    Czy jest jakis blad w takim rozumowaniu?

    0
  • #6 07 Cze 2010 14:26
    janiewiem
    Poziom 11  

    arrevalk napisał:
    Jeżeli dobrze zrozumiałem, odpalasz taska vLEDBlink() który jest w taskach demonstracyjnych razem ze źródłami freeRTOSA. A do tego w przerwaniu uruchamiasz funkcje vParTestToggleLED() która też pochodzi z przykładów.


    Niestety nie, funkcja vLEDBlink() jest napisana przeze mnie. Jest tam faktycznie coś z demówki ale raczej sporo obcięte i zmodyfikowane. I całość niestety nie działa bo diodka zaraz po uruchomieniu się włącza, po momencie się wyłącza, i na tym staje :) Nie reaguje na przycisk. Dzięki wielkie za pomoc, myślałem właśnie, że trzeba do tego wykorzystać semafory ale na stronie nie znalazłem przydatnych praktycznych informacji na ich temat (tzn. przykładu, który by mi się przydał). W każdym razie właśnie dotarł do mnie manual i pogłębiam wiedzę więc jak już to rozwiążę to zamieszczę dla potomnych.

    1
  • #7 07 Cze 2010 15:40
    arrevalk
    Poziom 25  

    nenpa8lo napisał:
    To ja podepne tutaj pytanko. Jezeli mam na przyklad 1000-10000 przerwan na sekunde, to wolanie FromISR zamiast obslugiwanie w przerwaniu, jest dla mnie przerostem formy nad trescia.
    Czy jest jakis blad w takim rozumowaniu?

    Ja semafory wykorzystuje np do sygnalizacji skompletowania ramki przy transmisji danych czy wykonaniu operacji na pomiarach adc (uśrednianie czy inne takie). Ogólnie nie ma sensu korzystać z mechanizmu semafora jeżeli task go "pobierający" jest odswierzany rzadziej niż sam semafor.

    0
  • #8 07 Cze 2010 19:50
    janiewiem
    Poziom 11  

    Panowie, już nie mam pomysłów... kupiłem nawet manuala, żeby to działało, oparłem przerwanie na semaforze, a tu dalej nie działa.... Stworzyłem osobny plik ledsISR.c , w którym umieściłem prościutki kod:

    Code:


    xSemaphoreHandle xSemafor;

    void initLeds()
    {
       vSemaphoreCreateBinary(xSemafor);
       AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_PIOA, 5, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL,ToggleLedISR);
       AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_PIOA);
       AT91C_BASE_PIOA->PIO_IER = AT91C_PIO_PA19; // B1

    }


    void ToggleLedTask(void *pvParameters) {


       while(1) {
          xSemaphoreTake(xSemafor, portMAX_DELAY);

          vParTestToggleLED( 0 );
       }
    }
    void ToggleLedISR()
    {
       int status;
       static portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

       status = AT91C_BASE_PIOA->PIO_ISR;
       xSemaphoreGiveFromISR(xSemafor, &xHigherPriorityTaskWoken);

       if(xHigherPriorityTaskWoken == pdTRUE) {
          portYIELD_FROM_ISR();
       }


    }


    Zrobiłem tak, ponieważ z tego co się orientuję + praktyka, jak nie skompiluje ISR w trybie ARM to nie pójdzie. Powiedzcie mi, co tu może być nie tak?!

    W mainie włączam taski w ten sposób:
    Code:

    int main( void )
    {
       /* Setup any hardware that has not already been configured by the low
       level init routines. */
       prvSetupHardware();

       vParTestToggleLED( 0 );

            initLeds()
       vStartLEDFlashTasks();  // druga diodka, która sobie swobodnie miga
       xTaskCreate( ToggleLedTask, ( signed char * ) "ISR", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 3, NULL );

       vTaskStartScheduler();

       /* We should never get here as control is now taken by the scheduler. */
         return 0;
    }


    Druga diodka, którą obsługuje vStartLEDFlash działa, nie działa tylko przerwanie, które powinno zmienic stan drugiej diodki. Czy może ta funkcja, która uruchamiana jest na początku ma z tym związek?

    Code:

    static void prvSetupHardware( void )
    {
       portDISABLE_INTERRUPTS();
       
       /* When using the JTAG debugger the hardware is not always initialised to
       the correct default state.  This line just ensures that this does not
       cause all interrupts to be masked at the start. */
       AT91C_BASE_AIC->AIC_EOICR = 0;
       
       /* Most setup is performed by the low level init function called from the
       startup asm file. */

       /* Enable the peripheral clock. */
        AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOA;


    }

    0
  • #9 07 Cze 2010 20:09
    arrevalk
    Poziom 25  

    Pierwsza sprawa:

    Code:
    void ToggleLedTask(void *pvParameters) { 
    
       while(1) {
          xSemaphoreTake(xSemafor, portMAX_DELAY);
          vParTestToggleLED( 0 );
       }
    }


    Powinno raczej wyglądać:
    Code:
    void ToggleLedTask(void *pvParameters) { 
    
       while(1) {
          if(xSemaphoreTake(xSemafor, portMAX_DELAY) == pdTRUE)
              vParTestToggleLED( 0 );
       }
    }

    Chyba że INCLUDE_vTaskSuspend masz zadeklarowane na 1.
    Ponadto w kodzie przerwania masz coś takiego:
    Code:
    if(xHigherPriorityTaskWoken == pdTRUE) { 
    
          portYIELD_FROM_ISR();
       }

    A w wersji 6.0.4 freeRTOS robi się to tak:
    Code:
    portYELD_FROM_ISR(xHigherPriorityTaskWoken);

    Jak masz jtaga to zobacz czy program wchodzi Ci w obsługę przerwania.
    I jeszcze jedno, nie wiem jak w przypadku portu na SAM7S ale w przypadku STM32 po utworzeniu semafora wywołanie SemaphoreTake zawsze zwraca pdTRUE, nawet jeżeli nie została wywołana funkcja SemaphoreGive

    0
  • #10 07 Cze 2010 20:19
    janiewiem
    Poziom 11  

    Właśnie INCLUDE_vTaskSuspend mam zadeklarowane na 1. Próbuję jak najbardziej trzymać się tego co jest w manualu.

    Co do portYELD_FROM_ISR(xHigherPriorityTaskWoken);

    Jeśli zrobię to w ten sposób to dostaję błąd error: 'portYIELD_FROM_ISR' undeclared (first use in this function).

    Właśnie nie mogę zdebugować programu, bo podczas prób wyskakuje napis informujący o tym, że breakpointy można stosować tylko w pamięci ram a ja mam freertosa we flashu :/

    0
  • #11 08 Cze 2010 00:38
    nenpa8lo
    Poziom 17  

    A co jest w vStartLEDFlashTasks();?

    0
  • #12 08 Cze 2010 00:42
    janiewiem
    Poziom 11  

    Jest tam ta druga diodka, ktora sobie miga i ona działa.

    Code:

    void vStartLEDFlashTasks()
    {

       xTaskCreate( vLEDBlink, ( signed char * ) "LEDtaskC", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL );
    }


    Ogólnie to rozpracowuje to na troche zmienionym demie freertosa (pousuwalem wszystkie wątki). To demo było przeznaczone dla AT91SAM7X256, ja używam AT91SAM7S64. Nie zmieniłem natomiast plików Cstartup.c i Cstartup.s79, zostały takie jak były dla X256, bo diodka jedna mi działała, ale czy możliwe że przez to skopały się przerwania? Bardzo proszę o pomoc, bo męczę się tu z magisterką a trafiłem na taki problem, który bardzo ciężko jest mi zinterpretować :/

    EDIT:

    Po zasileniu płytki włącza się na momencik dioda 0, co jest spowodowane wywolaniem w mainie vParTestToggleLED( 0 );, ale zaraz potem gaśnie, można sobie to tłumaczyć rozpoczęciem wątku xTaskCreate( ToggleLedTask...), ale przecież semafor jest jeszcze pusty, więc wątek powinien się zblokować aż do momentu aż na semaforze pojawi się token, i dopiero potem wyłączyć diodę, a tak nie jest.

    0
  • #13 08 Cze 2010 10:58
    nenpa8lo
    Poziom 17  

    Kilka postów wyżej napisano, że jeżeli po stworzeniu semafora odrazu weźmiesz to jest lipa bo się da.

    arrevalk napisał:
    I jeszcze jedno, nie wiem jak w przypadku portu na SAM7S ale w przypadku STM32 po utworzeniu semafora wywołanie SemaphoreTake zawsze zwraca pdTRUE, nawet jeżeli nie została wywołana funkcja SemaphoreGive

    0
  • #14 08 Cze 2010 11:07
    janiewiem
    Poziom 11  

    O cholera, sorry, źle to zinterpretowałem. Faktycznie, zaraz po utworzeniu semafora zabieram go, bo tworzę task, który go zabiera. W takim razie w którym momencie muszę stworzyć ten task zabierający semafor? I jeśli faktycznie ten task wykorzystuje ten błąd i od razu bierze semafor mimo ze go nie ma, to faktycznie znaczy to, że przerwania nie działają. Próbowałem poeksperymentować i dodałem w ISR po wywołaniu taska linijke

    Code:

       AT91C_BASE_AIC->AIC_EOICR = 0;

    Raz udało mi się zmienić stan diody, ale za każdym razem druga dioda się zacina, i wydaje mi się, że wszystko się po prostu zacina.
    A co do
    Code:


       portDISABLE_INTERRUPTS();
       

    czy ma to jakies znaczenie, że nie wywołuję nigdzie portENABLE_INTERRUPTS()?

    EDIT:

    Niestety, zmieniłem kod dodając od razu po stworzeniu semafora jednego take'a, ale nic to nie dało, działa wszystko tak samo...
    Code:


    void initLeds()
    {
       vSemaphoreCreateBinary(xSemafor);
       xSemaphoreTake(xSemafor,(portTickType)100 );


       AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_PIOA, 3, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL,ToggleLedISR);
       AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_PIOA);
       AT91C_BASE_PIOA->PIO_IER = AT91C_PIO_PA20; // B1

    }

    0
  • #15 08 Cze 2010 11:51
    nenpa8lo
    Poziom 17  

    No co ty, tworzysz i od razu bierzesz? Jeżeli już to powinieneś dać. Ale najlepszym sposobem jest przykład ze strony freertos:

    Code:
    if (xSemafor != NULL)
    
      {
         xSemaphoreTake(xSamafor, (portTickType)100);
      }

    Linia
    Code:
    AT91C_BASE_AIC->AIC_EOICR = 0; 
    jest obowiązkową ostatnią linią każdego przerwania. Jest to 'sygnał' obsłużenia przerwania.

    0
  • #16 08 Cze 2010 13:39
    arrevalk
    Poziom 25  

    Może naprostuje o co mi chodziło z tym semaforem.
    Otóż na porcie stm32(nie wiem jak inne) gdy utworzymy semafor(SemaphoreCreateBinary) to pierwsze wywołanie SemaphoreTake zwraca pdTRUE. Czyli semafor jest odrazu ustawiony, nawet jeżeli w kodzie nie została wywołana funkcja SemaphoreGive.

    Ominąć to można w prosty sposób, mianowicie odrazu po utworzeniu semafora go wziąść, tylko z czasem blokowania ustawionym na 0 (nie ma potrzeby oczekiwania na semafor). Inna sprawa że powinno to być wykonane zanim uruchomi się task/przerwanie dające ten semafor bo przez przypadek możemy pobrac semafor który cos sygnalizował.

    0
  • #17 08 Cze 2010 13:55
    janiewiem
    Poziom 11  

    Dlatego też "brałem" semafor zaraz po utworzeniu go, a przed utworzeniem taska. Jednak to nic nie dało. Zauważyłem jedną rzecz, że wpis

    Code:

       AT91C_BASE_AIC->AIC_EOICR = 0xFA;


    zawiesza procesor kiedy wystąpi przerwanie. Nie wiem dlaczego tak się dzieje, może daje go w złym miejscu? Powinienem go wpisac jeszcze w ISR czy już w tasku, który jest włączany przez ISR?

    0
  • #18 08 Cze 2010 14:22
    nenpa8lo
    Poziom 17  

    Przecież napisałem

    nenpa8lo napisał:
    Linia
    Kod:
    AT91C_BASE_AIC->AIC_EOICR = 0;
    jest obowiązkową ostatnią linią każdego przerwania. Jest to 'sygnał' obsłużenia przerwania.

    Poza tym wartość wpisywana do EOICR nie ma znaczenie, chodzi tylko o wpisanie. Piszesz że procesor sie zawiesza, masz debuger możesz powiedzieć co dokładnie się dzieje (typ wyjątku)?

    0
  • #19 08 Cze 2010 16:54
    janiewiem
    Poziom 11  

    Udało się. Nie wiem jak to zrobiłem. Jestem w tej chwili w takim szoku pozałamaniowym, że ciężko mi się skupić na pisaniu. Ale zostawię tutaj rozwiązanie dla potomnych, krok po kroku. Otóż.

    Najpierw inicjuję w main'ie przerwanie dla przycisku, który ma włączać diodę.

    Code:


    void prvSetupLedInterrupt()
    {
       
       vSemaphoreCreateBinary( xSemaphore  ); // zmienna static xSemaphoreHandle xSemaphore = NULL;

       if( xSemaphore )
       {
          /* We start by 'taking' the semaphore so the ISR can 'give' it when the first interrupt occurs. */
          xSemaphoreTake( xSemaphore , 0 );
          portENTER_CRITICAL();  // nie jestem pewien po co
          {
             /* Przycisk jest na PA20 */
             AT91C_BASE_PIOA->PIO_IER = AT91C_PIO_PA20;

             /* Włączamy przerwanie w AIC. */
             AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_PIOA, 5, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, ( void (*)( void ) ) vLed_ISR_Wrapper );
             AT91C_BASE_AIC->AIC_IECR = 0x1 << AT91C_ID_PIOA;
          }
          portEXIT_CRITICAL();
       }
            /* Tutaj podajemy stworzony semafor do funkcji ISR */
       vPassLedSemaphore( xSemaphore );
    }


    Jak widać podczas konfiguracji przerwania używam vLed_ISR_Wrapper, który jest kluczowym elementem. Podpatrzyłem to w demie Freertosa. Każde przerwanie zaczyna się wrapperem assemblerowym, który kopiuje kontekst, włącza handler przerwania, a handler daje token na semafor.

    Wrapper:
    Code:


    void vLed_ISR_Wrapper( void )
    {
       /* Save the context of the interrupted task. */
       portSAVE_CONTEXT();

       /* Call the handler itself.  This must be a separate function as it uses
       the stack. */
       __asm volatile ("bl vLed_ISR_Handler");

       /* Restore the context of the task that is going to
       execute next. This might not be the same as the originally
       interrupted task.*/
       portRESTORE_CONTEXT();
    }


    Handler:

    Code:


    void vLed_ISR_Handler( void )
    {

    portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
    int status;

            /* Odczytanie PIO_ISR jest konieczne */
       status = AT91C_BASE_PIOA->PIO_ISR;

       xSemaphoreGiveFromISR(xSemafor, &xHigherPriorityTaskWoken);
       if( xHigherPriorityTaskWoken )
       {
          portYIELD_FROM_ISR();
       }

       /* Clear the AIC ready for the next interrupt. */
       AT91C_BASE_AIC->AIC_EOICR = 0;
    }


    No i ostatecznie task wywoływany przez handler:
    Code:

    void ToggleLedTask(void *pvParameters) {

       while(1) {
           xSemaphoreTake(xSemaphore , portMAX_DELAY);

                vParTestToggleLED( 0 );
       }
    }


    WNIOSKI:

    - z tego co widać powyżej, do obsługi przerwania potrzebny jest wrapper, który kopiuje kontekst i uruchamia handler (jeśli nie jest konieczny to proszę sprostować, mnie niestety nie udało się uruchomić tego bez wrappera)
    - wrapper i handler muszą znajdować się w osobnym pliku .c, który MUSI być skompilowany w trybie ARM!

    Jeśli ktoś chciałby się odnieść do tego co napisałem, byłbym wdzięczny.

    0
  • #20 08 Cze 2010 17:32
    nenpa8lo
    Poziom 17  

    Wrapper w przerwaniu jest konieczny jeżeli używasz systemu z wywłaszczeniami. Plik w którym masz przerwanie musi być skompilowany w trybie ARM.

    0
  • #21 08 Cze 2010 17:38
    janiewiem
    Poziom 11  

    Teraz rozumiem, tryb z wywłaszczeniami tzn. pre-emptive?

    0
  • #22 08 Cze 2010 23:44
    michalko12
    Specjalista - Mikrokontrolery

    Wrappery w funkcjach przerwania to normalna sprawa, nie tylko w przypadku RTOSa. To że akurat w przykładzie z FreeRTOSa było to zrobione funkcjami do zachowania i odtworzenia kontekstu to czysty przypadek, po prostu te funkcje nadawały się na wrappery, normalnie to kompilator sam dba o wrappery informując go że dana funkcja jest przerwaniem ISR lub FIQ. Prawdopodobnie przykład z tymi wrapperami to zaszłość po błędzie kompilatora który generował błędne wrappery, nie wiem czy już to jest naprawione w nowszych wersjach.
    Jeszcze jedna uwaga, funkcja vLed_ISR_Wrapper powinna mieć atrybut "naked".

    Dodano po 1 [godziny] 12 [minuty]:

    A może i nie powinna bo te wrappery to nie są do końca wrappery

    0
  • #23 09 Cze 2010 00:04
    nenpa8lo
    Poziom 17  

    W GCC powinny miec 'naked'.

    0