cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 One pulse mode generation for two pulses wth different length

Gusa
Visitor

Hello,

Description: I am attempting to generate two simultaneous pulses of different lengths using a single timer (TIM4) and multiple channels.

pulse.png

Current Implementation: I am currently using One-Pulse Mode (OPM). In my existing setup, the pulse starts at CCR and ends at ARR. However, since ARR is a global register for the timer, all channels are constrained to the same end time, making it impossible to achieve independent pulse widths if they all trigger at the same ARR event.

Objective: To achieve independent pulse widths, I want the pulse to start at CNT = 0 and end at CNT = CCRx for each channel. This would allow CCR1 and CCR2 to define the lengths independently while sharing the same timer base.

The Problem: I am struggling to maintain the correct output state outside of the pulse window (specifically for t<0 and t>ARR). I have attempted to use LL_TIM_OCMODE_FORCED_INACTIVE to keep the pin low before the trigger and then switch to LL_TIM_OCMODE_PWM1 just before enabling the counter. 

Despite this, I am not seeing the expected pulse width (e.g., a CCR of 2 ticks is not rendering correctly).

First attempt
This is pseudocode it might no compile. The use is calling init with two pins and use generate pulse to generate both pulses at the same time.

 

void Init(CONFIG_T p_config) {
    uint32_t arr_value = 200;
    uint32_t crr_value = 2;
    
    /* Enable clocks */
    LL_AHB2_GRP1_EnableClock(p_config->PortClock);
    LL_APB1_GRP1_EnableClock(GetPeripheralClock(p_config->Timer));

    LL_GPIO_InitTypeDef gpio_init = {
        .Pin        = p_config->Pin,
        .Mode       = LL_GPIO_MODE_ALTERNATE,
        .Alternate  = p_config->AF,
        .Speed      = LL_GPIO_SPEED_FREQ_VERY_HIGH,
        .OutputType = LL_GPIO_OUTPUT_PUSHPULL,
        .Pull       = LL_GPIO_PULL_NO
    };
    LL_GPIO_Init(p_gpio_port, &gpio_init);

    /* Configure Timer: PWM1 + Polarity HIGH */
    /* PWM1 + HIGH: CNT < CCR → INACTIVE (HIGH), CNT >= CCR → ACTIVE (LOW) */
    LL_TIM_DisableCounter(p_timer);
    LL_TIM_SetCounterMode(p_timer, LL_TIM_COUNTERMODE_UP);
    LL_TIM_SetPrescaler(p_timer, 0);
    LL_TIM_SetAutoReload(p_timer, arr_value);
    LL_TIM_OC_SetPolarity(p_timer, channel, LL_TIM_OCPOLARITY_HIGH);
    LL_TIM_OC_SetMode(p_timer, channel, LL_TIM_OCMODE_PWM1);
    SetCompareChannel(channel, p_timer, crr_value);

    LL_TIM_OC_EnablePreload(p_timer, channel);
    LL_TIM_EnableARRPreload(p_timer);
    LL_TIM_CC_EnableChannel(p_timer, channel);

    /* Use One-Pulse Mode to stop automatically at ARR */
    LL_TIM_SetOnePulseMode(p_timer, LL_TIM_ONEPULSEMODE_SINGLE);

    LL_TIM_OC_SetMode(p_timer, channel, LL_TIM_OCMODE_FORCED_INACTIVE);

    /* Initialize counter to ARR → output LOW before pulse */
    LL_TIM_SetCounter(p_timer, 0);
    LL_TIM_ClearFlag_UPDATE(p_timer);
    LL_TIM_EnableAllOutputs(p_timer);
}

void GeneratePulse(PINS_T pin) {
    CONFIG_T const *p_config = &PinConfig[pin];
    TIM_TypeDef         *p_timer  = GetTimer(p_config->Timer);
    uint32_t             channel  = GetChannel(p_config->Channel);
    LL_TIM_DisableCounter(p_timer);

    /* Start pulse: reset counter and enable - One-Pulse Mode handles the rest */
    LL_TIM_GenerateEvent_UPDATE(p_timer);
    LL_TIM_ClearFlag_UPDATE(p_timer);

    LL_TIM_SetCounter(p_timer, 0);

    LL_TIM_OC_SetMode(p_timer, channel, LL_TIM_OCMODE_PWM1);

    LL_TIM_EnableCounter(p_timer);
}


Second attempt:
As a different approach I tried to use PWM2 with polarity low. The first timer set counter to ARR so the output is 0v. However, when I call generate pulse, the length of the first pulse is not the number of ticks I have selected, and after it reaches ARR it goes to 3v3. Due to that I need to refresh it manually. 

Please, let me know if it would be interesting to add the code.

Questions:

  1. Is switching from FORCED_INACTIVE to PWM1 the standard approach for starting a pulse at T=0, or is there a more robust configuration using PWM2 and inverted polarity?

  2. Why is a small CCR value (e.g., 2 ticks) not resulting in the expected output width when using this state-switching method?

  3. Is there a better way to achieve independent pulse lengths on multiple channels of the same timer using OPM?

    Thank you very much for your help.

2 REPLIES 2
TDK
Super User

Have the pulses start on tick 1 instead of tick 0. Set up CH1 and CH3 in combined PWM mode 2, others in PWM mode 1.

CCR1 = 1

CCR2 sets top pulse

CCR3 = 1

CCR4 sets bottom pulse

Reverse polarity of the bottom pulse.

TDK_0-1769628602580.png

 

If you feel a post has answered your question, please click "Accept as Solution".

Thank you very much for your help. I am afraid I am a bit new. Could you point me to an example about it?

Kind regards