I’m running on the RT1052 w/ FreeRTOS Kernel V10.2.1.
When doing (maybe heavy) communications over UART, my uart_rx_task somehow seems to be starving (very occasionally).
The starvation happens when in my uart_tx_task (priority 3), I send something, and then wait for an ACK sent via event group (that usually come from my uart_rx_task quickly/responsively) to be set.
I can confirm the ISR is receiving all data, but on these starvation occasions, the uart_rx_task fails to run for over a second, so the uart_rx_task never has the chance to process the data to signal the TX task.
I can confirm the RX task is starved and the TX task isn’t because on TX task timeout after not getting an ACK, I can dump the UART RX “cb” (see below) over the uart line - and see everything was received by the ISR, just UART task didn’t start back up.
AFTER I DUMP the buffer over UART TX, usually UART RX will start behaving normally again and work fine.
Was wondering if there was anything I’m doing wrong w/ this interrupt structure, or what else might cause the UART rx to starve when it’s at max priority.
One important note - seems that all NXP projects have time slicing turned off. Turning in on seems to break the project right now…
static TaskHandle_t uart_rx_task_handle, uart_tx_task_handle;
void uart_startup(){
BOARD_InitUARTPins();
NVIC_SetPriority(LPUART1_IRQn, 13);
if (xTaskCreate(uart_rx_task, “uart_rx_task”, 2000, NULL, 5, &uart_rx_task_handle) != pdPASS) {
assert(0);
}
}
void LPUART1_IRQHandler(void) {
uint8_t data;
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
if ((kLPUART_RxDataRegFullFlag)&LPUART_GetStatusFlags(LPUART1)) {
data = LPUART_ReadByte(LPUART1);
cb_push_back(&cb, &data);
xTaskNotifyFromISR( uart_rx_task_handle, UART_RX_DATA, eSetBits, &xHigherPriorityTaskWoken );
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
}
static void uart_rx_task(void *pvParameters) {
uint32_t rx_count = 0;
EventBits_t event_bits;
cb_init(&cb, (void *) cb_uart_backing_buffer, 1024);
lpuart_config_t config;
LPUART_GetDefaultConfig(&config);
config.enableTx = true;
config.enableRx = true;
LPUART_Init(LPUART1, &config, BOARD_DebugConsoleSrcFreq());
/* Enable RX interrupt. */
LPUART_EnableInterrupts(LPUART1, kLPUART_RxOverrunInterruptEnable | kLPUART_RxDataRegFullInterruptEnable);
EnableIRQ(LPUART1_IRQn);
while (1) {
xTaskNotifyWait( pdFALSE, /* Don’t clear bits on entry. /
ULONG_MAX, / Clear all bits on exit. /
&event_bits, / Stores the notified value. */
portMAX_DELAY );
if ((event_bits & UART_RX_DATA) == UART_RX_DATA){
process_read_data(&cb);
}
}
vTaskSuspend(NULL);
}
Just so you have the TX side to see, here is where the TX task is waiting for ACKs:
static status_t uart_tx_await_ack(uint8_t *data, uint16_t size){
EventBits_t event_bits;
uint8_t retries = 7;
uint8_t retry_backoff;
TickType_t wait_time;
//avoid triggering from stale acks/nacks…
xEventGroupClearBits(uart_ack_event_group, ON_ACK | ON_NACK);
do {
uart_tx_enqueue(data, size);
retry_backoff = (8 - retries) * 50;// ms extra
wait_time = (200+retry_backoff) / portTICK_PERIOD_MS ;
event_bits = xEventGroupWaitBits(uart_ack_event_group, ON_ACK | ON_NACK, pdTRUE, pdFALSE, wait_time);
if (((event_bits & ON_ACK) != ON_ACK) && (retries <2)){
// starvation case - dump the CB over uart here
}
} while (retries-- && ((event_bits & ON_ACK) != ON_ACK));
if ((event_bits & ON_ACK) == ON_ACK){
return kStatus_Success;
} else if ((event_bits & ON_NACK) == ON_NACK){
return kStatus_Fail;
}
return kStatus_Fail;
}