AnsweredAssumed Answered

SDADC inaccuracies when enabling multiple SDADC peripherals

Question asked by bodeker.brandon on Oct 7, 2015
Latest reply on Oct 7, 2015 by Danish Ali
Hello all,

I am using the STM32F373C8, and have recently ran into some issues when trying to use the sigma-delta ADC channels.

So this MCU has 3 SDADC's that can be configured. For my application, I am using SDADC1 and SDADC3. I am using these as voltage inputs to sense multiple voltage taps. There are 5 inputs to SDADC1 and 3 inputs to SDADC3.

I have run into the problem that when enabling both of these at the same time, the accuracy of the input drops. I have been unable to find any documentation saying this cannot be done, or any workarounds. 

I have tried just enabling each SDADC individually to see if it fixes the errors, and it does. They read darn near perfectly. Which makes me believe something is interfering when both are active. 

After checking my set-up code, I found that the analog power function call is the one call that causes the accuracy problems. If I remove this from either of the SDADC initialization sections, the other SDADC channel works great. Only problem is, the one that I disabled now, does not work. Here is the function call, and function call process. This is using the standard peripheral libraries for STM32F37x.
/* Enable SDADC1 analog interface */
PWR_SDADCAnalogCmd(PWR_SDADCAnalog_1, ENABLE);

And here is the function operation. I believe this takes the peripheral out of deep sleep mode to allow the it's usage. So basically it's the first line of code to enable part of the peripheral.

/**
  * @brief  Enables or disables the WakeUp Pin functionality.
  * @param  PWR_SDADCAnalog: specifies the SDADC.
  *          This parameter can be: PWR_SDADCAnalog_1, PWR_SDADCAnalog_2 or PWR_SDADCAnalog_3.
  * @param  NewState: new state of the SDADC Analog functionality.
  *          This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void PWR_SDADCAnalogCmd(uint32_t PWR_SDADCAnalog, FunctionalState NewState)
{  
  /* Check the parameters */
  assert_param(IS_PWR_SDADC_ANALOG(PWR_SDADCAnalog));  
  assert_param(IS_FUNCTIONAL_STATE(NewState));
  
  if (NewState != DISABLE)
  {
    /* Enable the SDADCx analog */
    PWR->CR |= PWR_SDADCAnalog;
  }
  else
  {
    /* Disable the SDADCx analog */
    PWR->CR &= ~PWR_SDADCAnalog;
  }   
}

For reference, here are the initialization calls for both of my SDADC's.

