Timer Input randomly skips interrupt.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-04-08 4:10 AM
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:Looks 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.
- Labels:
-
Interrupt
-
STM32F1 Series
-
TIM
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-04-08 4:33 AM
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.
Up vote any posts that you find helpful, it shows what's working..
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-04-08 6:33 AM
Are you sure it's not your oscilloscope which simply misses some of the pulses, as it's running at 250ksamples/s?
JW
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-04-08 7:10 AM
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-04-08 8:48 AM
> 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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-04-09 12:47 AM
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-04-09 6:05 AM
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:
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2021-04-09 7:46 AM
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
