cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7 UART DMA TX/RX issues

GStee.2
Associate II

We are currently seeing issues while trying to get UART communication working : we have have 2 stm32h7 (STM32H753) boards connected via RS-232. On board 1 we are sending 4kB of data each second and on board 2 we are receiving this data.

Tx on board 1 is done through DMA via a 4k buffer. Rx on board 2 is done via a 256 byte DMA buffer (so in order to get all the 4kB sent from board 1 we will have 32 interrupts, (16 for the half transfer and 16 for the transfer complete).

In this configuration we are able to receive all data properly. So far so good (we have done this test for 2 speeds : 921600 kbit and 460800 kbit).

In order to lower the number of interrupts we now increase the DMA Rx buffer from 256 bytes to 512 bytes. From now on it goes wrong. We do see on oscilloscope that board 1 is sending data but we don't see the data coming in on board 2.

We did take into account the information in the knowledge base DMA is not working on STM32H7 devices by aligning the tx buffer on 4kB base and the rx buffer on 512 byte base. furthermore we did disable I/D caching for this test.

Important remark : When we set a breakpoint in the callback of the HT/TC Rx interrupt we do stop in the callback and when we continue from there we are receiving data for a very short while but then it stops again (until we re-apply the breakpoint).

Any idea what could be causing this behavior? Please note that we are not using the FIFO mode of the UART's.

24 REPLIES 24

@Community member​ can you make a compact example to reproduce on a nucleo H743/753? maybe with a loopback between two uarts?

Piranha
Chief II

> I noticed that the issue only occurs when tx and rx are happening at the same time

The HAL is a broken bloatware.

https://community.st.com/s/question/0D50X0000C5Tns8SQC/bug-stm32-hal-driver-lock-mechanism-is-not-interrupt-safe

> We are not using circular DMA

And the HAL has a stupid API and design - except for a circular mode, it stops the reception after each receive "transaction". It's broken by design!

https://community.st.com/s/question/0D53W00001RiIo1SAF/stm32f407-haluartexreceivetoidle-function-missing

For a sane implementation from competent developer, look here:

https://github.com/MaJerle/stm32-usart-uart-dma-rx-tx

Pavel,

I am afraid it will be hard to do for us. We use our own build framework, operating system, libraries, etc to be able to run the project on multiple hardware platforms. But we will be happy to provide a guidance of how to make it on cube/hal.

Hi F.Belaid,

We did apply setting bit 20 of the  DMA_SxCR (it was not clear to me from the errata if this was only needed for tx/rx so we we tried all possible combinations). It did however not solve the issue. So in case of STM32H753 it also doesn' seem to help.

are you managing uart communication errors within your framework?

Why don't you increase your DMA buffer to 4096 bytes?

Also, you don't say or show what you do when you get interrupts for half/complete transfer. Knowing how you're saving the data can help diagnose any issues. You should post your code for the interrupt for half transfer and complete transfer.

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.
Georgy Moshkin
Senior II

I am using H7B0, H750, do not see any problems with USART DMA.. A lot of bidirectional transfers 921600bps. Receivers are always run in circular mode, so no need to re-start DMA after each transfer. NDTR is analyzed in the main loop.

myPortPtr->ndtrPtr= &(((DMA_Stream_TypeDef*)myPortPtr->huartPtr->hdmarx->Instance)->NDTR);

In the main loop circular buffer is parsed, e.g. byte by byte

	uint32_t cnt=myPortPtr->rxBufSize-*myPortPtr->ndtrPtr;
 
	if (cnt!=myPortPtr->rxPos)
	{
		*b=( (myPortPtr->rxBufPtr)[myPortPtr->rxPos] & 0xff);
		myPortPtr->rxPos++;
		if (myPortPtr->rxPos==myPortPtr->rxBufSize) {myPortPtr->rxPos=0;}
		updateCRC32byte(*b,&HYDRA_CRC32_RX);
		return true;
	}

or larger chunks. You can use interrupts complete/half complete interrupts to monitor overflow or just use a bigger circular buffer. The same works on F1, F407, F446

#ifdef STM32F1
#include "stm32f1xx_hal.h"
#define MY_ISR(p) p->huartPtr->Instance->SR
#define MY_ISR_TC USART_SR_TC
#endif
 
#ifdef STM32F4
#include "stm32f4xx_hal.h"
#define MY_ISR(p) p->huartPtr->Instance->SR
#define MY_ISR_TC USART_SR_TC
#endif
 
#ifdef STM32H7
#include "stm32h7xx_hal.h"
#define MY_ISR(p) p->huartPtr->Instance->ISR
#define MY_ISR_TC USART_ISR_TC
#endif

Disappointed with crowdfunding projects? Make a lasting, meaningful impact as a Tech Sponsor instead: Visit TechSponsor.io to Start Your Journey!

Hi @Community member​ 

We have also tried with bigger DMA buffers but that didn't help. Both during half/complete transfer interrupt we will call our own written callback function and this function copies data from the DMA memory buffer to an application buffer.

In practice this comes down to the following :

in our uart driver we define an rx buffer of 4096 bytes and we configure the dma to use this buffer as destination. When the HT occurs 2048 bytes (index 0 to 2047 of DMA rx buffer) are copied from this rx buffer to our application buffer. When the TC occurs also 2048 bytes (index 2048 to 4095) are copied from rx buffer to our application buffer. This is repeated continuously until it goes wrong at some point.

With wrong I do mean that after the copy from DMA rx buffer to application buffer we check the contents and we see that a byte was missed...

Please note that the rx buffer that is used is defined in non-cacheable RAM and that the DMA is set to circular mode for Rx and in Direct mode for Tx.

.

If you're missing a byte then you must be out of sync? So the last byte is the start of the next packet? Posting your code for both interrupts would also help diagnose any issues.

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.

Abdelhamid,

Yes, certainly we do. If any error condition is found (frame, parity, noise, overrun) both uart and dma are completely re-initialized. Error conditions are detected and reset in the interrupts, communication is disabled also from there, but re-initialization is done in the application thread.

We have a hardware trace over STM32H7 debug core and when an error occurs, it suppose to send a message over a separate wire, even if everything else is halted.

The issue (MCU freeze) happens regardless of communication errors. Even at the slowest baud rates (9600) and clean signals, MCU freezes. No any interrupt happens before that. Everything just stops. After that, no interrupts are served (such as systick) and only reset pin reanimates it back.

If this is a silicon issue (and it looks like it is), could you provide more details about it please? Our test setup uses STM32H743 with

DBGMCU->IDCODE==0x10036450;

but we cannot control the silicone version selection as it is done by the manufacturer, from their own stock. There is certainly more internal knowledge about this issue: HAL, for example, does not apply bit 20 for this silicone version.