Best efficient way to handle Interrupt

delphes wrote on Saturday, January 27, 2018:

Dear All,

There are different approach to handle IRQ with FreeRTOS, does somebody knows what is the most efficient way in terms of processor use, memory etc… here both cases :

FIRST case :

Interrupt function :

char cC;
cC = LL_USART_ReceiveData(Instance);
xQueueSendFromISR(xQueueUartRx, &cC, NULL);
vTaskNotifyGiveFromISR(uartExtTaskHandle, NULL);

//***************************************
Task with uartExtTaskHandle :

void uartExt_task(void const* argument)
{
char cChar;
while(true)
{
if (xQueueReceive(xQueueUartRx, &cChar, 0))
{
// do somethings
}
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
}

SECOND case :

Interrupt function :

portCHAR cC;
cC = LL_USART_ReceiveData8(USART6);
xQueueSendFromISR(xQueueUartRx,&cC, NULL);

//*************************************

Task for receiving car :

char Ccar;
for (;:wink: {
if (xQueueReceivexQueueUartRx,&Ccar, portMAX_DELAY) == pdTRUE)
{
// do somethings
}

heinbali01 wrote on Saturday, January 27, 2018:

Hi David,

To summarise your two approaches:

FIRST case :

void Interrupt_handler()
{
char cC;
	cC = LL_USART_ReceiveData(Instance);
	xQueueSendFromISR(xQueueUartRx, &cC, NULL);
	vTaskNotifyGiveFromISR(uartExtTaskHandle, NULL);
}

void uartExt_task(void const* argument)
{
char cChar;
	while(true)
	{
		if (xQueueReceive(xQueueUartRx, &cChar, 0))
		{
			// do somethings
		}
		ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
	}
}

SECOND case :

void Interrupt_handler()
{
portCHAR cC;
	cC = LL_USART_ReceiveData8(USART6);
	xQueueSendFromISR(xQueueUartRx,&cC, NULL);
}

void Task_for_receiving()
{
char Ccar;
	for (;;)
	{
		if (xQueueReceive( xQueueUartRx,&Ccar, portMAX_DELAY ) == pdTRUE)
		{
			// do somethings
		}
	}
}

The second approach looks better to me.

In the first approach you are using two wake-up (or notify) mechanisms: both a message queue and a task notification. One method is enough.

Note that the second approach is ok for a terminal driver that receives key strokes or so.

When the USART is exchanging big amounts of data, it is worth using multiple DMA-buffers. Only when a buffer is full, or when no more data are expected, you send a buffer to the user. After reading it, the user will have to pass back the buffer to the driver.

Personally, I like to use circular buffers (see stream_buffer.c), where the driver may write and the user (a single user only) may read from it. This can also be combined with the task-notify mechanism:

void Interrupt_handler()
{
char cC;
BaseType_t xSwitchRequired = pdFALSE;

	cC = LL_USART_ReceiveData( Instance );
	addByte (&xCircularBuffer, cC);
	vTaskNotifyGiveFromISR( uartExtTaskHandle, &( xSwitchRequired ) );
	/* Switch context if needed: */
	portYIELD_FROM_ISR( xSwitchRequired );
}

void Task_for_receiving(void const* argument)
{
char pcBuffer[ 128 ];
int iCount;

	while(true)
	{
		ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
		/* When waking up, 1 or more bytes have been placed in the
		circular buffer.  Read them in blocks: */
		for( ;; )
		{
			iCount = readBytes ( &xCircularBuffer, pcBuffer, sizeof pcBuffer );
			if( iCount <= 0 )
			{
				break;
			}
			// do somethings
		}
	}
}

Note also that I added a call to portYIELD_FROM_ISR(), to cause a context-switch.

delphes wrote on Saturday, January 27, 2018:

Hi Hein,

Thanks for your answer, how you garantee your circular buffer is thread safe ?

Regards

richard_damon wrote on Saturday, January 27, 2018:

The simplest way is to use a mutex for the task side. Normally the ISR side doesn’t have much to worry about, as only 1 ISR will be connected to a given buffer, so it just needs to make sure it updates things such that the task will always see something consistant, and the task need to make sure it keeps things clean for the ISR. One key for this is you move/take the data byte, THEN you update the data pointers.

Often I find the task side doesn’t need an explicit mutex for the curcular buffer, as there is a higner level aspect that makes it so that only one task has access to the buffer at any given time.

delphes wrote on Saturday, January 27, 2018:

Thanks Richard, what are you opinion concerning my original question to handle IRQ ?

rgds

heinbali01 wrote on Saturday, January 27, 2018:

David wrote:

Thanks for your answer, how you guarantee your circular buffer is thread safe ?

You’re welcome.
Richard Damon already answered your question.
The method is thread-safe as long as there is only one “giver” and one “taker”. As Richard comments, this is a design thing.
If you want to read USART data from several threads (tasks), the circular buffer should be protected with a mutex.

richard_damon wrote on Saturday, January 27, 2018:

As Hein said, there is no reason that I see to use both a Queue, and then also a direct to task notification (or a semaphore), as you normally only need one interlocking primative. If the baud rate isn’t to high, or the protocal makes it hard for the ISR to figure out the message breaks, just using a Queue can be the simplest, though it does have the overhead of likely causing a task switch in and out for each character. If the data rate is high, the messages are somewhat long, and the ISR can detect message boundries, then having the ISR buffer up a full message and then notify the task can lower the processor load to receive the message. This might be done with a circular buffer, or you might have a group of buffers and indicate which buffer has the message (particularly useful for large messages, to avoid needing to copy it into and out of a circular queue.

delphes wrote on Saturday, January 27, 2018:

Many thanks Hein and Richard for your valuable answer.

rtel wrote on Monday, January 29, 2018:

FreeRTOS V10.0.0 and on has a thread safe circular buffer, with an example of interrupt to task comms: https://www.freertos.org/RTOS-stream-buffer-example.html