{"id":4526,"date":"2026-06-10T13:22:23","date_gmt":"2026-06-10T13:22:23","guid":{"rendered":"https:\/\/blog.embeddedexpert.io\/?p=4526"},"modified":"2026-06-10T13:22:24","modified_gmt":"2026-06-10T13:22:24","slug":"stm32f407-discovery-audio-transmitting-i2s-data-using-dma","status":"publish","type":"post","link":"https:\/\/blog.embeddedexpert.io\/?p=4526","title":{"rendered":"STM32F407 Discovery Audio: Transmitting I2S Data 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_1omnae1omnae1omn-1024x559.png\" alt=\"\" class=\"wp-image-4527\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_1omnae1omnae1omn-1024x559.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_1omnae1omnae1omn-300x164.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_1omnae1omnae1omn-768x419.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_1omnae1omnae1omn-1150x627.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_1omnae1omnae1omn-750x409.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_1omnae1omnae1omn-400x218.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_1omnae1omnae1omn-250x136.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_1omnae1omnae1omn.png 1408w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Building upon the fundamentals of polling mode, this next section transitions to using Direct Memory Access (DMA) to handle the data stream. By offloading the continuous transmission of audio samples from the CPU directly to the hardware, this approach eliminates processor overhead and prevents audio stuttering during intensive application tasks.<\/p>\n\n\n\n<p><\/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 configuration.<\/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>Transitioning from polling mode to Direct Memory Access (DMA) marks a significant upgrade in high-fidelity audio system design on the STM32F407. While polling forces the CPU to constantly wait on hardware flags, DMA acts as an independent hardware bridge that copies audio data blocks directly from your memory arrays to the I2S data register. This automation frees up virtually all processor bandwidth, ensuring jitter-free, continuous playback even when the microcontroller is simultaneously managing complex application logic, display rendering, or user interfaces.<\/p>\n\n\n\n<h5 class=\"wp-block-heading\">Comparison: Polling Mode vs. DMA Mode<\/h5>\n\n\n\n<p>When choosing how to drive the I2S peripheral, balancing performance efficiency against code complexity is key. The table below highlights the operational differences between these two methods:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Feature<\/th><th>Polling Mode<\/th><th>DMA Mode<\/th><\/tr><\/thead><tbody><tr><td><strong>CPU Utilization<\/strong><\/td><td><strong>Extremely High (~100% during TX).<\/strong>&nbsp;The processor is completely locked in a tight loop checking hardware flags.<\/td><td><strong>Minimal (&lt;1%).<\/strong>&nbsp;The CPU only initiates the transfer and can then sleep or execute other application tasks.<\/td><\/tr><tr><td><strong>Audio Continuity<\/strong><\/td><td><strong>Prone to glitching.<\/strong>&nbsp;If an interrupt or heavy calculation delays the CPU loop, an audio underrun occurs, causing audible pops.<\/td><td><strong>Rock-solid reliability.<\/strong>&nbsp;Hardware timers and channels guarantee data arrives at the exact sampling interval.<\/td><\/tr><tr><td><strong>Implementation Complexity<\/strong><\/td><td><strong>Very Simple.<\/strong>&nbsp;Requires only a few lines of code to check the status register (<code>TXE<\/code>) and write to the data register (<code>DR<\/code>).<\/td><td><strong>Moderate to High.<\/strong>&nbsp;Requires configuring the DMA multiplexer, streams, channels, memory increments, and circular buffers.<\/td><\/tr><tr><td><strong>Data Handling<\/strong><\/td><td>Word-by-word (synchronous).<\/td><td>Block-by-block using Ping-Pong or Circular buffers (asynchronous).<\/td><\/tr><tr><td><strong>Ideal Use Case<\/strong><\/td><td>Quick hardware testing, sensor data validation, or simple non-time-critical sound effects.<\/td><td>Continuous high-quality audio streaming, media players, and real-time audio processing.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">2. STM32CubeMX Configuration:<\/h2>\n\n\n\n<p>We shall continue from the previous guide from <a href=\"https:\/\/blog.embeddedexpert.io\/?p=4503\" data-type=\"link\" data-id=\"https:\/\/blog.embeddedexpert.io\/?p=4503\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a>.<\/p>\n\n\n\n<p>Open the project in STM32CubeMX and configure I2S as follows:<\/p>\n\n\n\n<p>From DMA settings, enable SPI3_TX and set data width to half word for both peripheral and memory 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-10_15-40-19-1024x662.png\" alt=\"\" class=\"wp-image-4528\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-10_15-40-19-1024x662.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-10_15-40-19-300x194.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-10_15-40-19-768x497.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-10_15-40-19-1536x993.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-10_15-40-19-2048x1324.png 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-10_15-40-19-1150x744.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-10_15-40-19-750x485.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-10_15-40-19-400x259.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-10_15-40-19-250x162.png 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, from NVIC Settings, enable SPI3 global interrupt and generate the code as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"662\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-10_15-41-41-1024x662.png\" alt=\"\" class=\"wp-image-4529\" style=\"width:840px;height:auto\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-10_15-41-41-1024x662.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-10_15-41-41-300x194.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-10_15-41-41-768x497.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-10_15-41-41-1536x993.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-10_15-41-41-2048x1324.png 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-10_15-41-41-1150x744.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-10_15-41-41-750x485.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-10_15-41-41-400x259.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/2026-06-10_15-41-41-250x162.png 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>That all for the 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><\/p>\n\n\n\n<p>Open the project in STM32CubeIDE and open main.c<\/p>\n\n\n\n<p>In user code begin PV (Private Variable), declare the following:<\/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;}\">volatile uint8_t i2sTx_Completed=0;<\/pre><\/div>\n\n\n\n<p>The variable will act a flag and be handled in the interrupt to indicate that I2S3 has finished transmission the data using DMA.<\/p>\n\n\n\n<p>In user code begin 0, declare 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 HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s)\n{\n\tif(hi2s-&gt;Instance==SPI3)\n\t{\n\t\ti2sTx_Completed=1;\n\t}\n\n}<\/pre><\/div>\n\n\n\n<p>The function <strong>HAL_I2S_TxCpltCallback<\/strong>(<strong>I2S_HandleTypeDef<\/strong> *hi2s) will be called once the DMA has transferred all the data independent of the mode being circular or normal.<\/p>\n\n\n\n<p>Within the function, check if the interrupt source is SPI3 since I2S3 is controlled by SPI3 peripheral of STM32F407. If the source is SPI3, set i2sTx_Completed to 1 to indicate that the transfer is completed.<\/p>\n\n\n\n<p>In user 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;}\">HAL_I2S_Transmit_DMA(&amp;hi2s3, data, 7);\n\nwhile(i2sTx_Completed==0);\ni2sTx_Completed=0;\n\nHAL_Delay(10);<\/pre><\/div>\n\n\n\n<p><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Start the transfer in DMA mode.<\/li>\n\n\n\n<li>Wait until the DMA finishes the transfer.<\/li>\n\n\n\n<li>Delay by 10 ms and repeat.<\/li>\n<\/ul>\n\n\n\n<p>The flow of the code as follows:<\/p>\n\n\n\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_kycs16kycs16kycs-1024x559.png\" alt=\"\" class=\"wp-image-4530\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_kycs16kycs16kycs-1024x559.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_kycs16kycs16kycs-300x164.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_kycs16kycs16kycs-768x419.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_kycs16kycs16kycs-1150x627.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_kycs16kycs16kycs-750x409.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_kycs16kycs16kycs-400x218.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_kycs16kycs16kycs-250x136.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/Gemini_Generated_Image_kycs16kycs16kycs.png 1408w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Thats all for the firmware.<\/p>\n\n\n\n<p>Save, build the project and run it 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\/04\/image-1-1024x34.png\" alt=\"\" class=\"wp-image-4349\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-1024x34.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-300x10.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-768x26.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-1536x51.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-1150x38.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-750x25.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-400x13.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-250x8.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1.png 1986w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">5. Results:<\/h2>\n\n\n\n<p>By probing the pins of MCK, SDK, SD and WS, you should get the following:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"630\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/RigolDS12.png\" alt=\"\" class=\"wp-image-4515\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/RigolDS12.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/RigolDS12-300x185.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/RigolDS12-768x473.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/RigolDS12-750x461.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/RigolDS12-400x246.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/RigolDS12-250x154.png 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"630\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/RigolDS13.png\" alt=\"\" class=\"wp-image-4516\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/RigolDS13.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/RigolDS13-300x185.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/RigolDS13-768x473.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/RigolDS13-750x461.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/RigolDS13-400x246.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/06\/RigolDS13-250x154.png 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Note: Due to architecture of I2S, the first half word is missing.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>In next part, we shall initialize the audio codec device and generate some sounds using I2S.<\/p>\n\n\n\n<p>Stay tuned.<\/p>\n\n\n\n<p>Happy coding \ud83d\ude09<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Building upon the fundamentals of polling mode, this next section transitions to using Direct Memory Access (DMA) to handle the data stream. By offloading the continuous transmission of audio samples from the CPU directly to the hardware, this approach eliminates processor overhead and prevents audio stuttering during intensive application tasks. In this guide, we shall [&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-4526","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\/4526"}],"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=4526"}],"version-history":[{"count":1,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/4526\/revisions"}],"predecessor-version":[{"id":4531,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/4526\/revisions\/4531"}],"wp:attachment":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4526"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4526"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4526"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}