{"id":3481,"date":"2025-05-29T13:29:08","date_gmt":"2025-05-29T13:29:08","guid":{"rendered":"https:\/\/blog.embeddedexpert.io\/?p=3481"},"modified":"2025-05-29T13:29:10","modified_gmt":"2025-05-29T13:29:10","slug":"stm32-adc-application-part-4-read-multiple-channels-using-interrupt-and-dma","status":"publish","type":"post","link":"https:\/\/blog.embeddedexpert.io\/?p=3481","title":{"rendered":"STM32 ADC Application Part 4: Read Multiple Channels Using Interrupt and DMA"},"content":{"rendered":"\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"683\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/ChatGPT-Image-May-29-2025-at-03_43_23-PM-1024x683.png\" alt=\"\" class=\"wp-image-3482\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/ChatGPT-Image-May-29-2025-at-03_43_23-PM-1024x683.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/ChatGPT-Image-May-29-2025-at-03_43_23-PM-300x200.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/ChatGPT-Image-May-29-2025-at-03_43_23-PM-768x512.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/ChatGPT-Image-May-29-2025-at-03_43_23-PM-1150x767.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/ChatGPT-Image-May-29-2025-at-03_43_23-PM-750x500.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/ChatGPT-Image-May-29-2025-at-03_43_23-PM-400x267.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/ChatGPT-Image-May-29-2025-at-03_43_23-PM-250x167.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/ChatGPT-Image-May-29-2025-at-03_43_23-PM.png 1536w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>In this fourth part of the STM32 ADC series, we focus on using\u00a0<strong>interrupts<\/strong>\u00a0and\u00a0<strong>DMA<\/strong>\u00a0to efficiently read multiple ADC channels in scan mode. These techniques allow seamless, non-blocking acquisition of multi-channel analog data, significantly reducing CPU load while ensuring timely and reliable data collection.<\/p>\n\n\n\n<p>In this guide, we shall cover:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Interrupt vs DMA.<\/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. Interrupt VS DMA:<\/h2>\n\n\n\n<p>When implementing ADC (Analog-to-Digital Converter) functionality in STM32 microcontrollers, especially for reading multiple channels or acquiring high-speed signals, selecting between&nbsp;<strong>DMA (Direct Memory Access)<\/strong>&nbsp;and&nbsp;<strong>Interrupt<\/strong>modes is a critical architectural decision. Each approach has its own benefits, limitations, and ideal scenarios. This section breaks down both methods extensively.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n\n\n\n<h3 class=\"wp-block-heading\">\u2756 1. Overview<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Aspect<\/th><th>DMA<\/th><th>Interrupt<\/th><\/tr><\/thead><tbody><tr><td>Mechanism<\/td><td>Transfers ADC data directly to memory without CPU involvement.<\/td><td>ADC conversion completion triggers an ISR (Interrupt Service Routine).<\/td><\/tr><tr><td>CPU Involvement<\/td><td>Minimal or none<\/td><td>Moderate (CPU executes ISR)<\/td><\/tr><tr><td>Speed\/Efficiency<\/td><td>High-speed, suitable for large or continuous data<\/td><td>Moderate speed; overhead increases with more channels or higher sample rate<\/td><\/tr><tr><td>Complexity<\/td><td>Medium (initial setup, but very efficient later)<\/td><td>Low to medium (depends on logic in ISR)<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n\n\n\n<h3 class=\"wp-block-heading\">\u2756 2. DMA (Direct Memory Access)<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">\u2705 Advantages:<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Non-blocking<\/strong>: Frees the CPU to perform other tasks while data is being transferred.<\/li>\n\n\n\n<li><strong>High Throughput<\/strong>: Ideal for high-speed sampling or when reading multiple ADC channels rapidly.<\/li>\n\n\n\n<li><strong>Efficient Buffering<\/strong>: Allows use of circular buffers for continuous acquisition (great for real-time systems).<\/li>\n\n\n\n<li><strong>Lower Latency<\/strong>: Faster transfer from ADC data register to memory.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">\u274c Disadvantages:<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>More Complex Setup<\/strong>: Requires configuring the DMA controller, ADC, and sometimes a timer trigger.<\/li>\n\n\n\n<li><strong>Debugging Difficulty<\/strong>: Errors are harder to debug compared to simple polling or interrupt routines.<\/li>\n\n\n\n<li><strong>Fixed Transfer Size<\/strong>: Predefined buffer sizes must be used, although circular mode helps mitigate this.<\/li>\n\n\n\n<li><strong>Memory Bound<\/strong>: DMA uses RAM, so buffer sizes must be carefully managed in memory-limited systems.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">\u2705 Best Use Cases:<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>High-speed sampling (audio, vibration, high-rate sensors).<\/li>\n\n\n\n<li>Real-time systems needing minimal CPU interruption.<\/li>\n\n\n\n<li>Acquisition from multiple channels with predictable sampling timing.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n\n\n\n<h3 class=\"wp-block-heading\">\u2756 3. Interrupt Method<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">\u2705 Advantages:<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Simple to Implement<\/strong>: Suitable for beginners or low-speed applications.<\/li>\n\n\n\n<li><strong>Flexible Logic<\/strong>: Custom post-processing logic (e.g., filtering, calibration) can be executed immediately in the ISR.<\/li>\n\n\n\n<li><strong>Low Resource Requirement<\/strong>: Does not require special peripheral like DMA; uses standard interrupt controller.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">\u274c Disadvantages:<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>CPU Overhead<\/strong>: Each ADC conversion triggers an interrupt, causing increased context switching.<\/li>\n\n\n\n<li><strong>ISR Latency<\/strong>: If multiple channels or fast sampling is involved, ISR delay may lead to missed conversions.<\/li>\n\n\n\n<li><strong>Blocking Risk<\/strong>: If ADC interrupt is not prioritized properly, it can lead to delays in critical tasks.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">\u2705 Best Use Cases:<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Low-frequency or event-driven ADC sampling.<\/li>\n\n\n\n<li>Applications requiring immediate, small-scale post-processing.<\/li>\n\n\n\n<li>Prototyping and learning ADC fundamentals.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n\n\n\n<h3 class=\"wp-block-heading\">\u2756 4. Performance Comparison (Example Case)<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Scenario<\/th><th>DMA<\/th><th>Interrupt<\/th><\/tr><\/thead><tbody><tr><td>3 Channels @ 10kHz Sample Rate<\/td><td>\u2705 Efficient, handles easily<\/td><td>\u26a0\ufe0f CPU load increases, ISR stack grows<\/td><\/tr><tr><td>Single Channel @ 500Hz<\/td><td>\u26a0\ufe0f Overkill<\/td><td>\u2705 Simple and effective<\/td><\/tr><tr><td>Real-time control loop (e.g., PID)<\/td><td>\u2705 Precise timing with Timer + DMA<\/td><td>\u26a0\ufe0f ISR jitter may affect stability<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n\n\n\n<h3 class=\"wp-block-heading\">\u2756 5. Combined Use (DMA + Interrupt)<\/h3>\n\n\n\n<p>For advanced cases,&nbsp;<strong>DMA and Interrupt can be combined<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>DMA handles the transfer<\/strong>, and an\u00a0<strong>interrupt is triggered at the end of transfer<\/strong>\u00a0(EOT).<\/li>\n\n\n\n<li>Useful for batch processing or when memory buffers need to be processed after being filled.<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2. STM32CubeMX Setup:<\/h2>\n\n\n\n<p>From the setup of the previous guide from&nbsp;<a href=\"https:\/\/blog.embeddedexpert.io\/?p=3409\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a>.<\/p>\n\n\n\n<p>Open.ioc file and STM32CubeMX window shall appear.<\/p>\n\n\n\n<p>From left side menu, Analog and select ADC1 as following:<\/p>\n\n\n\n<p>Enable IN1, IN2 and IN4 as single ended input (Not all STM32 has this feature).<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"430\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_15-49-15-1024x430.jpg\" alt=\"\" class=\"wp-image-3462\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_15-49-15-1024x430.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_15-49-15-300x126.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_15-49-15-768x323.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_15-49-15-1536x645.jpg 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_15-49-15-2048x860.jpg 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_15-49-15-1150x483.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_15-49-15-750x315.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_15-49-15-400x168.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_15-49-15-250x105.jpg 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>This will translate to A0, A1 and A5 analog pins of Arduino in STM32F303RE Nucleo-64:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"534\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_15-54-46-1024x534.jpg\" alt=\"\" class=\"wp-image-3463\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_15-54-46-1024x534.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_15-54-46-300x156.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_15-54-46-768x400.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_15-54-46-1536x801.jpg 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_15-54-46-2048x1068.jpg 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_15-54-46-1150x600.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_15-54-46-750x391.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_15-54-46-400x209.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_15-54-46-250x130.jpg 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Next, we shall configure the ADC as following:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>First, set Number of Conversion to 3, this will enable Scan Conversion Mode<\/li>\n\n\n\n<li>Set end of Conversion Selection to End of sequence of conversion.<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"927\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-49-23-1024x927.jpg\" alt=\"\" class=\"wp-image-3485\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-49-23-1024x927.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-49-23-300x272.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-49-23-768x695.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-49-23-1150x1041.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-49-23-750x679.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-49-23-400x362.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-49-23-250x226.jpg 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-49-23.jpg 1226w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, set the rand of the channels as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"589\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_16-02-46-1024x589.jpg\" alt=\"\" class=\"wp-image-3465\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_16-02-46-1024x589.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_16-02-46-300x173.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_16-02-46-768x442.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_16-02-46-750x432.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_16-02-46-400x230.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_16-02-46-250x144.jpg 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-17_16-02-46.jpg 1046w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>By setting the rank as shown, the conversion sequence shall be like this:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Channel 1.<\/li>\n\n\n\n<li>Channel 2.<\/li>\n\n\n\n<li>Channel 6.<\/li>\n<\/ul>\n\n\n\n<p>Next, from DMA, enable DMA as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"867\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-53-08-1024x867.jpg\" alt=\"\" class=\"wp-image-3486\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-53-08-1024x867.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-53-08-300x254.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-53-08-768x650.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-53-08-1150x974.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-53-08-750x635.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-53-08-400x339.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-53-08-250x212.jpg 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-53-08.jpg 1358w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, from NVIC, enable ADC interrupt as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"941\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-55-55-1024x941.jpg\" alt=\"\" class=\"wp-image-3487\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-55-55-1024x941.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-55-55-300x276.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-55-55-768x706.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-55-55-1150x1057.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-55-55-750x689.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-55-55-400x368.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-55-55-250x230.jpg 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-55-55.jpg 1356w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>From parameters settings, enable DMA continuous request as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"992\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-58-05-1024x992.jpg\" alt=\"\" class=\"wp-image-3488\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-58-05-1024x992.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-58-05-300x291.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-58-05-768x744.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-58-05-1150x1114.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-58-05-750x727.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-58-05-400x388.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-58-05-250x242.jpg 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/2025-05-29_15-58-05.jpg 1294w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Thats all for the STM32CubeMX setup.<\/p>\n\n\n\n<p>Save the project and the project shall be generated.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3. Firmware Development:<\/h2>\n\n\n\n<p>Once the project has been generated, main.c shall be opened.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3.1 Interrupt Method<\/h2>\n\n\n\n<p>In main.c, start by declaring the following:<\/p>\n\n\n\n<p>In user code begin PV:<\/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;}\">#define ADC_Size 3\nuint16_t adcValue[ADC_Size];<\/pre><\/div>\n\n\n\n<p>This array shall hold the converted ADC data.<\/p>\n\n\n\n<p>Next, in user code begin 2, in main function, start the ADC in the interrupt mode 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;}\">HAL_ADC_Start_IT(&amp;hadc1);<\/pre><\/div>\n\n\n\n<p>Once the ADC completed the conversion, the ADC conversion completed callback shall be called 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;}\">void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)\n{\n\tif(hadc-&gt;Instance==ADC1)\n    {\n    \tfor (int i=0;i&lt;ADC_Size;i++)\n\t\t{\n\t\t\tadcValue[i]=HAL_ADC_GetValue(hadc);\n\t\t}\n\t\tHAL_ADC_Start_IT(hadc);\n    }<\/pre><\/div>\n\n\n\n<p>In the function, check if the interrupt source is ADC1, if it is, read all the channels and start the ADC in the interrupt mode again. <\/p>\n\n\n\n<p>Notice that we are forced to read the data using for loop which is inefficient at all.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3.2 DMA Method:<\/h2>\n\n\n\n<p>In user code begin 2 in main function, start the ADC in 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;}\">HAL_ADC_Start_DMA(&amp;hadc1, &amp;adcValue, 3);<\/pre><\/div>\n\n\n\n<p>The function shall start the ADC in DMA mode and takes the following parameters:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Instant to the ADC which is hadc1.<\/li>\n\n\n\n<li>Address of the array that holds the adc converted data.<\/li>\n\n\n\n<li>Size of the conversion which is 3.<\/li>\n<\/ul>\n\n\n\n<p>Once the ADC completed the conversion, adc conversion completed callback shall be called:<\/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_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)\n{\n \tif(hadc-&gt;Instance==ADC1)\n    {\n    \tHAL_ADC_Start_DMA(hadc, adcValue, 3);\n    }\n}<\/pre><\/div>\n\n\n\n<p>Similar to interrupt method, rather than reading the adc converted data manually, DMA handled that in the background.<\/p>\n\n\n\n<p>In the interrupt check the interrupt source and if it ADC1, start the ADC again.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Thats all for the guide. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">4. Results:<\/h2>\n\n\n\n<p>Start the debug session and add adcValue to Live Expression and you should get the following:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"996\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/05\/ezgif-44b3a66d2d5cb5.gif\" alt=\"\" class=\"wp-image-3490\" \/><\/figure>\n\n\n\n<p>Next, we shall cover the continuous mode using DMA and interrupt, 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>In this fourth part of the STM32 ADC series, we focus on using\u00a0interrupts\u00a0and\u00a0DMA\u00a0to efficiently read multiple ADC channels in scan mode. These techniques allow seamless, non-blocking acquisition of multi-channel analog data, significantly reducing CPU load while ensuring timely and reliable data collection. In this guide, we shall cover: 1. Interrupt VS DMA: When implementing ADC [&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-3481","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\/3481"}],"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=3481"}],"version-history":[{"count":4,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/3481\/revisions"}],"predecessor-version":[{"id":3491,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/3481\/revisions\/3491"}],"wp:attachment":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3481"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3481"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3481"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}