cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 timer misses count in Input Capture mode

Mechatronz
Associate II
Posted on June 29, 2018 at 10:16

Hi everyone. I am using STM32F303RE and STM32L011 nucleo boards as a part of my experiment. I used STM32F303RE to generate PWM signal (in Interrupt mode) for 1MHz.

Clock Source - PLLCLK (8Mhz * 9 = 72 MHz).

Timer Period = 71

PWM pulse = 35 (for 50% duty cycle)

I used picoscope to measure the frequency. You can see the image below.

0690X0000060LXDQA2.png

Now, i set up the STM32L011 in input capture mode (interrupt mode) to capture this 1 MHz signal. It has 32MHz clock source. The input signal is divided by 8 ( sConfigIC.ICPrescaler = TIM_ICPSC_DIV8;)

In Interrupt mode, i used CCR register to get the captures done. I read it through UART in serial plot.

0690X0000060LXEQA2.png

As you can see, the timer misses a count. I don't know the problem behind this but this always happens. The timer always misses a count. I tested with different signals but got the same result. Even tried PWM with STM32L011 and captured in another controller, but the result is the same.

Here is the capture_flag_function that i use to get the values

void capture_flag_function (void)

{

switch(capture_flag)

{

case 1:

compare_value = htim2.Instance->CCR1;

break;

case 2:

capture_new = htim2.Instance->CCR1;

CAPTURE_sm = BUFF_FULL;

break;

default:

break;

}

}

by comparing compare_value and capture_new, i obtain the count value.

if(capture_new > compare_value)

count_new = capture_new - compare_value;

if(capture_new < compare_value)

count_new = 65536 - compare_value + capture_new;

Does anyone have an idea of the problem I am facing? Or can you give me suggestions if I am doing something wrong. Thank you.

#count #stm32f303 #stm32-timer-counter #timer-interrupts #frequency-detection #stm32l0-timer #stm32f303-timer

Note: this post was migrated and contained many threaded conversations, some content may be missing.
24 REPLIES 24
Posted on June 30, 2018 at 14:49

Thank you again for your reply. It seems clearly that you didn't go through my previous replies. I have stated the timer settings there. I will do it once again for you.

/* TIM2 init function (STM32L011 32MHz max frequency) */

static void MX_TIM2_Init(void)

{

  TIM_ClockConfigTypeDef sClockSourceConfig;

  TIM_MasterConfigTypeDef sMasterConfig;

  TIM_IC_InitTypeDef sConfigIC;

  htim2.Instance = TIM2;

  htim2.Init.Prescaler = 0;

  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;

  htim2.Init.Period = 65535;

  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)

  {

    _Error_Handler(__FILE__, __LINE__);

  }

  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;

  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)

  {

    _Error_Handler(__FILE__, __LINE__);

  }

  if (HAL_TIM_IC_Init(&htim2) != HAL_OK)

  {

    _Error_Handler(__FILE__, __LINE__);

  }

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;

  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;

  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)

  {

    _Error_Handler(__FILE__, __LINE__);

  }

  sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;

  sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;

  sConfigIC.ICPrescaler = TIM_ICPSC_DIV8;

  sConfigIC.ICFilter = 0;

  if (HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)

  {

    _Error_Handler(__FILE__, __LINE__);

  }

}

As mentioned in previous replies, I clearly said that this problem exists even If I set the IC prescaler to DIV1 or DIV2 or DIV 4.

To answer your question about UART, i am reading through the USB serial connection. No dedicated wires for the sake of UART. But this doesn't matter as I tried without UART, in debug mode. Again, please refer to the pictures that I posted to reply Dhenry.

Regarding the interrupt, I am running a timer to capture the input signal. And with the code you are right. I am using CCR register to get the captures, as I have already stated before. Also, I tried in DMA mode with less CPU intervention as I thought this may solve the problem. Guess what? Same thing happens. The timer misses a count and then gets back to the expected value, which happens frequently. Again, as I mentioned before, this happended for even higher frequencies. It is always that single count or two (in some cases) that causes the accuracy issue.

Again, I am requesting you not to deviate from the main problem. I am stating again this clearly. UART is not the issue here. Please go through my previous replies on what has been discussed here and then make a comment. Thank you for your effort.

Posted on June 30, 2018 at 15:09

I am using single timer with the above given configurations.

Let me tell what I understood, correct me if I am wrong.

In Input capture mode, the CCR register latches to that value of counter after a rising/falling transistion is detected. Basically, If i am measuring a signal continuously, CCRx (current transition) - CCRx (previous transistion) will give me the counts between the two edges (provided that no overflow happens). I hope I am doing good till this point.

Using the IC callback feature, I used the values of CCRx register to get two

consecutive

values. So, if the input is of constant frequency,  I should be reading the same value. Am I right?
Posted on June 30, 2018 at 15:16

And if you want  to test, just generate a PWM signal of any frequency and test it in input capture mode. See if both are same without any counts being missed. My settings doesn't matter in this case. Its about the accuracy of the timer/clock.

Posted on June 30, 2018 at 15:19

I am not getting an interrupt every 1µSec. It varies a bit. Thats the issue here.

And I am slowing down the input signal just to improve accuracy. Its better to count few pulses and providing a value. Even if i keep the setting to DIV1, the same thing happens. It gets worse when I go to higher frequency, whether its IT or DMA.

