Skip to main content
Associate II
May 13, 2025
Question

trigger ADC exactly at midpoint of PWM ON time in Center-Aligned PWM Mode 2 (TIM1)?

  • May 13, 2025
  • 8 replies
  • 6367 views

_0-1747148083376.png

(I'm using NUCLEO-F103RB)

Hello,

I'm currently working on a BLDC motor control project using STM32CubeMX and STM32CubeIDE with STM32 TIM1 peripheral.

I have configured TIM1 in Center-Aligned PWM Mode 2, with:

Prescaler = 3

ARR = 899

So, PWM frequency is 10kHz (from a 72MHz system clock).

In this mode, since the counter counts up and down between 0 and ARR, the counter reaching ARR marks the exact midpoint of the PWM ON time.
I would like to trigger an ADC conversion exactly at that point (i.e., at 50% of the ON time).

To do this, I thought of using TRGO = Update Event, since the update event happens when CNT = ARR in Center-Aligned Mode 2.
However, I am not fully sure if this is guaranteed to occur at that midpoint and whether it's the most precise method.

:question_mark:My questions are:
Is TRGO = Update Event guaranteed to occur exactly when CNT = ARR in Center-Aligned Mode 2?

Is this the best practice to trigger ADC at the center of the PWM ON duration?

If not, is there a more accurate or recommended method?

Any insight or clarification would be greatly appreciated.
Thank you in advance!

8 replies

waclawek.jan
Super User
May 13, 2025

Update event occurs both on CNT=0 and CNT=ARR.

If you want trigger only upon CNT=ARR, set one channel to CCRx=ARR+1 and trigger on that.

Note, that there may be a delay between the Compare event and ADC trigger, due to resynchronization if the timer is at a different bus than ADC (or maybe even if it's on the same bus). Also note, that ADC trigger starts the sampling process, the ADC conversion start is further delayed by the sampling time.

What's the reason to use particularly Center-Aligned Mode 2?

JW

Visitor II
May 25, 2025

I'm also facing the same issue in the STM32G030C8T6 series Controller. I want to trigger the ADC using the TRGO2 trigger event on a specific Output compare OC2REF of timer 1, but it's not working. It seems to be a CUBEMX code generation error or a Timer Library-related issue. @st_it, please help us resolve this issue.

TDK
Super User
May 25, 2025

Maybe show your work instead of just saying it doesnt work. No reason to doubt the reference manual.

"If you feel a post has answered your question, please click ""Accept as Solution""."
Visitor II
May 26, 2025

Dear TDK, I'm sharing the code I tried, but it's not working. It's triggering only once, but not getting regular interrupts. I want to trigger the ADC using the TRGO2 trigger event on a specific Output compare OC4REF (Wrongly mentioned OC2REF in previous msg) of timer 1

 

void MX_TIM1_Init(void)
{

/* USER CODE BEGIN TIM1_Init 0 */

/* USER CODE END TIM1_Init 0 */

TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};

/* USER CODE BEGIN TIM1_Init 1 */

/* USER CODE END TIM1_Init 1 */
htim1.Instance = TIM1;
htim1.Init.Prescaler = FSW_26KHZ_PSC;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = FSW_26KHZ;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 1;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_OC_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC2REF;
sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_OC4REF;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
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(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_ACTIVE;
sConfigOC.Pulse = 399;
if (HAL_TIM_OC_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
sConfigOC.Pulse = 99;
if (HAL_TIM_OC_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
{
Error_Handler();
}
__HAL_TIM_ENABLE_OCxPRELOAD(&htim1, TIM_CHANNEL_4);
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = 0;
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.BreakFilter = 0;
sBreakDeadTimeConfig.BreakAFMode = TIM_BREAK_AFMODE_INPUT;
sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE;
sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH;
sBreakDeadTimeConfig.Break2Filter = 0;
sBreakDeadTimeConfig.Break2AFMode = TIM_BREAK_AFMODE_INPUT;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM1_Init 2 */

/* USER CODE END TIM1_Init 2 */
HAL_TIM_MspPostInit(&htim1);

}

waclawek.jan
Super User
May 26, 2025

> It's triggering only once, but not getting regular interrupts.

Are you talking about ADC interrupts? Maybe ADC does not convert upon the subsequent trigger, for example if it overflows. Read ADC chapter in RM.

JW

 

Visitor II
May 26, 2025

Dear Waclawek,
Yes, I'm talking about ADC working in Timer trigger interrupt Mode. ADC does its job perfectly if I select Trigger Event (TRGO2) as Update Event, but if I select OC4REF as Trigger Event (TRGO2) then it works only once.
Thanks.

waclawek.jan
Super User
May 26, 2025

> I'm using NUCLEO-F103RB

STM32F103 does not have TRGO2 in TIM1. See TIM1 and ADC chapters in RM0008.

JW

Visitor II
May 27, 2025

Dear Waclawek,
Ok, but I'm talking about the STM32G030C8T6 microcontroller.
Thanks.

waclawek.jan
Super User
May 27, 2025

@skverma92,

I've just noticed that you are not the original poster.

Please don't hijack other threads - start your own. Present a coherent description of your problem. Describe your hardware setup, and also describe what is in your software. Describe your observations and how are they different from the expected behaviour.

Read out and check/post content of relevant registers - TIM, ADC, maybe DMA if you use it. Check, if the flags which are supposed to trigger the interrupt, are set or not. A generic "interrupt does not fire" checklist is here.

JW

Visitor II
May 28, 2025

Dear Waclawek,
I'm a beginner in the ST Community. I just found this post almost similar to my issue. That's why I raised my issue in this post. Sorry, I don't want to hijack your thread. I'll do the necessary things to avoid this. Thanks.

Visitor II
May 28, 2025

Dear HI_st
FSW_26KHZ value is 799. External Trigger Conversion Source is selected as Timer 1 Trigger Out event 2. I'm attaching the ADC configuration code here. Thanks.

 

hadc1.Instance = ADC1;

hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;

hadc1.Init.Resolution = ADC_RESOLUTION_12B;

hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;

hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;

hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;

hadc1.Init.LowPowerAutoWait = DISABLE;

hadc1.Init.LowPowerAutoPowerOff = DISABLE;

hadc1.Init.ContinuousConvMode = DISABLE;

hadc1.Init.NbrOfConversion = 7;

hadc1.Init.DiscontinuousConvMode = DISABLE;

hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T1_TRGO2;

hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;

hadc1.Init.DMAContinuousRequests = ENABLE;

hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;

hadc1.Init.SamplingTimeCommon1 = ADC_SAMPLETIME_7CYCLES_5;

hadc1.Init.SamplingTimeCommon2 = ADC_SAMPLETIME_1CYCLE_5;

hadc1.Init.OversamplingMode = DISABLE;

hadc1.Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_HIGH;

if (HAL_ADC_Init(&hadc1) != HAL_OK)

{

Error_Handler();

}

 

 

ST Employee
May 28, 2025

I tried to configure ADC and TIM1 as it is in your project and it works to me right on my NUCELO board (ADC is periodically triggered and converted value is printed out). Why did you set DMAContinuousRequests as ENABLED, are you reading ADC value by DMA? if yes please share also DMA settings and code where you enabling ADC, DMA, TIM1 an all its interrupts. 

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
Visitor II
May 28, 2025

Dear HI_st,
Yes, I'm reading ADC Value by DMA. Here is the code,

GPIO_InitStruct.Pin = Pot1_Pin|Pot2_Pin|Pot3_Pin|Pot4_Pin

|Pot5_Pin|Pot6_Pin|Pot7_Pin;

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_Channel1;

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_CIRCULAR;

hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;

if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)

{

Error_Handler();

}

 

__HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);

 

/* ADC1 interrupt Init */

HAL_NVIC_SetPriority(ADC1_IRQn, 0, 0);

HAL_NVIC_EnableIRQ(ADC1_IRQn);