cancel
Showing results for 
Search instead for 
Did you mean: 

STM32G070R8T6 ADC values are to high

STM32G070R8T6

STM32CubeIDE Version: 1.8.0

We are bringing up our new board and using ADC1 to DMA PB0, PB1, PB2 and PB10 pin readings into memory. The actual level on the pins is ~1.358 volts and the DMA data from the pins seen by software are 1775 to 1786. The ADC is set to 12 bits so these readings are showing 1.43 to 1.439 volts. This is an error of 72mVolts which seems excessive.

The initial code by Cube was set to use Low Level drivers calls.The ADC is set to use 4 ranks and 19 cycle samples. The Cube code also has run a ADC calibrate so we are at a loss to pinpoint why the four ADC pin readings are so far off.

static void MX_ADC1_Init(void)
{
 
  /* USER CODE BEGIN ADC1_Init 0 */
 
  /* USER CODE END ADC1_Init 0 */
 
  LL_ADC_InitTypeDef ADC_InitStruct 		= {0};
  LL_ADC_REG_InitTypeDef ADC_REG_InitStruct	= {0};
  LL_GPIO_InitTypeDef GPIO_InitStruct 		= {0};
 
  /* Peripheral clock enable */
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC);
 
  LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOB);
  /**ADC1 GPIO Configuration
  PB0    ------> ADC1_IN8
  PB1    ------> ADC1_IN9
  PB2    ------> ADC1_IN10
  PB10   ------> ADC1_IN11
  */
  GPIO_InitStruct.Pin 	= SW12_ANALOG_PB0 | SW34_ANALOG_PB1 | SW56_ANALOG_PB2 | SW78_ANALOG_PB10;
  GPIO_InitStruct.Mode 	= LL_GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull 	= LL_GPIO_PULL_NO;
  LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
 
  /* ADC1 DMA Init */
 
  /* ADC1 Init */
  LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_1, LL_DMAMUX_REQ_ADC1);
  LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
  LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_HIGH);
  LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_CIRCULAR);
  LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);
  LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);
  LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_HALFWORD);
  LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_HALFWORD);
  /* USER CODE BEGIN ADC1_Init 1 */
 
  
  /* Select ADC as DMA transfer request */
  LL_DMAMUX_SetRequestID(DMAMUX1, LL_DMAMUX_CHANNEL_0, LL_DMAMUX_REQ_ADC1);
  
  /* Set DMA transfer addresses of source and destination */
  LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_1, LL_ADC_DMA_GetRegAddr(ADC1, LL_ADC_DMA_REG_REGULAR_DATA),
                         (uint32_t)&dmaBuff, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
  
  /* Set DMA transfer size */
  LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, ADC_CONVERTED_DATA_BUFFER_SIZE);
  
  /* Enable DMA transfer interruption: transfer complete */
  LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1);
  
  /* Enable DMA transfer interruption: half transfer */
  LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1);
  
  /* Enable DMA transfer interruption: transfer error */
  LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_1);
  
  /*## Activation of DMA #####################################################*/
  /* Enable the DMA transfer */
  LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
 
  /* Configure NVIC to enable ADC1 interruptions */
  NVIC_SetPriority(ADC1_IRQn, 0); /* ADC IRQ greater priority than DMA IRQ */
  NVIC_EnableIRQ(ADC1_IRQn);
 
  /* USER CODE END ADC1_Init 1 */
  /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
  */
 
   #define ADC_CHANNEL_CONF_RDY_TIMEOUT_MS (1U)
   #if (USE_TIMEOUT == 1)
   uint32_t Timeout ; /* Variable used for Timeout management */
   #endif /* USE_TIMEOUT */
 
  ADC_InitStruct.Clock			= LL_ADC_CLOCK_SYNC_PCLK_DIV4;
  ADC_InitStruct.Resolution 	= LL_ADC_RESOLUTION_12B;
  ADC_InitStruct.DataAlignment	= LL_ADC_DATA_ALIGN_RIGHT;
  ADC_InitStruct.LowPowerMode	= LL_ADC_LP_MODE_NONE;
  LL_ADC_Init(ADC1, &ADC_InitStruct);
 
  LL_ADC_REG_SetSequencerConfigurable(ADC1, LL_ADC_REG_SEQ_FIXED);
  LL_ADC_REG_SetSequencerChannels(ADC1, (ADC_CHSELR_CHSEL8_Msk | ADC_CHSELR_CHSEL9_Msk | ADC_CHSELR_CHSEL10_Msk | ADC_CHSELR_CHSEL11_Msk));
 
  // For Set the sequence
 
   /* Poll for ADC channel configuration ready */
   #if (USE_TIMEOUT == 1)
   Timeout = ADC_CHANNEL_CONF_RDY_TIMEOUT_MS;
   #endif /* USE_TIMEOUT */
   while (LL_ADC_IsActiveFlag_CCRDY(ADC1) == 0)
   {
   #if (USE_TIMEOUT == 1)
   /* Check Systick counter flag to decrement the time-out value */
   if (LL_SYSTICK_IsActiveCounterFlag())
     {
   if(Timeout-- == 0)
         {
   Error_Handler();
         }
     }
   #endif /* USE_TIMEOUT */
   }
   /* Clear flag ADC channel configuration ready */
  LL_ADC_ClearFlag_CCRDY(ADC1);
  ADC_REG_InitStruct.TriggerSource		= LL_ADC_REG_TRIG_SOFTWARE;
  ADC_REG_InitStruct.SequencerLength 	= LL_ADC_REG_SEQ_SCAN_ENABLE_4RANKS;
  ADC_REG_InitStruct.SequencerDiscont 	= LL_ADC_REG_SEQ_DISCONT_DISABLE;
  ADC_REG_InitStruct.ContinuousMode 	= LL_ADC_REG_CONV_SINGLE;
  ADC_REG_InitStruct.DMATransfer 		= LL_ADC_REG_DMA_TRANSFER_UNLIMITED;
  ADC_REG_InitStruct.Overrun 			= LL_ADC_REG_OVR_DATA_OVERWRITTEN;
  LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);
 
  // Scan low channel to high channel(0-->18: 8, 9, 10 and 11)
  LL_ADC_REG_SetSequencerScanDirection(ADC1, LL_ADC_REG_SEQ_SCAN_DIR_FORWARD);
  LL_ADC_SetOverSamplingScope(ADC1, LL_ADC_OVS_DISABLE);
  LL_ADC_SetTriggerFrequencyMode(ADC1, LL_ADC_CLOCK_FREQ_MODE_HIGH);
  LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_19CYCLES_5);
  LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_2, LL_ADC_SAMPLINGTIME_19CYCLES_5);
  LL_ADC_DisableIT_EOC(ADC1);
  LL_ADC_DisableIT_EOS(ADC1);
 
   /* Enable ADC internal voltage regulator */
   LL_ADC_EnableInternalRegulator(ADC1);
   /* Delay for ADC internal voltage regulator stabilization. */
   /* Compute number of CPU cycles to wait for, from delay in us. */
   /* Note: Variable divided by 2 to compensate partially */
   /* CPU processing cycles (depends on compilation optimization). */
   /* Note: If system core clock frequency is below 200kHz, wait time */
   /* is only a few CPU processing cycles. */
   uint32_t wait_loop_index;
   wait_loop_index = ((LL_ADC_DELAY_INTERNAL_REGUL_STAB_US * (SystemCoreClock / (100000 * 2))) / 10);
   while(wait_loop_index != 0)
   {
	   wait_loop_index--;
   }
 
   LL_ADC_REG_SetSequencerChannels(ADC1, (ADC_CHSELR_CHSEL8_Msk | ADC_CHSELR_CHSEL9_Msk | ADC_CHSELR_CHSEL10_Msk | ADC_CHSELR_CHSEL11_Msk));
 
  /** Configure Regular Channel
  */
  // PB2: pin 29, ADC_IN10
  // PB10: pin 30, ADC_IN11
  // PB11: pin 31, ADC_IN15
  // PB12: pin 32, ADC_IN16
  //LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_10);
  //LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_11);
  //LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_15);
  //LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_16);
 
   /* Poll for ADC channel configuration ready */
   #if (USE_TIMEOUT == 1)
   Timeout = ADC_CHANNEL_CONF_RDY_TIMEOUT_MS;
   #endif /* USE_TIMEOUT */
   while (LL_ADC_IsActiveFlag_CCRDY(ADC1) == 0)
   {
   #if (USE_TIMEOUT == 1)
   /* Check Systick counter flag to decrement the time-out value */
   if (LL_SYSTICK_IsActiveCounterFlag())
     {
   if(Timeout-- == 0)
         {
   Error_Handler();
         }
     }
   #endif /* USE_TIMEOUT */
 
   }
   /* Clear flag ADC channel configuration ready */
  LL_ADC_ClearFlag_CCRDY(ADC1);
  //LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_4, LL_ADC_SAMPLINGTIME_COMMON_1);
  LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_19CYCLES_5);
 
  /** Configure Regular Channel
  */
  //LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_4);
  LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_0 | LL_ADC_CHANNEL_1 | LL_ADC_CHANNEL_2| LL_ADC_CHANNEL_10);
 
   /* Poll for ADC channel configuration ready */
   #if (USE_TIMEOUT == 1)
   Timeout = ADC_CHANNEL_CONF_RDY_TIMEOUT_MS;
   #endif /* USE_TIMEOUT */
   while (LL_ADC_IsActiveFlag_CCRDY(ADC1) == 0)
   {
	   #if (USE_TIMEOUT == 1)
	   /* Check Systick counter flag to decrement the time-out value */
	   if (LL_SYSTICK_IsActiveCounterFlag())
		 {
	   if(Timeout-- == 0)
			 {
	   Error_Handler();
			 }
		 }
	   #endif /* USE_TIMEOUT */
   }
   /* Clear flag ADC channel configuration ready */
  LL_ADC_ClearFlag_CCRDY(ADC1);
 
  //LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_4, LL_ADC_SAMPLINGTIME_COMMON_1);
  LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_19CYCLES_5);
 
  /* USER CODE BEGIN ADC1_Init 2 */
  
  /* Configuration of ADC interruptions */
  /* Enable interruption ADC group regular overrun */
  LL_ADC_EnableIT_OVR(ADC1);
 
  LL_ADC_EnableIT_EOC(ADC1);
  LL_ADC_EnableIT_EOS(ADC1);
  LL_ADC_EnableIT_EOSMP(ADC1);
  LL_ADC_Enable(ADC1);
 
  /* USER CODE END ADC1_Init 2 */
 
}

