cancel
Showing results for 
Search instead for 
Did you mean: 

USART, DMA, and FreeRTOS, the combination's not working

OK, first, I'm not including code quite yet because I want to see if anyone has heard of this problem.  Code is available, though.

What I want:  USART to act as a console USART driving a queue for reception, and being fed by a queue for transmission.

Details:  FreeRTOS latest in the packs, STM562VETQ processor, custom hardware. CubeMXIDE 14.1.

Software, from the archives/examples, the one that uses the LPuart and L5 processor.

Copying the software (direct blocking transmit, circular DMA buffer scanned and read out to external buffer) and running it (changing LPUART to UART1) works perfectly.  This is the direct bare metal application transported over with the USART changed.  NO FreeRTOS used.  So far, so good.

Main Program from CubeMXIDE, sets up tasks, etc, calls bridge program (C++) which starts main program task (C++).  For this test, main program task is disabled.  DMA example moved into separate C module.  FreeRTOS running.

Program does not work.

 

 

 

	  if (HAL_OK != HAL_UARTEx_ReceiveToIdle_DMA(&CONSOLE_USART, CONSOLE_aRXBufferUser, CONSOLE_DMA_BUFFER_SIZE))
	  {
	    Error_Handler();
	  }

 

 

 

 to be specific, the program hangs somewhere in the call to HAL_UARTEx......, does not return to the program.

If I put a breakpoint at this line, then single step through it, the program is likely to work, and this is with FreeRTOS running.

"Timing Problems, says I"

Disabling all references to FreeRTOS (in this module and the main module where needed.....

Program works.

I'm not familiar enough with the innards of the drivers to be sure where the problem is.  I looked at Majerle's solution, but other than using low level drivers, it looks remarkably similar.  I have a similar problem in my own version that uses DMA receive.  The DMA doesn't happen and seems to be that the USART is never interrupting anything.

Anybody heard of this kind of thing happening?  Suggestions?

 

 

1 ACCEPTED SOLUTION

Accepted Solutions

Code attached for the routine.

Code works with FreeRTOS in the background.  Defining _FREERTOS_DB stops the program from working.  The program seems to stop in

 /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
  ATOMIC_SET_BIT(huart->Instance->CR3, USART_CR3_EIE);

 

stm32l5xx_hal_uart.c line 3552 which calls

__STATIC_FORCEINLINE uint32_t __LDREXW(volatile uint32_t *addr)
{
    uint32_t result;

   __ASM volatile ("ldrex %0, %1" : "=r" (result) : "Q" (*addr) );
   return(result);
}

on line 1151 in cmsis_gcc.h

stalled at the __ASM volatile.

Stopping the debugger, gives that line.  Restarting continues, but the program does not receive from the usart.

A breakpoint at line 511 in SYSTEM_CONSOLE.cpp is never reached, which indicates to me that the interrupt never fires.  There is NO change to the IOC file, the setups for the DMA, USART and rest of the subsystems DO NOT change, the only difference is that defining _FREERTOS_DB is now defined.  You may enable just the queue creation and it may or may not work.  Sometimes, stepping through the STMICRO usart code will allow the system to work.

 

 

View solution in original post

5 REPLIES 5
Karl Yamashita
Lead III

Show more relevant code. Your single if statement tells us nothing.

Tips and Tricks with TimerCallback https://www.youtube.com/@eebykarl
If you find my solution useful, please click the Accept as Solution so others see the solution.

Code attached for the routine.

Code works with FreeRTOS in the background.  Defining _FREERTOS_DB stops the program from working.  The program seems to stop in

 /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
  ATOMIC_SET_BIT(huart->Instance->CR3, USART_CR3_EIE);

 

stm32l5xx_hal_uart.c line 3552 which calls

__STATIC_FORCEINLINE uint32_t __LDREXW(volatile uint32_t *addr)
{
    uint32_t result;

   __ASM volatile ("ldrex %0, %1" : "=r" (result) : "Q" (*addr) );
   return(result);
}

on line 1151 in cmsis_gcc.h

stalled at the __ASM volatile.

Stopping the debugger, gives that line.  Restarting continues, but the program does not receive from the usart.

A breakpoint at line 511 in SYSTEM_CONSOLE.cpp is never reached, which indicates to me that the interrupt never fires.  There is NO change to the IOC file, the setups for the DMA, USART and rest of the subsystems DO NOT change, the only difference is that defining _FREERTOS_DB is now defined.  You may enable just the queue creation and it may or may not work.  Sometimes, stepping through the STMICRO usart code will allow the system to work.

 

 

The solution to this is a bit tricky, but it does work.

The code I have attached shows the solution, with the following requirements:

The software has been rewritten to allow FreeRTOS queues.  Write to the Console_transmit_queue to send to the console USART, and read data from the CONSOLE_receive_queue to obtain data.  String support routines for receive are not implemented, however string support routines for send data are supported.  Note that the dual buffers for receive are commented out. 

you must either rename _FREERTOS_DB to _FREERTOS, or enable _FREERTOS_DB.  This was a debugging setting to enable or disable FreeRTOS for the module.  STATIC_CONSOLE may not be needed, and likely ought to be left alone without further debugging.  While disabling FREERTOS_DB will compile, the receive buffers must be enabled to be able to get data out in a non-RTOS situation.

SOLUTION:

The main problem was that the HAL firmware was not properly initialized.  The real cause was that there are certain routines that cannot be interrupted by FreeRTOS. 

  1. For CONSOLE_StartReception, surround HAL_USARTEx_ReceiveToIdleDMA with taskENTER_CRITICAL and taskEXIT_CRITICAL.  The original code was not developed with an operating system in mind.
  2. Do the same with HAL_UARTeX_RxEventCallback.  It may not be needed, so it can be argued.

 

 

 

 

Code attached

The web interface seems to have dropped my reply because it doesn't like a *.hpp and a *.cpp file.  How clever.  It does however, accept a zip file of both files.

The ultimate problem is that the hal routines (see start DMA) cannot be interrupted.  The system timing allows the processor to go through half the ST code, and then gets interrupted.  The solution is to surround that code with taskENTER_CRITICAL and taskEXITCRITICAL.  This stops the FreeRTOS scheduler from interrupting the code with a context switch.  Please note that the original (ST MICRO) routines are NEVER (IMHO) designed to be interrupted, especially this one.

I put in two such blocks, but the critical one is:

		#ifdef _FREERTOS_DB
			taskENTER_CRITICAL();
		#endif
		if (HAL_OK != HAL_UARTEx_ReceiveToIdle_DMA(&W32_CONSOLE_USART, W32_CONSOLE_aRXBufferUser, W32_CONSOLE_DMA_BUFFER_SIZE))
		  {
			Error_Handler();
		  }
		#ifdef _FREERTOS_DB
			taskEXIT_CRITICAL();
		#endif

The rest of the code has been modified to use queues.  Using queues in any other location, including ones that you think ought to work, apparently doesn't. 

I put one other one in, but if that's needed or not I haven't bothered to find out

Note that this code, while modified from an ST Micro example, was NEVER written to exist with an operating system.

.These modifications have it working on my system.

The whole process is queue driven, both for inputs and outputs.

Notes:

There's debugging code in there.  Define _FREERTOS_DB to enable FreeRTOS for the module.  If it is not defined, the program will compile, but the mechanisms for getting data out will be commented out or not available. 

Don't bother with the _STATIC_CONSOLE directive, it's debugging, but feel free to experiment. 

Write to queue formatting data is supported, read from queue has to be handled by the application.

comments and questions are welcome.