cancel
Showing results for 
Search instead for 
Did you mean: 

Help with Timer Capture Issues

blue_dolphin1987
Associate II
Posted on November 23, 2012 at 11:03

Hi fellow forum mates,

I am a student working on a new project using stm32f4 eval board. My task is to measure the frequency from 3 different sources ranging from 1000hz to 725khz. I have been trying for days using a function generator to simulate the source. I have no problem measuring the lower frequency however when i am measuring at higher frequency . I notice sometimes the frequency i measure is almostactuallyhalf of the input. Any idea or solution as i am very lost.

InitTimer2(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* TIM2 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* GPIOA clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Connect TIM pins to AF */
GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_TIM2);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_TIM2);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_TIM2);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_TIM2);
/* Enable the TIM1 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);
/* Time Base configuration */
TIM_TimeBaseStructure.TIM_Prescaler = PRESCALER;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = 65535;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1 ;
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 = 0x02;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
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 = 0x02;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_3 ;
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 = 0x02;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_4 ;
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 = 0x02;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
/* TIM enable counter */
TIM_Cmd(TIM2, ENABLE);
}
void TIM2_IRQHandler(void) {
GPIO_ToggleBits(GPIOD, GPIO_Pin_12);
if(TIM_GetITStatus(TIM2, TIM_IT_CC1) == SET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
if(CaptureNumber2 == 0)
{
/* Get the Input Capture value */
Timer2Read1 = TIM_GetCapture1(TIM2);
CaptureNumber2 = 1;
}
else if(CaptureNumber2 == 1)
{
/* Get the Input Capture value */
Timer2Read2 = TIM_GetCapture1(TIM2);
/* Capture computation */
if (Timer2Read2 > Timer2Read1)
{
Timer2Diff = (Timer2Read2 - Timer2Read1);
}
else if (Timer2Read2 < 
Timer2Read1
)
{
Timer2Diff = ((0xFFFF - Timer2Read1) + Timer2Read2);
}
else
{
Timer2Diff
= 
0
;
}
CaptureNumber2
= 
0
;//cc2
/* Frequency computation */
Timer2FREQ = (uint32_t) SystemCoreClock/2/ (1+PRESCALER) /Timer2Diff;

//save result into array

setSensorResult(Timer2FREQ);
}
}
else if(TIM_GetITStatus(TIM2, TIM_IT_CC2) == SET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);
if(CaptureNumber2 == 0)
{
/* Get the Input Capture value */
Timer2Read1
= 
TIM_GetCapture2
(TIM2);
CaptureNumber2
= 
1
;
}
else if(CaptureNumber2 == 1)
{
/* Get the Input Capture value */
Timer2Read2
= 
TIM_GetCapture2
(TIM2);
/* Capture computation */
if (Timer2Read2 > Timer2Read1)
{
Timer2Diff = (Timer2Read2 - Timer2Read1);
}
else if (Timer2Read2 < 
Timer2Read1
)
{
Timer2Diff = ((0xFFFF - Timer2Read1) + Timer2Read2);
}
else
{
Timer2Diff
= 
0
;
}
CaptureNumber2
= 
0
;//cc2
/* Frequency computation */
Timer2FREQ = (uint32_t) SystemCoreClock/2/ (1+PRESCALER) /Timer2Diff;

//save result into array

Sensor::setSensorResult(Timer2FREQ);
}
}
else if(TIM_GetITStatus(TIM2, TIM_IT_CC3) == SET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC3);
if(CaptureNumber2 == 0)
{
/* Get the Input Capture value */
Timer2Read1
= 
TIM_GetCapture3
(TIM2);
CaptureNumber2
= 
1
;
}
else if(CaptureNumber2 == 1)
{
/* Get the Input Capture value */
Timer2Read2
= 
TIM_GetCapture3
(TIM2);
/* Capture computation */
if (Timer2Read2 > Timer2Read1)
{
Timer2Diff = (Timer2Read2 - Timer2Read1);
}
else if (Timer2Read2 < Timer2Read1)
{
Timer2Diff = ((0xFFFF - Timer2Read1) + Timer2Read2);
}
else
{
Timer2Diff = 0;
}
CaptureNumber2 = 0;
Timer2FREQ = (uint32_t) SystemCoreClock/2/ (1+PRESCALER) /Timer2Diff;

//save result into array

Sensor::setSensorResult(Timer2FREQ);
}
}
GPIO_ToggleBits(GPIOD, GPIO_Pin_12);
}

Appreciate all your help .
4 REPLIES 4
Posted on November 23, 2012 at 13:54

725 KHz seems a bit on the high side to be measuring periods, I'd have the hardware counting pulses over a defined window.

Your repetitive use of CaptureNumber2 for holding state is problematic.

If you are saturating the core, do less in the interrupt.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
blue_dolphin1987
Associate II
Posted on November 24, 2012 at 08:36

Hi Clive , 

You mean i should count the number of input pulse over a fixed duration ? (e.g no of pulse in 10ms )

Do you mind elaborating on Your repetitive use of CaptureNumber2 for holding state is problematic.?

I tried single steeping sections of the code and it seems there i no re-entry of interrupt before the current routine is serviced so does it means i am not saturating the core ?

Regards with Thanks.

Posted on November 24, 2012 at 15:03

Ok, so lets analyze some failure modes of the presented code.

What happens if CC1 occurs followed by CC2, what delta are you measuring? You need channel unique variables.

What happens if CC1 and CC2 trigger at the same time? The if/else tree ignores this.

What happens if the measurement occurs at a 0x10000 tick interval, setting the delta to zero and then dividing by zero?

What happens when the timer rolls over? The 16-bit unsigned computation of the delta is wrong, simple 16-bit unsigned math would be easier and provide the correct answer all the time. You'd still need to catch the zero case.

The interrupt will not re-enter, if multiple CC1 interrupts occur during servicing only one will be recognized.

Avoid unnecessary division where possible, hw division mask some of the cost, but precompute where you can.

Consider if PRESCALER allows for the granularity and range required.

To integrate over time, you'd count pulses in the TIMx->CNT register (ie one timer per source), and have a secondary timer sample this, at say the 10 ms interval, using DMA. You could use an Update interrupt instead, DMA just has a tighter service window.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
blue_dolphin1987
Associate II
Posted on November 26, 2012 at 02:43

Hi Clive, 

I made modification to on only one timer channel at a time and precompute most of the calculations within the interrupt however the same issues with the error in frequency calculation is still observed. 

I am trying the implementation that you suggested . Do you mind sharing some codes as i am still a little hazy about the method?

Regards