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

[STM32][FreeRTOS][Eclipse] Obsługa semafora binarnego

10 Sie 2010 09:54 3696 10
  • Poziom 23  
    Cześć
    Mam problem z obsługą semafora a mianowicie mam przerwanie IRQ0 i chcę mieć wątek który wykona określony kod.

    Moja implementacja :

    w main:
    Code:

    vSemaphoreCreateBinary(xBinarySemaphore);

    if(xBinarySemaphore!=NULL)
    {

    xTaskCreate( Vtask1, ( signed portCHAR * ) "task1", task1_TASK_STACK_SIZE, NULL, 1, NULL );
    xTaskCreate( Vtask2, ( signed portCHAR * ) "task2", task2_TASK_STACK_SIZE, NULL, 1, NULL );

    xTaskCreate( VtaskINT1, ( signed portCHAR * ) "VtaskINT1", 500, NULL,2, NULL );

    vTaskStartScheduler();
    }


    obsługa przerwania :

    Code:

    void EXTI0_IRQHandler(void)
    {
       portBASE_TYPE xHigherPriorytyTaskWoken=pdFALSE;

       xSemaphoreGiveFromISR(xBinarySemaphore,&xHigherPriorytyTaskWoken);
       EXTI_ClearFlag(EXTI_Line0);
       portEND_SWITCHING_ISR(xHigherPriorytyTaskWoken);
    }


    static void VtaskINT1( void *pvParameters )
    {
       xSemaphoreTake(xBinarySemaphore,0);

    while(1)
    {
          xSemaphoreTake(xBinarySemaphore,1000);
                    // Tutaj mój kod do wykonania

    }

    }


    I pojawia się problem ponieważ funkcja VtaskINT1 jest w stanie ready i cały czas się wykonuje mimo że według mnie powinna być w stanie blocked aż do momentu wystąpienia przerwania i ustawienia semafora. Może coś robię źle gdyż jestem początkujący w tym temacie, proszę o pomoc kogoś bardziej doświadczonego w temacie FreeRTOS-a.

    Pozdrawiam
  • Computer Controls
  • Pomocny post
    Specjalista - Mikrokontrolery
    Jeśli ma być tak jak ty to opisujesz to wywołanie powinno tak wyglądać

    Code:


    static void VtaskINT1( void *pvParameters )
    {
       xSemaphoreTake(xBinarySemaphore,0);

    while(1)
    {
          xSemaphoreTake(xBinarySemaphore,portMAX_DELAY);
                    // Tutaj twój kod do wykonania

    }

    }


    lub

    Code:
    static void VtaskINT1( void *pvParameters )
    
    {
       xSemaphoreTake(xBinarySemaphore,0);

    while(1)
    {
          if(xSemaphoreTake(xBinarySemaphore,1000))
          {
                    // Tutaj twój kod do wykonania
          }
    }

    }
  • Computer Controls
  • Poziom 23  
    Działa :) Dzięki.

    Jeszcze małe pytanko w wersji 1 : xTicksToWait jak ustawie na 0 to kod będzie się wykonywał zawsze niezależnie od semafora (wątek nie będzie blokowany) jak ustawie na 100 to co 100 Ticków a jak na portMAX_DELAY to tylko jak semafor będzie ustawiony ?? a w wersji drugiej xTicksToWait nie ma znaczenia bo kod wykona się tylko jak semafor będzie ustawiony czyli jak xSemaphoreTake zwróci TRUE ?? Bo nie wiem czy dobrze to rozumiem.
  • Specjalista - Mikrokontrolery
    1. portMAX_DELAY oznacza czekaj nieskończenie aż semafor będzie ustawiony
    2. Dokładnie tak jak piszesz. Task będzie blokowany na 1000 tików, jeśli w trakcie zostanie ustawiony semafor to task zostanie odblokowane wcześniej i xSemaphoreTake() zwróci TRUE w przeciwnym wypadku dopiero po upłynięciu danego czasu z FALSE jako rezultat funkcji xSemaphoreTake().
  • Poziom 21  
    [ARM7][FreeRTOS][Keil]
    Widzę, że koledzy poruszali podobny temat o który chcę zapytać:
    Otóż po utworzeniu semafora binarnego:
    Code:
    xSemaphoreHandle  hBinarySemaphore = NULL;       
    
    int main (void)
    {
       hw_SystemSetup();

       vSemaphoreCreateBinary(hBinarySemaphore);   
       if(NULL!=hBinarySemaphore)                
       {
          xTaskCreate(vTask1Continous, "continous", 400, NULL, IDLE_HOOK_priority+1, NULL);
          xTaskCreate(vTask2ServiceInterrupt, "interrupt", 400, "INT0've pressed!", IDLE_HOOK_priority+2, NULL);
       
          vTaskStartScheduler();
       }      
       while(1);
       return 0;
    }


    gdzie:
    Code:
    void vTask2ServiceInterrupt(void *pvParameters)
    
    {
        static unsigned char ucCounterINT0=0;    
       for(;;)
       {
           xSemaphoreTake( hBinarySemaphore, portMAX_DELAY );
                                                     //kod taska jako obsługa przerwania
       }
    }

    void vTask1Continous(void *pvParameters)   
    {
       static unsigned char ucCounter=0;
       for(;;)
       {
             //kod taska
       }
    }

    po starcie vTaskStartScheduler();
    program wchodzi do vTask2ServiceInterrupt (to jest ok bo najwyższy priorytet) ale wykonuje od razu kod tego tasku jeden raz - mimo że nie było przerwania a wiec semafor musi być pusty.
    1.Skąd przy starcie systemu semafor jest "pełny"?

    obsługa przerwania
    Code:
    __irq void EINT1IRQHandler(void)
    
    {
       static portBASE_TYPE xHigherPriorityTaskWoken;
       xHigherPriorityTaskWoken = pdFALSE;
       EXTINT |=EINT1_flag;    //clear flag          

       xSemaphoreGiveFromISR( hBinarySemaphore, &xHigherPriorityTaskWoken );
       VICVectAddr=0xFF;                
          if(xHigherPriorityTaskWoken == pdTRUE)
          {
             //portSWITCH_CONTEXT();
             portEXIT_SWITCHING_ISR( xHigherPriorityTaskWoken );
          }
     
       
    }
  • Poziom 21  
    Nie wiem w czym był/jest problem ale zrobienie czegoś takiego
    Code:

       xSemaphoreTake( hBinarySemaphore, 0 );   
       for(;;)
       {
           xSemaphoreTake( hBinarySemaphore, portMAX_DELAY );
          

    czyli zignorowanie pierwszego odczytu po utworzeniu semafora skutkuje tym, że wszystko dalej jest ok.
    ____________
    Jako dokumentacji do freertosa używam "A Practical Guide" przy obsłudze przerwań w sytuacji gdy uruchomiony task w przerwaniu ma wyższy priorytet od przerywanego jest napisane aby użyć "contex_switch"

    Cytat:
    NOTE: The actual macro to use to force a context switch from an
    ISR is dependent on the port. This is the correct macro for the
    Open Watcom DOS port. Other ports may require different syntax.


    Code:
    __irq void EINT1IRQHandler(void)
    
    {
       static portBASE_TYPE xHigherPriorityTaskWoken;
       xHigherPriorityTaskWoken = pdFALSE;
       EXTINT |=EINT1_flag;       

       xSemaphoreGiveFromISR( hBinarySemaphore, &xHigherPriorityTaskWoken );
       VICVectAddr=0xFF;               
          if(xHigherPriorityTaskWoken == pdTRUE)
          {
             //portSWITCH_CONTEXT();
             //portEXIT_SWITCHING_ISR( xHigherPriorityTaskWoken );
          }
     
       
    }

    i tu pojawia się problem: Bez przełączania kontekstu jest ok.Gdy odkomentowany jest jedno z makr przełączających kontekst - program się wiesza.
    Ktoś może podpowiedzieć jak właściwie powinno to wyglądać
  • Poziom 31  
    A jak wyglada implementacja tego macra ? Czy to jest wlasciwy port dla STM32 ?
  • Poziom 21  
    Mój port to a LPC2xxx.
    W pliku portmacro.h jest napisane:

    Code:
    /* If a switch is required then we just need to call */   
    
    /* vTaskSwitchContext() as the context has already been */
    /* saved. */

    #define portEXIT_SWITCHING_ISR(SwitchRequired)             \
    {                                              \
    extern void vTaskSwitchContext(void);                   \
                                                  \
          if(SwitchRequired)                            \
          {                                        \
             vTaskSwitchContext();                      \
          }                                        \
    }                                              \

    extern void vPortYield( void );
    #define portYIELD() vPortYield()

    a więc użycie portEXIT_SWITCHING_ISR() jest chyba tutaj poprawne.

    Zauważyłem w pliku serial.c w jednym z dem, że do obsługi przerwań stosuje się asemblerową wstawkę w serialISR.s w celu wrappera

    Code:
       ;/* Interrupt entry must always be in ARM mode. */
    
       ARM
       AREA   |.text|, CODE, READONLY


    vUART_ISREntry

       PRESERVE8

       ; Save the context of the interrupted task.
       portSAVE_CONTEXT         

       ; Call the C handler function - defined within serial.c.
       LDR R0, =vUART_ISRHandler
       MOV LR, PC            
       BX R0

       ; Finish off by restoring the context of the task that has been chosen to
       ; run next - which might be a different task to that which was originally
       ; interrupted.
       portRESTORE_CONTEXT

       END


    Ja nic takiego nie robiłem do obsługi mojego przerwania stąd być może to jest powodem- w symulatorze widzę, że po otrzymaniu przerwania program kończy się zawieszeniem:
    266: Undef_Handler B Undef_Handler
    0x00000040 EAFFFFFE B 0x00000040.


    Czy powyższy kod asemblerowy jest uniwersalny i można go stosować do tworzenia innych wrapperów przerwań?
  • Poziom 31  
    Ok LPC i STM to ARM-y wiec nie powinno byc problemu.

    Jak widzisz w przykladzie najpierw na samym poczatku przerwania powinien byc zachowany kontekst zadania a wiec powinno byc uzyte makro portSAVE_CONTEXT a na koncu portRESTORE_CONTEXT i wowczas faktycznie makro portEXIT_SWITCHING_ISR nie musi tego robic a tak program idzie w maliny.
  • Poziom 21  
    Użycie wrappera rozwiązało problem - OS ładnie przełącza taski w reakcji na przerwania - przynajmniej na symulatorze. Szkoda trochę, że w Practical Guide nie opisali choćby ogólnej metody tworzenia przerwań pod tym OS.
  • Poziom 31  
    ginar napisał:
    Szkoda trochę, że w Practical Guide nie opisali choćby ogólnej metody tworzenia przerwań pod tym OS.


    Bo nie ma czegos takiego. Jesli twoje przerwanie moze zmienic kontekst (czytaj przelaczyc zadania) to sam musisz o to zadbac. Nie kazde przerwanie musi zmienic kontekst a poniewaz jego zachowanie i odtwarzanie jest czasochlonne wiec nie nalezy tego robic w kazdym a tylko tam gdzie jest to niezbedne.