mercredi 3 septembre 2014

Using ADC with DMA on STM32 micrcontroller


The most received cry of help that I get form many people is about how using DMA with ADC for a continuous conversion of analog sensors inputs. So I made the decision to share the needed steps to configure it ( and also to avoid the duplication of my answer each time).

I will take advantage of STM32Cube project to make it easy and I admit that you have already STM32CubeMX software tool and the installed STM32CubeF4 Firmware. If it is ok, let's start then and for other people they can follow links to setup the tool and the firmware.







 Open STM32CubeMX tool and click on 'new project' I chose STM32F439BITx microcontroller from the list. You can chose your own if you are using a specific board. I guess that all ADCs in STM32 microcontrollers have this feature.













After that, form the Pinout view of the tool expand ADC peripheral and select the three internal channels (Temperature / Vref and Vbat ). You can select your own but I used these three channels as a demonstration in this tutorial.

In the second case, all what you have is to connect your analog inputs to the channels pins. Selected channel are colored in green in the microcontroller or you can operate conversely by selecting first your pin as ADC input by a right click on it. (exp PA0)















Now it's time to configure the ADC to operate as we want, (yes yes and the DMA too ;) ). So Click on ADC and add three regular channel configuration to be converted. Also don't forget to enable Scan Mode and continuous mode to have a continuous conversion of the three configured channels.











 Switch now to the 'DMA Settings' tab, add a DMA request by selecting the stream and the priority. Also enable the increment of the memory because we will store all converted values in a array. The used mode is the circular mode because we will have a continues transfer of a word (data width).





Click Ok, generate a project and open it (I use IAR wokbench you have the possibility to chose Keil or TrueSTUDIO too). We will add some extra code to complete our demo: In the main.c file we will declare a 32 bit array and we will start the ADC and the DMA by calling HAL_ADC_Start() and HAL_ADC_Start_DMA(). After each conversion the value is transferred form the data register of the ADC to the array and the DMA will increment the index automatically.
1:  /* USER CODE BEGIN PFP */  
2:  uint32_t ADC1ConvertedValues[1024];  
3:  /* USER CODE END PFP */  
4:  int main(void)  
5:  {  
6:   /* MCU Configuration----------------------------------------------------------*/  
7:   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */  
8:   HAL_Init();  
9:   /* Configure the system clock */  
10:   SystemClock_Config();  
11:   /* Initialize all configured peripherals */  
12:   MX_GPIO_Init();  
13:   MX_DMA_Init();  
14:   MX_ADC1_Init();  
15:   /* USER CODE BEGIN 2 */  
16:   // -- Enables ADC and starts conversion of the regular channels.  
17:   if( HAL_ADC_Start(&hadc1) != HAL_OK)  
18:    return 0;  
19:   // -- Enables ADC DMA request  
20:   if (HAL_ADC_Start_DMA(&hadc1, (uint32_t*)ADC1ConvertedValues, 2048) != HAL_OK)  
21:    return 0;  
22:   /* USER CODE END 2 */  
23:   /* Infinite loop */  
24:   while (1){}  
25:  }  

All converted values are transferred and stored in ADC1ConvertedValues which is an array of 1ko of size. Values are stored in the order of conversion ( Temp sensor then Vref then Vbat ).

This is a Live watch of ADC1ConvertedValues array while demo execution.

