cancel
Showing results for 
Search instead for 
Did you mean: 

Multiple ADC channels on STM32L072xx

CDesh
Associate III

I am using STM32L072xx module and I would like to use ADC4, ADC5 and ADC 17 (Vrefint) at the same time. But I want to use ADC4 with DMA of buffer size 500 or more. It is not necessary to store ADC5 & ADC17 in DMA.

Can I use DMA only for ADC 4 and dump 500 samples and use the following API calls for ADC5 and ADC17?

  HAL_ADC_Start( &hadc);

  /* Wait for the end of conversion */

  HAL_ADC_PollForConversion( &hadc, HAL_MAX_DELAY );

  /* Get the converted value of regular channel */

  adcData = HAL_ADC_GetValue ( &hadc);

Is this the right way of configuring multiple ADC channels?

1 ACCEPTED SOLUTION

Accepted Solutions
Imen.D
ST Employee

Hello @CDesh​ ,

You'll probably want to review the working "ADC_DMA_Transfer" example within the STM32CubeL0 MCU package and you can get inspired from this:

STM32Cube_FW_L0_V1.12.0\Projects\NUCLEO-L073RZ\Examples\ADC\ADC_DMA_Transfer

Imen

When your question is answered, please close this topic by clicking "Accept as Solution".
Thanks
Imen

View solution in original post

11 REPLIES 11
Imen.D
ST Employee

Hello @CDesh​ ,

You'll probably want to review the working "ADC_DMA_Transfer" example within the STM32CubeL0 MCU package and you can get inspired from this:

STM32Cube_FW_L0_V1.12.0\Projects\NUCLEO-L073RZ\Examples\ADC\ADC_DMA_Transfer

Imen

When your question is answered, please close this topic by clicking "Accept as Solution".
Thanks
Imen
Philippe Cherbonnel
ST Employee

Hello @CDesh​ ,

On STM32L0, you cannot use DMA on only some ADC channels: when activated, it transfers data of all channels configured (it is not the case on some other STM32 series like STM32L4 with ADC featuring 2 groups regular and injected, in this case group regular can use DMA and group injected can operate without DMA transfer).

The right way to use multiple channels with DMA transfer (recommended to use DMA):

1. Enable DMA transfer:

hadc1.Init.DMAContinuousRequests = ENABLE;

...

HAL_ADC_Init(&hadc1)

2. Configure as many channels as you want:

sConfig.Channel   = <ADC_CHANNEL_x>;

HAL_ADC_ConfigChannel(&adc1, &sConfig)

...

sConfig.Channel   = <ADC_CHANNEL_y>;

HAL_ADC_ConfigChannel(&hadc1, &sConfig)

...

sConfig.Channel   = <ADC_CHANNEL_z>;

HAL_ADC_ConfigChannel(&adc1, &sConfig)

3. Start conversion

HAL_ADC_Start_DMA(&hadc1, (uint32_t *)aADCxConvertedData, ADC_CONVERTED_DATA_BUFFER_SIZE)

You can refer to examples in STM32L0 FW package:

Using ADC LL driver:

 ...\Firmware\Projects\NUCLEO-L073RZ\Examples_LL\ADC\ADC_MultiChannelSingleConversion

Using ADC HAL driver:

 ...\Firmware\Projects\NUCLEO-L053R8\Examples\ADC\ADC_Sequencer

Best regards

Philippe

CDesh
Associate III

Thanks @Imen DAHMEN​  and @Philippe Cherbonnel​  for you feedback. I followed the similar approach and able to handle DMA on multiple ADC channels.

But this approach posing a different challenge.

Along with ADC DMA on PA4, I am also using comparator COMP2 in interrupt mode. When ever I enable the following lines , controller is getting stuck.

HAL_NVIC_SetPriority(ADC1_COMP_IRQn, 3, 0);

HAL_NVIC_EnableIRQ(ADC1_COMP_IRQn);

As a probably solution , I added following lines in the code which is not helping.

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)

{

HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);

HAL_DMA_Abort(&hdma_adc);

}

Is this because ADC1_COMP_IRQn = 12, is a shared number for ADC1, COMP1 and COMP2 Interrupts and ADC transfer is happening via DMA. Please note, this issue did not occur when PollForConversion (HAL_ADC_PollForConversion( &hadc, HAL_MAX_DELAY );) was used.

If so, I am looking for a lead to handle these interrupts. Any feedback would be appreciated.

Philippe Cherbonnel
ST Employee

Hello @CDesh​ ,

Using ADC and comparator with the common interrupt line ADC1_COMP_IRQn is possible.

I have 2 comments:

1. ADC vs DMA IRQ handlers:

Function HAL_ADC_Start_DMA() does not enable ADC interruptions (except overrun, which should not occur unless wrong DMA config or high transfers load),

therefore using it or using polling should make any difference regarding this IRQ handler.

The IRQ handler used will be the one of DMA, for example DMA1_Channel1_IRQHandler().

Can you try to disable comparators and check that you do not enter in ADC1_COMP_IRQHandler(), and effectively enter in DMA1_Channel1_IRQHandler() ?

2. IRQ handler shared by ADC and comparators

When you mention "controller is getting stuck", are you able to break the program with a debugger ? If yes, the call stack shows program handler mode (from IRQ) or in thread mode ?

A hypothesis is that you are stuck in an IRQ handler.

Do you process all possible IRQ event sources like this :

void ADC1_COMP_IRQHandler(void)

