UART with IDLE Interrupt Hardfaults

Hi,

FreeRTOS newbie here, I am using stm32 with whatever the STM32cubeide generates, so I’m using CMSIS v1.

My idea is that I have an UART Interrupt on Idle Callback, that sends the received array and its size to queue, which I then work in a “handler”-task.

So the handler task is just

"    void uart_handler_task(void const *argument)
{
	UART_Mail mail;
	for (;;)
	{
		xQueueReceive(uart_RXHandle, &mail, portMAX_DELAY);
		my_class_for_handling_msgs.some_func(mail);
	}
}"

and the UART IDLE Callback looks like this

"
UART_Mail uart_isr_buff;

void HAL_UART_IDLE_Callback(UART_HandleTypeDef *huart)
{
	__HAL_UART_DISABLE_IT(huart, UART_IT_IDLE);
	uart_isr_buff.size = MAX_SIZE - __HAL_DMA_GET_COUNTER(huart->hdmarx);
	portBASE_TYPE taskWoken = pdFALSE;
	xQueueSendFromISR(uart_RXHandle, (void* ) &uart_isr_buff, &taskWoken);
	portEND_SWITCHING_ISR(taskWoken);
	HAL_UART_AbortReceive(huart);
	__HAL_UART_CLEAR_IDLEFLAG(huart);
	__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);
	HAL_UART_Receive_DMA(huart, (uint8_t*) uart_isr_buff.arr, MAX_SIZE);
}
"

So it works pretty well, unless there are two messages coming really quick, then sometimes, but with a pretty high chance, I get a Hard Fault.

The stack looks like this, the last called function
Last position is

tasks.c-> void vListInsertEnd() ->"pxNewListItem->pxPrevious = pxIndex->pxPrevious;"

called from

tasks.c-> BaseType_t xTaskRemoveFromEventList()-> "prvAddTaskToReadyList(pxUnblockedTCB );"

called from

queue.c->BaseType_t xQueueGenericSendFromISR()->
"#else /* configUSE_QUEUE_SETS */
				....
						if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )"

called from my callback on xQueueSendFromISR(uart_RXHandle, (void* ) &uart_isr_buff, &taskWoken);

I can’t really say what is happening here, and I’m not sure that the way I am executing the callback is right. some advice where should i look? Some priorities I might have set wrong?

There are ways you can investigate the hard fault, but in this case it just looks like a memory corruption which will probably be caught when the symptom is exposed rather than the cause.

Before going into that though - there does look like a bug in your code. You have a single UART_Mail variable, and are passing a pointer to that variable on the queue. If another interrupt comes in before the handler task has finished with the UART_Mail variable, or worst a another interrupt comes in while the handler task is half way through accessing the UART_Mail variable, then the interrupt is going to corrupt the variable the handler task is accessing. Without knowing what is in the UART_Mail variable I can’t tell what the consequences of that would be.

I would suggest either using a stream buffer to pass the data if the number of bytes is quite small https://www.freertos.org/RTOS-stream-buffer-example.html or alternatively have a pool of UART_Mail buffers and have the interrupt take a variable from the pool, fill it, and queue a pointer to it - and have the handler task consume the variables from the queue and return it to the pool when it has finished access it. Different use case but you can see a similar scheme here getting something from a pool https://github.com/FreeRTOS/Lab-Project-coreMQTT-Agent/blob/main/lib/FreeRTOS/freertos-plus-mqtt/freertos_mqtt_agent.c#L493 and returning the structure to the pool https://github.com/FreeRTOS/Lab-Project-coreMQTT-Agent/blob/main/lib/FreeRTOS/freertos-plus-mqtt/freertos_mqtt_agent.c#L462 .

I thought I am passing a pointer to the variable so that xQueueSendFromISR will copy it to another variable- Isn’t that what is happening in prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); call? I declared my queue as static,

osMessageQId uart_RXHandle;
uint8_t uartRXBuffer[4 * sizeof(UART_Mail)];
osStaticMessageQDef_t uartRXControlBlock;

isn’t that already a “pool” of messages?