cancel
Showing results for 
Search instead for 
Did you mean: 

Can't get multiple ADC via DMA working

mh.ferris9
Associate II
Posted on August 12, 2015 at 17:50

Hi,

I've been jumping between the regular libraries and Hal and the Cube and I've now settled on the Cube and HAL libs. I'm trying to get multiple ADC channel conversions to work via DMA to an array, but with no success as yet. I'm using a Nucleo-F411RE, and trying to use PC0 (ADC1_CH10) and PC1(ADC1_CH11). I've got a single channel working correctly (PC0), but multiple channels still alludes me and looking at hte examples I'm not sure where I'm going wrong. I thought the only real difference would be changing the

AdcHandle.Init.NbrOfConversion = 2;

and setting the

HAL_ADC_ConfigChannel

ranks.

static
GPIO_InitTypeDef GPIO_InitStruct;
/* ADC handler declaration */
ADC_HandleTypeDef AdcHandle;
/* Variable used to get converted value */
__IO uint32_t uhADCxConvertedValue[2] = {0};
void
HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
GPIO_InitTypeDef GPIO_InitStruct;
static
DMA_HandleTypeDef hdma_adc;
/* Enable peripherals and GPIO Clocks */
/* Enable GPIO clock */
__HAL_RCC_GPIOC_CLK_ENABLE();
/* ADC3 Periph clock enable */
__HAL_RCC_ADC1_CLK_ENABLE();
/* Enable DMA2 clock */
__HAL_RCC_DMA2_CLK_ENABLE(); 
/* Configure peripheral GPIO */
/* ADC1 GPIO pin configuration */
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* Configure the DMA streams */
/* Set the parameters to be configured */
hdma_adc.Instance = DMA2_Stream0;
hdma_adc.Init.Channel = DMA_CHANNEL_0;
hdma_adc.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_adc.Init.MemDataAlignment = DMA_PDATAALIGN_WORD;
hdma_adc.Init.Mode = DMA_CIRCULAR;
hdma_adc.Init.Priority = DMA_PRIORITY_HIGH;
hdma_adc.Init.FIFOMode = DMA_FIFOMODE_DISABLE; 
hdma_adc.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
hdma_adc.Init.MemBurst = DMA_MBURST_SINGLE;
hdma_adc.Init.PeriphBurst = DMA_PBURST_SINGLE; 
HAL_DMA_Init(&hdma_adc);
/* Associate the initialized DMA handle to the the ADC handle */
__HAL_LINKDMA(hadc, DMA_Handle, hdma_adc);
}
int
main(
void
)
{
ADC_ChannelConfTypeDef sConfig;
/* STM32F4xx HAL library initialization:
- Configure the Flash prefetch, instruction and Data caches
- Configure the Systick to generate an interrupt each 1 msec
- Set NVIC Group Priority to 4
- Global MSP (MCU Support Package) initialization
*/
HAL_Init();
/* Configure the system clock to 100 MHz */
SystemClock_Config();
/* Configure the ADC peripheral */
AdcHandle.Instance = ADC1;
AdcHandle.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV4;
AdcHandle.Init.Resolution = ADC_RESOLUTION_12B;
AdcHandle.Init.ScanConvMode = DISABLE;
AdcHandle.Init.ContinuousConvMode = ENABLE;
AdcHandle.Init.DiscontinuousConvMode = DISABLE;
AdcHandle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
AdcHandle.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;
AdcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
AdcHandle.Init.NbrOfConversion = 2;
AdcHandle.Init.DMAContinuousRequests = ENABLE;
AdcHandle.Init.EOCSelection = DISABLE;
if
(HAL_ADC_Init(&AdcHandle) != HAL_OK)
{
/* Initialization Error */
Error_Handler(); 
}
/* Configure ADC regular channel */
sConfig.Channel = ADC_CHANNEL_10;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_144CYCLES;
sConfig.Offset = 0;
if
(HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK)
{
/* Channel Configuration Error */
Error_Handler(); 
}
sConfig.Channel = ADC_CHANNEL_11;
sConfig.Rank = 2;
if
(HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK)
{
/* Channel Configuration Error */
Error_Handler(); 
}
/* Start the conversion process and enable interrupt */
if
(HAL_ADC_Start_DMA(&AdcHandle, (uint32_t *)uhADCxConvertedValue, 1) != HAL_OK)
{
/* Start Conversation Error */
Error_Handler(); 
}
while
(1)
{
}
}

13 REPLIES 13
Posted on August 12, 2015 at 18:40

Sorry no life-guards on duty for that.

AdcHandle.Init.ScanConvMode = DISABLE; // Got more than one channel, you'd need to be scanning them.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
matic
Associate III
Posted on August 12, 2015 at 18:58