Posted on June 30, 2018 at 15:27

You have to do your own work.

You are running tooo many interrupts, the processor cannot keep up.

you need 2 timers, 1 as a counter to 8, and the other free running DIV1 32bit if you have it.

if you want to divide the input signal by 8 use the counter function.

use that timer's external clock input pin, not Input Capture pin.

use that timer in counter mode, set the reload to 8 ( or 9 ?)

use the reload interrupt to check your other free running timer for the 8 pulse width count.

in that interrupt you can toggle an IO pin, then you will see a solid lock.

Posted on June 30, 2018 at 15:31

Ok. I will try this. Thank you for the suggestion.

Posted on July 02, 2018 at 12:09

I setup two timers as you mentioned. I preferred to run the free timer on DMA mode, while having the counter in IT mode and toggled a pin. I indeed got almost a solid lock and its more than good. But, when I tried to obtain the count values from the free running timer, the counts get missed. I surely know I am missing something here.But don't know where exactly.

I tried to trim down the interrupt function, by taking into account only the DIER and SR flags. Here is the below function that I use.

void TIM_IT_function(void) // using this as the main interrupt function for the timer 2.

{

    TIM_TypeDef *const timer = htim2.Instance;

    uint_fast16_t IT_Enable = timer->DIER;

    uint_fast16_t status = timer->SR & IT_Enable;

    timer->SR = ~status;

    

        uint_fast16_t flag = status;

        status &= ~flag;

            

        switch(flag) {

                    case UPDATE_EVENT:

                        

                    capture_flag++;

                    Checkpin_GPIO_Port -> ODR^= Checkpin_Pin;

                    

                    capture_flag_function();                    

                    break;

                    

                default:

                    break;    

                }    

         

                IT_Enable &= ~status;

                timer->DIER = IT_Enable;

}

and here is the capture_flag_function that I use to read the counter value of free running timer (TIM 6)

void capture_flag_function(void)

{

            switch(capture_flag)

        {

            case 1:

                compare_value = htim6.Instance->CNT;

                break;

            

            case 2:

                capture_new = htim6.Instance->CNT;

                CAPTURE_sm = COMPLETE;

                break;

            

            default:

                break;

        }

}

Here is the settings for TIM2 and TIM6

/* TIM2 init function */

static void MX_TIM2_Init(void)

{

  TIM_ClockConfigTypeDef sClockSourceConfig;

  TIM_MasterConfigTypeDef sMasterConfig;

  htim2.Instance = TIM2;

  htim2.Init.Prescaler = 0;

  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;

  htim2.Init.Period = 99;

  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;

  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)

  {

    _Error_Handler(__FILE__, __LINE__);

  }

  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_ETRMODE2;

  sClockSourceConfig.ClockPolarity = TIM_CLOCKPOLARITY_NONINVERTED;

  sClockSourceConfig.ClockPrescaler = TIM_CLOCKPRESCALER_DIV1;

  sClockSourceConfig.ClockFilter = 0;

  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)

  {

    _Error_Handler(__FILE__, __LINE__);

  }

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;

  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;

  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)

  {

    _Error_Handler(__FILE__, __LINE__);

  }

}

/* TIM6 init function */

static void MX_TIM6_Init(void)

{

  TIM_MasterConfigTypeDef sMasterConfig;

  htim6.Instance = TIM6;

  htim6.Init.Prescaler = 0;

  htim6.Init.CounterMode = TIM_COUNTERMODE_UP;

  htim6.Init.Period = 65535;

  htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;

  if (HAL_TIM_Base_Init(&htim6) != HAL_OK)

  {

    _Error_Handler(__FILE__, __LINE__);

  }

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;

  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;

  if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK)

  {

    _Error_Handler(__FILE__, __LINE__);

  }

}

    HAL_TIM_Base_Start_DMA (&htim6,timer_base,1); //  timer_base [0] = 65535

I am performing this without UART. Also, I tried setting both the timer in DMA, still results in similar case. But I got a good fix when I measure the toggle pin with picoscope. Any suggestions?

Posted on July 03, 2018 at 08:28

in IT mode and toggled a pin

is that the 99 counter ? Tim2 ? 

is Tim 6 a 16bit Timer ?

this looks odd;

 capture_flag_function();     this seems difficult :{

try this  instead: of  capture_flag_function

static int lastCnt = 0;      // 32bit

static int compare_value  =0  // 32bit

static int16_t difference = 0;  // 16bit

compare_value = htim6.Instance->CNT;

difference = compare_value  - lastCnt ;

lastCnt = compare_value  ;

Posted on July 03, 2018 at 10:16

Yes Tim2 is set at 99 so I will be counting 100 pulses. And you are right. Tim6 is a 16 bit counter (thats why the max count of 65535).

As I was using capture_flag integer as a secondary counter, I kept that function alive. I even tried having simple difference values similar to the one you mentioned above. Yet, it misses count (even in debug mode).  I have tested a 1.68 MHz source and 450 kHz source . Irrespective of the source, the counts are always with a difference of 1 or 2. This was the case earlier.

I tried counting the pulses in a known amount of time. Still, the result is the same. The counts gets missed by a difference of 1-2. I am definitely missing something here as the error isn't accumulating over time.

Posted on July 03, 2018 at 11:26

1 or 2 counts over what period ?

is that Jitter errors, what is the full count ?

or math errors crossing the boundary... did you check it ?