1 ACCEPTED SOLUTION

Accepted Solutions
S.Ma
Principal

Also check if sample and hold time is ok and the adc frequency is within tolerances.

View solution in original post

21 REPLIES 21
Andrew Neil
Evangelist III

"The actual level on the pins is ~1.358 volts"

How have you confirmed that?

Multi-meter.

You're sure the meter is calibrated? The 72 mV error is ~ 5.5%

And you're measuring at the ADC pins - both ground and input?

Have you checked with an oscilloscope that there isn't noise that would be missed by a meter?

0693W00000QMCPzQAP.jpgTook a scope reading and there is a small amount of noise but the high/low readings don't fall into what the scope is showing. Scope is showing a 1.35volt mean and my 1msec samples min/max readings are 1775/1786 which equates to 1.43065/1.439516.

What exactly is voltage on VREF+ (VDDA)? Do you perform ADC calibration as outlined in RM? What is the VREF channel really [edit autocorrect correction] readout [/edit] and what is the calibration value for that channel in the system memory?

JW​

S.Ma
Principal

Check the errata sheet if any exist. Use VRef measurement to convert lsb to mV. Shouln't hurt.

S.Ma
Principal

Also check if sample and hold time is ok and the adc frequency is within tolerances.

We do a ADC calibration that the Cube generated code performs after the ADC and DMA channel are set up. We have tried shorter and longer sample periods but it has minimal effect. We have some type of setup issue with the ADC to DMA. Each of the ADC pins can have one of four signals present based a two dip switch settings. With both switches off(0-0) you get this:

0693W00000QMHbRQAX.bmpSwitch setting 1-0 you get this:

0693W00000QMHc0QAH.bmpBy finding the min and max on a particular pin, you can derive the dip switch settings. Late yesterday I noticed that when I changed the dip switch settings on PB0, every DMA buffer was being changed. Here is a debug capture of the DMA buffers where you can see the PB0 value walking across different indexes when it should be staying in the same DMA one. We must have something incorrect in the setup:

0693W00000QMHcoQAH.jpg

Before you run, try walking.

Write a polled implementation of reading out one channel, first, and make that stable.

JW