2014-09-23 12:18 PM
I'm trying to take advantage of the ability to synchronize two timers in a master/slave config. In my case, I'm using one timer as a PWM output, and would like to have a second timer track the first as closely as possible. I *think* I'm setting this up correctly to run these in parallel mode, but the second timer seems to lagging the first by ~175nS.
Here's the timer config:tmp = SystemCoreClock / 1000000;
tmp /= asic_clk;
TIM_DeInit(TIM_ASIC_SATCLK);
TIM_DeInit(TIM_ASIC_MAINCLK);
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_ICInit(TIM_ASIC_SATCLK,&TIM_ICInitStructure);
TIM_ICInit(TIM_ASIC_MAINCLK,&TIM_ICInitStructure);
/* Time base configuration for TIM 1 */
TIM_TimeBaseStructure.TIM_Period = tmp-1; //Value here should be 1 less than you would think, e.g. 8 rather than 9. 72/8 == 8mhz
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM_ASIC_SATCLK, &TIM_TimeBaseStructure);
/* PWM1 Mode configuration: Channel 1 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = tmp/2; //Value here should be what you would think, e.g. 6/2 == 3
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM_ASIC_SATCLK, &TIM_OCInitStructure);
/* Select the Master Slave Mode */
TIM_SelectMasterSlaveMode(TIM_ASIC_SATCLK, TIM_MasterSlaveMode_Enable);
/* Master Mode selection */
TIM_SelectOutputTrigger(TIM_ASIC_SATCLK, TIM_TRGOSource_Update);
/* Time base configuration for TIM 8 */
TIM_TimeBaseStructure.TIM_Period = tmp-1; //Value here should be 1 less than you would think, e.g. 8 rather than 9. 72/8 == 8mhz
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM_ASIC_MAINCLK, &TIM_TimeBaseStructure);
/* PWM1 Mode configuration: Channel 4 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = tmp/2; //Value here should be what you would think, e.g. 6/2 == 3
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_ICInitStructure.TIM_Channel = TIM_Channel_4; //Select Ch4 (otherwise CH 1 is default)
TIM_OC4Init(TIM_ASIC_MAINCLK, &TIM_OCInitStructure);
/* Slave Mode selection: TIM8 */
TIM_SelectSlaveMode(TIM_ASIC_MAINCLK, TIM_SlaveMode_Gated);
TIM_SelectInputTrigger(TIM_ASIC_MAINCLK, TIM_TS_ITR0);
TIM_OC1PreloadConfig(TIM_ASIC_SATCLK, TIM_OCPreload_Enable);
TIM_OC1PreloadConfig(TIM_ASIC_MAINCLK, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM_ASIC_SATCLK, ENABLE);
TIM_ARRPreloadConfig(TIM_ASIC_MAINCLK, ENABLE);
TIM_InternalClockConfig(TIM_ASIC_SATCLK);
TIM_InternalClockConfig(TIM_ASIC_MAINCLK);
TIM_CtrlPWMOutputs(TIM_ASIC_SATCLK, ENABLE);
TIM_CtrlPWMOutputs(TIM_ASIC_MAINCLK, ENABLE);
TIM_Cmd(TIM_ASIC_SATCLK, ENABLE);
TIM_Cmd(TIM_ASIC_MAINCLK, ENABLE);
Here's the GPIO config:
// Output ASIC satellite clock on TIM1 pin
GPIO_PinAFConfig(GPIO_SATCLK,GPIO_PinSource8,GPIO_AF_TIM1) ;
GPIO_InitStructure.GPIO_Pin = ASIC_SATCLK_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
if ( g_blk6.asic_clk == 30 )
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
else
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIO_SATCLK, &GPIO_InitStructure);
// Output ASIC main board clock on TIM8 pin
GPIO_PinAFConfig(GPIO_MAINCLK,GPIO_PinSource9,GPIO_AF_TIM8) ;
GPIO_InitStructure.GPIO_Pin = ASIC_MAINCLK_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
if ( g_blk6.asic_clk == 30 )
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
else
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIO_MAINCLK, &GPIO_InitStructure);
Anyone have any ideas?
Thanks!
#stm32-timer-synchronization
2014-09-24 12:42 AM
>
TIM_SelectSlaveMode(TIM_ASIC_MAINCLK, TIM_SlaveMode_Gated);
Certainly not. In the slave, you want to use reset or trigger modes.
Reset mode sounds to be straighforward, but due to resynchronisation delays in the TRGO(TIMx)->TRGI(TIMy) line there still will be some delay (probably one APB2 clock in case of TIM1->TIM8, but the RM completely omits the fine timing issues so one is left to experimentation). I personally would try trigger mode on the slave, with setting its counter to some non-zero initial value, to compensate for this delay. Or, if perfect sync is requred, I'd set both timers as slaves and trigger them from a common source (TIM2/TIM4/TIM5).
JW
2014-09-26 05:25 AM
Thanks for the reply! Trying your last suggestion (using timer 2 as master, slaving both other timers off it), as that would be perfect for my application. But timer 1 and 8 still aren't in sync (off by about 20nS). What am I missing?
tmp = SystemCoreClock / 1000000;
tmp /= asic_clk;
TIM_DeInit(TIM_ASIC_SATCLK);
TIM_DeInit(TIM_ASIC_MAINCLK);
TIM_DeInit(TIM2);
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_ICInit(TIM_ASIC_SATCLK,&TIM_ICInitStructure);
TIM_ICInit(TIM_ASIC_MAINCLK,&TIM_ICInitStructure);
TIM_ICInit(TIM2,&TIM_ICInitStructure);
/* Time base configuration for TIM 2 */
TIM_TimeBaseStructure.TIM_Period = tmp-1; //Value here should be 1 less than you would think, e.g. 8 rather than 9. 72/8 == 8mhz
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* PWM1 Mode configuration: Channel 1 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = tmp/2; //Value here should be what you would think, e.g. 6/2 == 3
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
/* Select the Master Slave Mode */
TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);
/* Master Mode selection */
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
/* Time base configuration for TIM 1 */
TIM_TimeBaseStructure.TIM_Period = tmp-1; //Value here should be 1 less than you would think, e.g. 8 rather than 9. 72/8 == 8mhz
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM_ASIC_SATCLK, &TIM_TimeBaseStructure);
/* PWM1 Mode configuration: Channel 1 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = tmp/2; //Value here should be what you would think, e.g. 6/2 == 3
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM_ASIC_SATCLK, &TIM_OCInitStructure);
/* Time base configuration for TIM 8 */
TIM_TimeBaseStructure.TIM_Period = tmp-1; //Value here should be 1 less than you would think, e.g. 8 rather than 9. 72/8 == 8mhz
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM_ASIC_MAINCLK, &TIM_TimeBaseStructure);
/* PWM1 Mode configuration: Channel 4 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = tmp/2; //Value here should be what you would think, e.g. 6/2 == 3
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_ICInitStructure.TIM_Channel = TIM_Channel_4; //Select Ch4 (otherwise CH 1 is default)
TIM_OC4Init(TIM_ASIC_MAINCLK, &TIM_OCInitStructure);
/* Slave Mode selection: TIM1 */
TIM_SelectSlaveMode(TIM_ASIC_SATCLK, TIM_SlaveMode_Trigger);
TIM_SelectInputTrigger(TIM_ASIC_SATCLK, TIM_TS_ITR0);
/* Slave Mode selection: TIM8 */
TIM_SelectSlaveMode(TIM_ASIC_MAINCLK, TIM_SlaveMode_Trigger);
TIM_SelectInputTrigger(TIM_ASIC_MAINCLK, TIM_TS_ITR0);
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_OC1PreloadConfig(TIM_ASIC_SATCLK, TIM_OCPreload_Enable);
TIM_OC1PreloadConfig(TIM_ASIC_MAINCLK, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM2, ENABLE);
TIM_ARRPreloadConfig(TIM_ASIC_SATCLK, ENABLE);
TIM_ARRPreloadConfig(TIM_ASIC_MAINCLK, ENABLE);
TIM_InternalClockConfig(TIM2);
TIM_InternalClockConfig(TIM_ASIC_SATCLK);
TIM_InternalClockConfig(TIM_ASIC_MAINCLK);
TIM_CtrlPWMOutputs(TIM2, ENABLE);
TIM_CtrlPWMOutputs(TIM_ASIC_SATCLK, ENABLE);
TIM_CtrlPWMOutputs(TIM_ASIC_MAINCLK, ENABLE);
TIM_Cmd(TIM2, ENABLE);
TIM_Cmd(TIM_ASIC_SATCLK, ENABLE);
TIM_Cmd(TIM_ASIC_MAINCLK, ENABLE);
2014-09-26 05:47 AM
I don't dig the ''library'' gobbledygook, but this:
> TIM_Cmd(TIM_ASIC_SATCLK, ENABLE);
> TIM_Cmd(TIM_ASIC_MAINCLK, ENABLE);
... defeats the purpose of trigger mode.
Read the fine manual. I know it won't make sense at the first reading; re-read it. Focus on the registers' description. This particular bit of information, for example, is hidden in the note in the description of CEN bit of TIM_CR.
JW
PS. You don't need to set the MSM bit for TIM2, as you don't need it to be in sync with TIM1 and TIM8.