{"id":4533,"date":"2026-06-13T12:31:28","date_gmt":"2026-06-13T12:31:28","guid":{"rendered":"https:\/\/blog.embeddedexpert.io\/?p=4533"},"modified":"2026-06-13T12:31:30","modified_gmt":"2026-06-13T12:31:30","slug":"getting-started-with-stm32-low-layer-ll-uart-transmit-using-dma","status":"publish","type":"post","link":"https:\/\/blog.embeddedexpert.io\/?p=4533","title":{"rendered":"Getting Started with STM32 Low Layer (LL): UART Transmit Using DMA"},"content":{"rendered":"\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"559\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_vlq0kgvlq0kgvlq0-1024x559.png\" alt=\"\" class=\"wp-image-4534\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_vlq0kgvlq0kgvlq0-1024x559.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_vlq0kgvlq0kgvlq0-300x164.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_vlq0kgvlq0kgvlq0-768x419.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_vlq0kgvlq0kgvlq0-1150x627.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_vlq0kgvlq0kgvlq0-750x409.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_vlq0kgvlq0kgvlq0-400x218.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_vlq0kgvlq0kgvlq0-250x136.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_vlq0kgvlq0kgvlq0.png 1408w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Achieve the absolute pinnacle of microcontroller efficiency by offloading the entire UART communication workload from the CPU to the\u00a0<strong>Direct Memory Access (DMA)<\/strong>\u00a0controller. This guide details how to configure an independent DMA stream using Low Layer (LL) drivers to automatically transfer whole data buffers directly from RAM to the UART peripheral registers without fetching a single instruction or triggering a single CPU interrupt during the transmission.<\/p>\n\n\n\n<p>In this guide, we shall cover the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Introduction. <\/li>\n\n\n\n<li>STM32CubeMX setup.<\/li>\n\n\n\n<li>Firmware Development.<\/li>\n\n\n\n<li>Results.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">1. Introduction:<\/h2>\n\n\n\n<p>While shifting from polling to interrupt-driven UART transmission drastically reduces CPU stalling, high-speed communication lines can still present a hidden tax on system performance. At elevated baud rates or dense data streams, an interrupt-driven system forces the CPU to constantly pause its main execution thread, save its context to the stack, execute the Interrupt Service Routine (ISR) for a single byte, and restore its context. This context-switching overhead can bottleneck complex calculation routines or real-time control loops.<\/p>\n\n\n\n<p>The&nbsp;<strong>Direct Memory Access (DMA)<\/strong>&nbsp;controller provides a hardware-level solution to this problem. It acts as a secondary, specialized processor core whose sole responsibility is moving data directly across the microcontroller\u2019s internal bus matrix.<\/p>\n\n\n\n<p>By configuring a dedicated DMA stream (such as&nbsp;<strong>DMA1, Stream 6, Channel 4<\/strong>&nbsp;for USART2 TX on the STM32F4), you establish an autonomous pipeline between your application RAM buffer and the UART peripheral. When a transmission is initiated, the CPU simply hands the starting memory address and the total data length to the DMA controller, then immediately walks away to perform other tasks.<\/p>\n\n\n\n<p>The DMA controller takes over the internal bus, pulling bytes from RAM and dropping them into the UART Transmit Data Register (<code>TDR<\/code>) at the exact rate dictated by the hardware flag, operating completely in the background. The CPU remains&nbsp;<strong>0% utilized<\/strong>&nbsp;during the entire transfer process, receiving a single, clean interrupt only when the complete block of data has cleared memory.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Key Structural Differences: Interrupt Mode vs. DMA Mode<\/h3>\n\n\n\n<p>The table below breaks down how these two background processing techniques differ across critical architectural metrics:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Operational Metric<\/th><th>Interrupt-Driven Mode<\/th><th>Direct Memory Access (DMA) Mode<\/th><\/tr><\/thead><tbody><tr><td><strong>CPU Workload During Transfer<\/strong><\/td><td><strong>Medium:<\/strong>&nbsp;The CPU must halt and execute an ISR for&nbsp;<em>every individual byte<\/em>&nbsp;in the buffer.<\/td><td><strong>Zero:<\/strong>&nbsp;The DMA hardware manages the entire buffer transfer; the CPU is completely free.<\/td><\/tr><tr><td><strong>Context-Switching Overhead<\/strong><\/td><td><strong>High:<\/strong>&nbsp;Constantly pushes and pops CPU registers to\/from the stack for every byte character.<\/td><td><strong>None:<\/strong>&nbsp;Data bypasses the CPU core entirely via the internal bus arbiter.<\/td><\/tr><tr><td><strong>Interrupt Frequency<\/strong><\/td><td><strong>High:<\/strong>&nbsp;Triggers&nbsp;N&nbsp;interrupts for a buffer of&nbsp;Nbytes.<\/td><td><strong>Very Low:<\/strong>&nbsp;Triggers exactly&nbsp;<strong>one<\/strong>&nbsp;interrupt per buffer (Transfer Complete &#8211; TC).<\/td><\/tr><tr><td><strong>Maximum Data Throughput<\/strong><\/td><td>Limited by the speed at which the CPU can enter and exit the UART ISR safely.<\/td><td>Limited only by the physical baud rate and internal bus matrix arbitration.<\/td><\/tr><tr><td><strong>Configuration Complexity<\/strong><\/td><td><strong>Moderate:<\/strong>&nbsp;Requires writing an ISR to handle&nbsp;<code>TXE<\/code>&nbsp;and&nbsp;<code>TC<\/code>&nbsp;flags step-by-step.<\/td><td><strong>High:<\/strong>&nbsp;Requires setting up memory\/peripheral addresses, data direction, increments, and streams.<\/td><\/tr><tr><td><strong>Ideal Use Case<\/strong><\/td><td>Sporadic communication, short command strings, or lower baud rates (e.g., debugging).<\/td><td>Bulk data streaming, high-speed sensor arrays, audio processing, or display updates.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2. STM32CubeMX setup:<\/h2>\n\n\n\n<p><\/p>\n\n\n\n<p>We shall continue from the previous guide from <a href=\"https:\/\/blog.embeddedexpert.io\/?p=4518\" data-type=\"link\" data-id=\"https:\/\/blog.embeddedexpert.io\/?p=4518\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a>.<\/p>\n\n\n\n<p>Open the .ioc file in STM32CubeMX.<\/p>\n\n\n\n<p>From the Connectivity, select USART2, DMA setting tab and configure the DMA as follows after clicking on add:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Mode to normal.<\/li>\n\n\n\n<li>Data width for byte for both peripheral and memory<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"662\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-47-24-1024x662.png\" alt=\"\" class=\"wp-image-4535\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-47-24-1024x662.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-47-24-300x194.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-47-24-768x497.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-47-24-1536x993.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-47-24-2048x1324.png 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-47-24-1150x744.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-47-24-750x485.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-47-24-400x259.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-47-24-250x162.png 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Next, from system core NVIC, code generation, disable code generation for the DMA IRQ handler as follows<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"662\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-49-20-1024x662.png\" alt=\"\" class=\"wp-image-4536\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-49-20-1024x662.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-49-20-300x194.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-49-20-768x497.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-49-20-1536x993.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-49-20-2048x1324.png 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-49-20-1150x744.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-49-20-750x485.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-49-20-400x259.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-49-20-250x162.png 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>After that, from Project Manager tab, Advanced Settings, set DMA to use LL as follows and click on Generate Code.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"662\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-48-27-1024x662.png\" alt=\"\" class=\"wp-image-4537\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-48-27-1024x662.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-48-27-300x194.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-48-27-768x497.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-48-27-1536x993.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-48-27-2048x1324.png 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-48-27-1150x744.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-48-27-750x485.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-48-27-400x259.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-13_14-48-27-250x162.png 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Thats all for STM32CubeMX configuration.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3. Firmware Development:<\/h2>\n\n\n\n<p>Open the project in STM32CubeIDE and open main.c of the project.<\/p>\n\n\n\n<p>We start by declaring the following function:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">void UART_Send_DMA(uint8_t * ch, uint16_t len)<\/pre><\/div>\n\n\n\n<p>This function shall send the data over uart using DMA. The function shall take the following two parameters:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Pointer to the array that holds the characters to be sent.<\/li>\n\n\n\n<li>length of the array.<\/li>\n<\/ul>\n\n\n\n<p>Within the function, we start by clearing the flag as follows:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">Tx_Done = 0;<\/pre><\/div>\n\n\n\n<p>Next, disable the DMA stream as follows:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_6);<\/pre><\/div>\n\n\n\n<p>Set the peripheral address as follows:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">LL_DMA_SetPeriphAddress(DMA1, LL_DMA_STREAM_6, LL_USART_DMA_GetRegAddr(USART2));<\/pre><\/div>\n\n\n\n<p>Set the memory address:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_6, (uint32_t)ch);<\/pre><\/div>\n\n\n\n<p>Set the data length:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_6, len);<\/pre><\/div>\n\n\n\n<p>Clear transfer completed interrupt flag and enable transfer complete interrupt as follows for UART:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">LL_USART_ClearFlag_TC(USART2);\nLL_USART_EnableIT_TC(USART2);<\/pre><\/div>\n\n\n\n<p>Enable the DMA request for uart as follows:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">LL_USART_EnableDMAReq_TX(USART2);<\/pre><\/div>\n\n\n\n<p>Clear transfer complete interrupt flag and enable transfer completed interrupt interrupt for DMA as follows:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">LL_DMA_EnableIT_TC(DMA1, LL_DMA_STREAM_6);\nLL_DMA_EnableStream(DMA1, LL_DMA_STREAM_6);<\/pre><\/div>\n\n\n\n<p>Next, for DMA interrupt handler:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">void DMA1_Stream6_IRQHandler(void)\n{\n    if (LL_DMA_IsActiveFlag_TC6(DMA1))\n    {\n        LL_DMA_ClearFlag_TC6(DMA1);\n        LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_6);\n\n\n    }\n}<\/pre><\/div>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Check if the source is Stream 6 from DMA1, if it is.<\/li>\n\n\n\n<li>Clear the TC flag.<\/li>\n\n\n\n<li>Disable the stream.<\/li>\n<\/ul>\n\n\n\n<p>We shall use TC of the UART to determine the end of the transfer rather than DMA since it will generate an interrupt once the last character has been transmitted.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>In use code begin 3 in while 1 loop:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">buff_len=sprintf(uart_buff,&quot;Counter Value =%d \\r\\n&quot;,counter++);\n\nUART_Send_DMA((uint8_t *)uart_buff,buff_len);\n\nwhile(Tx_Done==0);\n\nTx_Done=0;\n\nLL_mDelay(100);<\/pre><\/div>\n\n\n\n<p>Thats all for the firmware.<\/p>\n\n\n\n<p>Save, build and run the project as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"34\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/image-1024x34.png\" alt=\"\" class=\"wp-image-4523\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/image-1024x34.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/image-300x10.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/image-768x26.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/image-1536x51.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/image-1150x38.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/image-750x25.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/image-400x13.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/image-250x8.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/image.png 1986w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">4. Results:<\/h2>\n\n\n\n<p>Open your favourite terminal application, set the baudrate to 115200 and you should get the following:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"707\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/05\/2026-05-31_09-50-11-1024x707.png\" alt=\"\" class=\"wp-image-4501\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/05\/2026-05-31_09-50-11-1024x707.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/05\/2026-05-31_09-50-11-300x207.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/05\/2026-05-31_09-50-11-768x530.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/05\/2026-05-31_09-50-11-1536x1060.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/05\/2026-05-31_09-50-11-1150x794.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/05\/2026-05-31_09-50-11-750x518.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/05\/2026-05-31_09-50-11-400x276.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/05\/2026-05-31_09-50-11-250x173.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/05\/2026-05-31_09-50-11.png 1866w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Congratulations, you send the string successfully using interrupt.<\/p>\n\n\n\n<p>You may download the project from&nbsp;<a href=\"https:\/\/github.com\/hussamaldean\/Embedded-Expert-Post-Projects\/tree\/main\/Projects\/LL_UART\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a>.<\/p>\n\n\n\n<p>Happy coding\u00a0\ud83d\ude09<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Achieve the absolute pinnacle of microcontroller efficiency by offloading the entire UART communication workload from the CPU to the\u00a0Direct Memory Access (DMA)\u00a0controller. This guide details how to configure an independent DMA stream using Low Layer (LL) drivers to automatically transfer whole data buffers directly from RAM to the UART peripheral registers without fetching a single [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,11,12],"tags":[],"class_list":["post-4533","post","type-post","status-publish","format-standard","hentry","category-embedded-systems","category-peripheral-drivers","category-stm32"],"_links":{"self":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/4533"}],"collection":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4533"}],"version-history":[{"count":1,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/4533\/revisions"}],"predecessor-version":[{"id":4538,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/4533\/revisions\/4538"}],"wp:attachment":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4533"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4533"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4533"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}