Maybe you should also change number of transfers to 2 here:

/* Start the conversion process and enable interrupt */

if

(HAL_ADC_Start_DMA(&AdcHandle, (uint32_t *)uhADCxConvertedValue, 2) != HAL_OK)

{

/* Start Conversation Error */

Error_Handler();

}

But I am not 100 % sure.

mh.ferris9
Associate II
Posted on August 13, 2015 at 10:42

Clive you champ, appreciate that.  And Obid, also had to change that to 2, I had played around with that number a few times thinking thats where my problem was, but completely missed the scan enable.  

Can't believe I missed it though, I've done a few projects doing this exact thing but with the old libs and had scan enabled - but then leave it off here.
David Pekin
Senior
Posted on February 23, 2018 at 00:32

Hello.  I'm having a similar problem on the STM32F303 Nucleo board.  I'm trying to sample both PA.1 and PA.4.  I had single channel sampling of PA.4 working and now I need both channels.  I've read this post and thought this would be the answer but I'm still unsuccessful. 

I do get the ADC complete callback and indeed the PA.4 adc data is in the first element of the DMA array but I get either 0 or  around 4030 as the second adc conversion element.  I have the same analog voltage going into PA.1 and PA.4 (A1 and A2 on the Nucleo-F303RE board).  

Thanks for another set of eyes on this issue.

/* Definition of ADCx channels */

#define ADCx_CHANNELa ADC_CHANNEL_1

/* Definition of ADCx channels pins */

#define ADCx_CHANNELa_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()

#define ADCx_CHANNELa_GPIO_PORT GPIOA

#define ADCx_CHANNELa_PIN GPIO_PIN_4

/* Definition of ADCx channels */

#define ADCx_CHANNELb ADC_CHANNEL_2

#define ADCx_CHANNELb_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()

#define ADCx_CHANNELb_GPIO_PORT GPIOA

#define ADCx_CHANNELb_PIN GPIO_PIN_1

/

static void ADC_Config(void)

{

ADC_ChannelConfTypeDef sConfig;

/* Configuration of ADCx init structure: ADC parameters and regular group */

AdcHandle.Instance = ADCx;

AdcHandle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;

AdcHandle.Init.Resolution = ADC_RESOLUTION_12B;

AdcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;

AdcHandle.Init.ScanConvMode = ENABLE;  

AdcHandle.Init.EOCSelection = DISABLE; 

AdcHandle.Init.LowPowerAutoWait = DISABLE;

AdcHandle.Init.NbrOfConversion = 2;         

AdcHandle.Init.DiscontinuousConvMode = DISABLE;       

AdcHandle.Init.NbrOfDiscConversion = 2;     

AdcHandle.Init.ContinuousConvMode = ENABLE; /* Continuous mode enabled conversion trig */

AdcHandle.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_Tx_TRGO; /* Trig of conversion start by external event */

AdcHandle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;

AdcHandle.Init.DMAContinuousRequests = ENABLE;

AdcHandle.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;

if (HAL_ADC_Init(&AdcHandle) != HAL_OK)

{

/* ADC initialization error */

Error_Handler();

}

sConfig.Channel = ADCx_CHANNELa;

sConfig.Rank = ADC_REGULAR_RANK_1;

sConfig.SamplingTime = ADC_SAMPLETIME_181CYCLES_5;

sConfig.SingleDiff = ADC_SINGLE_ENDED;

sConfig.OffsetNumber = ADC_OFFSET_NONE;

sConfig.Offset = 0;

if (HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK)

{

/* Channel Configuration Error */

Error_Handler();

}

sConfig.Channel = ADCx_CHANNELb;

sConfig.Rank = ADC_REGULAR_RANK_2;

if (HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK)

{

/* Channel Configuration Error */

Error_Handler();

}

}

void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)

{

GPIO_InitTypeDef GPIO_InitStruct;

static DMA_HandleTypeDef DmaHandle;

RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInitStruct;

/*##-1- Enable peripherals and GPIO Clocks #################################*/

/* Enable clock of GPIO associated to the peripheral channels */

ADCx_CHANNELa_GPIO_CLK_ENABLE();

/* Enable clock of ADCx peripheral */

ADCx_CLK_ENABLE();

/* Note: In case of usage of asynchronous clock derived from ADC dedicated */

/* PLL 72MHz, with ADC setting */

/* 'AdcHandle.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1', */

/* the clock source has to be enabled at RCC top level using macro */

/* '__HAL_RCC_ADC12_CONFIG(RCC_ADC12PLLCLK_DIV1)' or function */

/* 'HAL_RCCEx_PeriphCLKConfig()' (refer to comments in file */

/* 'stm32f3_hal_adc.c_ex' header). */

/* Enable asynchronous clock source of ADCx */

RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_ADC12;

RCC_PeriphCLKInitStruct.Adc12ClockSelection = RCC_ADC12PLLCLK_DIV1;

HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);