{

HAL_ADC_IRQHandler(&AdcHandle);

HAL_COMP_IRQHandler(&Comp1Handle);

HAL_COMP_IRQHandler(&Comp2Handle);

}

Best regards

Philippe

CDesh
Associate III

@Philippe Cherbonnel​  Thanks for your response.

(1) when I comment following sections, code is working as expected with 4000 samples in DMA buffer

  • HAL_NVIC_SetPriority(ADC1_COMP_IRQn, 3, 0);
  • HAL_NVIC_EnableIRQ(ADC1_COMP_IRQn);

(2) I am getting stuck when I apply break points with debugger too. Unable to break the program with debugger at HAL_ADC_DeInit(&hadc); / HAL_ADC_Init( &hadc );

CDesh
Associate III

@Philippe Cherbonnel​ 

I am processing IRQ event sources like:

void ADC1_COMP_IRQHandler(void)

{

  HAL_COMP_IRQHandler(&Comp1Handle);

}

Also using overrun setting as mentioned below:

hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;

Philippe Cherbonnel
ST Employee

Hello @CDesh​ ,

I have made a basic program running on board NUCLEO-STM32L053R8, see file attached "ADC_and_comparator_IT.zip":

- ADC conversion data transferred by DMA

- COMP1 trigger with interruption

- program generated from CubeMX for CubeIDE (if needed, you can generated for IAR-EWARM or MDK-ARM with CubeMX)

- Other details are in file readme.txt

All is working correctly with IRQ handler ADC1_COMP_IRQHandler shared by ADC and comparator.

Can you test it on your side and compare with your program ?

Best regards

Philippe

CDesh
Associate III

@Philippe Cherbonnel​ 

I did compare your program with mine.

It would work fine if single channel with limited samples are used. But I am using the following with 3 different ADC channels with different buffer lengths

for (j=0; j<2; j++) {

MX_AdcInit(ADC_CHANNEL, (uint32_t *)aADCxConvertedData,4000 );

extract data from DMA buffer

}

where as :

void MX_AdcInit( uint32_t Channel, uint32_t* pData, uint32_t Length )

{

  ADC_ChannelConfTypeDef sConfig;

  hadc.Instance = ADC1;

  hadc.Init.OversamplingMode   = DISABLE;

  hadc.Init.ClockPrescaler    = ADC_CLOCK_SYNC_PCLK_DIV1;

  hadc.Init.LowPowerAutoPowerOff = DISABLE;

  hadc.Init.LowPowerFrequencyMode = ENABLE;

  hadc.Init.LowPowerAutoWait   = DISABLE;

  hadc.Init.Resolution      = ADC_RESOLUTION_12B;

  hadc.Init.SamplingTime     = ADC_SAMPLETIME_160CYCLES_5;

  hadc.Init.ScanConvMode     = ADC_SCAN_DIRECTION_FORWARD;

  hadc.Init.DataAlign       = ADC_DATAALIGN_RIGHT;

  hadc.Init.ContinuousConvMode  = ENABLE;

  hadc.Init.DiscontinuousConvMode = DISABLE;

  hadc.Init.ExternalTrigConv   = ADC_SOFTWARE_START;      /* Software start to trig the 1st conversion manually, without external event */

  hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;

  hadc.Init.EOCSelection     = ADC_EOC_SINGLE_CONV;

  hadc.Init.DMAContinuousRequests = ENABLE;

  hadc.Init.Overrun        = ADC_OVR_DATA_PRESERVED;   /* DR register is overwritten with the last conversion result in case of overrun */

  ADCCLK_ENABLE();

  if (HAL_ADC_Init(&hadc) != HAL_OK)

   {

    Error_Handler();

   }

  HAL_ADCEx_Calibration_Start(&hadc, ADC_SINGLE_ENDED);

  /* Deselects all channels*/

  sConfig.Channel = ADC_CHANNEL_MASK;

  sConfig.Rank = ADC_RANK_NONE;

  HAL_ADC_ConfigChannel( &hadc, &sConfig);

  sConfig.Channel = Channel;

  sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;

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

   {

    Error_Handler();

   }

  if(HAL_ADC_Start_DMA(&hadc,(uint32_t *)pData,Length) != HAL_OK)

   {

       PRINTF("ADC DMA error \n\r");

   Error_Handler();

   }

  ADCCLK_DISABLE();

}

for j=0, I am able to getting values in buffer appropriately. When j increments to 1,

MX_AdcInit(ADC_CHANNEL, (uint32_t *)aADCxConvertedData,ADC_DATA_BUFFER_SIZE ); line is freezing forever.

I also tried the following, but is not helping.

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)

{

HAL_ADC_Stop(&hadc);

or

HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);

or

HAL_ADC_Stop_DMA(&hadc);

or

HAL_DMA_Abort(&hadc);

}

CDesh
Associate III

@Philippe Cherbonnel​ 

I believe nesting of interrupts is not handled properly. Following are priorities assigned to IRQs

HAL_NVIC_SetPriority(ADC1_COMP_IRQn, 3, 0);

HAL_NVIC_EnableIRQ(ADC1_COMP_IRQn);

HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);

HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);

Following is the output. ADC DMA1 IRQ is often getting interrupted by COM IRQ

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

adcFinal: 4095

Conversion completed

bufferVal:4095

Conversion completed

Comparator:1

Comparator:2

bufferVal:0

Comparator:3

Comparator:4

Comparator:5

Comparator:6

Comparator:7

adcFinal: 4095

Conversion completed

bufferVal:4095

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

Any suggestion would be appreciated.