Semaphore from SPI Interrupt not working!

biker126 wrote on Monday, October 08, 2007:

hello again guys!

I’m sorry to make a new post again but this is the first time I’m using freeRTOS semaphores and I can’t get them work :frowning:

I want to synch my SPI Task with the SPI ISR, so that the Task writes a byte, then waits for the semaphore. When the write is done the ISR fires and should release the semaphore so the SPI Task can go on (read and do anothe write etc).

here’s my code:


only the relevant part of the code

for (i = 0; i < ui8_dataLength; i++)
// take semaphore once so that its taken
xSemaphoreTake(SPISemaphore, (portTickType)portMAX_DELAY);               

// write the data
S0SPDR = *pui8_readPointer++;   

// take semaphore again, its empty already so task should block here!
xSemaphoreTake(SPISemaphore, (portTickType)portMAX_DELAY);
// when the ISR has fired the write was done and the semaphore (should) be
// give back. thus, we can read the received data now
*pui8_writePointer++ = S0SPDR;

with the for-loop I send several bytes (some sort of "message"). the messages to send are received by queue from another task (that should be working).


void SPI_ISR( void )
    uint32 xTaskWoken = pdFALSE;   
    ui8_readDummyByte = S0SPSR;
    // Clear the interrupt
    S0SPINT = 0x01;   
// problem lies here: xTaskWoken wont become 0x1 !!
    xTaskWoken = xSemaphoreGiveFromISR(SPISemaphore, xTaskWoken);
    // Acknowledge interrupt in the VIC
    VICVectAddr = 0;
    // Switch to the SPITask
    portEXIT_SWITCHING_ISR( xTaskWoken );

within the spi.h the ISR is defined as __attribute__((naked)) (the ISR itself is working - or at least its executed)
the semaphore I create in with:

I already checked: PCLKSEL0, PINSEL0/1, PCONP, VicAddr10, VicPrio10, VicEnable, S0SPICR, SP0SPCCR and they all should be ok (as the ISR is running).

the only big error I’ve found is that xTaskWoken won’t get true…
System is still LPC2378/68 with GCC/eclipse

I’m at bit clueless now so I would appreciate it if someone has some ideas and tips!

adarkar9 wrote on Monday, October 08, 2007:

You need to move the first xSemaphoreTake() call outside of your loop.  As written, the second time through the loop you are taking the semaphore before writing the second byte.  The ISR will not run after the first time so you will be stuck waiting for the semaphore forever.

biker126 wrote on Monday, October 08, 2007:

are you sure about that?

I already thought it might got something to do with getting stuck at a "take". but if I understand it right you need to call xSemaphoreTake() TWICE in order to block ONCE? since the first time you attempt to take it the semaphore is free, thus you can take it and continue (no blocking) and then the second time you attmpt to take the semaphore its empty and thus the program blocks.

so what I was thinking is that each time before a SPI write I take the semaphore to empty it, and each time after writing I "take" it again to block.

only thing that come to my mind is that RIGHT AFTER the SPI write the ISR fires (before the task asks for the semaphore a 2nd time) and gives the semaphore back. then the task asks a 2nd time for it and DOESN’T block since its been released already. so what happens in this situation would be that the task blocks at the FIRST semaphoreTake (in the 2nd run of the loop) and it’ll never be released as there was no SPI write yet (and thus no ISR firing).

however, I would guess that its kinda unlickly for the ISR to fire inbetween "S0SPDR = *pui8_readPointer++;" and "xSemaphoreTake(…)". and if it does, I dunno really how to solve that issue :-/

biker126 wrote on Monday, October 08, 2007:


I think are are indeed right :slight_smile:

I forgot that a xSemaphoreTake() if it blocks still takes the Semaphore as soon as its release and thus the Semaphore is empty again ^^

so I simply need one xSemaphoreTake() call before the first SPI write ever (to initial empty the semaphore. in uC/OS-II you can create empty semaphores… :-p) and then after every single SPI write the semaphore gets emptied again  so it always blocks after a write.

that still doesn’t solve the problem tho, as the semaphore still ain’t given back by the ISR!! :-/

saiberion wrote on Tuesday, October 09, 2007:

In general, if a semphore isn’t active a task will block on the semaphore.
But after successful creation you should take the semaphore one time to make sure it’s empty.

if (xSem != NULL)
___xSemaphoreTake(xSem, 10);
The semaphore is now existing and empty

Task Code:
___xSemaphoreTake(xSem, portMAX_DELAY);
___//work with received data
After data is processed the task loop starts again and will block in the semaphore now

ISR Code:
// receive Data and store it
xWoken = xSemaphoreGiveFromISR(xSem, xWoken);
This will wake the Task blocking on semaphore.

For your example it might work to remove the fisr xSemaphoreTake and put it after task creation.

biker126 wrote on Tuesday, October 09, 2007:

thats what I did already. my code structure looks exactly as your suggestion. the problem is tho, that xSemaphoreGiveFromISR() fails to release the semaphore - or at least it fails to wake the task (xWoken is always zero).

saiberion wrote on Tuesday, October 09, 2007:

As far as I know the xWoken mechanism is used if you do multiple calls of xSemaphoreGiveFromISR within the same interrupt. As the documentation says the first call of this function always returns 0 (pdFALSE).
If you use the interrupt to handle a 2nd semaphore or queue this call should return 1 (pdTRUE) and at the end of the Interrupt the determination of the highest priority task is forced with portEXIT_SWITCHING_ISR(xWoken).

So if you application still doesn’t send the data maybe you are blocked somewhere else.

biker126 wrote on Tuesday, October 09, 2007:

I have replaced the semaphore by a MessageQueue with room for 1 message now (this should be the same as a semaphore…).

the ISR is posting messages into the queue now and the task is w8ing for them after writing 1 byte. this all works fine, however there’s one more odd thing:

the time from when the ISR is executed (I set a Pin to detect that with a logic analyzer) to until the xQueueReceive() stops blocking (right after xQueueReceive i set another Pin) is 128us!!!

if I under stand freeRTOS correct, then the ISR should force a reschedule (xQueueSendToBackFromISR() gives a TaskWoken back which I pass to EXIT_SWITCHING_ISR(TaskWoken)) and then my task (which is w8ing for a message to be posted) should get scheduled immediatly (it is the highest task that got something to do. all other tasks like LED/LCD one have lower priority).

but I dont think that the scheduling time is 128us (that would be aweful for a "RTOS" :-p).

so I’m wondering what could be the reason for that huge delay between execution of ISR and the Task continuening execution…