cancel
Showing results for 
Search instead for 
Did you mean: 

Input capture timer - measure the correct frequency

mfcprog
Associate II
Posted on April 17, 2012 at 15:01

Hi,

I want to capture a signal with 30Hz frequency by a timer input capture. The timer is already working but the measured frequency is not 30Hz. I always measure a frequency of 29.85Hz or 30.3Hz. Are there any mistakes according to may selected period or prescaler values? By the way I`m using the STM32F103XC processor.

void TIMER_InitInputCaptureTimer(TIM_TypeDef* TIMx, unsigned short tmrchannel, unsigned short period)

{

  TIM_ICInitTypeDef  TIM_ICInitStructure;

 TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

 unsigned short PrescalerValue = (unsigned short) (SYSTEM_TIMERCLOCK / TIMER_TAKT) - 1;

 TIM_TimeBaseStructure.TIM_Period       = period -1; //65535;

   TIM_TimeBaseStructure.TIM_Prescaler     = (TIMER_TAKT - 1); //0;

   TIM_TimeBaseStructure.TIM_ClockDivision    = 0;

   TIM_TimeBaseStructure.TIM_CounterMode     = TIM_CounterMode_Up;

   TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;

    TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure);

 TIM_PrescalerConfig(TIMx, PrescalerValue, TIM_PSCReloadMode_Immediate);

 TIM_ICInitStructure.TIM_Channel = tmrchannel; //TIM_Channel_3;

   TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;

   TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;

   TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;      // Div:1, every edge

   TIM_ICInitStructure.TIM_ICFilter = 0x3;

   TIM_ICInit(TIMx, &TIM_ICInitStructure);

 /* Enable the CC3 Interrupt Request */

   TIM_ClearITPendingBit(TIM1, tmrchannel); //TIM_IT_CC3);

 TIM_ITConfig(TIMx, tmrchannel, ENABLE);

}

void TIMER_Init(TIM_TypeDef* TIMx, unsigned char mode, unsigned short tmrchannel, unsigned char irqchannel,

 unsigned short period, unsigned short fps)

{

 NVIC_InitTypeDef NVIC_InitStructure;

 switch(mode)

 {

 case TMR_MODE_UPD:

  TIMER_InitUpdTimer(TIMx, tmrchannel, fps);

  break;

 case TMR_MODE_INPUTCAPTURE:

  TIMER_InitInputCaptureTimer(TIMx, tmrchannel, period);

  break;

 default:

  return;

 }

 

 

 NVIC_InitStructure.NVIC_IRQChannel = irqchannel; //TIM1_CC_IRQChannel;

   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

   NVIC_Init(&NVIC_InitStructure);

}

And that`s my call of the timer function:

volatile unsigned short IC3ReadValue1 = 0, IC3ReadValue2 = 0;

volatile unsigned short CaptureNumber = 0;

volatile unsigned int Capture = 0;

volatile unsigned int TIM1Freq = 0; 

int d2i(float d)

{

 float value;

 value = d;

 value /= 10;

 value += 0.5;

 return value;

}

TIMER_Init(TIM1, TMR_MODE_INPUTCAPTURE, TIM_Channel_3, TIM1_CC_IRQChannel, 64800, 0);

TIM_Cmd(TIM1, ENABLE);

My Interrupt ISR:

void TIM1_CC_IRQHandler (void)

{

   TIM1->SR & TIM_IT_CC3)

 {  /* capture timer */

    

  //clear pending bit

  TIM1->SR = (unsigned short)~TIM_FLAG_CC3;

  if(CaptureNumber == 0)

     {

       /* Get the Input Capture value */

       IC3ReadValue1 = TIM1->CCR3;

       CaptureNumber = 1;

     }

  else if(CaptureNumber == 1)

     {

       /* Get the Input Capture value */

       IC3ReadValue2 = TIM1->CCR3;

       /* Capture computation */

       if (IC3ReadValue2 > IC3ReadValue1)

       {

         Capture = (IC3ReadValue2 - IC3ReadValue1);

       }

       else

       {

         Capture = ((0xFFFF - IC3ReadValue1) + IC3ReadValue2);

       }

       /* Frequency computation */

    TIM1Freq =  d2i ((float)(2 * TIMER_TAKT * 10 / Capture));

          CaptureNumber = 0;

   }

  /* flag: new rising edge interrupt INTR received */

  captureFlag++;

 /* clear all flags */   

 TIM1->SR = ~0xFF;  

 }

}

I hope someone of you could give me some hints to measure the exact frequency of the input signal.

best regards

Hans
10 REPLIES 10
Posted on April 17, 2012 at 15:56

The math for the wrapping of the counter is not correct, 65536 (period = 65535, ie N-1) is typically used because you can play simple tricks with 16-bit math. If you use a different value you must account for it consistently.

64800 != 65536

So

Capture = ((0xFFFF - IC3ReadValue1) + IC3ReadValue2);

Should presumably be

Capture = ((64800 + IC3ReadValue2) - IC3ReadValue1);

