Skip to main content
Graduate
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
  • 1594 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!

    This topic has been closed for replies.

    8 replies

    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, please help us resolve this issue.

    Super User
    May 25, 2025

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

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

    }

    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.

    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.

    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. 

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