Using semaphore problem

grzegorzw wrote on Wednesday, May 13, 2009:

I init semaphore before scheduler starts:

      vSemaphoreCreateBinary( xSPI0Semaphore );

in running task I take semaphore:

      xSemaphoreTake( xSPI0Semaphore, portMAX_DELAY );

WHAT IS WRONG:
Semaphore is taken in first call, without giving anywhere before. So first taking procedure is not blocking.

TRY:
I check semaphore before taking:

      q = uxQueueMessagesWaiting( xSPI0Semaphore );

and q value is "1", but should be "0".

Please help

anonymous wrote on Wednesday, May 13, 2009:

This is the documented behaviour: binary semaphores are initially released. You might want to do a xSemaphoreTake(xSPIOSemaphore, 0) just after creating the semaphore if you want it to be taken before the scheduler starts (the 0 doesn’t matter here, since you *will* get the semaphore immediately).

grzegorzw wrote on Thursday, May 14, 2009:

Thank you for your help. I have still a problem with using semaphore.

I wrote a task:

void vSPI0Task( void *pvParameters )
{
    struct tspiIO spiIO;
    portBASE_TYPE q;

    xSemaphoreTake( xSPI0Semaphore, 0 );

    for( ;; )
    {
        xQueueReceive( xSPIInputQueue, &spiIO, portMAX_DELAY );

        SPI_AsyncWriteFirst( AT91C_BASE_SPI0, spiIO.DataToSend , spiIO.DataToSendLen, spiIO.WriteDummy );
        SPI_AsyncReadNext( AT91C_BASE_SPI0, spiIO.ReceivedData, spiIO.ReceivedDataLen, spiIO.ReadDummy );

        AT91C_BASE_SPI0->SPI_IER = AT91C_SPI_RXBUFF;
        PDC_EnableWriteAndRead((AT91S_PDC*)((__u32)(AT91C_BASE_SPI0) + 0x100));

        xSemaphoreTake( xSPI0Semaphore, portMAX_DELAY );
        xQueueSendToBack( xSPIOutputQueue, &spiIO, portMAX_DELAY );
    }
}

And a interrupt subroutine (I am using YAGARTO; interrupts without nesting):

void __attribute__ ((interrupt ("IRQ")))SPI0_irq_handler( void )
{
    volatile __u32 spiSr;
    portBASE_TYPE xHigherPriorityTaskWoken;

    spiSr = AT91C_BASE_SPI0->SPI_SR;
    xHigherPriorityTaskWoken = pdFALSE;
    if ( spiSr & AT91C_SPI_RXBUFF )
    {
        AT91C_BASE_SPI0->SPI_IDR = AT91C_SPI_RXBUFF;

        xSemaphoreGiveFromISR( xSPI0Semaphore, &xHigherPriorityTaskWoken );
    }

    AT91C_BASE_AIC->AIC_ISR;
    AT91C_BASE_AIC->AIC_EOICR = 0;

    if( xHigherPriorityTaskWoken == pdTRUE )
    {
           portYIELD_FROM_ISR();
    }
}
   
The problem lies in the fact that when the task is blocked in “taking semaphore state”, interrupt can’t give it and the task is “blocked permanently”. In debug mode (step run), when the interrupt comes before “taking semaphore state” and the semaphore is given, program works.

grzegorzw wrote on Thursday, May 14, 2009:

I found a solution:

void vSPI0_Wrapper() __attribute__((naked));
void vSPI0_Handler();

void vSPI0_Wrapper( void )
{
    portSAVE_CONTEXT();
    vSPI0_Handler();
    portRESTORE_CONTEXT();

}

void vSPI0_Handler( void )
{
    volatile __u32 spiSr;
    portBASE_TYPE xHigherPriorityTaskWoken;

    spiSr = AT91C_BASE_SPI0->SPI_SR;
    xHigherPriorityTaskWoken = pdFALSE;
    if ( spiSr & AT91C_SPI_RXBUFF )
    {
        AT91C_BASE_SPI0->SPI_IDR = AT91C_SPI_RXBUFF;
        xSemaphoreGiveFromISR( xSPI0Semaphore, &xHigherPriorityTaskWoken );
    }

    AT91C_BASE_AIC->AIC_ISR;
    AT91C_BASE_AIC->AIC_EOICR = 0;

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

davedoors wrote on Thursday, May 14, 2009:

You need to write your interrupt service routine as detailed on the documentation page and as detailed by the provided examples.  See the "interrupt service routines" section of http://www.freertos.org/portlpc2106.html for a start.