Electronic – STM32F051 ADC DMA overrun

adcdmastm32

I am trying to use ADC with 2 channels and DMA, but I only get 1 reading before the overrun bit gets set. Also the 1 reading is wrong, I am expecting a value of approx 2100, but I am getting around 70. I can see in the hadc->DR that the value are correct, but it is not transferred to the array somehow (adc_value in runADC200Hz function below).

here is the code I am using:

main.c

static void MX_ADC_Init(void)
{
  ADC_ChannelConfTypeDef sConfig = {0};

  /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) 
  */
  hadc.Instance = ADC1;
  hadc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
  hadc.Init.Resolution = ADC_RESOLUTION_12B;
  hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
  hadc.Init.EOCSelection = ADC_EOC_SEQ_CONV;
  hadc.Init.LowPowerAutoWait = DISABLE;
  hadc.Init.LowPowerAutoPowerOff = DISABLE;
  hadc.Init.ContinuousConvMode = ENABLE;
  hadc.Init.DiscontinuousConvMode = DISABLE;
  hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc.Init.DMAContinuousRequests = ENABLE;
  hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  if (HAL_ADC_Init(&hadc) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure for the selected ADC regular channel to be converted. 
  */
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
  sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure for the selected ADC regular channel to be converted. 
  */
  sConfig.Channel = ADC_CHANNEL_1;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

static void MX_DMA_Init(void) 
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Channel1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 3, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);

}

stm32f0xx_hal_msp.c

void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hadc->Instance==ADC1)
  {

    /* Peripheral clock enable */
    __HAL_RCC_ADC1_CLK_ENABLE();
  
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**ADC GPIO Configuration    
    PA0     ------> ADC_IN0
    PA1     ------> ADC_IN1 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* ADC1 DMA Init */
    /* ADC Init */
    hdma_adc.Instance = DMA1_Channel1;
    hdma_adc.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_adc.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_adc.Init.MemInc = DMA_MINC_ENABLE;
    hdma_adc.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_adc.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_adc.Init.Mode = DMA_CIRCULAR;
    hdma_adc.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_adc) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(hadc,DMA_Handle,hdma_adc);

    /* ADC1 interrupt Init */
    HAL_NVIC_SetPriority(ADC1_COMP_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(ADC1_COMP_IRQn);

  }

}

stm32f0xx_it.c

void DMA1_Channel1_IRQHandler(void)
{
  HAL_DMA_IRQHandler(&hdma_adc);
}

/**
  * @brief This function handles ADC and COMP interrupts (COMP interrupts through EXTI lines 21 and 22).
  */
void ADC1_COMP_IRQHandler(void)
{
  HAL_ADC_IRQHandler(&hadc);
}

I am running the ADC every 5ms like this:

static uint32_t adc_value[2];

void runADC200Hz(void)
{
  if (HAL_ADC_Start_DMA(&hadc, (uint32_t*) &adc_value[0], 2) != HAL_OK){
      asm("nop");
  }
}

After the first conversion these flags are set:

  • ADC ready (ADRDY)
  • End of sampling (EOSMP)
  • End of conversion (EOC)
  • End of sequence (EOS)
  • ADC overrun (OVR)

I have been setting up ADC with DMA several times before with no problems, but that was on F2 and F7. Strange thing is that I have access to another code which works for the exact same chip, with the exact same code I have written above. I have no idea where I should look and compare beyond those functions above. Any suggestion on what Im doing wrong here? I have tried every type of sampletime but it is the same result. thanks for any comments!

Best Answer

You are repeatedly calling the ADC in DMA mode, yet you have configured it to run continuous conversion mode. Meaning it will never stop sampling, repeating the sequence until stopped.

If your goal is to sample at 200 Hz, change it to these:

hadc.Init.ContinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConv =  ADC_EXTERNALTRIGCONV_T1_xxx;
hadc.Init.ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;

And configure a timer to trigger the sampling sequence.


I forgot, you also need to qualify adc_value as volatile since it changes outside of the compilers knowledge (the dma changes it).

The static keyword is poorly named and actually means private in this context. It has no effect other than reducing scope.