I’ve been working on USART support with queues, and using DMA. This is on an ST Micro processor, CubeMXIDE, Latest version of FreeRTOS they support, custom processor.
The actual code is from a GitHub example for the L5 processor, using a circular DMA buffer and providing dual flopping buffers for output.
I’ve modified that to remove the output buffers, drive an output queue, read from an input queue, and run under FreeRTOS (which it didn’t). The DMA mode uses a circular buffer, and in the event callback from the DMA, the buffer is read (and now drives a queue).
The actual solution you’d have to apply depending on your processor and individual code.
The problem:
Code seems to work partially and works fully in a non-FreeRTOS environment, but the code stalls in STMicro routines (their custom drivers) when FreeRTOS is used.
Finding a breakpoint in their system driver code and then single stepping through the code may or may not work once or so.
The reason:
The drivers that ST Micro writes are not, and have never been (IMHO) designed to be used under an operating system. The problem here is that the code for initializing the DMA subsystem is interrupted when FreeRTOS does a task switch.
The solution:
Surround the critical driver initialization with taskENTER_CRITICAL and taskEXIT_CRITICAL as in the following example, which is specific to ST MICRO code.
/* Initializes Rx sequence using Reception To Idle event API.
As DMA channel associated to UART Rx is configured as Circular,
reception is endless.
If reception has to be stopped, call to HAL_UART_AbortReceive() could be used.
Use of HAL_UARTEx_ReceiveToIdle_DMA service, will generate calls to
user defined HAL_UARTEx_RxEventCallback callback for each occurrence of
following events :
- DMA RX Half Transfer event (HT)
- DMA RX Transfer Complete event (TC)
- IDLE event on UART Rx line (indicating a pause is UART reception flow)
*/
taskENTER_CRITICAL();
if (HAL_OK != HAL_UARTEx_ReceiveToIdle_DMA(huart, aRXBufferUser, DMA_RX_BUFFER_SIZE))
{
Error_Handler();
}
taskEXIT_CRITICAL();
This ensures that the code is not interrupted while being set up.
Note also that the queue mechanism is in the event callback routine, and looks like
/* Copy received data in "User" buffer for evacuation */
for (i = 0; i < hal_usart[which]->uwNbReceivedChars; i++)
{
xQueueSendFromISR(hal_usart[which]->receive_queue, &hal_usart[which]->aRXBufferUser[old_pos + i], &xHigherPriorityTaskWoken);
if (hal_usart[which]->CONSOLE_LOOPBACK) xQueueSendFromISR(hal_usart[which]->send_queue, &hal_usart[which]->aRXBufferUser[old_pos + i], &xHigherPriorityTaskWoken);
}
This implementation with an array of low level usart drivers is specific to my code, but should give you an idea.
If you have this problem, this may be the solution.