0
\$\begingroup\$

I am working on an STM32H743 project where I need to read ADC data synchronized with a center-aligned PWM signal generated by TIM8. My goal is to ensure ADC sampling is correctly aligned at the center of the PWM cycle to reduce noise and get accurate readings.

I have configured ADC1 and TIM8 as follows (using STM32 HAL):


static 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_DIV1;
 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 = ENABLE;
 hadc1.Init.NbrOfConversion = 1;
 hadc1.Init.DiscontinuousConvMode = DISABLE;
 hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T8_TRGO;
 hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
 hadc1.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR;
 hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
 hadc1.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
 hadc1.Init.OversamplingMode = DISABLE;
 hadc1.Init.Oversampling.Ratio = 1;
 if (HAL_ADC_Init(&hadc1) != HAL_OK)
 {
 Error_Handler();
 }
 /** Configure the ADC multi-mode
 */
 multimode.Mode = ADC_DUALMODE_REGSIMULT;
 multimode.DualModeData = ADC_DUALMODEDATAFORMAT_32_10_BITS;
 multimode.TwoSamplingDelay = ADC_TWOSAMPLINGDELAY_1CYCLE;
 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_1CYCLE_5;
 sConfig.SingleDiff = ADC_SINGLE_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 */
}
static void MX_TIM8_Init(void)
{
 /* USER CODE BEGIN TIM8_Init 0 */
 /* USER CODE END TIM8_Init 0 */
 TIM_ClockConfigTypeDef sClockSourceConfig = {0};
 TIM_MasterConfigTypeDef sMasterConfig = {0};
 TIM_OC_InitTypeDef sConfigOC = {0};
 TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
 /* USER CODE BEGIN TIM8_Init 1 */
 /* USER CODE END TIM8_Init 1 */
 htim8.Instance = TIM8;
 htim8.Init.Prescaler = 0;
 htim8.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED1;
 htim8.Init.Period = 8000;
 htim8.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
 htim8.Init.RepetitionCounter = 0;
 htim8.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
 if (HAL_TIM_Base_Init(&htim8) != HAL_OK)
 {
 Error_Handler();
 }
 sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
 if (HAL_TIM_ConfigClockSource(&htim8, &sClockSourceConfig) != HAL_OK)
 {
 Error_Handler();
 }
 if (HAL_TIM_PWM_Init(&htim8) != HAL_OK)
 {
 Error_Handler();
 }
 sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
 sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
 sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
 if (HAL_TIMEx_MasterConfigSynchronization(&htim8, &sMasterConfig) != HAL_OK)
 {
 Error_Handler();
 }
 sConfigOC.OCMode = TIM_OCMODE_PWM1;
 sConfigOC.Pulse = 4000;
 sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
 sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
 sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
 sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
 sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
 if (HAL_TIM_PWM_ConfigChannel(&htim8, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
 {
 Error_Handler();
 }
 if (HAL_TIM_PWM_ConfigChannel(&htim8, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
 {
 Error_Handler();
 }
 sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
 sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
 sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
 sBreakDeadTimeConfig.DeadTime = 10;
 sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
 sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
 sBreakDeadTimeConfig.BreakFilter = 0;
 sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE;
 sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH;
 sBreakDeadTimeConfig.Break2Filter = 0;
 sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
 if (HAL_TIMEx_ConfigBreakDeadTime(&htim8, &sBreakDeadTimeConfig) != HAL_OK)
 {
 Error_Handler();
 }
 /* USER CODE BEGIN TIM8_Init 2 */
 /* USER CODE END TIM8_Init 2 */
 HAL_TIM_MspPostInit(&htim8);
}

My current settings trigger ADC conversion on TIM8 update event (TRGO). Since TIM8 is configured in center-aligned mode, the update event triggers on both overflow and underflow, meaning twice per PWM cycle.

My questions:

How can I verify that ADC sampling is correctly aligned at the center of the PWM cycle without using an oscilloscope? Could using output compare or capture mode in the timer help with providing a more precise ADC trigger event? Are there recommended ways or best practices to configure timer triggers to generate a single ADC trigger per PWM cycle at the middle of the PWM on-time in center-aligned mode? Thanks in advance for all insights and suggestions!

asked Sep 9 at 11:13
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Configure the trigger timer and ADC timer to have a difference of 1 in their counts. Then the sampling instant will drift slowly through all possible positions, and you can plot which gives you the best results. Basically you've built yourself an equivalent time sampling oscilloscope without a display. \$\endgroup\$ Commented Sep 9 at 14:10

1 Answer 1

1
\$\begingroup\$

My goal is to ensure ADC sampling is correctly aligned at the center of the PWM cycle to reduce noise and get accurate readings. How can I verify that ADC sampling is correctly aligned at the center of the PWM cycle without using an oscilloscope?

Neil_UK (in comments) has an excellent suggestion, capturing successive samples, where each sample is delayed by small increments of time...exactly as a well-triggered oscilloscope does.
This requires that the waveform you're sampling is nearly synchronous with TIM8. One might connect a waveform generator to ADC input, and adjust its frequency to be very close to TIM8's frequency. Then send successive samples to a memory array.
For example, suppose TIM8's period is one millisecond, so that successive ADC samples are sequenced one millisecond apart. A function generator set to 1.010 kHz (or to 0.99 kHz) will create a sample sequence of one hundred samples corresponding to one complete function generator cycle of 1000 Hz.

schematic

simulate this circuit – Schematic created using CircuitLab


A slightly different approach might substitute TIM8 for the function generator. Since TIM8 also triggers the ADC, every sample should yield the same ADC result.
To determine the time between TIM8's leading edge, and ADC's sample capture, add an RC time constant between TIM8's GPIO output and ADC input. Knowing the RC time constant should allow you to compute the delay from TIM8's leading edge - to the ADC's sampling moment.

schematic

simulate this circuit

answered Sep 9 at 15:58
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.