/* Enable clock of DMA associated to the peripheral */

ADCx_DMA_CLK_ENABLE();

/*##-2- Configure peripheral GPIO ##########################################*/

/* Configure GPIO pin of the selected ADC channel */

GPIO_InitStruct.Pin = ADCx_CHANNELa_PIN;

GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;

GPIO_InitStruct.Pull = GPIO_NOPULL;

HAL_GPIO_Init(ADCx_CHANNELa_GPIO_PORT, &GPIO_InitStruct);

GPIO_InitStruct.Pin = ADCx_CHANNELb_PIN;

GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;

GPIO_InitStruct.Pull = GPIO_NOPULL;

HAL_GPIO_Init(ADCx_CHANNELb_GPIO_PORT, &GPIO_InitStruct);

/*##-3- Configure the DMA streams ##########################################*/

/* Configure DMA parameters */

DmaHandle.Instance = ADCx_DMA;

DmaHandle.Init.Direction = DMA_PERIPH_TO_MEMORY;

DmaHandle.Init.PeriphInc = DMA_PINC_DISABLE;

DmaHandle.Init.MemInc = DMA_MINC_ENABLE;

DmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; /* Transfer from ADC by half-word to match with ADC resolution 10 or 12 bits */

DmaHandle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; /* Transfer to memory by half-word to match with buffer variable type: half-word */

DmaHandle.Init.Mode = DMA_CIRCULAR;

DmaHandle.Init.Priority = DMA_PRIORITY_HIGH;

/* Deinitialize & Initialize the DMA for new transfer */

HAL_DMA_DeInit(&DmaHandle);

HAL_DMA_Init(&DmaHandle);

/* Associate the initialized DMA handle to the ADC handle */

__HAL_LINKDMA(hadc, DMA_Handle, DmaHandle);

/*##-4- Configure the NVIC #################################################*/

/* NVIC configuration for DMA interrupt (transfer completion or error) */

/* Priority: high-priority */

HAL_NVIC_SetPriority(ADCx_DMA_IRQn, 1, 0);

HAL_NVIC_EnableIRQ(ADCx_DMA_IRQn);

/* NVIC configuration for ADC interrupt */

/* Priority: high-priority */

HAL_NVIC_SetPriority(ADCx_IRQn, 0, 0);

HAL_NVIC_EnableIRQ(ADCx_IRQn);

}
Posted on February 23, 2018 at 01:31

PA4 is on ADC2 (CH1)

Scan mode isn't going to work here, you'd need to set a single channel on ADC1 and ADC2, and do a DMA from the Common Data Register, doing a 32-bit read pulling both samples, and a 32-bit write to a 16-bit array (ie two elements at once)

Some defines and array missing. I would generally avoid doing an IRQ for every pair, either make a large sample buffer, or just background write into array/variable you read as frequently as you need it.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on February 23, 2018 at 01:38

The strangest thing I observe is that when I swap the A/D channel configuration gpio pins, NOTHING CHANGES in the DMA data buffer.  So, originally I have ADC_Channel1 configured to read PA.4 and ADC_Channel2 configured to read PA.1.  After conversion the result array[0] has the correct value from PA.4 and 

result array[1] has 0.   Then I swap so that  ADC_Channel1 configured to read PA.1 and ADC_Channel2 configured to read PA.4.  After conversion the result array[0] still has the correct value from PA.4 and 

result array[1] has 0.  This is too weird...  

Posted on February 23, 2018 at 01:47

F3 1UP

 

F3 2UP DUAL PA1,PA4

 

SPL implementations

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on February 23, 2018 at 02:09

I'm not sure I understand.  There's the channel list that you can setup for the ADC conversion.  I don't need the ADC conversion synced down to the u-second.  I just want to read the voltages on 2 GPIO lines. You can't do that with a single ADC???  If that's so, why have a channel list?  I must be missing something here.  If I need 2 separate ADCs I guess I could replicate my single channel ADC and read the 2 voltages sequentially polling.  This just doesn't sound right.  Is it a HAL problem?  What am I missing?

Posted on February 23, 2018 at 02:22

So I just read your reply that PA.4 and PA.1 are connected to different ADCs.  I've got PA.0, PA.1, and PA.4 on the connector.  Are two of these on the same ADC where I can do the 2 channel DMA with a single ADC. 

I can even do it polling but I you can't use a channel list with a polling ADC call, right?

Thanks