53 commentaires :

  1. Wow this is great. First post on the web explaining properly how to use these HAL libraries, since the 800-page documentation somehow still manages to suck. Merci beaucoup!

    A couple of questions: I don't quite get what the data length of 2048 means, considering you're passing the pointer to a 1024-long array of 32-bit unsigned ints. Could you explain what you're doing there exactly?
    Also: that setting for circular conversion, what exactly does that mean? Example: I have 8 channels to convert. I want the most recent value for each channel to be stored in one of the elements of an array of 8 uint32_t. Will the value for one channel always be in the same place, or will they "rotate" within the array? Could you clarify this?

    Thank you very much again!

    RépondreSupprimer
    Réponses
    1. Hi Marcors,

      To get this kind of information may be you must read the 1718-page documentation. But you need only one page to find the answer of your question. So as you said, we have an array of 1024 element of uint32_t and I put a 2048 as data length to the HAL_ADC_Start_DMA() function.

      In this case, The ADC converted value is coded in 16-bit in ADC_DR register (the real value is in 12 bits according to our resolution). In the other side, the size of our array element is 32 bits. Thus, the number of data transfer (stored in DMA_SxNDTR register) must be a multiple of 2 (1024*2=2048) else we will lose some data. This constraint is indicated in the table 47 in the reference manual of STM32F4 family.

      In thehe circular mode, the number of data items to be transferred is automatically reloaded with the initial value programmed during the stream configuration phase (stored in DMA_SxNDTR register) , and the DMA requests continue to be served. your explanation is correct and you will get this behavior for the DMA transaction.

      you are welcome at any time and I'm sorry for this delayed response.

      Supprimer
  2. Hi,
    thank you very much, i ve been looking for this code for ages.
    Still, i have one question: When i try tu run ADC in single scan mode, triggered by TIM2 TRGO update, it wont trigger. But when i use HAL_ADC_Start_IT(&hadc1); it works well.
    Any ideas why?

    RépondreSupprimer
  3. Hi Tomáš,

    So a quick overview of ADC interrupts, you can see that HAL_ADC_Start_IT() function in ADC driver only activate 2 interrupts of ADC peripheral ( End of conversion of a regular group and the Overrun interrupt) and HAL_ADCEx_InjectedStart_IT() activate also two interrupts ( End of conversion of a injected group and the Overrun interrupt) and the Analog watchdog interrupt is enabled when you set ITMode enabled in Watchdog configuration and you call HAL_ADC_AnalogWDGConfig().

    HAL_ADC_Start_IT() and HAL_ADCEx_InjectedStart_IT() also enable the ADC if it is disabled.

    Thus, It's easy to admit that HAL_ADC_Start_IT() has no effect in your configuration.

    But I have some helpful remarks to investigate deeply in your configuration:
    - make sure that he Timer 2 is enabled and the trigger output is sensible to the update event (the bit MMS is set to 010 in the TIM2_CR2 register)
    - make sure that the Timer 2 TRGO is selected as trigger source of your regular channels conversion. ( read the EXTSEL[3:0] bits in the ADC_CR2 register (it most be equal to 0110 in your case)
    - The polarity of the trigger must selected to falling, rising or both falling or rising.

    I hope that these remarks helps you !

    RépondreSupprimer
  4. Thank you very much for quick response :)
    I forgot to set DMA Continuous Requests = Enable in Cube :/
    Now it works fine

    RépondreSupprimer
  5. Hi, can you create an example for simultaneous launch 2x regular channels. Because I Can not understand. thank you

    RépondreSupprimer
    Réponses
    1. I'm sorry Nazar, I just saw your comment right now :(

      In this example, you find three regular channels configured, just remove any of them in CubeMX ADC configuration and it will works. ( You can disable the DMA if you want too, and access to the Data register each time the conversion completes).

      Supprimer
  6. Hello Ismail,

    i´m working with a stm32f4 discovery board. I tried to do the configurations that you recomended and also i have tried to configured for some other projects the dma.
    I realized that may be i´m missing some code, i mean, may be cube is not genearting some code or it´s generating wrong code for the example that you give.
    I´m getting this code in my project:

    void MX_DMA_Init(void)
    {
    /* DMA controller clock enable */
    __DMA2_CLK_ENABLE();

    /* DMA interrupt init */
    HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);

    }

    That means that somehow cube is allowing interrupts for dma, what i never configured.

    Is it possible, that you post may be your whole generated code or upload a file, so that i can check if i´m right?

    Thank you for your answere.

    Samir

    RépondreSupprimer
    Réponses
    1. Hi Samir,

      Let's say that DMA interrupt is enabled in case you want to catch one of these events: DMA transfer complete/ DMA half transfer complete/ DMA FIFO error/ DMA direct mode error/ DMA transfer error. Callbacks of these function are to define in 'hdma_adc1' pointer ( lock at DMA_HandleTypeDef structure in stm32f4xx_hal_dma.h file).

      The configuration is done in HAL_ADC_MspInit function in the generated file stm32f4xx_hal_msp.c, thus if you let callbacks as NULL none of them will be taken in consideration by the HAL. It's a choice made to let the user control DMA transfert if he wish.

      You can disable the DMA interrupts if you want by removing the two line under /* DMA interrupt init */ comment and also by removing DMA2_Stream0_IRQHandler function in stm32f4xx_it.c generated file.

      hope that I answer your question and for the code source,the code that I have is the same as the generated code by 4.8 version of CubeMX. all modification that I made are done in main function and are listed on the article.

      You can disable the DMA interrupts if you want by removing the two lines under /* DMA interrupt unit */ comment and also by removing the DMA2_Stream0_IRQHandler function in stm32f4xx_it. c generated file.

      Hope that I replied on your question and for the code source, the code that I have is the same as the generated code by 4.8 versions of CubeMX. all modifications that I made are done in the main function and are listed in the article.

      Supprimer
    2. Hello Ismail,

      thank you very much for your answer. I´ve found the configurations in the file, that you mentioned above.

      I´m wondering still why the dma is not saving data in the memory from the adc, It dosn´t saves any data from the adc in the array. I´m using in my programm the DMA for ADC1-IN8.

      Do you know how i could check, if the dma is correctly configured with any dma-function of the HAL-drivers?

      What do you think it could be my problem? I post some of my code from the main.c and stm32f4xx_hal_msp.c:

      -----------------
      main.c
      --------------------

      /* ADC1 init function */
      void MX_ADC1_Init(void)
      {

      ADC_ChannelConfTypeDef sConfig;

      /**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
      */
      hadc1.Instance = ADC1;
      hadc1.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV8;
      hadc1.Init.Resolution = ADC_RESOLUTION12b;
      hadc1.Init.ScanConvMode = ENABLE;
      hadc1.Init.ContinuousConvMode = ENABLE;
      hadc1.Init.DiscontinuousConvMode = DISABLE;
      hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
      hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
      hadc1.Init.NbrOfConversion = 1;
      hadc1.Init.DMAContinuousRequests = ENABLE;
      hadc1.Init.EOCSelection = EOC_SINGLE_CONV;
      HAL_ADC_Init(&hadc1);

      /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
      */
      sConfig.Channel = ADC_CHANNEL_8;
      sConfig.Rank = 1;
      sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
      HAL_ADC_ConfigChannel(&hadc1, &sConfig);

      }


      /**
      * Enable DMA controller clock
      */
      void MX_DMA_Init(void)
      {
      /* DMA controller clock enable */
      __DMA2_CLK_ENABLE();

      /* DMA interrupt init */
      HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
      HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);

      }

      /** Pinout Configuration
      */
      void MX_GPIO_Init(void)
      {

      /* GPIO Ports Clock Enable */
      __GPIOB_CLK_ENABLE();

      }

      -----------------------------------

      ---------------------------------
      stm32f4xx_hal_msp.c
      ---------------------------------



      void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
      {

      GPIO_InitTypeDef GPIO_InitStruct;
      if(hadc->Instance==ADC1)
      {
      /* USER CODE BEGIN ADC1_MspInit 0 */

      /* USER CODE END ADC1_MspInit 0 */
      /* Peripheral clock enable */
      __ADC1_CLK_ENABLE();

      /**ADC1 GPIO Configuration
      PB0 ------> ADC1_IN8
      */
      GPIO_InitStruct.Pin = GPIO_PIN_0;
      GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
      GPIO_InitStruct.Pull = GPIO_NOPULL;
      HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

      /* Peripheral DMA init*/

      hdma_adc1.Instance = DMA2_Stream0;
      hdma_adc1.Init.Channel = DMA_CHANNEL_0;
      hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
      hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
      hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
      hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
      hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
      hdma_adc1.Init.Mode = DMA_CIRCULAR;
      hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
      hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
      hdma_adc1.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
      hdma_adc1.Init.MemBurst = DMA_MBURST_SINGLE;
      hdma_adc1.Init.PeriphBurst = DMA_PBURST_SINGLE;
      HAL_DMA_Init(&hdma_adc1);

      __HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);

      /* USER CODE BEGIN ADC1_MspInit 1 */

      /* USER CODE END ADC1_MspInit 1 */
      }

      }

      Hopefully you can help me to find the problem. Thanks for your answer.

      Samir

      Supprimer
    3. Hi Samir,

      Check the frequency of ADC and DMA to not have an Overrun problem, if the conversion is done and data are not yet transferred, DMA transfers are then disabled and DMA requests are no longer accepted! and I guess this is why your array still empty.

      Ismail

      Supprimer
  7. Ce commentaire a été supprimé par l'auteur.

    RépondreSupprimer
  8. Hello Ismail,

    thank you for your answer.

    What kind of values did you used for the SYSCLK, AHB Prescaler and APB2 Prescaler in your example? Could you may be upload one single shot of the clock configuration of your configuration of your cube programm?

    I need this to compare with the values, that cube configured by itself for my programm. Or if you cannot upload the shot may be you can tell me the values of that clock values for the ADC.

    Thank you very much.

    Samir

    RépondreSupprimer
    Réponses
    1. Sorry Samir, I take care now of your comment !

      For me I set the APB prescaler to /2 to have 8Mhz for the ADC and I let the HCLK to have 16Mhz for the DMA. it's the only modification I did.

      Ismail

      Supprimer
  9. Was anyone successful using Scan Mode without DMA on STM32Fxxx?
    I tried with 2 channels something like:

    for(;;)
    {
    if( HAL_ADC_Start(&hadc1) != HAL_OK)
    return;

    if (HAL_ADC_PollForConversion(&hadc1, 100) == HAL_OK)
    {
    val[0] = HAL_ADC_GetValue(&hadc1);
    val[1] = HAL_ADC_GetValue(&hadc1);

    }
    }

    Both val[0] and val[1] are the same, how do I tell GetValue which channel I want to read?

    RépondreSupprimer
    Réponses
    1. Hi Dareman,

      Can I look to your ADC configuration?

      Ismail.

      Supprimer
  10. Ce commentaire a été supprimé par l'auteur.

    RépondreSupprimer
  11. Hi Ismail,

    What do I do if I want to sample a sound signal with ADC at 44.1KHz and collect 1024 samples to do an FFT. Do I use timer, or what is the smarter way? PLease let me know. Thanks.

    RépondreSupprimer
    Réponses
    1. Hi Nabeel,

      I had never did this before, but try to configure a Time with the at least the multiple of the 44.1kHz frequency and activate its trigger source to the ADC. By this way, your ADC will be triggered each pulse and it will convert your input channel.

      After that, you can read data in EndOfConversion interruption. But the smartest way is the use of DMA due to the highest number of samples!

      Ismail

      Supprimer
    2. Okay, but do I still need to use Timer to pace ADC at 44.1KHz if I use DMA?

      Supprimer
  12. Yes, I guess! not 44.1! to pace ADC to 88.2 at least.

    RépondreSupprimer
  13. Hi Ismail,
    I just want to say thank you. I was stuck since two days. My 2 channels ADC->DMA->Interrupt chain refused to work. The DMA made its job and generated an IT, but only for the first occurrence and then stopped working. After reading your post, I figured out that the "DMA continuous request" was OFF! Everything works fine now. Your post was very useful.

    Merci encore et excellente journée!

    Daniel

    RépondreSupprimer
    Réponses
    1. Hi Daniel,

      I'm happy to hear that my post helped you, you are welcome.

      Bonne journée à vous aussi.

      Ismail

      Supprimer
  14. Hello Ismail,

    Thank you for your demonstration,

    I would like to ask you how can i configure 1 ADC with 2 external channels ( channel 1 and 2 for example)

    I configured ADC1 like:
    /* ADC1 init function */
    void MX_ADC1_Init(void)
    {

    ADC_ChannelConfTypeDef sConfig;

    /**Common config
    */
    hadc1.Instance = ADC1;
    hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
    hadc1.Init.Resolution = ADC_RESOLUTION_12B;
    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
    hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
    hadc1.Init.LowPowerAutoWait = DISABLE;
    hadc1.Init.ContinuousConvMode = ENABLE;
    hadc1.Init.NbrOfConversion = 3;
    hadc1.Init.DiscontinuousConvMode = DISABLE;
    hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
    hadc1.Init.DMAContinuousRequests = ENABLE;
    hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
    hadc1.Init.OversamplingMode = DISABLE;
    HAL_ADC_Init(&hadc1);

    /**Configure Regular Channel
    */
    sConfig.Channel = ADC_CHANNEL_1;
    sConfig.Rank = 1;
    sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLE_5;
    sConfig.SingleDiff = ADC_SINGLE_ENDED;
    sConfig.OffsetNumber = ADC_OFFSET_NONE;
    sConfig.Offset = 0;
    HAL_ADC_ConfigChannel(&hadc1, &sConfig);

    /**Configure Regular Channel
    */
    sConfig.Channel = ADC_CHANNEL_6;
    sConfig.Rank = 2;
    HAL_ADC_ConfigChannel(&hadc1, &sConfig);

    /**Configure Regular Channel
    */
    sConfig.Channel = ADC_CHANNEL_10;
    sConfig.Rank = 3;
    HAL_ADC_ConfigChannel(&hadc1, &sConfig);

    }

    But only ADC_CHANNEL_1 that i saw when testing my project.

    Merci
    Ghada

    RépondreSupprimer
    Réponses
    1. Hi Ghada,

      Have you started the conversion by software trigger? Are you sure that the sources are connected to your µC? Have you verified the configuration of GPIO pins (they must be set to analog).

      Try to switch Channel 1 by 6, to see if channel 6 is converted or not.

      Supprimer
  15. Ce commentaire a été supprimé par l'auteur.

    RépondreSupprimer
  16. HI Ismail!
    Great tutorial, thank you!
    I am trying to use one channel of the ADC together with the DMA, in a FreeRTOS environment (but that's not important). I want to ask you, where do I have to define those callbacks for the DMA transfer complete? And what is the difference between the interrupt handler of the DMA and these callback functions? I mean, in the callback, I assume I must read the buffer (because it is full), am I correct?
    Thank you so much!

    RépondreSupprimer
    Réponses
    1. Hi Andreeea,

      sorry I missed the notification of your comment.

      In general, the callback is called from the DMA ISR. To know if the buffer is full/half-full read the DMA status in the callback before doing your processing.

      In our case, The DMA callback will be called each time the copy the data from ADC data register. You can check the number of uncopied data in the DMA register to know if your buffer is full or not yet.

      Thanks,

      Supprimer
  17. Hi Ismail,

    I'm using a STM32F3. I don't know how it worked for you. For me, if I use HAL_ADC_Start and then HAL_ADC_Start_DMA, this last one returns HAL_BUSY and nothing ADC related work. Solution is not use HAL_ADC_Start.

    RépondreSupprimer
    Réponses
    1. Hi Pedro,

      I used STM32F439BITx target for this demo, however as described in ST website the HAL (Hardware Abstraction Layer) must be compatible with all STM32 microcontroller series. I don't have an F3 target to check it for you. (also other people which comments in this article didn't find the same problem than you, it can happen that the HAL is busy because you are using a blocking call of another STM32 peripheral).

      Regards,

      Supprimer
    2. Hello Pedro,

      I'm currently working on a stm32f303k8 and not using the HAL_ADC_Start fixed it for me as well. Thank you so much!

      Supprimer
  18. Hi Ismail,
    first of all I have to thank you, your tutorial allowed me to make huge steps forward in my project!
    As I needed to sample data from ADC1 and ADC3, I implemented the same code also for the second ADC.
    This means using another DMA channel and buffer.
    The ADC is triggered by the timer 3, set to 10kHz, while I check data every 1kHz.
    Then I send the data through a serial port to a computer visualizer.
    If I sample and send only one channel, they both work very well.
    But if I use them together this is what happens: The data coming from channel ADC3 (yellow) are ok,
    but the data coming from channel ADC1 are filled also with data coming from ADC3.
    I tried to understand what happens during the DMA data tranfer, because I think there is the problem.
    I was expecting 10 new values every ms, but the number of new samples at the TIM3 interrupt vary every time,
    from 1 to 28!
    Do you have an idea? Can you help me?
    thank you very much in advance!
    regards
    Leonardo

    Here's the code:

    #define DMA1_ARRAY_LEN 30
    #define DMA2_ARRAY_LEN 30

    int main(void)
    {

    /* USER CODE BEGIN 1 */

    uint32_t DMA1_error, DMA2_error;
    char str[32];

    /* USER CODE END 1 */

    /* MCU Configuration----------------------------------------------------------*/

    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();

    /* Configure the system clock */
    SystemClock_Config();

    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_DMA_Init();
    MX_ADC1_Init();
    MX_ADC3_Init();
    MX_OPAMP1_Init();
    MX_OPAMP2_Init();
    MX_OPAMP3_Init();
    MX_OPAMP4_Init();
    MX_SPI2_Init();
    MX_TIM3_Init();
    MX_USART1_UART_Init();

    /* USER CODE BEGIN 2 */

    LED_OFF(LED_GREEN_Pin);
    LED_OFF(LED_RED_Pin);

    HAL_OPAMP_Start(&hopamp1);
    HAL_OPAMP_Start(&hopamp2);
    HAL_OPAMP_Start(&hopamp3);
    HAL_OPAMP_Start(&hopamp4);

    SensorPower_ON();

    // -- Enables ADC DMA request
    if (HAL_ADC_Start_DMA(&hadc1, (uint32_t*)ADC1ConvertedValues, 2*DMA1_ARRAY_LEN) != HAL_OK)
    return 0;

    if (HAL_ADC_Start_DMA(&hadc3, (uint32_t*)ADC3ConvertedValues, 2*DMA2_ARRAY_LEN) != HAL_OK)
    return 0;

    // Enable Timer3
    HAL_TIM_Base_Start(&htim3);

    /* USER CODE END 2 */

    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {

    HAL_Delay(1);

    DMA1_error = HAL_DMA_GetError(&hdma_adc1);
    DMA2_error = HAL_DMA_GetError(&hdma_adc3);

    sprintf(str, "$%d %d;", ADC1ConvertedValues[1], ADC3ConvertedValues[1]); // serial port plotter viewer
    UART_return = HAL_UART_Transmit(&huart1, str, strlen(str), 1000);

    // Toggle green LED

    SysTick_Counter++;
    if (SysTick_Counter == 700)
    LED_ON(LED_GREEN_Pin);
    if (SysTick_Counter == 800)
    LED_OFF(LED_GREEN_Pin);
    if (SysTick_Counter == 900)
    LED_ON(LED_GREEN_Pin);
    if (SysTick_Counter == 1000)
    {
    LED_OFF(LED_GREEN_Pin);
    SysTick_Counter = 0;
    }

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

    }
    /* USER CODE END 3 */

    }

    RépondreSupprimer
  19. Hi Leonardo,

    I see that in your infinite loop you alwyas sends the first element of ADC1ConvertedValues/ADC3ConvertedValues tables.

    If I sample and send only one channel, they both work very well.
    >> One channel in each ADC?

    But if I use them together this is what happens: The data coming from channel ADC3 (yellow) are ok,
    >> And when you sampled only one channel you didn't use ADC1 and ADC3 together? (yellow?)

    but the data coming from channel ADC1 are filled also with data coming from ADC3.
    >> You want to say that the value of ADC1 channel is the same as ADC3 channel?

    I tried to understand what happens during the DMA data tranfer, because I think there is the problem.
    >> see my answer to samir revelo23.

    I was expecting 10 new values every ms, but the number of new samples at the TIM3 interrupt vary every time from 1 to 28!
    >> The TIM trigger each ADC conversion, you must look if the ADC END of conversion Or DMA Transfert complete interrupt are triggered 10 Times.

    Regards,
    Ismail

    RépondreSupprimer
  20. Hi Ismail,
    I decided to send only one sample per array thinking to perform in a way a downsamplig.
    Apparently it works.
    I tried using only one ADC at a time, thus disabling the other one. ADC1 ON, ADC3 OFF and ADC1 OFF, ADC3 ON. In these two cases I see the correct signals.
    When I use them together this is what happens:
    let's say that sig1 is: ,5,6,5,4,5,6,7,6,5,6,5,4,5,6,7...
    and sig3 is: 55,55,57,56,55,54,55,56,57,58,59,58,57,57,56,55...
    when I look at the DMA sampled data buffer I get,
    for ADC3: 55,55,57,56,55,54,55,56,57,58,59,58,57,57,56,55...
    for ADC1: 5,6,55,56,5,6,7,6,57,58,59,7,6,5,55,56...
    so I get both the signals in this buffer!

    Now I try to check if ADC END of conversion Or DMA Transfert complete interrupt are triggered 10 Times.

    thank you!

    leonardo

    RépondreSupprimer
  21. Hallo Ismail,

    I solved the problem and I think it could be interestng for you , too.
    I changed this line(s) in your code:
    HAL_ADC_Start_DMA(&hadc1, (uint32_t*)ADC1ConvertedValues, 2048)
    to this
    HAL_ADC_Start_DMA(&hadc1, ADC1ConvertedValues, 1024)

    And now I get the two ADCs and two DMAs working fine together.
    For sure you can understand better than me what's happening here, but it worked!

    thank you!
    Leo

    RépondreSupprimer
  22. Many thanks for your post! It is very helpful for getting the multi-channel DMA configured.

    I think your call to HAL_ADC_Start() is not needed. HAL_ADC_Start_DMA() will do what is needed. (I left it out and conversion still works.)

    I'm also curious about configuring DMA as circular. My desire is to start a single conversion with a buffer sized for the number of channels configured. I tried with "Normal" (vs. Circular) configuration and it seems to work.

    Regardless, merci beaucoup!

    RépondreSupprimer
    Réponses
    1. You are welcome, thanks for your feedback :)

      Supprimer
  23. Ce commentaire a été supprimé par l'auteur.

    RépondreSupprimer
  24. Good day, I am using 4 channels from ADC1. I followed everything from your code but I keep on getting "Program exit reached" every time I run the debugger. I modified a part of your code to:

    uint32_t ADC_values[4];
    HAL_ADC_Start_DMA(&hadc, ADC_values, 8);

    The program worked and I was able to run the debugger but I am not getting the expected results.
    The four elements from the ADC_value array are showing the same values that exceeds the 4095 value. It shows a value of 144000000. I tried varying the different inputs and I found out that the 144000000 value came from my 4th input. Every time I vary my 4th input, the 4 elements from the array also varies which makes my first 3 analog inputs useless.

    RépondreSupprimer
    Réponses
    1. Hi Karl,

      Please review your configuration to be sure that you have all 4 channels configured and not only the last one. Also try HAL_ADC_Start_DMA(&hadc, ADC_values, 4); if you are not working with STM32F4 family.

      Regards,

      Supprimer
    2. Ce commentaire a été supprimé par l'auteur.

      Supprimer
    3. I got it working, thank you very much for your code.

      Supprimer
  25. Ce commentaire a été supprimé par l'auteur.

    RépondreSupprimer
  26. Ce commentaire a été supprimé par l'auteur.

    RépondreSupprimer
  27. Ce commentaire a été supprimé par l'auteur.

    RépondreSupprimer
  28. Ce commentaire a été supprimé par l'auteur.

    RépondreSupprimer
  29. Hi

    I am looking to convert at around 44.1kHz using DMA. Do you know how to achieve this?

    Thanks

    RépondreSupprimer
    Réponses
    1. Please read comments above, I had the same demand and I answered it :)

      Supprimer
  30. GREAT POST THANK YOU,I WANT TO TAKE INPUT FROM 16 CHANNELS BUT I WANT TO TAKE ONE CHANNEL 8192 SAMPLES USING DMA AND THEN DO FFT AND SEND TO WEB,MY DOUBT HERE IS 8192*16*16K MEMORY IS NOT AVILABLE IN STM32F4 SO I DECIDED TO DO ONE CHANNEL WITH DMA ONE TIME THEN SWITCH TO NEXT CHANNEL VICEVERSA.BUT HOW TO REINITIALISE ADC-DMA WITH SECOND CHANNEL

    RépondreSupprimer
    Réponses
    1. Thank you,

      You must configure an End-Of-Conversion interrupt, and in this last one when occus you must change the channel selection in the ADC.

      Supprimer