cancel
Showing results for 
Search instead for 
Did you mean: 

Timer(s) with 2 Ext inputs, one gating the other

digital_dreamer
Associate II
Posted on December 12, 2013 at 06:16

I've been coding on the STM32F4 for the past couple years and love the MCU.

However, I'm having trouble figuring out how to bring in two external signals and use one as the gate for the other. I'm planning on bringing in a 10MHz frequency and have a counter count it via external reference 1Hz signal (RTC output). Currently, I have no problem getting a counter going via external trigger. I have no problem with using a external signal as a gate, but I can't do both -- have a external clock counting only during the logic high/low of another external signal. This should be simple, but I just can't see it. Each attempt causes the external clock to revert to internal. I tried using two counters in a master/slave setup. My last attempt, shown below, is using a 32-bit timer andthe Input Capture registers, which appears to be what many of the examples use. Preferably, I don't want to use a interrupt, to keep latencies down. But, if I must, I'll just add the latency in the calculation. I figure I may have to use an interrupt to clear the registers.

void
SetupCounter(
void
)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable TIM2 clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* Enable GPIOA clock */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
/* Configure PA0 and PA1 pins as AF */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN ;
GPIO_Init(GPIOA, &GPIO_InitStructure); 
/* Connect TIM2 ETR to PA0 pin and TIM2 CH2 to PA1 */
GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_TIM2);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_TIM2);
/* Input Trigger selection */
TIM_ETRClockMode1Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0);
/* TIM2 time base configuration - fed by external calibrated timebase */
TIM_TimeBaseStructure.TIM_Period = -1;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* TIM1 Input Capture Configuration */
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0;
TIM_PWMIConfig(TIM2, &TIM_ICInitStructure);
TIM_SelectMasterSlaveMode(TIM2,TIM_MasterSlaveMode_Enable);
/* Select the slave Mode: Gated mode */
TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Gated);
/* Select the TIM2 Input Trigger: TIM_TS_ITR2 */
TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2);
/* Enable the TIM2 global Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Enable the CC2 Interrupt Request */
TIM_ITConfig(TIM2, TIM_IT_CC2, ENABLE);
/* Enable TIM2 counter */
TIM_Cmd(TIM2, ENABLE);
}
void
TIM2_IRQHandler(
void
)
{
/* Clear TIM2 Capture compare interrupt pending bit */
TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);
/* Get the Input Capture value */
CCR2Value = TIM2->CCR2;
TIM2->CCR2 = 0;
TIM2->CNT = 0;
CCR2Flag = SET;
}

2 REPLIES 2
Posted on December 12, 2013 at 10:04

IMO this is why external clock mode 2 exists. Thus, setting TIMx_SMCR.ECE to 1 to enable external clock mode 2, then setting TIMx_SMCR.TS to TI1FP1 or TI2FP2 (0b101 or 0b110), with TIMx_SMCR.SMS set to gated mode (0b101) should do the trick. This of course works only for timers which have the ETR signal.

JW

digital_dreamer
Associate II
Posted on December 14, 2013 at 08:14

Thanks,waclawek.jan! Your comment made lots of sense, and you set me in the right direction.

I often wondered what the clock mode 2 was for. I've looked at those block diagrams in the reference manual many times. No explanation is made regarding the mode 2: just how to use it - not what it's for. I got it working with TIM8 first, but needed it to be 32-bit. If I couldn't get it working on TIM2, I was just going to cascade TIM8 with another. Here's my working result with the 32-bit TIM2, using pin A15 and A1: (Note that I'm using the 1-Hz output from the RTC (PC13) against a reference timebase for calibrating the RTC. One would need to measure the entire calibration window, usually set at 32 seconds. But, can be configured to 16 or 8 seconds.)

/**
**********************************************
** Setup Timer 2 for RTC calibration
**
** PA15: TIM2_ETR - Timebase input
** PA1: TIM2_CH2 - 1-Hz trigger/gate input
**********************************************
*/
void
SetupRTCCounter(
void
)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// Enable TIM2 clock
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
// Enable GPIOA clock
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
// Configure PA1 and PA15 pins as AF
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN ;
GPIO_Init(GPIOA, &GPIO_InitStructure); 
// Connect TIM2 ETR to PA15 pin and TIM2 CH2 to PA1
GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_TIM2);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource15, GPIO_AF_TIM2);
// TIM2 time base configuration - fed by external calibrated timebase
TIM_TimeBaseStructure.TIM_Period = -1;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* TIM2 Input Capture Configuration */
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV2; 
// Divide by 2 to get full 1-Hz period
TIM_ICInitStructure.TIM_ICFilter = 0;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
/* Input Trigger selection for pin PA15 (reference timebase) */
TIM_ETRClockMode1Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0);
TIM_TIxExternalClockConfig(TIM2, TIM_TIxExternalCLK1Source_TI2, TIM_ICPolarity_Rising, 0x0);
/* Input Trigger selection for pin PA1 (1-Hz input from RTC) */
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0); 
// Can't do divide by 2 here, do it in TIM_ICInit
// Enable Master/Slave mode
TIM_SelectMasterSlaveMode(TIM2,TIM_MasterSlaveMode_Enable);
// Select the slave Mode: Gated mode
TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);
// Select the TIM2 Input Trigger: TIM_TS_ITR2
TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2);
/* Enable the TIM2 global Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// Enable the CC2 Interrupt Request
TIM_ITConfig(TIM2, TIM_IT_CC2, ENABLE);
// Enable TIM2 counter
TIM_Cmd(TIM2, ENABLE);
}

void
TIM2_IRQHandler(
void
)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);
// Clear TIM2 Capture compare interrupt pending bit
RTCOutputReady = SET;
// Set data ready flag
}