AnsweredAssumed Answered

ADC with DMA - HAL libraries

Question asked by Marc O.S. on Nov 10, 2014
Latest reply on Nov 10, 2014 by Marc O.S.
Hello everyone,

I'm new to the forum so excuse me if I fail to hold to certain standards in my first posts.

I'm currently working with a STM32F4 Discovery board using the CubeMX-generated configuration code to program an application. Part of that is converting 8 ADC channels in DMA circular mode, and as I have never used DMA before, there are some things I don't quite understand and some things I can't get working quite the way I want. So I came to you guys for help.

The ADC config I set up with CubeMX is:
ADC1, ADC2 and ADC3 global interrupts enabled (although I don't ever touch those);
DMA2 Stream0 global interrupt enabled;
DMA request on ADC1, peripheral to memory, priority high, circular mode;
12 bit resolution, right aligned, EOC flag at end of all conversions (8 in total);

Here's the configuration code generated by CubeMX:

/* 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.NbrOfDiscConversion = 1;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 8;
  hadc1.Init.DMAContinuousRequests = ENABLE;
  hadc1.Init.EOCSelection = EOC_SEQ_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_0;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);
 
    /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
    */
  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = 2;
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);
 
    /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
    */
  sConfig.Channel = ADC_CHANNEL_2;
  sConfig.Rank = 3;
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);
 
    /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
    */
  sConfig.Channel = ADC_CHANNEL_3;
  sConfig.Rank = 4;
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);
 
    /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
    */
  sConfig.Channel = ADC_CHANNEL_4;
  sConfig.Rank = 5;
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);
 
    /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
    */
  sConfig.Channel = ADC_CHANNEL_5;
  sConfig.Rank = 6;
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);
 
    /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
    */
  sConfig.Channel = ADC_CHANNEL_6;
  sConfig.Rank = 7;
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);
 
    /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
    */
  sConfig.Channel = ADC_CHANNEL_7;
  sConfig.Rank = 8;
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);
 
}

Later on for the DMA:
/**
  * Enable DMA controller clock
  */
void MX_DMA_Init(void)
{
  /* DMA controller clock enable */
  __DMA2_CLK_ENABLE();
 
  /* DMA interrupt init */
  /* Sets the priority grouping field */
  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_0);
  HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
 
}

My interrupt handling function seems to work fine as far as I can tell and debug. Note that it stops the ADC+DMA modules, but does not DeInit() them. During the main code, I call the "Start" version of these functions, so that the ADC interrupt doesn't cause the rest of my code to be neglected, since it runs way faster than the rest of it. 
// ADC DMA interrupt handler
void HAL_ADC_ConvCpltCallback( ADC_HandleTypeDef * hadc){
    printf("ADC conversion done.\n");
    HAL_ADC_Stop_DMA(hadc);
    HAL_ADC_Stop(hadc);
}

And this is how I call the ADC DMA (I start and stop it every now and then during the code, this is only the first call after defining my storage variable):
// Define variable to hold the 8 ADC values
    // Why 32 bits if the resolution is 12b?   
    uint32_t adcData[8];
    // Start ADC DMA
    HAL_ADC_Start(&hadc1);
    HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adcData, 8);

Now this is where I suspect I'm doing something wrong: what I want to happen is to have a variable with 8 elements, each of which will hold the converted value from one of the 8 analog channels as soon as a conversion sequence of the 8 values is complete. But while debugging I noticed that this adcData variable will only update the first 2 of the 8 values it contains, while the other 6 are left 0. Maybe I understood the continuous scan mode circular buffer ADC DMA wrong? Can someone tell me what could be going wrong here? If the code I posted isn't enough I'd be happy to provide more.

I know most people are showing a bit of resistance in adopting the new HAL libraries, but I'd appreciate it very much if someone could point me in the right direction. Thank you very much in advance!

Outcomes