3
votes

I am trying to turn on and off a led using FreeRTOS on STM32 F401RE MCU in IAR Workbench IDE.

The led belongs the STM32 nucleo board. There are two task one turn on the Led, the other task turn off the same Led.

Here is the code:

The main Code:

SemaphoreHandle_t xMutex;
int main()
{  

  if ( xMutex == NULL )  
  {
      xMutex = xSemaphoreCreateMutex();  

      if ( ( xMutex ) != NULL )
        xSemaphoreGive( ( xMutex ) ); 

   }

   xTaskCreate(LedOn, "Led On", 100, NULL,  1, NULL);
   xTaskCreate(LedOff, "Led Off", 100, NULL, 1, NULL);
   vTaskStartScheduler();
   while(1){}

}

The tasks:

void LedOn(void *argument)
{
   for(;;)
   {  
      xSemaphoreTake( xMutex, ( TickType_t )5000 ) ;     
      HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
      vTaskDelay(5000); 
      xSemaphoreGive(xMutex);
    }
}

void LedOff(void *argument)
{
   for(;;)
   {  
      xSemaphoreTake( xMutex, ( TickType_t )5000 ) ;     
      HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);
      vTaskDelay(5000); 
      xSemaphoreGive(xMutex);
    }
}

My intension here is:

Led on task is responsible to turn on Led for 5s

Led off task is responsible to turn of Led for 5s

So this will continue till power off

My problem here is:

In the initial case, the Led stays 5s on after that led stays 5s off it works for only two context switching after two switching the led remains on.

When i debug after two switch the break point doesnt hit the tasks

After a little bit try I guess I found the answer:

Each task should have its delay time so we need to add a delay time in order to task proceed its operation, but i thougt i was add the delay time between xTakeSemaphore and xGiveSemaphore methods, are mutexes delay time which states how the resource should be locked not task delay time.

The Solution is:

void LedOn(void *argument)
{
   for(;;)
   {  
       if(xSemaphoreTake(xMutex, portMAX_DELAY)== pdTRUE)
       {
          HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET); 
          vTaskDelay(pdMS_TO_TICKS(5000));
          xSemaphoreGive(xMutex);
          vTaskDelay(pdMS_TO_TICKS(5000));
       }    
    }
}

void LedOff(void *argument)
{
   for(;;)
   {
       if( xSemaphoreTake( xMutex, portMAX_DELAY)== pdTRUE)
       {
          HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET); 
          vTaskDelay(pdMS_TO_TICKS(5000));
          xSemaphoreGive(xMutex);  
          vTaskDelay(pdMS_TO_TICKS(5000));
       }  
   }

}
4

4 Answers

2
votes

Consider that "task A" is LedOn and "task B" is LedOff. Or, the reverse as it doesn't matter for the problem.

Assume task B has acquired the mutex.

Your problem is that task A's xSemaphoreTake is [probably] timing out, without acquiring the mutex.

You should check the return code.

The reason is that it has a timeout value of 5000 ticks. But, task B does a vTaskDelay(5000). And, while it is doing this, it has the mutex locked

So, most probably, task A's xSemaphoreTake will timeout before task B releases the mutex.

And, then, you flip the LED value, and do a delay. But, then you're doing an xSemaphoreGive on a mutex that task A does not have locked.

In other words, you have a race condition.

Either set an infinite timeout in the take calls or at least set a larger value than the value you give to the delay function.

Try a take value of (e.g.) 10000

2
votes

just wait forever for the mutex

xSemaphoreTake( xMutex, portMAX_DELAY);

in both tasks.

1
votes

The usual premise of a priority scheduled RTOS is that the highest priority task/thread that is ready to run pre-empts the one that is running with a lower priority.

Both LedOff, and LedOn tasks are created with the same priority, and thus a context switch doesn't occur immediately when the semaphore is signalled.

A context switch potentially happens when it goes round the loop and attempts to take the semaphore again. Two tasks are now contending on it.

Who wins is essentially an implementation detail of FreeRTOS - and particularly whether semaphore-take operations on the semaphore happen in strict FIFO order or not - ISTR that VxWorks (which FreeRTOS seems heavily modelled on) would optionally do this.

The alternative approach which is seen with POSIX-threads (which FreeRTOS also supports) is to wake up the waiting thread, and the semaphore to be taken by the one gets scheduled first - which is certain to the be one already running.

As a more general point, you have hugely over-complicated a finite state machine with two states. A highly reliable way to achieve it would be:

void LedFlasher(void *argument)
{
   for(;;)
   {      
      HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
      vTaskDelay(5000); 
      HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);
      vTaskDelay(5000); 
   }
}
-1
votes

A bit late .. ;) but I think there is a bug in your program. The initial release of the mutex (xSemaphoreGive in main) breaks it. A mutex is created ready to be taken i.e. in released state.