2026-01-27 8:28 AM
I am using a STM32G4 MCU and STM32CubeIDE.
I want to synchronise two timers (TIM3 and TIM15) that are generating PWM signals so that they both start at the same time.
TIM3 is set to 250 kHz and the PWM is modulated via DMA on channel 1. The modulated signal is a 10 kHz sine wave.
TIM15 is set to 10 kHz and the PWM is a simple 50% duty cycle.
I need the timers to start together to ensure that the PW Modulated 10 kHz sine wave (TIM3) is synchronised to the 10 kHz PWM (TIM15).
I thought it would be straight forward enough.
I have configured TIM15 as master. In CubeMX I have set Master/Slave Mode (MSM bit) to "Enable (Trigger delayed for master/slaves simultaneous start)" and Trigger Event Selection to "Enable (CNT_EN)".
I have configured TIM3 as a slave. In CubeMX I have set Slave Mode to "Trigger Mode" and Trigger Source to "ITR6" (per the reference manual).
To the CubeIDE generated code I have added:
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_1, BDPos_CCRValue_Buffer, CCRValue_BufferSize);
HAL_TIM_PWM_Start(&htim15, TIM_CHANNEL_1);
/* USER CODE END 2 */BDPos_CCRValue_Buffer and CCRValue_BufferSize are a pointer to the sine wave data buffer and the buffer size.
When I run the code the timers are not synchronised. After debugging through the code I found that TIM3 starts immediately on executing HAL_TIM_PWM_Start_DMA and does not wait for TIM15 to be started. TIM15 starts when this code is executed:
/* Enable the Capture compare channel */
TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);void TIM_CCxChannelCmd(TIM_TypeDef *TIMx, uint32_t Channel, uint32_t ChannelState)
{
uint32_t tmp;
/* Check the parameters */
assert_param(IS_TIM_CC1_INSTANCE(TIMx));
assert_param(IS_TIM_CHANNELS(Channel));
tmp = TIM_CCER_CC1E << (Channel & 0x1FU); /* 0x1FU = 31 bits max shift */
/* Reset the CCxE Bit */
TIMx->CCER &= ~tmp;
/* Set or reset the CCxE Bit */
TIMx->CCER |= (uint32_t)(ChannelState << (Channel & 0x1FU)); /* 0x1FU = 31 bits max shift */
}TIM15 starts when the last line is executed (ie after /* Set or reset the CCxE Bit */).
Is there a way to make TIM3 wait for TIM15 to start?
Thanks.
Solved! Go to Solution.
2026-01-27 12:32 PM
For anything but the most basic timer setups, I avoid using HAL. You'll have to tweak the initialization if it isn't quite doing what you want. That said, if you reset CEN and CNT, it may start up how you want. Hard to know what you're seeing vs what you expect to see.
2026-01-27 12:52 PM
Well I expected (hoped) that the slave timer would start at the same time as the master timer so that my signals would be aligned. Is that to much to expect? If it is then I'll stop trying now and find a different solution.
2026-01-28 2:46 AM
I tried switching to the LL libraries instead of HAL. It does exactly the same thing - sets the TIM3 CEN bit - during MX_TIM15_Init():
MX_TIM15_Init(void)
-> LL_TIM_Init(TIM15, &TIM_InitStruct)
--> LL_TIM_GenerateEvent_UPDATE(TIMx):
__STATIC_INLINE void LL_TIM_GenerateEvent_UPDATE(TIM_TypeDef *TIMx)
{
SET_BIT(TIMx->EGR, TIM_EGR_UG);
}This one line (SET_BIT(TIMx->EGR, TIM_EGR_UG)) updates several TIM3 registers but not any TIM15 registers. It updates:
TIM3->CR1.CEN bit set
TIM3->SR.TIF, SR.CC4IF, SR.CC3IF, SR.CC2IF, SR.CC1IF bits set
TIM3->CNT.CNT set to 0x10c
TIM3->DMAR.DMAR bit set
Strangely it seems not to change the TIM15->EGR which is referenced in this line. TIM_EGR_UG is an unsigned long with value = 1.
I'm not sure what is going on here.
2026-01-28 3:08 AM
Eventually I found (with the help of my AI buddy) the definition for the SET_BIT macro:
#define SET_BIT(REG, BIT) ((REG) |= (BIT))So all it's doing is ORing the referenced register with the 2nd argument. The referenced register is definitely TIM15->EGR, TIM_EGR_UG is 1 so I would expect bit 0 of TIM15->EGR to be 1 after this executes. But it isn't and several registers in TIM3 get updated instead. What's going on?
2026-01-28 5:36 AM
According to the reference manual (RM0440):
30.7.6 TIM15 event generation register (TIM15_EGR)
Bit 0 UG: Update generation
This bit can be set by software, it is automatically cleared by hardware.
0:No action
1:Reinitialize the counter and generates an update of the registers. Note that the prescaler
counter is cleared too (anyway the prescaler ratio is not affected).
Does anyone know what "update of the registers" means in practice?
It doesn't say anything about updating the registers of other timers.
2026-01-28 5:44 AM
An update event sets CNT back to 0 (if upcounting) and loads/updates ARR if preloading is enabled.
2026-01-28 6:16 AM
OK thanks I guess that's what the manual says but it adds "and generates an update of the registers".
I've switched back to HAL (because LL made no difference).
I tried switching the run order of the MX generated timer initialisation functions, MX_TIM3_Init() and MX_TIM15_Init(), so that the master timer is initialised before the slave timer. Now after both timers are initialised TIM3 CEN bit is NOT set and the TIM3 signal does NOT start until TIM15 is started. I feel like I'm getting somewhere. Of course it means I'll need to make this edit each time I regenerate the code so I'll review the timer selections eg use TIM1 instead of TIM15.
The next issue is the 10 kHz waveforms are not aligned. There seems to be a few microseconds of delay between starting TIM15 (master) and TIM3 (slave) starting. Is this to ne expected? Is there a way to adjust this?
2026-01-28 6:43 AM
> There seems to be a few microseconds of delay between starting TIM15 (master) and TIM3 (slave) starting. Is this to ne expected? Is there a way to adjust this?
This is expected. It takes some time for the logic to propagate so master and slave will be a few ticks off. It should be the same amount of ticks each time. Two options:
The RM talks about this some.
2026-01-28 7:43 AM - edited 2026-01-28 7:45 AM
Shouldn't be *micro*seconds though, given tens of megahertzs clock...
Even
should yield consistently sync within fraction of microseconds, at those clocks
JW
2026-01-28 8:10 AM
Thanks they both sound like great ideas.
I tried swapping the master/slave modes of both timers, so TIM3 becomes the master and TIM15 the slave. This solves the MX_TIMx_Init() run order issue too. I expected the startup delay to now show up on TIM15 as it is now the slave but the signals look identical to before I switched master/slave modes. So I expect that the few ticks of slave startup delay is an insignificant amount of time whereas the delay I'm seeing is 4 uS, which happens to correspond to the TIM3 period, ie one pulse.
Could the perceived delay be caused by DMA? When is the first DMA request sent - on timer startup or on the first timer update?