{"id":12005,"date":"2024-01-29T00:21:25","date_gmt":"2024-01-28T22:21:25","guid":{"rendered":"https:\/\/deepbluembedded.com\/?p=12005"},"modified":"2024-02-01T09:44:41","modified_gmt":"2024-02-01T07:44:41","slug":"stm32-adc-multi-channel-scan-continuous-mode-dma-poll-examples","status":"publish","type":"post","link":"https:\/\/deepbluembedded.com\/stm32-adc-multi-channel-scan-continuous-mode-dma-poll-examples\/","title":{"rendered":"STM32 ADC Multi-Channel Scan Continuous Mode DMA Example"},"content":{"rendered":"\n

In this tutorial, we’ll discuss the STM32 ADC Multi-Channel Scan Continuous Conversion Mode with DMA<\/strong> for reading the ADC conversion results of a regular group of channels. You’ll learn how STM32 ADC Multi-Channel Scan mode works and how to use it to read a regular group of multiple ADC channels and get the conversion data using DMA with the STM32 HAL API functions.<\/p>\n\n\n\n

The practical example we’ll implement in this tutorial is an STM32 LED dimmer using multiple potentiometers on multiple analog input pins and multiple PWM outputs to control the brightness of some LEDs. We’ll read the multiple analog input channels (regular group) in continuous-conversion mode using the DMA. Without further ado, let’s get right into it!<\/p>\n\n\n

Table of Contents<\/h2>\n
    \n
  1. STM32 ADC Multi-Channel Scan (Continuous-Conversion)<\/a>\n\n\n<\/li>\n\n<\/li>\n\n
  2. STM32 ADC Multi-Channel Continuous Mode Example<\/a>\n\n<\/li>\n
  3. STM32 ADC Multi-Channel Scan Continuous Mode DMA Example<\/a>\n\n\n<\/li>\n\n<\/li>\n\n<\/li>\n\n
  4. STM32 ADC Multi-Channel Scan (Continuous-Conversion) Polling<\/a>\n\n<\/li>\n
  5. STM32 ADC Multi-Channel Scan (Continuous-Conversion) Interrupt<\/a>\n\n<\/li>\n
  6. Wrap Up<\/a>\n<\/li><\/ol>\n\n\n
    \n\n\n

    STM32 ADC Multi-Channel Scan (Continuous-Conversion)<\/strong><\/h2>\n\n\n

    In this tutorial, we’ll explore the STM32 ADC Multi-Channel Scan Mode<\/strong> in continuous-conversion mode. In this mode, the ADC will start converting the configured regular group of channels one by one according to the channel ranks (from low to high) till the end of the group where it stops and generates an interrupt signal indicating the end of the regular group’s conversion process.<\/p>\n\n\n\n

    The ADC will then “automatically<\/strong>” restart\/re-trigger itself to start converting the regular group channels one by one again and keep repeating forever. This is unlike the Multi-Channel Scan Single-Conversion Mode<\/a><\/strong> that we’ve discussed in a previous tutorial. Where the ADC stops at the end of the regular group conversion and it needs to be “manually<\/strong>” started again.<\/p>\n\n\n\n

    The STM32 ADC Multi-Channel Scan Continuous Mode can only be used with DMA to get the ADC reading results as soon as the group conversion is completed. And that’s what we’ll do in the practical example project hereafter in this tutorial.<\/p>\n\n\n\n

    \"STM32-ADC-Multi-Channel-Scan-Continuous-Conversion-Mode-DMA-Interrupt-Polling\"<\/figure>\n\n\n\n

    <\/p>\n\n\n

    1. STM32 ADC Multi-Channel Polling<\/strong> (Continuous Conversion)<\/h3>\n\n\n

    Can we use Polling with STM32 ADC Multi-Channel Scan in Continuous-Conversion Mode? The answer is NO. The polling can only be used with the ADC Multi-Channel Scan in Discontinuous Mode, Refer to this tutorial<\/strong> & example project<\/a> for more details.<\/p>\n\n\n\n

    Therefore, it’s impractical and near impossible to implement a polling-based software or state machine to read the ADC scan (regular group) conversions in the continuous-conversion mode.<\/p>\n\n\n

    2. STM32 ADC Multi-Channel Interrupt<\/strong> (Continuous Conversion)<\/h3>\n\n\n

    Using interrupts with ADC Multi-Channel scan mode is just impossible as stated in the documentation of the STM32 microcontroller’s datasheet. Because the interrupt signal is fired at the end of the regular group conversion. Whether it’s a single-conversion or continuous-conversion mode, it won’t work.<\/p>\n\n\n\n

    In other words, the ADC result buffer will keep getting overwritten by the regular group channels one by one from the lowest rank to the highest. At the time of interrupt firing, you’ll only find the last group’s channel reading in the ADC buffer and all the previous channels are gone forever.<\/p>\n\n\n

    3. STM32 ADC Multi-Channel DMA<\/strong> (Continuous Conversion)<\/h3>\n\n\n

    And here comes the third method which is using the DMA unit that can directly transfer the ADC result from the peripheral to the memory in the manner that you want and program it to. All that is done without any CPU intervention and upon DMA transfer completion, it can notify the CPU to process the data or whatever.<\/p>\n\n\n

    \n
    \u2755 Note<\/div>\n\n\n

    To sum things up, the STM32 ADC in Multi-Channel Scan Mode (Continuous-Conversion) can only be reliably used with DMA. The interrupt-based reading is impossible to implement and the polling method can potentially cause your system to fail (halt forever) or get the channels’ readings mixed up no matter how hard you try to build a perfect state machine to capture the readings at the right time.<\/p>\n\n<\/div><\/div>\n\n\n


    \n\n\n

    STM32 ADC Multi-Channel Continuous Mode Example<\/strong><\/h2>\n\n\n

    In this example project, we’ll create an STM32 LED Dimmer using ADC & PWM to read multiple analog inputs from 4x potentiometers to control the brightness of 4x PWM outputs going to 4x LEDs. This demo will run the STM32 ADC in multi-channel continuous-conversion mode<\/strong> using DMA.<\/p>\n\n\n\n

      \n
    • Set up a new project as usual with system clock @ 72MHz<\/span><\/li>\n\n\n\n
    • Set up 4x Analog Input Pins (Channel 6, 7, 8, and 9) In Continuous-Conversion Mode<\/strong> (The 4x Pot. Pin)<\/span><\/li>\n\n\n\n
    • Set up Timer2 in PWM mode with output on channels 1-4 (The 4x LED Pins) <\/span><\/li>\n<\/ul>\n\n\n\n
      \n\n\n

      STM32 ADC Multi-Channel Scan Continuous Mode<\/strong><\/strong><\/strong> DMA Example<\/strong><\/strong><\/strong><\/h2>\n\n\n

      In this LAB, our goal is to build a system that initializes multiple ADC analog input pins (4x channels: 6-9). And also configure a timer module to operate in PWM mode with 4x outputs on channels 1-4 (4x LED pins). Therefore, we can start an ADC regular group conversion, get & map the results to the PWM duty cycle registers, and repeat the whole process over and over again.<\/p>\n\n\n\n

      And now, let’s build this system step-by-step<\/span><\/strong><\/span><\/p>\n\n\n

      \n\n

      Step #1<\/strong><\/p>\n\n<\/div>\n\n\n

      Open STM32CubeMX<\/strong>, create a new project, and select the target microcontroller. <\/p>\n\n\n

      \n\n

      Step #2<\/strong><\/p>\n\n<\/div>\n\n\n

      Configure The ADC1<\/strong> Peripheral, Enable the Channels: 6-9<\/strong>, and enable the continuous mode. You’ll find that the analog channel has these default configurations which happens to be ok for us in this example project.<\/p>\n\n\n\n

      \"STM32-ADC-Multi-Channel-Scan-Continuous-Mode-Example-CubeMX\"<\/figure>\n\n\n\n

      <\/p>\n\n\n

      \n\n

      Step #3<\/strong><\/p>\n\n<\/div>\n\n\n

      The DMA interrupt is enabled by default in the NVIC controller tab. The STM32 DMA configurations for the ADC will be as shown below.<\/p>\n\n\n\n

      \"STM32-Multi-Channel-Scan-DMA-Single-Conversion-HAL-Example-CubeMX-Configuration\"<\/figure>\n\n\n\n

      <\/p>\n\n\n

      \n\n

      Step #4<\/strong><\/p>\n\n<\/div>\n\n\n

      Configure Timer2<\/strong> To Operate In PWM<\/strong> Mode With Output On CH1-4<\/strong>.<\/p>\n\n\n\n

      \"STM32-Multiple-Channel-Scan-Single-Conversion-Example-CubeMX\"<\/figure>\n\n\n\n

      <\/p>\n\n\n

      \n\n

      Step #5<\/strong><\/p>\n\n<\/div>\n\n\n

      Go to the RCC<\/strong> clock configuration page and enable the HSE external crystal oscillator<\/strong> input.<\/p>\n\n\n\n

      \"STM32<\/figure>\n\n\n\n

      <\/p>\n\n\n

      \n\n

      Step #6<\/strong><\/p>\n\n<\/div>\n\n\n

      Go to the clock configurations page, and select the HSE<\/strong> as a clock source, PLL<\/strong> output, and type in 72<\/strong>MHz for the desired output system frequency. Hit the “Enter<\/code>” key, and let the application solve for the required PLL dividers\/multipliers to achieve the desired clock rate.<\/p>\n\n\n\n

      \"STM32-Blue-Pill-Proteus-Library-Simulation-Clock-Configuration\"<\/figure>\n\n\n\n

      <\/p>\n\n\n

      \n\n

      Step #7<\/strong><\/p>\n\n<\/div>\n\n\n

      Name & Generate The Project Initialization Code For CubeIDE or The IDE You’re Using.<\/p>\n\n\n

      STM32 ADC Multi-Channel Scan Continuous-Conversion (DMA) Example Code<\/strong><\/h3>\n\n\n

      Here is The Application Code For This LAB (main.c)<\/strong><\/span><\/p>\n\n\n\n

      \/*\n * LAB Name: STM32 ADC Multi-Channel (Scan) Continuous-Conversion Mode - DMA\n * Author: Khaled Magdy\n * For More Info Visit: www.DeepBlueMbedded.com\n*\/\n#include \"main.h\"\n\nuint32_t AD_RES_BUFFER[4];\nADC_HandleTypeDef hadc1;\nDMA_HandleTypeDef hdma_adc1;\nTIM_HandleTypeDef htim2;\n\nvoid SystemClock_Config(void);\nstatic void MX_GPIO_Init(void);\nstatic void MX_DMA_Init(void);\nstatic void MX_ADC1_Init(void);\nstatic void MX_TIM2_Init(void);\n\nint main(void)\n{\n    HAL_Init();\n    SystemClock_Config();\n    MX_GPIO_Init();\n    MX_DMA_Init();\n    MX_ADC1_Init();\n    MX_TIM2_Init();\n    HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);\n    HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);\n    HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3);\n    HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4);\n    HAL_ADC_Start_DMA(&hadc1, AD_RES_BUFFER, 4);\n\n    while (1)\n    {\n    \t\/\/ Nothing To Do!\n    }\n}\n\nvoid HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)\n{\n    \/\/ Conversion Complete & DMA Transfer Complete As Well\n    \/\/ So The AD_RES_BUFFER Is Now Updated & Let's Move Values To The PWM CCRx\n    \/\/ Update The PWM Channels With Latest ADC Scan Conversion Results\n    TIM2->CCR1 = (AD_RES_BUFFER[0] << 4);  \/\/ ADC CH6 -> PWM CH1\n    TIM2->CCR2 = (AD_RES_BUFFER[1] << 4);  \/\/ ADC CH7 -> PWM CH2\n    TIM2->CCR3 = (AD_RES_BUFFER[2] << 4);  \/\/ ADC CH8 -> PWM CH3\n    TIM2->CCR4 = (AD_RES_BUFFER[3] << 4);  \/\/ ADC CH9 -> PWM CH4\n}<\/pre>\n\n\n\n

      <\/p>\n\n\n

      ADC Multi-Channel DMA Code Explanation<\/strong><\/strong><\/strong><\/h3>\n\n\n

      We only trigger the ADC once to start conversion in DMA mode. As it’s running in the continuous-conversion mode, it’ll keep triggering itself after the completion of the regular group conversion and we don’t need to “manually” start\/trigger the ADC again.<\/p>\n\n\n\n

      Upon DMA transfer completion, the DMA interrupt is fired. In its ISR, we read the ADC_RES_BUFFER<\/code> array to get the readings of the ADC Channels (6-9). Then we map those readings from 12-bit to 16-bit values to be written to the timer’s CCRx registers for PWM duty cycle control.<\/p>\n\n\n

      \n
      \u2755 Note<\/div>\n\n\n

      Using the DMA is the best way to read the STM32 ADC in multi-channel (scan) mode whether it’s a single-conversion (one-shot) or a continuous-conversion mode.<\/p>\n\n<\/div><\/div>\n\n

      Wiring<\/strong><\/h3>\n\n\n

      I know it looks messy, but there isn’t too much about it. These are only 4x LEDs and 4x potentiometers connected to the corresponding GPIO pins on the STM32 blue pill board.<\/p>\n\n\n\n

      \"STM32-ADC-Multiple-Channel-Scan-Example\"<\/figure>\n\n\n\n

      <\/p>\n\n\n

      STM32 ADC Multi-Channel Continuous-Conversion DMA Example Testing<\/strong><\/h3>\n\n\n
      \n