AnsweredAssumed Answered

Missing ADC Samples when using double buffer circular DMA on STM32F407VG

Question asked by haacks.kai on Dec 19, 2013
Latest reply on Dec 19, 2013 by Clive One
Hi,

when I set up a timer triggered AD conversion on a STM32F407VG I found that using the DMA2 works only if I dont't use the double buffer circular mode.
With double buffer circular DMA mode I have randomly missing samples in my data.
If I use DMA without double buffer mode everything works as expected.
I could not find this kind of problem in the errata.
Is there may be still something I miss?
Here is my source code:

void RCC_Configuration(void)
{
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
}

/**************************************************************************************/

void GPIO_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
}

/**************************************************************************************/

void ADC_Configuration(void)
{
  ADC_CommonInitTypeDef ADC_CommonInitStructure;
  ADC_InitTypeDef ADC_InitStructure;

  /* ADC Common Init */
  ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
  ADC_CommonInit(&ADC_CommonInitStructure);

  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
  ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 1 Channel
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // Conversions Triggered
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_NbrOfConversion = 1;
  ADC_Init(ADC1, &ADC_InitStructure);

  /* ADC1 regular channel 11 configuration */
  ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_15Cycles);

  /* Enable DMA request after last transfer (Single-ADC mode) */
  ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);

  ADC_ITConfig(ADC1, ADC_IT_OVR, ENABLE);

  /* Enable ADC1 DMA */
  ADC_DMACmd(ADC1, ENABLE);

  /* Enable ADC1 */
  ADC_Cmd(ADC1, ENABLE);
}

/**************************************************************************************/

__IO uint16_t buffer[2][BUFFERSIZE];

DMA_InitTypeDef DMA_InitStructure;

static void DMA_Configuration(void)
{

    DMA_DeInit(DMA2_Stream4);
    DMA_StructInit (&DMA_InitStructure);

    DMA_InitStructure.DMA_Channel = DMA_Channel_0;
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&buffer[0][0];
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
    DMA_InitStructure.DMA_BufferSize = BUFFERSIZE; // Count of 16-bit words
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    DMA_Init(DMA2_Stream4, &DMA_InitStructure);

    DMA_DoubleBufferModeConfig(DMA2_Stream4, (uint32_t)&buffer[1][0], DMA_Memory_0);
    DMA_DoubleBufferModeCmd(DMA2_Stream4, ENABLE);

  /* Enable DMA Stream Half / Transfer Complete interrupt */
    DMA_ITConfig(DMA2_Stream4, DMA_IT_TC, ENABLE);
    DMA_ITConfig(DMA2_Stream4, DMA_IT_TE, ENABLE);

  /* DMA2_Stream0 enable */
    DMA_Cmd(DMA2_Stream4, ENABLE);
}

/**************************************************************************************/

void TIM2_Configuration(void)
{
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

  /* Time base configuration */
  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  TIM_TimeBaseStructure.TIM_Period = (72000000 / 200000) - 1; // 200 KHz, from 84 MHz TIM2CLK (ie APB1 = HCLK/4, TIM2CLK = HCLK/2)
  TIM_TimeBaseStructure.TIM_Prescaler = 0;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

  /* TIM2 TRGO selection */
  TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); // ADC_ExternalTrigConv_T2_TRGO

  /* TIM2 enable counter */
  TIM_Cmd(TIM2, ENABLE);
}

/**************************************************************************************/

void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;

  /* Enable the DMA Stream IRQ Channel */
  NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream4_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

  NVIC_InitStructure.NVIC_IRQChannel = ADC_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

/**************************************************************************************/

void ADC_IRQHandler(void)
{
    uint16_t *ptr = &buffer[0];
    static uint32_t cnt = 0;

    if(ADC_GetITStatus(ADC1, ADC_IT_OVR))
    {
        ADC_ClearITPendingBit(ADC1, ADC_IT_OVR);
        STM_EVAL_LEDToggle(LED6);
    }

    if(ADC_GetITStatus(ADC1, ADC_IT_EOC))
    {
        ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
        STM_EVAL_LEDToggle(LED6);
    }

}
/**************************************************************************************/

void DMA2_Stream4_IRQHandler(void) // Called at 1 KHz for 200 KHz sample rate, LED Toggles at 500 Hz
{
  /* Test on DMA Stream transmit error interrupt */
  if(DMA_GetITStatus(DMA2_Stream4, DMA_IT_TEIF4))
  {
    /* Clear DMA Stream transmit error interrupt pending bit */
    DMA_ClearITPendingBit(DMA2_Stream4, DMA_IT_TEIF4);

    /* Turn LED3 off: Half Transfer */
    STM_EVAL_LEDOff(LED3);
  }

  /* Test on DMA Stream Transfer Complete interrupt */
  if(DMA_GetITStatus(DMA2_Stream4, DMA_IT_TCIF4))
  {
    /* Clear DMA Stream Transfer Complete interrupt pending bit */
    DMA_ClearITPendingBit(DMA2_Stream4, DMA_IT_TCIF4);
  }
}

/**************************************************************************************/

int main(void)
{
    /* Setup STM32 system (clock, PLL and Flash configuration) */
    SystemInit();
    
    RCC_Configuration();
    
    GPIO_Configuration();
    
    NVIC_Configuration();
    
    TIM2_Configuration();
    
    DMA_Configuration();
    
    ADC_Configuration();
    
    STM_EVAL_LEDInit(LED3); /* Configure LEDs to monitor program status */
    STM_EVAL_LEDInit(LED6); /* Configure LEDs to monitor program status */
    
    STM_EVAL_LEDOn(LED3); /* Turn LED3 on, 500 Hz means it working */
    
    /* Start ADC1 Software Conversion */
    ADC_SoftwareStartConv(ADC1);
    
    while(1); // Don't want to exit
}

Outcomes