{"id":835,"date":"2025-05-29T22:26:09","date_gmt":"2025-05-30T05:26:09","guid":{"rendered":"https:\/\/nuclearprojects.com\/blog\/?p=835"},"modified":"2025-06-04T23:16:01","modified_gmt":"2025-06-05T06:16:01","slug":"setting-up-stm32-uart-dma-for-gnss-data-reception","status":"publish","type":"post","link":"https:\/\/nuclearprojects.com\/blog\/setting-up-stm32-uart-dma-for-gnss-data-reception\/","title":{"rendered":"Setting up STM32 UART DMA For GNSS Data Reception"},"content":{"rendered":"<p>There are multiple ways to setup the STM32 UART for data reception, including polling, interrupts and using DMA. Polling is simple, you just keep checking the UART for data. However, this is a &#8220;blocking&#8221; function and ties-up the CPU from doing anything else. Using interrupts to check for data reception is a step-up from polling, but still relies on making many interrupt calls to the CPU. One way this is implemented is to setup an interrupt to fire when a new byte is received on the UART, then process it. In the case of GNSS data (GPS, Galileo, etc.), this could mean firing several hundred times per second. Doable, but there seems to be a better way; <strong>DMA<\/strong>.<\/p>\n<p><strong>Direct Memory Access<\/strong>, or <strong>DMA<\/strong>, appears to be the least CPU intensive option as the CPU isn&#8217;t involved in the data transfer itself. Using the DMA we can basically setup this memory buffer off to the side, let it catch data, then process it later.<\/p>\n<p>The DMA will place data into a buffer that needs to be created, such as:<\/p>\n<pre><strong><span style=\"color: #993366;\">uint8_t<\/span><\/strong> buffer[200];<\/pre>\n<p>The DMA can be setup to write to the buffer in &#8220;normal&#8221; mode or &#8220;circular&#8221; mode. In normal mode, DMA writes to the end of the buffer and stops. In contrast, when in &#8220;circular&#8221; mode, the DMA will continue writing to the buffer in a loop. Once it reaches the end of the buffer, it begins again, overwriting any data from the beginning of the buffer. This works well for receiving and processing data sentences of varying lengths.<\/p>\n<p><strong>Configuration<\/strong>:<\/p>\n<ul>\n<li>In STM32CubeIDE, setup a USART in asynchronous mode (USART1 used here)<\/li>\n<li>Under DMA Settings, <strong>Add<\/strong> a new DMA for receiving, use &#8220;Circular&#8221; mode<\/li>\n<li>Under NVIC Settings, ensure USART and DMA interrupts are enabled<\/li>\n<\/ul>\n<p>To initiate the DMA transfer of UART data, we&#8217;ll use the following function placed before the <em>while(1)<\/em> loop.<\/p>\n<div style=\"background-color: #ffffff; padding: 0px 0px 0px 2px;\">\n<div style=\"color: #000000; background-color: #ffffff; font-family: 'Consolas'; font-size: 10pt; white-space: pre;\">\n<pre><span style=\"color: #3f7f5f;\">\/\/ receive until idle, then trigger interrupt<\/span>\r\n<span style=\"color: #000000;\">HAL_UARTEx_ReceiveToIdle_DMA(&amp;huart1, buffer, <\/span><span style=\"color: #7f0055; font-weight: bold;\">sizeof<\/span><span style=\"color: #000000;\">(buffer));\r\n<\/span>\r\n<span style=\"color: #7f0055; font-weight: bold;\">while<\/span><span style=\"color: #000000;\"> (1)<\/span><\/pre>\n<\/div>\n<\/div>\n<p>Outside of the &#8220;main&#8221; function we need to place the interrupt handler:<\/p>\n<div style=\"background-color: #ffffff; padding: 0px 0px 0px 2px;\">\n<div style=\"color: #000000; background-color: #ffffff; font-family: 'Consolas'; font-size: 10pt; white-space: pre;\">\n<pre style=\"margin: 0;\"><span style=\"color: #7f0055; font-weight: bold;\">void<\/span> <span style=\"color: #000000; font-weight: bold;\">HAL_UARTEx_RxEventCallback<\/span><span style=\"color: #000000;\">(<\/span><span style=\"color: #005032;\">UART_HandleTypeDef<\/span><span style=\"color: #000000;\"> *huart, <\/span><span style=\"color: #005032;\">uint16_t<\/span><span style=\"color: #000000;\"> Size)<\/span>\r\n<span style=\"color: #000000;\">{<\/span>\r\n<span style=\"color: #339966;\">  \/\/ put code here<\/span>\r\n\r\n<span style=\"color: #3f7f5f;\">  \/\/ don't need to call this if using DMA circular mode<\/span>\r\n<span style=\"color: #3f7f5f;\">  \/\/ HAL_UARTEx_ReceiveToIdle_DMA(&amp;huart1, buffer, sizeof(buffer));<\/span>\r\n<span style=\"color: #000000;\">}<\/span><\/pre>\n<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<p>Calling <span style=\"color: #000000;\"><em>HAL_UARTEx_ReceiveToIdle_DMA<\/em> function will enable three interrupts:<\/span><\/p>\n<ul>\n<li><strong>DMA\u00a0Transfer Complete (TC)<\/strong> &#8211; fires when buffer is full<\/li>\n<li><strong>DMA Half Transfer (HT)<\/strong> &#8211; fires when buffer is 50% full<\/li>\n<li><strong>UART Idle Line (IDLE)<\/strong> &#8211; fires when the UART is idle for at least one byte worth of time\n<ul>\n<li>The IDLE interrupt indicates that the UART line is no longer transmitting data.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>In my case, I&#8217;m only interested in the IDLE interrupt. Since the other two interrupts will still fire when appropriate, those need to be disabled. We can add two lines below the <span style=\"color: #000000;\"><em>HAL_UARTEx_ReceiveToIdle_DMA<\/em> function call to disable the TC and HT interrupts:<\/span><\/p>\n<div style=\"background-color: #ffffff; padding: 0px 0px 0px 2px;\">\n<div style=\"color: #000000; background-color: #ffffff; font-family: 'Consolas'; font-size: 10pt; white-space: pre;\">\n<pre style=\"margin: 0;\"><span style=\"color: #000000;\"><span style=\"color: #3f7f5f;\">\/\/ receive until idle, then trigger interrupt<\/span> \r\nHAL_UARTEx_ReceiveToIdle_DMA(&amp;huart1, buffer, <\/span><span style=\"color: #7f0055; font-weight: bold;\">sizeof<\/span><span style=\"color: #000000;\">(buffer)); <\/span><span style=\"color: #3f7f5f;\">\/\/ receive until idle, then trigger interrupt<\/span>\r\n<span style=\"color: #000000;\">__HAL_DMA_DISABLE_IT(huart1.<\/span><span style=\"color: #0000c0;\">hdmarx<\/span><span style=\"color: #000000;\">, DMA_IT_HT); <\/span><span style=\"color: #3f7f5f;\">\/\/ Disables \"Half Transfer\" interrupt<\/span>\r\n<span style=\"color: #000000;\">__HAL_DMA_DISABLE_IT(huart1.<\/span><span style=\"color: #0000c0;\">hdmarx<\/span><span style=\"color: #000000;\">, DMA_IT_TC); <\/span><span style=\"color: #3f7f5f;\">\/\/ Disables \"Transfer Complete\" interrupt\r\n<\/span><strong><span style=\"color: #993366;\">while <\/span><\/strong>(1)\r\n<\/pre>\n<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<p><strong>Overrun Error Handling<\/strong><\/p>\n<p>One issue I&#8217;ve run into is if there is data present on the UART RX line during startup (like if the GNSS is powered-on at the same time and immediately begins sending data), it will trigger an error flag, specifically ORE or Overrun Error, which will prevent DMA from working. This appears to happen prior to the <span style=\"color: #000000;\"><em>HAL_UARTEx_ReceiveToIdle_DMA<\/em> function being called. The fix thus far for this is to clear the error flag just before calling <em>HAL_UARTEx_ReceiveToIdle_DMA<\/em>. The updated code:<\/span><\/p>\n<div style=\"background-color: #ffffff; padding: 0px 0px 0px 2px;\">\n<div style=\"color: #000000; background-color: #ffffff; font-family: 'Consolas'; font-size: 10pt; white-space: pre;\">\n<pre style=\"margin: 0;\"><span style=\"color: #3f7f5f;\">\/\/ Check if ORE flag is set, which can happen if data is present on UART RX line<\/span>\r\n<span style=\"color: #7f0055; font-weight: bold;\">if<\/span><span style=\"color: #000000;\"> (__HAL_UART_GET_FLAG(&amp;huart1, UART_FLAG_ORE)) {<\/span>\r\n<span style=\"color: #000000;\">  __HAL_UART_CLEAR_FLAG(&amp;huart1, UART_CLEAR_OREF);<\/span>\r\n<span style=\"color: #000000;\">}\r\n\r\n<span style=\"color: #3f7f5f;\">\/\/ receive until idle, then trigger interrupt\r\n<\/span>HAL_UARTEx_ReceiveToIdle_DMA(&amp;huart1, buffer, <span style=\"color: #7f0055; font-weight: bold;\">sizeof<\/span>(buffer)); <span style=\"color: #3f7f5f;\">\/\/ receive until idle, then trigger interrupt\r\n<\/span>__HAL_DMA_DISABLE_IT(huart1.<span style=\"color: #0000c0;\">hdmarx<\/span>, DMA_IT_HT); <span style=\"color: #3f7f5f;\">\/\/ Disables \"Half Transfer\" interrupt\r\n<\/span>__HAL_DMA_DISABLE_IT(huart1.<span style=\"color: #0000c0;\">hdmarx<\/span>, DMA_IT_TC); <span style=\"color: #3f7f5f;\">\/\/ Disables \"Transfer Complete\" interrupt\r\n<\/span><strong><span style=\"color: #993366;\">while <\/span><\/strong>(1)\r\n<\/span><\/pre>\n<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<p>The IDLE interrupt is a great option for receiving the GNSS data sentences, which can vary in length. As long as the GNSS module is setup to provide at least 1 byte worth of space, I shouldn&#8217;t have any problem receiving all data within the circular buffer and keeping them separated.<\/p>\n<p>Now, data being received from the UART will be directly written into our buffer. After each data sentence is completed, the IDLE interrupt will fire and call the interrupt handler created earlier. The handler has a variable called <strong>Size<\/strong>, which tells us the position of the last byte received within our <strong>buffer<\/strong>. This will be helpful for keeping track of sentence lengths and start\/stop positions for processing purposes. Here is an example for visualization:<\/p>\n<p><a href=\"https:\/\/nuclearprojects.com\/blog\/setting-up-stm32-uart-dma-for-gnss-data-reception\/headtail\/\" rel=\"attachment wp-att-851\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-851 alignnone\" src=\"https:\/\/nuclearprojects.com\/blog\/wp-content\/uploads\/2025\/05\/headtail.jpg\" alt=\"\" width=\"618\" height=\"100\" srcset=\"https:\/\/nuclearprojects.com\/blog\/wp-content\/uploads\/2025\/05\/headtail.jpg 973w, https:\/\/nuclearprojects.com\/blog\/wp-content\/uploads\/2025\/05\/headtail-300x48.jpg 300w, https:\/\/nuclearprojects.com\/blog\/wp-content\/uploads\/2025\/05\/headtail-768x124.jpg 768w\" sizes=\"auto, (max-width: 618px) 100vw, 618px\" \/><\/a><\/p>\n<p>The head and tail can be set based on personal preference to some extent. In my example, while the actual head of the data is located at BYTE[4], because Size is returned as 5, it&#8217;s quite convenient to set Head equal to this value. Alternatively I could use Head = Size &#8211; 1, but then on the next pass I couldn&#8217;t just set Tail = Head, I&#8217;d have to do Tail = Head + 1. Simply using Size to mark the Head of the current sentence and the Tail of the next sentence seems straight forward enough for me.<\/p>\n<p><strong>Let&#8217;s read some GNSS data.<\/strong><\/p>\n<p>I had created some variables to keep track of the head and tail positions for the circular buffer. The head position will be updated within the callback function for our IDLE event. In the main loop I&#8217;ll watch for the case when Head != Tail, then process the data between the current tail and head.<\/p>\n<div style=\"background-color: #ffffff; padding: 0px 0px 0px 2px;\">\n<div style=\"color: #000000; background-color: #ffffff; font-family: 'Consolas'; font-size: 10pt; white-space: pre;\">\n<pre style=\"margin: 0;\"><span style=\"color: #7f0055; font-weight: bold;\">void<\/span> <span style=\"color: #000000; font-weight: bold;\">HAL_UARTEx_RxEventCallback<\/span><span style=\"color: #000000;\">(<\/span><span style=\"color: #005032;\">UART_HandleTypeDef<\/span><span style=\"color: #000000;\"> *huart, <\/span><span style=\"color: #005032;\">uint16_t<\/span><span style=\"color: #000000;\"> Size) <\/span><span style=\"color: #3f7f5f;\">\/\/ Size=position of last byte received<\/span>\r\n<span style=\"color: #000000;\">{<\/span>\r\n<span style=\"color: #000000;\"> uart1BufferHead = Size; <\/span><span style=\"color: #3f7f5f;\">\/\/ store position of last byte received, this is our \"head\"<\/span>\r\n<span style=\"color: #000000;\">}<\/span><\/pre>\n<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<p>For processing the data, another function will be called in the main loop. Within that function I&#8217;ll update the tail position based on the amount of buffer bytes processed. The tail update will look something like:<\/p>\n<div style=\"background-color: #ffffff; padding: 0px 0px 0px 2px;\">\n<div style=\"color: #000000; background-color: #ffffff; font-family: 'Consolas'; font-size: 10pt; white-space: pre;\">\n<pre style=\"margin: 0;\"><span style=\"color: #3f7f5f;\">\/\/ Update tail<\/span>\r\n<span style=\"color: #000000;\">uart1BufferTail = (uart1BufferTail + (6 + payloadLength + 2)) % size;<\/span><\/pre>\n<\/div>\n<\/div>\n<p>In my case, I&#8217;m processing a message from the U-Blox GNSS module. It starts with 6 bytes of header information, followed by a various number of &#8220;payload&#8221; bytes, ending with 2 checksum bytes. I&#8217;ve formatted the code to make that easily readable and identifiable for myself. The modulo (%) operation compares the size of the newly calculated tail against the buffer &#8220;size&#8221;. This will automatically &#8220;wrap&#8221; the tail value to the beginning of the buffer. For example, if the buffer size is 200 bytes (array positions [0] to [199]), but the above calculation resulted in a tail position of [205], the modulo would change the final value to [5]. Slick!<\/p>\n<p>Eventually, the tail position should equal the head position once all the &#8220;new&#8221; buffer data has been read. At that point the program should stop processing data.<\/p>\n<p>&nbsp;<\/p>\n<p><strong>Changing UART baud rate after startup (on the fly)<\/strong><\/p>\n<p>In my application, I must initialize the UART at baud rate 9600 due to the default baud rate of the U-Blox GNSS module. Once UART is started, I can send an update message to the module to change it&#8217;s baud rate to 115200. After that, I need to change the STM32&#8217;s baud rate to match. This is easily accomplished by this code:<\/p>\n<div style=\"background-color: #ffffff; padding: 0px 0px 0px 2px;\">\n<div style=\"color: #000000; background-color: #ffffff; font-family: 'Consolas'; font-size: 10pt; white-space: pre;\">\n<pre style=\"margin: 0;\"><span style=\"color: #3f7f5f;\">\/\/ Reinitialize UART at 115200 baud<\/span>\r\n<span style=\"color: #000000;\"> HAL_UART_DeInit(&amp;huart1); <\/span><span style=\"color: #3f7f5f;\">\/\/ Deinitialize before reinitializing<\/span>\r\n<span style=\"color: #000000;\"> huart1.<\/span><span style=\"color: #0000c0;\">Init<\/span><span style=\"color: #000000;\">.<\/span><span style=\"color: #0000c0;\">BaudRate<\/span><span style=\"color: #000000;\"> = 115200;<\/span>\r\n<span style=\"color: #7f0055; font-weight: bold;\"> if<\/span><span style=\"color: #000000;\"> (HAL_UART_Init(&amp;huart1) != <\/span><span style=\"color: #0000c0; font-style: italic;\">HAL_OK<\/span><span style=\"color: #000000;\">) {<\/span>\r\n<span style=\"color: #000000;\">   Error_Handler();<\/span>\r\n<span style=\"color: #000000;\"> }<\/span>\r\n<span style=\"color: #000000;\"> HAL_Delay(100);<\/span><\/pre>\n<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<p>Note, when changing the baud rate, I found the DMA had to be re-started as well (call the <span style=\"color: #000000;\"><em>HAL_UARTEx_ReceiveToIdle_DMA<\/em> function again and disable the desired interrupts as done before).<\/span><\/p>\n<p>&nbsp;<\/p>\n<p>Sites I found helpful on this topic:<\/p>\n<p><a href=\"https:\/\/www.steppeschool.com\/pages\/blog\/stm32-uart-polling-dma\">https:\/\/www.steppeschool.com\/pages\/blog\/stm32-uart-polling-dma<\/a><\/p>\n<p><a href=\"https:\/\/stm32world.com\/wiki\/STM32_UART_DMA_Idle_Detection\">https:\/\/stm32world.com\/wiki\/STM32_UART_DMA_Idle_Detection<\/a><\/p>\n<p><a href=\"https:\/\/deepbluembedded.com\/stm32-uart-receive-unknown-length-idle-line-detection-examples\/\">https:\/\/deepbluembedded.com\/stm32-uart-receive-unknown-length-idle-line-detection-examples\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>There are multiple ways to setup the STM32 UART for data reception, including polling, interrupts and using DMA. Polling is simple, you just keep checking the UART for data. However, this is a &#8220;blocking&#8221; function and ties-up the CPU from doing anything else. Using interrupts to check for data reception is a step-up from polling, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[29,1],"tags":[],"class_list":["post-835","post","type-post","status-publish","format-standard","hentry","category-electronics","category-everything-else"],"_links":{"self":[{"href":"https:\/\/nuclearprojects.com\/blog\/wp-json\/wp\/v2\/posts\/835","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nuclearprojects.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nuclearprojects.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nuclearprojects.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nuclearprojects.com\/blog\/wp-json\/wp\/v2\/comments?post=835"}],"version-history":[{"count":19,"href":"https:\/\/nuclearprojects.com\/blog\/wp-json\/wp\/v2\/posts\/835\/revisions"}],"predecessor-version":[{"id":861,"href":"https:\/\/nuclearprojects.com\/blog\/wp-json\/wp\/v2\/posts\/835\/revisions\/861"}],"wp:attachment":[{"href":"https:\/\/nuclearprojects.com\/blog\/wp-json\/wp\/v2\/media?parent=835"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nuclearprojects.com\/blog\/wp-json\/wp\/v2\/categories?post=835"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nuclearprojects.com\/blog\/wp-json\/wp\/v2\/tags?post=835"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}