Problem about xSemaphoreTake()

brilliantdt wrote on Thursday, April 30, 2015:

Hi,
After I use xSemaphoreGiveFromISR() in an interrupt handler, I use xSemaphoreTake() with portMAX_DELAY in a task. However, the function xSemaphoreTake() can not take the semaphore. What is the problem?

Regaeds

rtel wrote on Thursday, April 30, 2015:

[I deleted your duplicate posts]

What is the problem?

Unfortunately you don’t give enough information to even guess. Please at least post the code that creates the semaphore, gives the semaphore and takes the semaphore, so we can see if the functions are being used correctly.

Regards.

brilliantdt wrote on Friday, May 01, 2015:

void ISR_Bp1(void)
{
static portBASE_TYPE xHigherPriorityTaskWoken1;
xHigherPriorityTaskWoken1 = pdFALSE;

portBASE_TYPE isr1 = xSemaphoreGiveFromISR( xBinarySemaphore1, &xHigherPriorityTaskWoken1 );
if(isr1==pdTRUE)
{
USART_Write(AT91C_BASE_US0, 0xe1, 0);
}

LED0_ON;
}

void ISR_Bp2(void)
{
static portBASE_TYPE xHigherPriorityTaskWoken2;
xHigherPriorityTaskWoken2 = pdFALSE;

portBASE_TYPE isr2 = xSemaphoreGiveFromISR( xBinarySemaphore2, &xHigherPriorityTaskWoken2 );
if(isr2==pdTRUE)
{
USART_Write(AT91C_BASE_US0, 0xf1, 0);
}

LED0_OFF;
}

static void vLED0Task( void *pvParameters )
{
while(1)
{
if( xSemaphoreTake(xBinarySemaphore1,portMAX_DELAY) == pdTRUE)
{
USART_Write(AT91C_BASE_US0, 0xee, 0);
}
}
}

static void vLED1Task( void *pvParameters )
{
while(1)
{
if( xSemaphoreTake(xBinarySemaphore2,portMAX_DELAY) == pdTRUE)
{
USART_Write(AT91C_BASE_US0, 0xff, 0);
}
}
}

int main(void)
{

ConfigureUsart0();
      
PIO_Configure(pins, PIO_LISTSIZE(pins));
USART_Write(AT91C_BASE_US0, 0x11, 0);
    
ConfigureButtons();
ConfigureLeds();

vSemaphoreCreateBinary( xBinarySemaphore1 );			
vSemaphoreCreateBinary( xBinarySemaphore2 );


portBASE_TYPE flagLED0 = xTaskCreate( vLED0Task, ( signed portCHAR * )"LED0", configMINIMAL_STACK_SIZE, NULL, vLED0Task_PRIORITY, NULL );

if(flagLED0 == pdTRUE)
{ 
  USART_Write(AT91C_BASE_US0, 0x22, 0);
}
USART_Write(AT91C_BASE_US0, 0x12, 0);
portBASE_TYPE flagLED1 = xTaskCreate( vLED1Task, ( signed portCHAR * )"LED1", configMINIMAL_STACK_SIZE, NULL, vLED1Task_PRIORITY, NULL );
    
if(flagLED1 == pdTRUE)
{ 
  USART_Write(AT91C_BASE_US0, 0x23, 0);
}
USART_Write(AT91C_BASE_US0, 0x13, 0);

vTaskStartScheduler();
// Main loop
while (1) {
  
}

}

As you can see, after I create binary xBinarySemaphore1 and xBinarySemaphore2 in the main function, the two tasks can respectively take these two semaphores. However, after I give semaphores using xSemaphoreGiveFromISR() in the two ISR functions(I am sure that the interrupt handlers respond because LEDO is ON or OFF ,and different byte is transmitted with USART), the task vLED0Task and vLED1Task can not take the semaphores. What is wrong with my codes? Thanks a lot!

rtel wrote on Friday, May 01, 2015:

Nothing obviously wrong with the code. However, you are not requesting in a context switch in the interrupt (you do not pass the xHigherPriorityTaskWoken variable to call to portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() at the end of the interrupt). Could it be you are using the co-operative scheduler? What is configUSE_PREEMPTION set to in FreeRTOSConfig.h?

Regards.

brilliantdt wrote on Friday, May 01, 2015:

I only want to see if the task can take the semaphore, so I do not request in a context switch in the interrupt. In my FreeRTOSConfig.h, configUSE_PREEMPTION is set to 1(I copy FreeRTOSConfig.h from the demo of AT91SAM9XE to my project).

rtel wrote on Friday, May 01, 2015:

…and you are 100% sure that the tick interrupt is running? Can you
set a break point in the tick interrupt and/or see xTickCount
incrementing in the FreeRTOS/Source/tasks.c file?

Regards.

brilliantdt wrote on Friday, May 01, 2015:

I think the xTickCount is incrementing because 0x00 is always transmitted by USART to my computer.The code is in the following.

portBASE_TYPE xTaskIncrementTick( void )
{
tskTCB * pxTCB;
portTickType xItemValue;
portBASE_TYPE xSwitchRequired = pdFALSE;

/* Called by the portable layer each time a tick interrupt occurs.
Increments the tick then checks to see if the new tick value will cause any
tasks to be unblocked. */
traceTASK_INCREMENT_TICK( xTickCount );
if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE )
{
	/* Increment the RTOS tick, switching the delayed and overflowed
	delayed lists if it wraps to 0. */
	++xTickCount;
            USART_Write(AT91C_BASE_US0, 0x00, 0);
    ........................(omited)

}

