Problem with blocking a task

jorick23 wrote on Monday, April 23, 2007:

I have a project I’m developing that currently has only one task, a command processor that reads commands from the serial port, acts on them, and sends a response back out the serial port.

I’ve been having problems with transmitting the response back.  It appeared that the transmit FIFO was overflowing and I was losing characters, even though I was blocking when the transmit FIFO became full (I used the STR912F demo for IAR as the template).  Debugging showed that the task would correctly block when taking a semaphore when the FIFO filled and then wait for the semaphore to be given back by the UART ISR when the FIFO became free.  But when running full bore, the FIFO overflowed as if the semaphore weren’t there.

I then had an idea that maybe the task was blocking on the semaphore and then immediately awakening again because it was the only task in the system and the scheduler had to wake somebody after the lone task went to sleep.  This occurred to me once before with the uCOS RTOS, and I was wondering if it was happening in FreeRTOS as well.

Here’s the code in the Send Character function I use to block on, which doesn’t work:

___if (UART_GetFlagStatus (mkapsUARTBase [ziUART], UART_FLAG_TxFIFOFull)) {
______xSemaphoreTake (mapsTxSemaphore [ziUART], portMAX_DELAY);

But when I make it a loop to force it to go back to sleep if the FIFO is still full, it works very well:

   while (UART_GetFlagStatus (mkapsUARTBase [ziUART], UART_FLAG_TxFIFOFull)) {
      xSemaphoreTake (mapsTxSemaphore [ziUART], portMAX_DELAY);

Is this what could be happening?  Why doesn’t the Idle Task run while waiting for the FIFO to become free?

nobody wrote on Monday, April 23, 2007:

The idle task will run if there are not other tasks, no doubt about that.  Maybe you have a race condition on the checking of the FIFO.  I’m not sure why you are checking the fifo before calling the semaphore function.  You should just be able to check the semaphore on the assumption that it is given by the interrupt service routine.  This way the race condition will be avoided.  Maybe I am missing the point?

jorick23 wrote on Tuesday, April 24, 2007:

The semaphore is there to let me know when the FIFO becomes empty (actually, the number of bytes left in the FIFO goes below a certain threshold).

Here’s the function to send a character in its entirety:

void Serial_SendChar (int ziUART, const char zcChar) {

// If the transmit FIFO is full, the task blocks to wait for space to become
// free.  It’s okay to block in a critical section since interrupts will be
// enabled for other tasks once a switch is forced.

___while (UART_GetFlagStatus (mkapsUARTBase [ziUART], UART_FLAG_TxFIFOFull)) {
______xSemaphoreTake (mapsTxSemaphore [ziUART], portMAX_DELAY);

// The character is transmitted.

___UART_SendData (mkapsUARTBase [ziUART], zcChar);

I make sure that the FIFO has space available before sending a character to it.  Once the FIFO is full, I take the semaphore which has already been taken when the UART was initialized, so the task blocks waiting for it to be given back.  When the FIFO becomes free again, the UART causes a serial transmit interrupt.  The code that handles this interrupt is shown here:

___if (UART_GetFlagStatus (mkapsUARTBase [ziUART], UART_RawIT_Transmit)) {
______xlWakeTask = xSemaphoreGiveFromISR (mapsTxSemaphore [ziUART], xlWakeTask);
______UART_ClearITPendingBit (mkapsUARTBase [ziUART], UART_IT_Transmit);

The semaphore is given back in the interrupt, and the task that called Serial_SendChar is awakened.  The semaphore is taken again and the character is sent to the UART.

So the interrupt does nothing but unblock the task when the FIFO becomes free.  The task is responsible for sending the character and checking to see if the FIFO is full.  Granted, it’s not exactly your standard serial port functionality, but it saves some code and it works.