cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H743 ADC/DMA apparently skipping samples

FColl.1
Associate III

I have a set up where I'm reading from ADC1 via DMA1 S0 into a circular buffer in RAM2. The problem is that it is randomly skipping samples. I initialise the buffer to a known bad value so I can see that some addresses in the buffer are not being overwritten. If I feed a sine into the input, the picture at the bottom of this post is what I get out. The flat line at the top is the pre-initialised data. The gaps in the sine wave are not in the same place each time. The gaps are in clusters of 8 samples or multiples of 8 samples.

I started with running the ADC in continuous mode but am now triggering from a timer. The timer for triggering the ADC1 is LPTIM1 running at 16.384kHz. I have only a single rank.

I've deliberately put the memory buffer at 0x30000000 to avoid bus conflicts with the cpu.

I am running a CUBEMX with freertos set-up. I've tried disabling all other activity. The only cpu activity is the idle loop and a single suspended task. The system tick timer interrupt will be firing at 1kHz.

I've tried putting the data buffer at 0x24000000, same thing. Shifting it up to 0x30002710.... same thing.

I've tried DMA in circular and normal mode. Same thing. Likewise the DMA fifo, enabling it, made no difference.

I am using the analog watchdog on the ADC, so I tried turning that off. Same outcome.

I was originally (and still need to be) converting two channels, ADC1 and ADC2, using dual regular simultaneous mode. I turned off ADC2 for testing but that did not help. When it was on, ADC2 had the skipped data in exactly the same place. This is not surprising given that there is only one dma transfer of a word per sample, the ADC2 is in top 16bits and ADC1 bottom 16 bits.

From what I can see, the ADC triggering is not stopped or missed, as there are no apparent discontinuities in the sine wave. The DMA trigger from the ADC must be occurring because the DMA target address is being incremented even when the data is not transferred. It seems to point to a DMA problem. It is like there is a weak address line from the DMA or something, so the DMA thinks it has written the data but nothing got transferred.

For the experts... I'll include some of the MX generated code here:

