2026-01-28 10:33 AM - edited 2026-01-28 10:56 AM
Hello,
Description: I am attempting to generate two simultaneous pulses of different lengths using a single timer (TIM4) and multiple channels.
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:
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?
Why is a small CCR value (e.g., 2 ticks) not resulting in the expected output width when using this state-switching method?
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.
2026-01-28 11:30 AM - edited 2026-01-28 11:31 AM
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.
2026-01-28 11:45 AM
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
2026-01-28 2:30 PM
I don't know of an example which shown your exact use case. You may need to start from an example and tailor it to suit your needs.
Here is an example showing PWM usage.
All of this can be clicked in STM32CubeMX. All that is left is to start the PWM outputs in user code.
2026-01-29 5:29 AM
Thank you for your answer I am reviewing it.
Just one question.
I am forcing the output to be low and after that just before the timer init I am putting into PWM1 and this is introducing the delay in the first pulse. How it is usually done? I think this is not a common pattern and I should use what you recommended.
I would like to use tim4 for that.
Kind regards.
2026-01-29 6:12 AM
> How it is usually done?
It doesn't matter. Everyone uses timers differently because everyone has different needs and requirements. Focus on what you need rather than what other people are doing. If you don't want the delay, then you can't use what you are doing as the solution.