brilliantdt wrote on Friday, May 01, 2015:

I think the xTickCount is incrementing beacause 0x00 is transmitted by USART to my computer all the time. The code is in the following:

portBASE_TYPE xTaskIncrementTick( void )
{
tskTCB * pxTCB;
portTickType xItemValue;
portBASE_TYPE xSwitchRequired = pdFALSE;

/* Called by the portable layer each time a tick interrupt occurs.
Increments the tick then checks to see if the new tick value will cause any
tasks to be unblocked. */
traceTASK_INCREMENT_TICK( xTickCount );
if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE )
{
	/* Increment the RTOS tick, switching the delayed and overflowed
	delayed lists if it wraps to 0. */
	++xTickCount;
            USART_Write(AT91C_BASE_US0, 0x00, 0);
    (omitted......)

}

davedoors wrote on Friday, May 01, 2015:

This looks very dangerous. You are using USART_Write from multiple tasks so it must have some mutual exclusion mechanism built in, but then you using it in an interrupt to? How does it work? Does it poll the uart for the Tx end? What is your tick frequency? Your processor is probably spending most of its time in the tick interrupt writing to the uart.

brilliantdt wrote on Friday, May 01, 2015:

Even though I do not use USART_Write in the tick interrupt writing to the uart, xSemaphoreTake() still does not work. It can only run once because I use vSemaphoreCreateBinary to create semaphore. Do you know what is wrong with my code?

heinbali01 wrote on Friday, May 01, 2015:

Hi Zhang Yi,

Just wondering if this will make a change:

void ISR_Bp1(void)
{
/* No need to make variable this static. */
portBASE_TYPE xHigherPriorityTaskWoken1 = pdFALSE;
    ...
    portEND_SWITCHING_ISR( xHigherPriorityTaskWoken1 );
}

void ISR_Bp2(void)
{
portBASE_TYPE xHigherPriorityTaskWoken2 = pdFALSE;
    ...
    portEND_SWITCHING_ISR( xHigherPriorityTaskWoken2 );
}

In other words, adding the portEND_SWITCHING_ISR() calls.

Like Dave, I also wonder if you can access the USART from within any ISR. Just leave it out and see if it makes a change.

Regards.

brilliantdt wrote on Monday, May 04, 2015:

Hi,
Your suggestion is effective! Would you please give me some explaination why it is effective.

brilliantdt wrote on Monday, May 04, 2015:

Hi,
Your suggestion is effective! Would you please give me some explaination why it is effective.

heinbali01 wrote on Monday, May 04, 2015:

Hi Zhang Yi,

Good to hear.

Richard (from Real Time Engineers) already suggested to use portYIELD_FROM_ISR() or portEND_SWITCHING_ISR(), see here above.

Calling xSemaphoreGiveFromISR() does not cause a task switch. It does set the semaphore, but your task will keep on waiting eternally because of the time-out portMAX_DELAY:

if( xSemaphoreTake(xBinarySemaphore2, portMAX_DELAY) == pdTRUE)
{
}

Note that the way to ‘initiate’ a task-switch may differ a bit from port to port, in your case you use portEND_SWITCHING_ISR().

Why does this happen in two steps?

First you give to a semaphore and maybe you also write to a queue. The ISR might cause several tasks to get woken-up:

    void some_isr_routine()
    {
    portBASE_TYPE xTaskWoken = pdFALSE;

        /* These are three call that may put tasks in a runnable state.
        The variable xTaskWoken will have an OR'ed value,
        it becomes non-zero if at least one task will be come runnable. */
        xQueueSendFromISR( xQueue, &vItem, &xTaskWoken );
        xSemaphoreGiveFromISR( xSemaphore, &xTaskWoken );
        vTaskNotifyGiveFromISR( pxTask, &xTaskWoken );

        /* Now inform the OS to check if a task-switch is necessary.
        */
        portEND_SWITCHING_ISR( TaskWoken );
    }

In the above example, the ISR may wake-up several tasks. The task with the highest priority will start running first. This will happen immediately, after the “iret” instruction of the ISR.

I hope the picture is clear now?

Regard, Hein

brilliantdt wrote on Friday, May 08, 2015:

In my previous view, I think if only one task corresponds to one semaphore, it is not necessary to use portEND_SWITCHING_ISR(). However, even I set the semaphore corresponding to the task with the highest priority, it is still not effective.

I think the above situation is a bit different from what you explain. Is it correct? Thanks for your explaination.

Regards

rtel wrote on Friday, May 08, 2015:

To add to what Hein has already said, the portEND_SWITCHING_ISR()/portYIELD_FROM_ISR() mechanism allows better control over when a context switch is to be performed. For example, if you receive an interrupt each time a character is received on a UART, and the interrupt handler is buffering characters, then you don’t necessarily want to perform a context switch on each character, but only when a complete message is received, so you would call portEND_SWITCHING_ISR()/portYIELD_FROM_ISR() only when the buffer contained a complete message.

…that said, if you omit the call to portEND_SWITCHING_ISR()/portYIELD_FROM_ISR(), then the task should run on the next tick interrupt anyway, so although in your case it would delay when the task executed, it would not prevent it from executing - so there is something else wrong. Somewhere a task switch is not being performed when it should.

Can you set up a test where you have a break point that is hit in vTaskSwitchContext() after the interrupt that gives the semaphore has executed so you can step through the code to see why the task is not selected.

Regards.