cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 Strange behavior of ADCs in alternate trigger mode

CTord.1
Associate II

I will give you some context about what I am doing and my problem.

I am using a STM32H723ZG board for a project. In this project I must sample the signal coming from an image sensor that sends the pixel values as pulses with analog amplitudes. This sensor also provides a trigger signal so that the pixels data should be sampled at the rising edge of the trigger signal (an example is shown in the image below).

0693W00000aHp1hQAC.pngT_SAR (sampling time in terms of ADC clock) and T_SMPL (conversion time in terms of ADC clock) define how long the sampling and conversion process takes. Based on the application note How to get the best ADC accuracy in STM32 microcontrollers, it is difficult to estimate the Sampling Time for the ADC in order to fill completely the internal capacitors. So I used the SAR stimation tool (shown in the page 37 of the mentioned application note) to estimate that 8.5 adc cycles were necessary for the sampling operation for a 16- bit ADC with a resolution of 12 bits in fast channel (the board that I am using doesn't have direct channels for those ADCs).

Based on the following table (available in the pg.1014 in the reference manual RM0468:(

0693W00000aHp21QAC.pngThe T_SAR = 6.5 ADC clocks and the sampling time T_SMPL = 8.5 ADC clocks. so the total time for a sampling and conversion operation is 15 ADC clocks. The ADC frequnecy clock is 50 MHz, so the total time would be: 15 ADC clocks = 300 ns. In other words, in continuous mode, the sampling and conversion frequency would be 3,33 MHz.

The pixel pulses are sent with a frequency of 5 Mhz. For that reason I decided to use the two 16-bit ADCs with 12 bits resolution in alternate trigger mode. In this mode, both ADCs are triggered by an external signal alternately. So, in this way, I can read the signal at 5MHz.

The alternate trigger mode can only be implemented with injected channels (mentioned in the reference manual), which eliminates the possibility of using the DMA because DMA doesn't work with injected channels. So, I am using the ADCs with polling.

I am also calibrating the ADCs independently before starting them. However, there is a strange behaviour in the sampled signal after uploading the code. When I upload the code, I randomly get any of three possible outcomes:

0693W00000aHp2VQAS.pngEvery time I upload the code to the board, one of the three results (shown above) appears and it does not change over time. The signal that I would like to get all the time is the first one (clause a)). I would like to know why is this happening and how I could fix it. At the beginning, I though that the internal ADC capacitors were not full so it could be a source of error. So I did what I explained before. Then, I though it was because there wasn't enough waiting time for the initialization of the peripherals, but I already tried putting delays of 10 ms after the calibration and initialization of the ADCs (before they start sampling) and the results are still the same.

My configurations for the ADCs are: For the ADC1:

  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  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 = DISABLE;
  hadc1.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR;
  hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  hadc1.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
  hadc1.Init.OversamplingMode = DISABLE;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
 
  /** Configure the ADC multi-mode
  */
  multimode.Mode = ADC_DUALMODE_ALTERTRIG;
  multimode.DualModeData = ADC_DUALMODEDATAFORMAT_DISABLED;
  multimode.TwoSamplingDelay = ADC_TWOSAMPLINGDELAY_1CYCLE;
  if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
  {
    Error_Handler();
  }
 
  /** Disable Injected Queue
  */
  HAL_ADCEx_DisableInjectedQueue(&hadc1);
 
  /** Configure Injected Channel
  */
  sConfigInjected.InjectedChannel = ADC_CHANNEL_4;
  sConfigInjected.InjectedRank = ADC_INJECTED_RANK_1;
  sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_8CYCLES_5;
  sConfigInjected.InjectedSingleDiff = ADC_SINGLE_ENDED;
  sConfigInjected.InjectedOffsetNumber = ADC_OFFSET_NONE;
  sConfigInjected.InjectedOffset = 0;
  sConfigInjected.InjectedNbrOfConversion = 1;
  sConfigInjected.InjectedDiscontinuousConvMode = DISABLE;
  sConfigInjected.AutoInjectedConv = DISABLE;
  sConfigInjected.QueueInjectedContext = DISABLE;
  sConfigInjected.ExternalTrigInjecConv = ADC_EXTERNALTRIGINJEC_EXT_IT15;
  sConfigInjected.ExternalTrigInjecConvEdge = ADC_EXTERNALTRIGINJECCONV_EDGE_RISING;
  sConfigInjected.InjecOversamplingMode = DISABLE;

For the ADC2:

  hadc2.Instance = ADC2;
  hadc2.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
  hadc2.Init.Resolution = ADC_RESOLUTION_12B;
  hadc2.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc2.Init.LowPowerAutoWait = DISABLE;
  hadc2.Init.ContinuousConvMode = DISABLE;
  hadc2.Init.NbrOfConversion = 1;
  hadc2.Init.DiscontinuousConvMode = DISABLE;
  hadc2.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR;
  hadc2.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  hadc2.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
  hadc2.Init.OversamplingMode = DISABLE;
  if (HAL_ADC_Init(&hadc2) != HAL_OK)
  {
    Error_Handler();
  }
 
  /** Disable Injected Queue
  */
  HAL_ADCEx_DisableInjectedQueue(&hadc2);
 
  /** Configure Injected Channel
  */
  sConfigInjected.InjectedChannel = ADC_CHANNEL_4;
  sConfigInjected.InjectedRank = ADC_INJECTED_RANK_1;
  sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_8CYCLES_5;
  sConfigInjected.InjectedSingleDiff = ADC_SINGLE_ENDED;
  sConfigInjected.InjectedOffsetNumber = ADC_OFFSET_NONE;
  sConfigInjected.InjectedOffset = 0;
  sConfigInjected.InjectedNbrOfConversion = 1;
  sConfigInjected.InjectedDiscontinuousConvMode = DISABLE;
  sConfigInjected.AutoInjectedConv = DISABLE;
  sConfigInjected.QueueInjectedContext = DISABLE;
  sConfigInjected.InjecOversamplingMode = DISABLE;

Finally, based on the datasheet, the maximum ADC frequency is 50 MHz. So to reach that value I checked the ADC clock scheme and there is a internal frequency divider that divides the frequency by 2 (see image below). Then, the divided frequency is used by the ADC (pg. 981, reference manual RM0468).

0693W00000aHp68QAC.pngFor that reason I used 100 MHz in the ADC clock Mux in the Clock Configuration Section in STM32CubeMX (which would be the adc_ker_ck input from the image above):

0693W00000aHp8sQAC.png

3 REPLIES 3

The "result" waveforms, do they display a combined output from both ADCs? Can you display them independently?

JW

Hi @Community member​,

Yes, it looks like both ADCs follow the same pattern, but one has a different offset relative to the other and sometimes they have the same offset (as in the image of clause a)). Is it possible to always have the same offset for both?

CT

Offset may be result of different calibration. You can observe the calibration results in the CALFACT registers. You can even change them, although wouldn't recommend that, and I'd personally strive to find the primary source of the differences, if there are any.

Note, that the primary requirement for any serious ADC work, including calibration, is rock stable VREF+ against VREF-/VSSA (which includes VREF+ source, VREF+ and VREF-/VSSA routing including its "quietness" and decoupling from digital VSS, recommended or more decoupling capacitors' combo between VREF+ and VREF-/VSSA at the absolute minimal distance, etc. We are talking millivolts or better here. VREF+ is subject to similar pulsed current consumption from the switched capacitors as ADC inputs are, and its impact on ADC measurement results is huge.

JW