uint32_t SDADC1_Config(void)
{
  SDADC_AINStructTypeDef SDADC_AINStructure;
  GPIO_InitTypeDef GPIO_InitStructure;
  uint32_t SDADCTimeout = 0;
    
  /* SDADC1 APB2 interface clock enable */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SDADC1, ENABLE);
    
  /* PWR APB1 interface clock enable */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
    
  /* Enable SDADC1 analog interface */
  PWR_SDADCAnalogCmd(PWR_SDADCAnalog_1, ENABLE);
  
  /* Set the SDADC divider: The SDADC should run @6MHz */
  /* If Sysclk is 72MHz, SDADC divider should be 12 */
  RCC_SDADCCLKConfig(RCC_SDADCCLK_SYSCLK_Div12);
    
  /* RCC_AHBPeriph_GPIOB Peripheral clock enable */
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
    
  /* RCC_AHBPeriph_GPIOE Peripheral clock enable */
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE, ENABLE);
    
  /* SDADC1 GPIO Configuration. Configured Analog input, no pull up/down   
    PB0     ------> SDADC1_AIN6P
    PB1     ------> SDADC1_AIN5P
    PB2     ------> SDADC1_AIN4P
    PE8     ------> SDADC1_AIN8P
    PE9     ------> SDADC1_AIN7P
  */ 
  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AN;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
    
  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AN;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
  GPIO_Init(GPIOE, &GPIO_InitStructure);
    
  /* Selects external reference mode. VDD is referenced (3.3v) */
  SDADC_VREFSelect(SDADC_VREF_VDDA);
  
  /* Insert delay equal to ~5 ms */
  Delay(5);
    
  /* Enable SDADC1 */
  SDADC_Cmd(SDADC1, ENABLE);
  
  /* Enter initialization mode */
  SDADC_InitModeCmd(SDADC1, ENABLE);
    
  /* wait for INITRDY flag to be set */
  SDADCTimeout = 30;
  while((SDADC_GetFlagStatus(SDADC1, SDADC_FLAG_INITRDY) == RESET) && (--SDADCTimeout != 0));
  
  if(SDADCTimeout == 0)
  {
    /* INITRDY flag can not set */
    return 1;
  }
  
  /* SDADC1 Analog Input configuration conf0: use single ended zero offset mode/4 gain/VSSA common */
  SDADC_AINStructure.SDADC_InputMode = SDADC_InputMode_SEZeroReference;
  SDADC_AINStructure.SDADC_Gain = SDADC_Gain_1;
  SDADC_AINStructure.SDADC_CommonMode = SDADC_CommonMode_VSSA;
  SDADC_AINStructure.SDADC_Offset = 0;
  SDADC_AINInit(SDADC1, SDADC_Conf_0, &SDADC_AINStructure);
    
  /* select SDADC1 channel 4 to use conf0 */
  SDADC_ChannelConfig(SDADC1, SDADC_Channel_6, SDADC_Conf_0);
  
  /* select SDADC1 channel 5 to use conf0 */
  SDADC_ChannelConfig(SDADC1, SDADC_Channel_5, SDADC_Conf_0);
    
  /* select SDADC1 channel 6 to use conf0 */
  SDADC_ChannelConfig(SDADC1, SDADC_Channel_4, SDADC_Conf_0);
    
  /* select SDADC1 channel 7 to use conf0 */
  SDADC_ChannelConfig(SDADC1, SDADC_Channel_8, SDADC_Conf_0);
    
  /* select SDADC1 channel 8 to use conf0 */
  SDADC_ChannelConfig(SDADC1, SDADC_Channel_7, SDADC_Conf_0);
    
  /* select channels 4,5,6,7,8 for injected group*/
  SDADC_InjectedChannelSelect(SDADC1, SDADC_Channel_6 | SDADC_Channel_5 | SDADC_Channel_4 | SDADC_Channel_8 | SDADC_Channel_7);
    
  /* Allows each injected conversion in SDADC1 to finish before starting the next conversion*/
  SDADC_DelayStartInjectedConvCmd(SDADC1, ENABLE);
    
  /* Enables SDADC1 DMA transfer for injected conversions*/
  SDADC_DMAConfig(SDADC1, SDADC_DMATransfer_Injected, ENABLE);
  
  /* Exit initialization mode */
  SDADC_InitModeCmd(SDADC1, DISABLE);
  
  /* configure calibration to be performed on conf0 to check for offsets */
  SDADC_CalibrationSequenceConfig(SDADC1, SDADC_CalibrationSequence_1);
    
  /* start SDADC1 Calibration */
  SDADC_StartCalibration(SDADC1);
    
  /* Set calibration timeout: 5.12 ms at 6 MHz in a single calibration sequence */
  SDADCTimeout = 4*30720;
    
  /* wait for SDADC1 Calibration process to end */
  while((SDADC_GetFlagStatus(SDADC1, SDADC_FLAG_EOCAL) == RESET) && (--SDADCTimeout != 0));
    
  if(SDADCTimeout == 0)
  {
    /* EOCAL flag can not set */
    return 2;
  }
    
  return 0;
}//end SDADC_Config

