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 */
 
}

21 REPLIES 21

Already did the polled version months ago. Have several STM F series products running multiple ADC's via DMA. This is the first one using ST Cube IDE with Low Level drivers. There is something different about the newer Low Level drivers vs. the older Standard Peripheral drivers.

> Already did the polled version months ago.

> There is something different about the newer Low Level drivers vs. the older Standard Peripheral drivers.

So you effectively did not, as you are using an entirely different "library". It's a different set of cruches. You have to learn walking with them, again.

Or just learn walking normally.

JW

The polled version was using the Cube with LL drivers but it did not get this far as the board had minor tweaks done. You can poll four channels all you want but you will never know if the DMA is set up correctly until you run it. It's not "crutches" as the G and F series are very close. If you have any suggestions on if you think the DMA setup is incorrect then I would appreciate it.

Our current running theory is that our modifications to the Cube generated ADC/DMA code is causing a issue with the DMAing of data. It appears that each DMA is four ADC samples(the size appears correct) but the alignment of the data to specific pins is off. Because the DMA data to pin alignment is off our min/max calculations are not correct per pin. If a single pin is noisy all the ADC readings will be shown to be off.

Because this processor is a M0+(vs the M0's we have used in the past) we can't do a direct comparison to our older code. Close inspection of the DMA setup looks correct.

Your original problem was the values being too high. That's why I recommend to perform measurements the simplest possible way, not to be distracted by some potentially entirely unrelated problem. You can solve DMA any time later, perhaps starting a new thread for that.

JW

Do you understand why we think the DMA issue could be related ? At this point we know there are two issues but would not have seen the incorrect values till possibly later when verifying each individual PB pin value. If you are a moderator and would rather have two separate threads we can do that. This problem is currently being worked on and we don't want to go back to the previous polled ADC build because pin locations have changed on the hardware so it would required a half day of pin reconfiguration. Fixing the DMA issue first would seem to be the more timely avenue.

Piranha
Chief II

Wrong indexes are caused by ADC overrun happening because of interference by the debugger. When that happens, the correct action is:

  1. Stop the ADC with ADSTP.
  2. Disable the DMA channel.
  3. Reconfigure the CNDTR for DMA channel.
  4. Enable the DMA channel.
  5. Start the ADC with ADSTART.

I thought this might be the answer but I don't see any overrun isr's flags being set. Just End of Sequence and End of Sample flags.

0693W00000QMInJQAX.jpg

Debugger may be intrusive simply by reading the registers. I don't know the minutiae of 'G0 ADC and maybe it's not a problem here; but not reading them by debugger while it's running means to stay on the safe side.

DMA is set as circular or you restart it in some way? And how exactly is ADC clocked and triggered?​

You haven't answered questions from my first post either.

Sounds unlikely that DMA would be related to unexpected readout, IMO.

JW​

Ok let me cycle back to your first questions I missed.

  • What exactly is voltage on VREF+ (VDDA)? VBAT, VREF+ and VDD pins are 3.26volts.
  • Do you perform ADC calibration as outlined in RM? Yes the Cube IDE generated code does the calibration in the Activate_ADC() function. This is called after the MX_ADC1_Init() function.
  • What is the VREF channel readout and what is the calibration value for that channel in the system memory? The Cube generated Low Level calibration code is a bit different in that the calibration of ADC1 never returns a value. It simply runs the calibration and waits for the finished flag.

The ADC is triggered in the main loop. The system has no RTOS so we track system ticks(@ 1msec) and trigger the ADC. We currently went from a 1msec sample time to 500msec to see if the debugger was possibly causing a overrun condition.

    if (adcSampleTimer >= 500)
    {
    	adcSampleTimer = 0;
 
    	// Start conversion on ADC1
    	LL_ADC_REG_StartConversion(ADC1);
    }

At completion of the DMA we move the buffer pointer to the next head memory location.

  if(LL_DMA_IsActiveFlag_TC1(DMA1) == 1)
  {
	  dmaHead++;
	  dmaHead &= (MAX_DMA_XFERS-1);
 
	  /* 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[dmaHead][0], LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
 
    /* Clear flag DMA transfer complete */
    LL_DMA_ClearFlag_TC1(DMA1);
    
  }

When we move the dip switches attached to PB0 to a position that has a big swing in the signal those swing levels end up moving across different DMA buffer locations instead of staying on the correct index almost as if we had more then four channels configured.

On one of our older projects using a STM32F030K6T6 we ran across a DMA issue when it was interfaced with the TX side of a serial port. The first DMA would word but nothing after. We eventually found that we had to disable the DMA after each transfer was complete, and then re-enable it for the next transmission. We thought maybe this G0 DMA had some weirdness with the ADC or some cache that needed to be cleared(like the SPI bus on other ST micro's.)