I am experiencing a puzzling “seemingly” freeze issue in our application built on NXP iMXRT1052 using FreeRTOS.
NXP provides a UART driver and a FreeRTOS version of interrupt driven UART driver that is built on topic of it.
The FreeRTOS version of UART driver provides a callback that can be called from a task context as well as an UART ISR context; this driver utilizes a software ring buffer.
If there is already requested UART bytes in the ring buffer, it calls the callback from a task context as well.
At any rate, the callback invokes xEventGroupSetBitsFromISR() and portYield_From_ISR() to wake up a task waiting for either Tx or Rx to complete.
-
Is is completely safe to use those two APIs from non-ISR context?
-
Does xEventGroupSetBitsFromISR() wake up xEventGroupWaitBits() even if xEventGroupSetBitsFromISR() gets called before xEventGroupWaitBits() is called?
I ask because I see a scenario where a task calls xEventGroupSetBitsFromISR() because requested UART bytes are already in the ring buffer.
/* Non-blocking call */
LPUART_TransferReceiveNonBlocking(handle->base, handle->t_state, &handlerxTransfer, &n);
ev = xEventGroupWaitBits(
handle->rxEvent, RTOS_LPUART_COMPLETE | RTOS_LPUART_RING_BUFFER_OVERRUN | RTOS_LPUART_HARDWARE_BUFFER_OVERRUN,
pdTRUE, pdFALSE, ticksToWait);
where LPUART_TransferReceiveNonBlocking() is defined as
status_t LPUART_TransferReceiveNonBlocking(LPUART_Type *base,
lpuart_handle_t *handle,
lpuart_transfer_t *xfer,
size_t *receivedBytes)
{
.
.
.
/* Call user callback since all data are received. */
if (0U == bytesToReceive)
{
if (NULL != handle->callback)
{
handle->callback(base, handle, kStatus_LPUART_RxIdle, handle->userData);
}
}
.
.
}
where the callback is defined as
static void LPUART_RTOS_Callback(LPUART_Type *base, lpuart_handle_t *state, status_t status, void *param)
{
lpuart_rtos_handle_t *handle = (lpuart_rtos_handle_t *)param;
BaseType_t xHigherPriorityTaskWoken, xResult;
xHigherPriorityTaskWoken = pdFALSE;
xResult = pdFAIL;
if (status == kStatus_LPUART_RxIdle)
{
xResult = xEventGroupSetBitsFromISR(handle->rxEvent, RTOS_LPUART_COMPLETE, &xHigherPriorityTaskWoken);
}
else if (status == kStatus_LPUART_TxIdle)
{
xResult = xEventGroupSetBitsFromISR(handle->txEvent, RTOS_LPUART_COMPLETE, &xHigherPriorityTaskWoken);
}
else if (status == kStatus_LPUART_RxRingBufferOverrun)
{
xResult =
xEventGroupSetBitsFromISR(handle->rxEvent, RTOS_LPUART_RING_BUFFER_OVERRUN, &xHigherPriorityTaskWoken);
}
else if (status == kStatus_LPUART_RxHardwareOverrun)
{
/* Clear Overrun flag (OR) in LPUART STAT register */
LPUART_ClearStatusFlags(base, kLPUART_RxOverrunFlag);
xResult =
xEventGroupSetBitsFromISR(handle->rxEvent, RTOS_LPUART_HARDWARE_BUFFER_OVERRUN, &xHigherPriorityTaskWoken);
}
if (xResult != pdFAIL)
{
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
The puzzling “freeze” seems to be caused by the code around these.
When the freeze occurs, no other tasks seem to be running with the exception of UART interrupts. UART Rx ISR continuously gets triggered over and over and nothing else appears to be “allowed” to run.
Any experience with this kind of stuff?