uint32_t SDADC3_Config(void)
{
  SDADC_AINStructTypeDef SDADC_AINStructure;
  GPIO_InitTypeDef GPIO_InitStructure;
  uint32_t SDADCTimeout = 0;
    
  /* SDADC3 APB2 interface clock enable */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SDADC3, ENABLE);
    
  /* Enable SDADC3 analog interface */
  PWR_SDADCAnalogCmd(PWR_SDADCAnalog_3, ENABLE);
  
  /* Set the SDADC divider: The SDADC should run @6MHz */
  /* If Sysclk is 72MHz, SDADC divider should be 12 */
  RCC_SDADCCLKConfig(RCC_SDADCCLK_SYSCLK_Div12);
    
    /* RCC_AHBPeriph_GPIOB Peripheral clock enable */
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
    
  /* RCC_AHBPeriph_GPIOE Peripheral clock enable */
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE);
    
  /* SDADC1 GPIO Configuration. Configured Analog input, no pull up/down   
    PB14     ------> SDADC3_AIN8P
    PB15     ------> SDADC3_AIN7P
    PD8      ------> SDADC3_AIN6P
  */ 
  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AN;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14 | GPIO_Pin_15;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
    
  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AN;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
  GPIO_Init(GPIOD, &GPIO_InitStructure);
  
  /* Selects external reference mode. VDD is referenced (3.3v) */
  SDADC_VREFSelect(SDADC_VREF_VDDA);
  
  /* Insert delay equal to ~5 ms */
  Delay(5);
    
  /* Enable SDADC3 */
  SDADC_Cmd(SDADC3, ENABLE);
  
  /* Enter initialization mode */
  SDADC_InitModeCmd(SDADC3, ENABLE);
    
  /* wait for INITRDY flag to be set */
  SDADCTimeout = 30;
  while((SDADC_GetFlagStatus(SDADC3, SDADC_FLAG_INITRDY) == RESET) && (--SDADCTimeout != 0));
  
  if(SDADCTimeout == 0)
  {
    /* INITRDY flag can not set */
    return 1;
  }
    
  /* SDADC3 Analog Input configuration conf0: use single ended zero offset mode/4 gain/VSSA common */
  SDADC_AINStructure.SDADC_InputMode = SDADC_InputMode_SEZeroReference;
  SDADC_AINStructure.SDADC_Gain = SDADC_Gain_1;
  SDADC_AINStructure.SDADC_CommonMode = SDADC_CommonMode_VSSA;
  SDADC_AINStructure.SDADC_Offset = 0;
  SDADC_AINInit(SDADC3, SDADC_Conf_0, &SDADC_AINStructure);
    
  /* select SDADC3 channel 8 to use conf0 */
  SDADC_ChannelConfig(SDADC3, SDADC_Channel_8, SDADC_Conf_0);
  
  /* select SDADC3 channel 7 to use conf0 */
  SDADC_ChannelConfig(SDADC3, SDADC_Channel_7, SDADC_Conf_0);
    
  /* select SDADC3 channel 6 to use conf0 */
  SDADC_ChannelConfig(SDADC3, SDADC_Channel_6, SDADC_Conf_0);
    
  /* select channels 6,7,8 for injected group */
  SDADC_InjectedChannelSelect(SDADC3, SDADC_Channel_8 | SDADC_Channel_7 | SDADC_Channel_6);
    
  /* Allows each injected conversion in SDADC3 to finish before starting the next conversion*/
  SDADC_DelayStartInjectedConvCmd(SDADC3, ENABLE);
    
  /* Enables SDADC3 DMA transfer for injected conversions*/
  SDADC_DMAConfig(SDADC3, SDADC_DMATransfer_Injected, ENABLE);
    
  /* Launches a SDADC3 injected conversion when an SDADC1 injected conversion is launched*/
  SDADC_InjectedSynchroSDADC1(SDADC3, ENABLE);
  
  /* Exit initialization mode */
  SDADC_InitModeCmd(SDADC3, DISABLE);
  
  /* configure calibration to be performed on conf0 to check for offsets */
  SDADC_CalibrationSequenceConfig(SDADC3, SDADC_CalibrationSequence_1);
    
  /* start SDADC3 Calibration */
  SDADC_StartCalibration(SDADC3);
    
  /* Set calibration timeout: 5.12 ms at 6 MHz in a single calibration sequence */
  SDADCTimeout = 4*30720;
    
  /* wait for SDADC3 Calibration process to end */
  while((SDADC_GetFlagStatus(SDADC3, SDADC_FLAG_EOCAL) == RESET) && (--SDADCTimeout != 0));
    
  if(SDADCTimeout == 0)
  {
    /* EOCAL flag can not set */
    return 2;
  }
    
  return 0;
}//end SDADC3_Config


This issue is really holding me back from being able to do what I need this to do. If anyone can give some insight on this, it would be greatly appreciated.

FYI, this set-up is using DMA to transfer injected conversion values.

Thanks!

Outcomes