This code in general works as follows:
There are 3 interrupts: the Rx Complete Interrupt (created by DMA), the Tx Complete Interrupt (also created by DMA) and the „Idle Line“ Interrupt, coded manually in the stm32f1xx_it.c.
The Rx Complete Interrupt always occurs, when the Rx Buffer rolls over. To demonstrate this, the Rx buffer size was intentionally made very low (16 bytes) for this example. We use this interrupt to count the „Buffer rollovers“. A Buffer may only roll over once before an Idle Interrupt occurs for a valid transmission. If it rolls over two or more times, we can assume, that some data have been overwritten and the transmission is corrupted.
The incoming data is checked when the idle interrupt occurs. The start position of the new incoming data stream is the last end position, which can be calculated from querying the DMA “CNDTR” register (which tells us, how many bytes are left until the buffer is full / rolls over) and subtracting that value from the (known) buffer size. This information needs to be consistent over the whole runtime of the program, so you have to store it “in a safe place” 😉 – don’t lose it …
With the information of the last known buffer position and the current buffer position we can calculate the length of the received data. We have to check, if a buffer rollover occurred during the transmission and have to take that into account. See the source code for details …
The Tx Complete Interrupt is used here only to toggle the LED of the Bluepill.
Hallo, habe das mal ausprobiert. Läuft ganz gut. Allerdings wenn die vom Terminalprogramm gesendete Stringlänge > 218 ist, dann hängt das Programm beim zweiten Mal senden und der String wird nicht mehr zurück gesendet. Ich habe USART2 genommen bei 9600 bd. Die Buffer habe ich beide auf 1024 gesetzt.
great article! i learn a lot by reading it. thank you.
Thanks for a perfect tutorial. I was struggling with UART communications on an STM32429 board, and could not find any working example except for yours. After porting it to this board, it works great!
Great tutorial, it helped me solve a problem i was having with UART1 TX using DMA on my STM32F446RE Nucleo board. I have one question: is it always required to enable UART Global Interrupt when using DMA for Memory to Peripheral data transfer?
Thank-you in advance for your time.
nope, the global interrupt is only needed, if you want to react on the „idle interrupt“ (which I use in this tutorial). Without the idle interrupt you need to know the length of the incoming transmission, or you have to poll. The DMA transfer itself works without the global interrupt – but DMA interrupts have to be enabled (obviously …)
If your DMA works catching the last byte of RX transmission, probably you have to enable Memory Increment option ONE MORE TIME:
https://community.st.com/s/question/0D50X0000BmoycOSQQ/stm32f030-hal-problem-with-uart-dma-receive-memory-increment-not-set-properly-with-workaround
many thanks. Was having trouble figuring out how this worked and this did the trick. Finding out the ST docs and support are not so good. Perfect example and explanation.