cancel
Showing results for 
Search instead for 
Did you mean: 

Timer Input randomly skips interrupt.

viteo
Associate II

I'm sertanly missing something.

I have a board with STM32F103 that control fan via PWM and have to check it's speed with Fan's TACH out. TACH out is consistent PWM signal with duty cicle 50% and frequency 80 Hz.

Chip is running with 64MHz clock, APB1 is 32 MHz. TIM 2 prescaler is 32, clock division 4, input prescaler and filter I've checked with a lot different combinations.

	TIM_TimeBaseInitStruct.TIM_Prescaler = 32;
	TIM_TimeBaseInitStruct.TIM_Period = 200;
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV4;
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);
 
	TIM_ICInitStruct.TIM_Channel = TIM_Channel_2;
	TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
	TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
	TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
	TIM_ICInitStruct.TIM_ICFilter = 0b1011;
 
	TIM_ICInit(TIM2, &TIM_ICInitStruct);
	TIM_ITConfig(TIM2, TIM_IT_CC2, ENABLE);
	NVIC_EnableIRQ(TIM2_IRQn);
 
	TIM_Cmd(TIM2, ENABLE);

Interrupt is simple as possible: im not counting value for now (math from SPL examle I used to were giving me inconsistent results), now I'm just reading it. When I measure the difference between two interrupts I'm getting consecutive results like this: 47 21 95 90 156 65405 95 65363 159 144 22 48 etc.

So, I fire output PIN on every interrupt for debug purpose with oscilloscope.

void TIM2_IRQHandler()
{
	GPIO_SetBits(GPIOB, PIN_LED_OUT);
	if(TIM_GetITStatus(TIM2, TIM_FLAG_CC2) != RESET)
	{
		TIM_ClearFlag(TIM2, TIM_FLAG_CC2);
		value = TIM_GetCapture2(TIM2);
	}
	GPIO_ResetBits(GPIOB, PIN_LED_OUT);
}

But the picture I get looks like this:0693W000008yz12QAA.pngLooks like at random interruptions are not fired.

I've tried different channels, different filter settings, different timer scale settings, even different voltage levels. I can't change MCU clock speed.

I suspect that explanation to that is simple, i'm just missing or don't understand something.

Hope for your help stm gurus.

7 REPLIES 7

Prescale and Period values are N-1, with such a small range I'd use a prescale of zero. Currently count is modulo 201

F​or time measurement, consider another TIM in maximal mode, ie full count, and either use input capture or pwm input there.

C​lock divider is for input filtering.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..

Are you sure it's not your oscilloscope which simply misses some of the pulses, as it's running at 250ksamples/s?

JW

viteo
Associate II

Thank you. You are both right.

First of all, looks like modulo 200 really is one of the problem.

So, I moved Timer Input to TIM1 with Period 0xFFFF and Prescale 1000. Capture results in ticks is pretty consistent.

Also, now I see, that interrupts are not skipped, that was oscilloscope skipping impulse XD.

TIM_TimeBaseInitStruct.TIM_Prescaler = 1000;
	TIM_TimeBaseInitStruct.TIM_Period = 0xFFFF;
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
 
	TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;
	TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
	TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
	TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
	TIM_ICInitStruct.TIM_ICFilter = 0b1011;
 
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStruct);
	TIM_ICInit(TIM1, &TIM_ICInitStruct);
	TIM_Cmd(TIM1, ENABLE);
	TIM_ITConfig(TIM1, TIM_IT_CC1, ENABLE);
	NVIC_EnableIRQ(TIM1_CC_IRQn);
void TIM1_CC_IRQHandler()
{
	if (TIM_GetITStatus(TIM1, TIM_FLAG_CC1) != RESET)
	{
		GPIO_SetBits(GPIOB, PIN_LED_OUT);
		TIM_ClearITPendingBit(TIM1, TIM_IT_CC1);
		if (CaptureNumber == 0)
		{
 
			/* Get the Input Capture value */
			IC1ReadValue1 = TIM_GetCapture1(TIM1);
			CaptureNumber = 1;
		}
		else if (CaptureNumber == 1)
		{
			/* Get the Input Capture value */
			IC1ReadValue2 = TIM_GetCapture1(TIM1);
 
			/* Capture computation */
			if (IC1ReadValue2 > IC1ReadValue1)
			{
				Capture = (IC1ReadValue2 - IC1ReadValue1);
			}
			else
			{
				Capture = ((0xFFFF - IC1ReadValue1) + IC1ReadValue2);
			}
			/* Frequency computation */
 
			TIM1Freq = (uint32_t) 64000 / Capture;
 
			CaptureNumber = 0;
		}
		delay_us(100); //debug only for oscilloscope capture
		GPIO_ResetBits(GPIOB, PIN_LED_OUT);
	}
}

And TIM1Freq gives me the exact value same as on oscilloscope. Nice.

Sad that i cant use original timer, because board is already manufactured. So I think I will measure frequency with EXTI interrupt. Not so elegant as this solution.

> Sad that i cant use original timer, because board is already manufactured. So I think I will measure frequency with EXTI interrupt. Not so elegant as this solution.

Tell us more details: which pin?

JW

viteo
Associate II

I have PWM output on PA3 (TIM2_CH4) and TACH input on PA1 (TIM2_CH2). Because of PWM timer settings its counter resets at TIM_Period, as @Community member  rightly pointed out. Well, im still might use Timer edge detector, but i should find another way to measure time intervals inside interrupt.

viteo
Associate II

So, I ended up with reading TIM1 value at the beginning of interrupt with TIM_GetCounter(TIM1);

volatile uint32_t FAN1Freq;
void TIM2_IRQHandler()
{
	static uint16_t capture1 = 0, capture2 = 0;
	static uint16_t isCaptured = 0;
	static uint16_t delta = 0;
	uint16_t ticks = TIM_GetCounter(TIM1);
 
	if (TIM_GetITStatus(TIM2, TIM_FLAG_CC2) != RESET)
	{
		TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);
		if (isCaptured == 0)
		{
			/* Get the Input Capture value */
			capture1 = ticks;
			isCaptured = 1;
		}
		else if (isCaptured == 1)
		{
			/* Get the Input Capture value */
			capture2 = ticks;
 
			/* Delta computation */
			delta = capture2 - capture1;
 
			/* Frequency computation */
			FAN1Freq = (uint32_t) 64000 / (delta);
			isCaptured = 0;
		}
	}
}

Thank you very much guys.:raised_hand:

Some information regarding a "Capture computation"...

First, the else part is off by one and should be:

Capture = (uint16_t)((0x10000 - IC1ReadValue1) + IC1ReadValue2);

Second, all of that if-else overflow processing is unnecessary and should be replaced with a simple:

Capture = (uint16_t)(IC1ReadValue2 - IC1ReadValue1);

An explanation:

https://stackoverflow.com/questions/7221409/is-unsigned-integer-subtraction-defined-behavior