Assuming you have suitable sized variables, and cast them appropriately.

No idea what TIMER_TAKT is, but the prescaler is limited to 16-bit. Or what SYSTEM_TIMERCLOCK is. Both would be important to understand the cycle times, and thus the period you're measuring.

Ideally you want the prescale value as small as possible to keep the measurement granularity fine, but you must also consider the range of frequencies you want to measure, and that the period will fit within the timer's 16-bit cycle count.

If you have course granularity you're going to observe +/- swings in frequency measurements in line with the beating of input frequency and sampling frequency with respect to each other.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
mfcprog
Associate II
Posted on April 17, 2012 at 17:18

Thanks for your reply.

#define TIMER_TAKT  1000

#defíne SYSTEM_TIMERCLOCK 36000000

ok if I`m right, then 1000-1 is for that prescaler to high? Which value would you choose?

best regards

Hans

Posted on April 17, 2012 at 19:04

For 30 Hz, the fastest clock would be 1.9 MHz (65536 ticks thereof), to give some range I'd probably shoot for 1 MHz, this could get down to 15.3 Hz minimum. Presuming your clock is 36 MHz then a prescale of 36-1 would give you better precision. ie 1 us ticks, vs 27.77 us ticks.

With the 36 KHz you are using, the measurement could fall in the 29.975, 30, 30.025 Hz bins. You have to decide what the frequency range is you want to measure, and also the accuracy/granularity.

Are you sure your timer is at 36 MHz, if your PLL/HCLK is at 72 MHz, and you use RCC_HCLK_Div2 for the APB bus, then the timer will be at 72 MHz

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
mfcprog
Associate II
Posted on April 17, 2012 at 21:45

Hi clive1,

yes you`re right the processor is running with 72MHz and if the prescaler != 0 the timer is also running with 72MHz.

Is it true that I have to get a Capture value of 30000000 if I use a prescaler value of 72 and a period of 65536 and the input capture signal has a frequency of 30Hz?

timer frequency = 72MHz / 72 => 1µs intervall

for 30Hz = 1µs intervall * 30000000;

Capture = ((0xFFFF - IC3ReadValue1) + IC3ReadValue2);

Tim1Frequency = 72MHz / prescaler / Capture;

best regards

Hans

Posted on April 17, 2012 at 23:00

Is it true that I have to get a Capture value of 30000000 if I use a prescaler value of 72 and a period of 65536 and the input capture signal has a frequency of 30Hz?

I'd be surprised. 30 Hz has a period of 333 ms, a counter ticking at 1 us will report a delta of 33333 ticks.

TIM_TimeBaseStructure.TIM_Period = 65535; // 0x10000 - 1, 16-bit limit
TIM_TimeBaseStructure.TIM_Prescaler = Prescaler - 1; //0;
..
uint16 IC3ReadValue, IC3ReadValue;
void TIM1_CC_IRQHandler (void)
{
uint32 Capture;
if (TIM1->SR & TIM_IT_CC3)
{ /* capture timer */
//clear pending bit
TIM1->SR = (unsigned short)~TIM_FLAG_CC3;
if(CaptureNumber == 0)
{
/* Get the Input Capture value */
IC3ReadValue1 = TIM1->CCR3;
CaptureNumber = 1;
}
else if(CaptureNumber == 1)
{
/* Get the Input Capture value */
IC3ReadValue2 = TIM1->CCR3;
/* Capture computation */
if (IC3ReadValue2 > IC3ReadValue1)
{
Capture = ((uint32)IC3ReadValue2 - (uint32)IC3ReadValue1);
}
else
{
Capture = (((uint32)IC3ReadValue2 + 0x10000) - (uint32)IC3ReadValue1);
}
/* For 30 Hz sample @ 1 MHz (1 us), Capture = 33333 */
/* Frequency computation */
Tim1Frequency = (72000000 / Prescaler) / Capture;
IC3ReadValue1 = IC3ReadValue2;
}
/* flag: new rising edge interrupt INTR received */
captureFlag++;
}
}

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on April 17, 2012 at 23:10

Want it to do rounding, then this integer math should suffice

Tim1Frequency = ((72000000 / Prescaler) + (Capture / 2)) / Capture;

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Cesar cfg
Associate II
Posted on February 13, 2013 at 08:32

Hello,

I am able to accurately capture one PWM input signal on the STM32F0x  Discovery board. But, I have not  any luck capturing 3 PWM singnals on a single timer.

Please ''Clive1 ''do you have any idea to help me.?!

 

Thank you.

Posted on February 13, 2013 at 09:06

It should be possible. You'd need to keep an array of values to keep track of the prior CCRx registers, and service each of the CCx interrupt sources with their own entry in the array. Configure all the channels in a similar fashion, they use the same time base.

The CCRx registers basically latch the current CNT count value as they trigger, you then need to compute the delta ticks of the count (0 .. 65535) to determine the periodicity.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Cesar cfg
Associate II
Posted on February 21, 2013 at 12:26

Thank you Clive you are sincerely the master of the masters.