{"id":4297,"date":"2019-10-30T18:40:58","date_gmt":"2019-10-30T17:40:58","guid":{"rendered":"https:\/\/msalamon.pl\/?p=4297"},"modified":"2025-12-27T18:36:38","modified_gmt":"2025-12-27T17:36:38","slug":"uart-reception-using-dma-is-a-piece-of-cake-lesson-from-the-stm32-course","status":"publish","type":"post","link":"https:\/\/msalamon.pl\/en\/uart-reception-using-dma-is-a-piece-of-cake-lesson-from-the-stm32-course\/","title":{"rendered":"UART Reception Using DMA Is a Piece of Cake (Lesson from the STM32 Course)"},"content":{"rendered":"\n<p>Recently in my mailing group I asked a question about which STM32 topic interests you the most right now. I received lots of different answers, but one topic came up several times. It was <strong>receiving messages of arbitrary length from UART via DMA<\/strong>. If the reader wants it \u2013 I\u2019m writing it!<\/p>\n\n\n\n<!--more-->\n\n\n\n<p>Many myths have grown around this topic. Some say it can\u2019t be done. Others claim it\u2019s a huge effort. I have no idea where that came from. Perhaps it\u2019s partly because the HAL libraries don\u2019t take into account the very important UART IDLE interrupt at all, which is essential here. Maybe also because it can be done in several ways and each has its pros and cons.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">How to receive UART via DMA?<\/h1>\n\n\n\n<p>As you know, or are just finding out \u2013 when starting DMA reception, we must declare the number of characters after which a DMA transfer-complete interrupt will be generated. At half of that count, a half-transfer interrupt will also fire.<\/p>\n\n\n\n<p>UART communication is such that device messages often have different lengths. Take for example this <a href=\"http:\/\/msalamon.pl\/lokalizacja-gps-przy-pomocy-neo6mv2-na-stm32\/\">GPS I described here recently<\/a>. If there\u2019s no fix, the messages will be very short with no data between separators in the form of commas. After GPS lock, messages can be several dozen characters long. Additionally, different message types also have different lengths. How are you supposed to know how long the next message will be?<\/p>\n\n\n\n<p>You could simply wait for the fixed amount of data to overflow. Then one, two or more messages might arrive before the DMA interrupt is triggered. But what if no more messages come in and the last one was critical? That\u2019s not the most convenient situation, is it?<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">UART IDLE LINE interrupt<\/h3>\n\n\n\n<p>STM32 microcontrollers have lots of interesting interrupts. One of the more interesting is UART IDLE LINE. It can be used for multi-master communication, but it can also serve us to detect the end of a message.<\/p>\n\n\n\n<p>How does it work? After detecting the first incoming character on the UART, the receiver line is monitored for activity. <strong>If there is no activity on this line for a period equal to the length of one character, an interrupt is raised.<\/strong> Do you already see the use case?<\/p>\n\n\n\n<p>Let\u2019s say exactly 20 characters are coming to the microcontroller over UART. After the time of the 21st character we get the IDLE interrupt. End of message detected. What next?<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">End of message and the DMA matter<\/h3>\n\n\n\n<p>Alright, but what does DMA have to do with this? During my research I saw several implementation approaches. One interesting way was to cyclically start DMA for, say, 10 characters. In my opinion it generates a lot of unnecessary work restarting DMA. On the other hand, it needs little RAM and is completely independent of UART message length.<\/p>\n\n\n\n<p>My proposal is a DMA buffer that can hold the longest possible message we can expect. So where do we get the DMA interrupt from? There\u2019s a nice relationship that <strong>by stopping the \u201cracing\u201d DMA you generate a transfer-complete interrupt<\/strong>. And that\u2019s the key to success!<\/p>\n\n\n\n<p>It\u2019s enough to stop DMA in the IDLE interrupt to enter the DMA transfer-complete handler. Magic!<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">DMA interrupt handling<\/h3>\n\n\n\n<p>There are two \u201cunfortunatelys.\u201d One is that the HAL libraries don\u2019t handle the IDLE interrupt. You have to write your own and replace the standard HAL interrupt handling. The second unfortunately is that <strong>Cube will always try to restore the default interrupt handling when regenerating the project, and you have to remember that<\/strong>.<\/p>\n\n\n\n<p>There\u2019s also a big plus in HAL. It generates separate handlers for each UART and each DMA channel. Replacing the interrupt is therefore harmless to other interrupts.<\/p>\n\n\n\n<p>Okay, enough theorizing. Let me walk you through the implementation.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">UART reception via DMA project<\/h2>\n\n\n\n<p>I wrote the code for the <a href=\"https:\/\/sklep.msalamon.pl\/produkt\/nucleo-f411re\/?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=uart_dma&amp;utm_content=Text\">Nucleo F411RE<\/a> using STM32CubeIDE 1.0.2 and HAL libraries version 1.24.1. There won\u2019t be a schematic this time. I didn\u2019t connect anything extra to the board.&nbsp;<\/p>\n\n\n\n<h1 class=\"wp-block-heading\"><figure><a href=\"https:\/\/sklep.msalamon.pl\/kategoria-produktu\/dev-boardy\/stm32-nucleo\/?utm_source=blog&amp;utm_medium=banner&amp;utm_campaign=uart_dma&amp;utm_content=nucleo\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner.jpg\" alt=\"\" width=\"1200\" height=\"400\" class=\"aligncenter wp-image-1593 size-full\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner.jpg 1200w, https:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner-300x100.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner-1024x341.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner-768x256.jpg 768w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\"><\/a><\/figure><\/h1>\n\n\n\n<p>Generate the project with the standard peripherals initialized. You can easily do this by selecting the <a href=\"https:\/\/sklep.msalamon.pl\/produkt\/nucleo-f411re\/?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=uart_dma&amp;utm_content=Text\">Nucleo<\/a> board instead of the microcontroller when creating the project.<\/p>\n\n\n\n<p>Since you chose the default peripherals, you already have the UART and the built-in LED enabled. We\u2019ll use both today.<\/p>\n\n\n\n<p>However, you need to further configure the UART. Configure the DMA request for USART2_RX in Normal mode. Also add the USART2 global interrupt in the NVIC tab. See the screenshots below.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_uart_config.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"296\" height=\"300\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_uart_config-296x300.jpg\" alt=\"\" class=\"wp-image-1229\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_uart_config-296x300.jpg 296w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_uart_config-24x24.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_uart_config-36x36.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_uart_config-79x80.jpg 79w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_uart_config.jpg 676w\" sizes=\"auto, (max-width: 296px) 100vw, 296px\" \/><\/a><\/figure>\n<\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_nvic_config.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"148\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_nvic_config-300x148.jpg\" alt=\"\" class=\"wp-image-1230\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_nvic_config-300x148.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_nvic_config-24x12.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_nvic_config-36x18.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_nvic_config-160x80.jpg 160w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_nvic_config.jpg 676w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p>And that\u2019s it on the Cube side. Let\u2019s move on to the code. I prepared a library to handle the whole thing. It allows you to use more than one UART in DMA receive mode. However, it requires a few steps and modifications of the generated code to work.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Code<\/h3>\n\n\n\n<p>First, a small note. <strong>I noticed that the latest CubeMX v5.4.0 swaps the initialization order of DMA and UART2, which results in incorrect DMA operation!&nbsp;<\/strong>Make sure that before the while(1) loop you have it as below.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  \/* Initialize all configured peripherals *\/\n  MX_GPIO_Init();\n  MX_DMA_Init();        \/\/ &amp;lt;- IMPORTANT: DMA MUST BE BEFORE USART2!!!\n  MX_USART2_UART_Init();\n  \/* USER CODE BEGIN 2 *\/\n\n  \/* USER CODE END 2 *\/\n\n  \/* Infinite loop *\/\n  \/* USER CODE BEGIN WHILE *\/\n    while (1)\n    {<\/pre>\n\n\n\n<p>I\u2019ll start by discussing the structure that is the core of each UART you want to use for DMA reception.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">typedef struct\n{\n  UART_HandleTypeDef* huart; \/\/ UART handler\n\n  uint8_t DMA_RX_Buffer[DMA_RX_BUFFER_SIZE]; \/\/ DMA direct buffer\n  uint8_t UART_Buffer[UART_BUFFER_SIZE]; \/\/ UART working circular buffer\n\n  uint16_t UartBufferHead;\n  uint16_t UartBufferTail;\n  uint8_t UartBufferLines;\n}UARTDMA_HandleTypeDef;<\/pre>\n\n\n\n<p>It contains a handle to the UART and two buffers:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>DMA-dedicated buffer for receiving the current message<\/li>\n\n\n\n<li>UART circular buffer into which subsequent messages from the DMA buffer are placed (after the transfer completes)<\/li>\n<\/ul>\n\n\n\n<p>as well as a few helper variables for handling the circular buffer and a counter of full lines (the number of messages ending with the \u2018\\n\u2019 character).<\/p>\n\n\n\n<p>From the point of view of detecting incoming UART messages there are 3 functions:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void UARTDMA_Init(UARTDMA_HandleTypeDef *huartdma, UART_HandleTypeDef *huart);\n\nvoid UARTDMA_UartIrqHandler(UARTDMA_HandleTypeDef *huartdma);\nvoid UARTDMA_DmaIrqHandler(UARTDMA_HandleTypeDef *huartdma);<\/pre>\n\n\n\n<p>In the initialization I work with interrupts:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>I enable the UART IDLE interrupt<\/li>\n\n\n\n<li>I enable the DMA TC (Transfer Complete) interrupt<\/li>\n\n\n\n<li>I disable the DMA HT (Half Transfer) interrupt \u2013 it won\u2019t be needed and may even get in the way<\/li>\n\n\n\n<li>I start UART DMA reception into the dedicated buffer<\/li>\n<\/ul>\n\n\n\n<p>You have to pass a pointer to the previously created structure to this function (for example in main).<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void UARTDMA_UartIrqHandler(UARTDMA_HandleTypeDef *huartdma);<\/pre>\n\n\n\n<p>This function handles UART interrupts. I check in it whether the interrupt was triggered by IDLE. If so, I read the appropriate registers to clear the flag and stop DMA. <strong>The DMA TC interrupt is raised automatically.<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void UARTDMA_DmaIrqHandler(UARTDMA_HandleTypeDef *huartdma);<\/pre>\n\n\n\n<p>Now the most important function from the perspective of the entire code. We are in a state where characters have stopped being transmitted on the UART. This is a signal that we probably received the entire message. Probably, because we might be dealing with a software UART that doesn\u2019t keep timing. Unfortunately, the IDLE interrupt is absolute and only considers inactivity for the duration of a single character.<\/p>\n\n\n\n<p>What happens here? First, I read the interrupt registers and check whether we\u2019re dealing with a Transfer Complete interrupt. If so, then:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>I clear the interrupt flag (HAL by default does this for us in its handlers, but this is OUR handler)<\/li>\n\n\n\n<li>I check how many characters came in via DMA using the <strong>NDTR<\/strong> register.<\/li>\n\n\n\n<li>I copy from the DMA buffer into the next free positions of the UART circular buffer. This is a ring buffer, so written data will wrap around. Along the way I check whether the currently copied character is an end-of-line \u2018\\n\u2019. If so, I increment the appropriate variable in the structure.<\/li>\n\n\n\n<li>I clear interrupts<\/li>\n\n\n\n<li>I set the DMA registers to the beginning of the DMA buffer and again set the number of characters to receive, which equals the DMA buffer size.<\/li>\n\n\n\n<li>I start DMA again<\/li>\n<\/ol>\n\n\n\n<p>And it keeps spinning. Subsequent messages land in the free spaces of the UART buffer.<\/p>\n\n\n\n<p>However, <strong>to keep it spinning we must replace the interrupts!&nbsp;<\/strong>How to do that? Let\u2019s target the stm32fxx_it.c file<\/p>\n\n\n\n<p>For our configuration we\u2019re looking for two functions related to USART2 and DMA1 channel 5. These are<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void USART2_IRQHandler(void);\n\nvoid DMA1_Stream5_IRQHandler(void)<\/pre>\n\n\n\n<p>Notice that there is the default HAL interrupt handling <em>HAL_DMA_IRQHandler<\/em> and <em>HAL_UART_IRQHandler<\/em>. We don\u2019t want that, so comment them out. <strong>Reminder: after regenerating the project they will return! Edit \u2013 below I added how to solve this more elegantly.<\/strong> With the new method, the default interrupts won\u2019t interfere even if they come back \ud83d\ude09<\/p>\n\n\n\n<p>Now add our functions in the appropriate places. For me it looks like this<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/**\n* @brief This function handles DMA1 stream5 global interrupt.\n*\/\nvoid DMA1_Stream5_IRQHandler(void)\n{\n\/* USER CODE BEGIN DMA1_Stream5_IRQn 0 *\/\n\n\/* USER CODE END DMA1_Stream5_IRQn 0 *\/\n\/\/HAL_DMA_IRQHandler(&amp;amp;hdma_usart2_rx);\n\/* USER CODE BEGIN DMA1_Stream5_IRQn 1 *\/\n  UARTDMA_DmaIrqHandler(&amp;amp;huartdma);\n\/* USER CODE END DMA1_Stream5_IRQn 1 *\/\n}\n\n\/**\n* @brief This function handles USART2 global interrupt.\n*\/\nvoid USART2_IRQHandler(void)\n{\n\/* USER CODE BEGIN USART2_IRQn 0 *\/\n\n\/* USER CODE END USART2_IRQn 0 *\/\n\/\/HAL_UART_IRQHandler(&amp;amp;huart2);\n\/* USER CODE BEGIN USART2_IRQn 1 *\/\n  UARTDMA_UartIrqHandler(&amp;amp;huartdma);\n\/* USER CODE END USART2_IRQn 1 *\/\n}<\/pre>\n\n\n\n<p>There\u2019s one more thing. This file won\u2019t know these functions, let alone our structure. You have to provide them in the user section at the very top of the file.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/* USER CODE BEGIN Includes *\/\n#include \"UART_DMA.h\"\n\/* USER CODE END Includes *\/\n\n\/* USER CODE BEGIN EV *\/\nextern UARTDMA_HandleTypeDef huartdma;\n\/* USER CODE END EV *\/<\/pre>\n\n\n\n<p>And that\u2019s it. It will work \ud83d\ude42 Now it would be nice to add something in main to react to what arrives on UART. I won\u2019t invent anything fancy here. I\u2019ll receive two messages \u201cON\u201d and \u201cOFF\u201d, which will turn the built-in LED on the Nucleo on and off respectively.<\/p>\n\n\n\n<p>To check whether there\u2019s something in the queue to parse and to fetch a data line, I\u2019ll use two functions<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">uint8_t UARTDMA_IsDataReady(UARTDMA_HandleTypeDef *huartdma);\nint UARTDMA_GetLineFromBuffer(UARTDMA_HandleTypeDef *huartdma, char *OutBuffer);<\/pre>\n\n\n\n<p>Their names are pretty self-explanatory. Just a bit more code in main. Remember that you need to create the structure variable for the whole machinery.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/* USER CODE BEGIN PD *\/\nUARTDMA_HandleTypeDef huartdma;\n\/* USER CODE END PD *\/<\/pre>\n\n\n\n<p>A small buffer to parse the fetched lines will also be useful.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/* USER CODE BEGIN PV *\/\nchar ParseBuffer[8];\n\/* USER CODE END PV *\/<\/pre>\n\n\n\n<p>Remember to initialize UART with DMA reception<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/* USER CODE BEGIN 2 *\/\nUARTDMA_Init(&amp;amp;huartdma, &amp;amp;huart2);\n\/* USER CODE END 2 *\/<\/pre>\n\n\n\n<p>And finally, we fetch the data and parse it<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/* USER CODE BEGIN WHILE *\/\nwhile (1)\n{\n  if(UARTDMA_IsDataReady(&amp;amp;huartdma))\n  {\n    UARTDMA_GetLineFromBuffer(&amp;amp;huartdma, ParseBuffer);\n    if(strcmp(ParseBuffer, \"ON\") == 0)\n    {\n      HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);\n    }\n    else if(strcmp(ParseBuffer, \"OFF\") == 0)\n    {\n      HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);\n    }\n  }\n\/* USER CODE END WHILE *\/\n\n\/* USER CODE BEGIN 3 *\/\n}<\/pre>\n\n\n\n<ol class=\"wp-block-list\">\n<li>I check whether there\u2019s a line to parse<\/li>\n\n\n\n<li>If so, I fetch it in full<\/li>\n\n\n\n<li>With a simple strcmp I check what the message is and react accordingly<\/li>\n\n\n\n<li>I enjoy the result<\/li>\n<\/ol>\n\n\n\n<p>Difficult? Of course not \ud83d\ude42 Let\u2019s also look at the timing, because that\u2019s always interesting. Reception of two characters:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_timing_2chars.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1920\" height=\"288\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_timing_2chars.jpg\" alt=\"\" class=\"wp-image-1232\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_timing_2chars.jpg 1920w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_timing_2chars-300x45.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_timing_2chars-1024x154.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_timing_2chars-768x115.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_timing_2chars-1536x230.jpg 1536w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_timing_2chars-24x4.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_timing_2chars-36x5.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_timing_2chars-160x24.jpg 160w\" sizes=\"auto, (max-width: 1920px) 100vw, 1920px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p>UART IDLE interrupt handling is 1.437 \u00b5s, and DMA TC is 4.125 \u00b5s. Blazing fast considering these times include GPIO handling time. <strong>By the way, I have to compare handling of some peripherals with HAL and \u201cbare registers.\u201d What do you think?<\/strong><\/p>\n\n\n\n<p>And what if I send 50 characters?<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_timing_50chars.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1920\" height=\"292\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_timing_50chars.jpg\" alt=\"\" class=\"wp-image-1233\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_timing_50chars.jpg 1920w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_timing_50chars-300x46.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_timing_50chars-1024x156.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_timing_50chars-768x117.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_timing_50chars-1536x234.jpg 1536w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_timing_50chars-24x4.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_timing_50chars-36x5.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/10\/uart_dma_timing_50chars-160x24.jpg 160w\" sizes=\"auto, (max-width: 1920px) 100vw, 1920px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p>UART IDLE handling didn\u2019t change \u2013 it doesn\u2019t depend on message length. The time needed to handle the DMA TC interrupt changed and is now 33.975 \u00b5s. The increase comes from the need to copy a larger number of characters into the UART circular buffer.<\/p>\n\n\n\n<p>I think these times are very good.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">EDIT \u2013 interrupt handling<\/h2>\n\n\n\n<p>I got a tip on Facebook on how to better solve the problem of default HAL interrupt handling. Just put \u201cours\u201d before the factory one and return immediately \ud83d\ude42 So simple, and I didn\u2019t think of it while writing the code. I won\u2019t remove my original idea even though it\u2019s not very elegant.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/**\n* @brief This function handles DMA1 stream5 global interrupt.\n*\/\nvoid DMA1_Stream5_IRQHandler(void)\n{\n\/* USER CODE BEGIN DMA1_Stream5_IRQn 0 *\/\n\n  UARTDMA_DmaIrqHandler(&amp;amp;huartdma);\n  return;\n\n\/* USER CODE END DMA1_Stream5_IRQn 0 *\/\n  HAL_DMA_IRQHandler(&amp;amp;hdma_usart2_rx);\n\/* USER CODE BEGIN DMA1_Stream5_IRQn 1 *\/\n\n\/* USER CODE END DMA1_Stream5_IRQn 1 *\/\n}\n\n\/**\n* @brief This function handles USART2 global interrupt.\n*\/\nvoid USART2_IRQHandler(void)\n{\n\/* USER CODE BEGIN USART2_IRQn 0 *\/\n\n  UARTDMA_UartIrqHandler(&amp;amp;huartdma);\n  return;\n\n\/* USER CODE END USART2_IRQn 0 *\/\n  HAL_UART_IRQHandler(&amp;amp;huart2);\n\/* USER CODE BEGIN USART2_IRQn 1 *\/\n\n\/* USER CODE END USART2_IRQn 1 *\/\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p>You\u2019ll find lots of ideas on the Internet for implementing UART over DMA. I think what I did is one of the simpler implementations. It requires some RAM for two buffers, but in STM32 we have plenty of it \ud83d\ude42 Besides, if you expect short messages, you don\u2019t need large buffers.<\/p>\n\n\n\n<p>I hope you enjoyed the article and understood how it works. If you have any doubts or questions, the comments section is yours. I\u2019ll be grateful if you share this article with your friends. Let good content spread as widely as possible.<\/p>\n\n\n\n<p>The UART and DMA topic will also be covered in my <strong>STM32 course for beginners<\/strong> (conducted in Polish). Thanks to video, I\u2019ll have better tools there to talk about it in more depth. Of course, there will be more complex applications in an enjoyable form. If you haven\u2019t signed up for my newsletter yet, you\u2019re welcome to do so (the newsletter is in Polish). Click the banner to join the waiting list for the course.<\/p>\n\n\n\n<p>In the emails you\u2019ll receive current information and you\u2019ll have a real impact on what happens not only in the course, but also on the blog (emails\/newsletter are in Polish).<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/kursstm32.pl\/?utm_source=blog&amp;utm_medium=banner&amp;utm_campaign=uart_dma&amp;utm_content=Text\" target=\"_blank\" rel=\"noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"820\" height=\"198\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/06\/Kurs-STM32.png\" alt=\"STM32 course\" class=\"wp-image-1017\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/06\/Kurs-STM32.png 820w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/06\/Kurs-STM32-300x72.png 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/06\/Kurs-STM32-768x185.png 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/06\/Kurs-STM32-24x6.png 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/06\/Kurs-STM32-36x9.png 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/06\/Kurs-STM32-160x39.png 160w\" sizes=\"auto, (max-width: 820px) 100vw, 820px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p>The full project along with the library can be found as usual on my GitHub: <a href=\"https:\/\/github.com\/lamik\/UART_DMA_receiving\" target=\"_blank\" rel=\"noopener\">link<\/a><\/p>\n\n\n\n<p><span>If you noticed any error, disagree with something, would like to add something important, or simply feel like discussing the topic, write a comment. Remember that the discussion should be polite and in accordance with the rules of the Polish language.<\/span><\/p>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;4297&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;0&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;0&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;0&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;0\\\/5 - (0 votes)&quot;,&quot;size&quot;:&quot;24&quot;,&quot;title&quot;:&quot;UART Reception Using DMA Is a Piece of Cake (Lesson from the STM32 Course)&quot;,&quot;width&quot;:&quot;0&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} - ({count} {votes})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 0px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 19.2px;\">\n            <span class=\"kksr-muted\"><\/span>\n    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>Recently in my mailing group I asked a question about which STM32 topic interests you the most right now. I received lots of different answers, but one topic came up several times. It was receiving messages of arbitrary length from UART via DMA. If the reader wants it \u2013 I\u2019m [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":3322,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","_links_to":"","_links_to_target":""},"categories":[160],"tags":[175,178,176,174,177],"class_list":["post-4297","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-stm32","tag-electronics","tag-kursstm32","tag-programming","tag-stm32","tag-stm32cubemx"],"_links":{"self":[{"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/posts\/4297","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/comments?post=4297"}],"version-history":[{"count":3,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/posts\/4297\/revisions"}],"predecessor-version":[{"id":4396,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/posts\/4297\/revisions\/4396"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/media\/3322"}],"wp:attachment":[{"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/media?parent=4297"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/categories?post=4297"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/tags?post=4297"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}