xTaskNotifyFromISR RX UART

I am using a PIC32 and wondering if I can use xTaskNotifyFromISR to pass the received data from the UART to the task instead of creating a queue. The PIC32 interrupts on every byte received so I believe it might work. Here is my code:

> void InterruptHandler ( void )
> {
>   int8_t RxData;
>   portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
> 
>   while ( PLIB_USART_ReceiverDataIsAvailable(UART_MODULE_ID) ) 
>   {
>     RxData = PLIB_USART_ReceiverByteReceive(UART_MODULE_ID);
>     (void)xTaskNotifyFromISR ( TaskHandleGet(), RxData, eSetValueWithoutOverwrite, &xHigherPriorityTaskWoken );
>   }
>   // clear the Rx Interrupt flag
>   PLIB_INT_SourceFlagClear( INT_ID_0, UART_INT_RX_SOURCE );
> 
>   portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
> }

> void UART_RxTask ( void *pvParameters )
> {
>   uint32_t Data;
>   for ( ;; )
>   {
>     /* Wait for data to be received... */
>     (void)xTaskNotifyWait ( 0x00, ULONG_MAX, &Data, portMAX_DELAY );
> 
>     ProcessData( (uint8_t)Data );
>   }
> }

My concern is that I might lose a byte because the task hasn’t processed the previous notification. I thought there was a way to check to see if the notification has been processed, but I also don’t want to wait in the interrupt until the task clears the notification as it might cause problems. Let me know your thoughts.

You should either use a custom queue with notifications and flush the queue in the post-processing task or consider to use FreeRTOS stream buffers - circular buffers. They could be a pretty good approach for your use case. See the API here RTOS stream buffer API functions.

1 Like

Because of the possibility of the Task not getting the byte before the next one arrives, I would not use the notification system to relay the data from the ISR to the task.

Note, you CAN’T wait in the ISR for the task to take the data, at best you could have the ISR turn off the interrupt and leave the character in the Serial Port Rx Register.

I would just use a Queue (or StreamBuffer) or a lightweight homegrown version. In all cases you need to decide what should happen if the queue gets full, though you may be able to make the queue long enough that can’t happen if there is an upper limit to the size of a message that can be sent to it.

Is this the more efficient way to process receive bytes? Fill a queue in the RX interrupt and then use task notification to signal the task to run which pulls the data from the queue? Let me know your thoughts.

void InterruptHandler ( void )
{
int8_t RxData;
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

while ( PLIB_USART_ReceiverDataIsAvailable(UART_MODULE_ID) )
{
RxData = PLIB_USART_ReceiverByteReceive(UART_MODULE_ID);
(void)xQueueSendFromISR( xRxQ, &RxData , &xHigherPriorityTaskWoken );
(void)vTaskNotifyGiveFromISR( TaskHandleGet(), &xHigherPriorityTaskWoken );
}
// clear the Rx Interrupt flag
PLIB_INT_SourceFlagClear( INT_ID_0, UART_INT_RX_SOURCE );

portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
}

void UART_RxTask ( void pvParameters )
{
int8_t Data;
for ( ;; )
{
/
Wait for data to be received… */
(void) ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
(void)xQueueReceive( xRxQ, &Data, portMAX_DELAY );
ProcessData( (uint8_t)Data );
}
}

If you use a FreeRTOS Queue, you don’t need the task notify, as the receiver can just block on the Queue.

If you build your own ‘queue’ with an array and read and write pointers (sort of like what the FreeRTOS queue does internally) then you would use the TaskNotify to let it know that more data is available, and that might be someone more efficient, as Queues have some extra overhead to handle the fact that there might be multiple tasks waiting on or pushing data onto the Queue.

A StreamBuffer basically does this itself, so this method isn’t apt to be much better than a stream buffer, just a bit since you can optimize for only putting (and maybe only getting) one character at a time.

As usual, there is no generic answer to your question. “More efficient” is use case dependent.

Aside from what @richard-damon wrote: A queue may or may not be the appropriate data structure to handle this. My customers typically employ ring buffers. That’s applications with many serial interfaces that serve polling based devices with a) constant data traffic and b) potentially rather long packets, so maintaing a queue with many 1-byte entries may be too inefficient.

Another interesting observation we made was that in such applications where there is a constant flow of incoming USART data, signalling is not the most efficient way to serve those interfaces. Since we KNOW that there will be constant data traffic, it’s actually more useful to delay the receiver task and then process whatever data has arrived in the meantime, then sleep again, because otherwise the input processor tasks (which are high pri) may starve out the rest of the system.

Another question, of course, is which instance does the “final input processing,” that is, the protocol decoding. In some very slim and leightweight applications it may actually be the ISR that processes the packet FSM (like strip the frames, verify the chcksum etc) and then schedules a finished packet from a preallocated buffer pool. Does work if we don’t need to be prepared for multiple protocols and the protocol is fully half duplex. Next option would be to have the deferred input task do most of the work. The “standard” implementation would be to have the deferred input task store the characters in an mbuf struct such that any other task can call Read() against the mbufs; in that case, you are fully protocol independent all the way down to the application task. It all depends on your system requirements.

When using protocols which are query & response based, like Modbus, I think it’s better to fill a buffer with the incoming message using either DMA or interrupts, then notify the relevant task which processes that buffer. Of course, using DMA instead of interrupts is better in this situation. I’m not familiar with PIC32, but I guess they have DMA capabilities.

When processing Modbus on STM32, I use DMA for USART RX. STM32 also has a interrupt which is fired when idle line detected. In ISR, I disable DMA, check the address and notify the task if Modbus address matches.

1 Like