void MX_ADC1_Init(void)
{
 
  /* USER CODE BEGIN ADC1_Init 0 */
 
  /* USER CODE END ADC1_Init 0 */
 
  ADC_MultiModeTypeDef multimode = {0};
  ADC_ChannelConfTypeDef sConfig = {0};
 
  /* USER CODE BEGIN ADC1_Init 1 */
 
  /* USER CODE END ADC1_Init 1 */
  /** Common config
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV8;
  hadc1.Init.Resolution = ADC_RESOLUTION_16B;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc1.Init.LowPowerAutoWait = DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.NbrOfConversion = 1;
  hadc1.Init.DiscontinuousConvMode = ENABLE;
  hadc1.Init.NbrOfDiscConversion = 1;
  hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_LPTIM1_OUT;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
  hadc1.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR;
  hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
  hadc1.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
  hadc1.Init.OversamplingMode = ENABLE;
  hadc1.Init.Oversampling.Ratio = 8;
  hadc1.Init.Oversampling.RightBitShift = ADC_RIGHTBITSHIFT_3;
  hadc1.Init.Oversampling.TriggeredMode = ADC_TRIGGEREDMODE_SINGLE_TRIGGER;
  hadc1.Init.Oversampling.OversamplingStopReset = ADC_REGOVERSAMPLING_CONTINUED_MODE;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure the ADC multi-mode
  */
  multimode.Mode = ADC_MODE_INDEPENDENT;
  if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_3;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_8CYCLES_5;
  sConfig.SingleDiff = ADC_DIFFERENTIAL_ENDED;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  sConfig.OffsetSignedSaturation = DISABLE;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC1_Init 2 */
 
  /* USER CODE END ADC1_Init 2 */
 
}
void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{
 
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(adcHandle->Instance==ADC1)
  {
  /* USER CODE BEGIN ADC1_MspInit 0 */
 
  /* USER CODE END ADC1_MspInit 0 */
    /* ADC1 clock enable */
    __HAL_RCC_ADC12_CLK_ENABLE();
 
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**ADC1 GPIO Configuration
    PA6     ------> ADC1_INP3
    PA7     ------> ADC1_INN3
    */
    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
    /* ADC1 DMA Init */
    /* ADC1 Init */
    hdma_adc1.Instance = DMA1_Stream0;
    hdma_adc1.Init.Request = DMA_REQUEST_ADC1;
    hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
    hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_adc1.Init.Mode = DMA_NORMAL;
    hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
    hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
    {
      Error_Handler();
    }
 
    __HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);
 
    /* ADC1 interrupt Init */
    HAL_NVIC_SetPriority(ADC_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(ADC_IRQn);
  /* USER CODE BEGIN ADC1_MspInit 1 */
 
  /* USER CODE END ADC1_MspInit 1 */
  }
void MX_DMA_Init(void)
{
 
  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();
 
  /* DMA interrupt init */
  /* DMA1_Stream0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 5, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
 
}

Timer set

void MX_LPTIM1_Init(void)
{
 
  /* USER CODE BEGIN LPTIM1_Init 0 */
 
  /* USER CODE END LPTIM1_Init 0 */
 
  /* USER CODE BEGIN LPTIM1_Init 1 */
 
  /* USER CODE END LPTIM1_Init 1 */
  hlptim1.Instance = LPTIM1;
  hlptim1.Init.Clock.Source = LPTIM_CLOCKSOURCE_APBCLOCK_LPOSC;
  hlptim1.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV1;
  hlptim1.Init.Trigger.Source = LPTIM_TRIGSOURCE_SOFTWARE;
  hlptim1.Init.OutputPolarity = LPTIM_OUTPUTPOLARITY_HIGH;
  hlptim1.Init.UpdateMode = LPTIM_UPDATE_IMMEDIATE;
  hlptim1.Init.CounterSource = LPTIM_COUNTERSOURCE_INTERNAL;
  hlptim1.Init.Input1Source = LPTIM_INPUT1SOURCE_GPIO;
  hlptim1.Init.Input2Source = LPTIM_INPUT2SOURCE_GPIO;
  if (HAL_LPTIM_Init(&hlptim1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN LPTIM1_Init 2 */
 
  /* USER CODE END LPTIM1_Init 2 */
 
}
 
void HAL_LPTIM_MspInit(LPTIM_HandleTypeDef* lptimHandle)
{
 
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
  if(lptimHandle->Instance==LPTIM1)
  {
  /* USER CODE BEGIN LPTIM1_MspInit 0 */
 
  /* USER CODE END LPTIM1_MspInit 0 */
  /** Initializes the peripherals clock
  */
    PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM1;
    PeriphClkInitStruct.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSE;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
    {
      Error_Handler();
    }
 
    /* LPTIM1 clock enable */
    __HAL_RCC_LPTIM1_CLK_ENABLE();
  /* USER CODE BEGIN LPTIM1_MspInit 1 */
 
  /* USER CODE END LPTIM1_MspInit 1 */
  }
}

My code to start the sampling:

	// Interrupt if the DMA can't keep up with the ADC
	// If this occurs the DMA is blocked until the overrun is flag is cleared
	__HAL_ADC_ENABLE_IT(hadc1, ADC_IT_OVR);
 
	// Interrupt if the DMA experiences issues getting the data written
	__HAL_DMA_ENABLE_IT(hadc1->DMA_Handle, DMA_IT_TE);
	__HAL_DMA_ENABLE_IT(hadc1->DMA_Handle, DMA_IT_DME);
	result = HAL_DMA_RegisterCallback(hadc1->DMA_Handle, HAL_DMA_XFER_ERROR_CB_ID, DMA_transfer_error_callback);
	if(result != HAL_OK)
	{
		log_error("Couldn't register DMA transfer error callback, code = %d",(int)result);
	}
	result = HAL_DMA_RegisterCallback(hadc1->DMA_Handle, HAL_DMA_XFER_ABORT_CB_ID, DMA_abort_callback);
	if(result != HAL_OK)
	{
		log_error("Couldn't register DMA Abort callback, code = %d",(int)result);
	}
 
	// Clear the watchdog flag to make sure we don't get a spurious hit
	__HAL_ADC_CLEAR_FLAG(hadc1, ADC_FLAG_AWD1);
	while((hadc1->Instance->ISR) & (0x1 << 7))
	{
		__HAL_ADC_CLEAR_FLAG(hadc1, ADC_FLAG_AWD1);
	}
 
	//Start the ADCs
	result = HAL_ADC_Start_DMA(hadc1, (uint32_t *)analog_dma_buffer , ANALOGUE_SENSOR_BUFFER_SIZE);
	if(result != HAL_OK)
	{
		log_error("Couldn't start  ADC, code = %d",(int)result);
		retval = result;
	}
 
	result = HAL_LPTIM_Counter_Start(&hlptim1, 2);
	if(result != HAL_OK)
	{
		log_error("Couldn't start adc timer, code = %d",(int)result);
		retval = result;
	}

I've learned a lot about the STM32 ADC over the last few days, more than I'd hoped to really! I've tried everything I can think of and made no progress on this. Does anyone have any ideas on what else to check? My only other option is to try dropping the DMA and go back to an interrupt based scheme. But I'm trying DMA as the CPU really needs to be busy doing other things.

CubeMX version 6.3.0 RC5

0693W00000FCw4UQAT.png

1 ACCEPTED SOLUTION

Accepted Solutions
FColl.1
Associate III

I disabled the dcache and this is what I see.

0693W00000FCwFXQA1.png

View solution in original post

3 REPLIES 3
FColl.1
Associate III

Could this be caused by a cache problem?

FColl.1
Associate III

I disabled the dcache and this is what I see.

0693W00000FCwFXQA1.png

Georgy Moshkin
Senior II

I had a problem with sample skipping here. For timer triggering fixing ADC clock solved this issue. For highest sampling rate continuous mode solved wrong sampling rate problem. Check this thread: STM32H750 (rev.V) Needs higher ADC clock for dual interleaved mode when triggered from timer? and comment.

Disappointed with crowdfunding projects? Make a lasting, meaningful impact as a Tech Sponsor instead: Visit TechSponsor.io to Start Your Journey!