cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F105 Multichannel DMA ADC Read Buffer Offset Error

Jon Enz
Associate III

MCU: STM32F105RCTx

Firmware Version: STM32Cube FW_F1 V1.8.0

I have DMA setup to read 8 channels of the ADC into a fixed buffer and I'm having an issue where after an extended period of time (usually hours) the ADC channels are read into the wrong buffer index. The failure usually results in the destination index shifting back 1 from the correct index for a small amount of time (<100ms), and then the destination index shifts forward 1 from the correct index for an extended period of time (a couple of minutes). After that the issue seems to heal itself. I have the readings sent out over CAN in a 20ms periodic message and can observe the failure there.

This is high level view of the failure where you can observe the indexes switching.0693W00000FBEl8QAH.png 

This is a zoomed-in view of the point in time where the failure occurs.

0693W00000FBElNQAX.png 

The ADC and DMA settings are configured in Cube IDE.

0693W00000FBEmGQAX.png0693W00000FBEmLQAX.png 

The relevant generated code in main.c is:

/**
  * Enable DMA controller clock
  */
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, 7, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
  /* DMA1_Channel6_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 7, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
  /* DMA1_Channel7_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel7_IRQn, 7, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
 
}
 
/**
  * @brief ADC1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_ADC1_Init(void)
{
 
  /* USER CODE BEGIN ADC1_Init 0 */
 
  /* USER CODE END ADC1_Init 0 */
 
  ADC_ChannelConfTypeDef sConfig = {0};
 
  /* USER CODE BEGIN ADC1_Init 1 */
 
  /* USER CODE END ADC1_Init 1 */
  /** Common config
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 8;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_9;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_3;
  sConfig.Rank = ADC_REGULAR_RANK_2;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_7;
  sConfig.Rank = ADC_REGULAR_RANK_3;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_8;
  sConfig.Rank = ADC_REGULAR_RANK_4;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_14;
  sConfig.Rank = ADC_REGULAR_RANK_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_15;
  sConfig.Rank = ADC_REGULAR_RANK_6;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_6;
  sConfig.Rank = ADC_REGULAR_RANK_7;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_4;
  sConfig.Rank = ADC_REGULAR_RANK_8;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC1_Init 2 */
 
  /* USER CODE END ADC1_Init 2 */
 
}

The DMA is started periodically in software and observed after the transfer complete callback is called.

#define NUM_ADC_CHANNELS  ( 8 )
 
typedef struct {
  volatile uint32_t num_conversions;  
  uint32_t buffer[NUM_ADC_CHANNELS];
  volatile bool conversion_ready;
} adc_t;
 
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
  if (hadc->Instance == hadc1.Instance) {
    adc.num_conversions++;
    adc.conversion_ready = true;
  }
}
 
void Task_ADC() {
  HAL_ADC_Start_DMA(&hadc1, adc.buffer, NUM_ADC_CHANNELS);
}

It is worth noting that a previous configuration was used that had only 6 ADC channels and the issue was not noticed. I don't see any reason why adding the extra two channels could have caused this.

Has anyone encountered this error before or know if something about this configuration could cause an error?

2 REPLIES 2
Georgy Moshkin
Senior II

I would try configuring two GPIOs as outputs, place SET/RESET in different places and analyze on 2 channel scope is there any congestion. Or you can see if problem goes away if you change compiler settings to "optimize for speed". If problem goes away with this configuration it may indicate some blocking problems, when some areas of code run much longer time that you would imagine.

Another possibility is that somehow one DMA sample is lost. I observed such problem when used maximum sampling rate and a lot of DMA1 transfers (ADC in circular+DAC+UART simultaneously). In some cases UART DMA transfer shifted ADC DMA received buffer by one sample. Problem was solved by disabling ADC external trigger and moving UART to DMA2. Note that in my case ADC was continuously running in circular mode, so there was no any "self healing" and lost samples count accumulated over time as UART data was sent to the computer.

And less likely it may be the problem on the monitoring software side, the one which shows those graphs. If you still have this problem I need to see how Task_ADC() is used, and how I2C DMA or other peripherals with DMA are called.

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

If problem goes away with this configuration it may indicate some blocking problems, when some areas of code run much longer time that you would imagine.

  • I'm not sure if I follow how this would cause the ADC or DMA to error.

And less likely it may be the problem on the monitoring software side, the one which shows those graphs.

  • Those graphs are just the buffer index data spit out over CAN. I'm confident that is not the issue since the signals (voltage, current, and temp readings) the ADC represents clearly fail in the same way.

If you still have this problem I need to see how Task_ADC() is used, and how I2C DMA or other peripherals with DMA are called.

  • Task_ADC() is called on a 20ms interval by a small function scheduler. I have removed the I2C DMA configurations, so the only thing using DMA is the ADC. I will update if this configuration helps.