cancel
Showing results for 
Search instead for 
Did you mean: 

ADC with DMA - HAL libraries

marlos
Associate
Posted on November 10, 2014 at 12:35

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!
2 REPLIES 2
marlos
Associate
Posted on November 10, 2014 at 16:40

OK guys so here's a thought:

I have been fiddling a bit more and I tried something different with a very interesting result. Here's the new way in which I'm setting up the storage variable for the ADC readings:

// Define variable to hold the 8 ADC values
// Why 32 bits if the resolution is 12b? 
uint16_t adcData[8];
// Start ADC DMA 
HAL_ADC_Start(&hadc1);
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adcData, 16);

Now I changed the type from uint32_t to uint16_t, and doubled the transfer lenth in the HAL_ADC_Start_DMA function from 8 to And alas, now the adcData array contains 8 elements and all of them have values, but only the first 4 are being updated (before, the 4 first values of type uint32_t updated all the time and 4 values were empty all the time). Is the sneaky DMA double-buffering something? I really don't understand well how all that circular/double-buffering works from the documentation, could someone explain to me what exactly is going on and how I can get what I want, which is 8 values in 8 array elements, plain and simple? Cheers
engenharia2
Associate II
Posted on January 21, 2016 at 18:29

Hello Marc O.S.!

Do you have any solution for this issue?