2016-10-12 08:28 AM
Hello all,
I'm trying to measure the frequency of a signal using the input capture. I'm using the STM32F030's timer 3, which is a 16 bit timer. I want to extend the resolution to 32 bits, using the Capture interrupt and the Overflow interrupt. The frequency measurement works OK, but I'm having a problem with spurious interrupts. Please look at the code below. The only interrupts I have activated are UIE and CC1IE. In the ISR, I check both bits, and take appropriate actions. However, sometimes,the code marked ''THIS PATH IS EXECUTED'' is run, which means that the relevant status flags (UIF and CC1IF) are 0. I know that there can be a race condition involving the pipeline and/or the APB->AHB bridge/write buffer which may be causing the processor to tail-chain the interrupts. But I'd like to avoid these spurious interrupts, because they unnecessarily overload the processor. Any ideas or explanations? Note: I can't use linked timers to extend the resolution, because I'm already using other resources. Thank you very much, Diego. Here is the code for the initialization (TOP is defined as 65536):TIM3->SR = 0; TIM3->DIER = 0; TIM3->PSC = 47; TIM3->ARR = TOP - 1; TIM3->CCMR1 = STM32_TIM_CCMR1_CC1S(1) | STM32_TIM_CCMR1_IC1F(3); TIM3->CCER = TIM_CCER_CC1E; TIM3->DIER = TIM_DIER_CC1IE | TIM_DIER_UIE; TIM3->CR1 = TIM_CR1_CEN; Here is the ISR code:
#define TOP 65536 void isr(TIM_TypeDef *TIMx) { static bool enabled; static uint16_t last_capture; static uint32_t overflow; int current_capture; uint16_t sr = TIMx->SR; if (sr & TIM_SR_CC1IF) { current_capture = TIMx->CCR1; if (sr & TIM_SR_CC1OF) { TIMx->SR = ~TIM_SR_CC1OF; enabled = FALSE; return ; } if (sr & TIM_SR_UIF) { TIMx->SR = ~TIM_SR_UIF; if (current_capture < TOP / 2) { overflow += TOP; } } if (enabled) { period = current_capture - last_capture + overflow; } enabled = TRUE; overflow = 0; last_capture = current_capture; if (sr & TIM_SR_UIF) { if (current_capture >= TOP / 2) { overflow += TOP; } } } else if (sr & TIM_SR_UIF) { TIMx->SR = ~TIM_SR_UIF; overflow += TOP; } else {
//THIS PATH IS EXECUTED!
GPIOB->BSRR.H.clear = (1 << 13); GPIOB->BSRR.H. set = (1 << 13); } }2016-10-12 08:50 AM
> I know that there can be a race condition involving the pipeline and/or the APB->AHB bridge/write buffer which may be causing the processor to tail-chain the interrupts.
> But I'd like to avoid these spurious interrupts, because they unnecessarily overload the processor. Clear the status register as early as possible:uint16_t sr = TIMx->SR;
TIMx->SR = 0;
and there's no need to clear individual SR bits later. This should help, but if it won't, read back the status register into a dummy and/or add NOPs (__asm(''nop'');).
Btw. where do you clear TIMx_SR.CCIF1? Did I miss something?
JW2016-10-12 09:37 AM
Hi JW,
Thanks for your quick answer! If I do:uint16_t sr = TIMx->SR;
TIMx->SR = 0;
couldn't I miss an event which could fire between the read of the SR and the write to 0? As per your suggestion, I removed the individual bit clears, and modified the code:
uint16_t sr = TIMx->SR;
TIMx->SR = ~sr;
It still works, and it's more compact, but it doesn't prevent the spurious interrupts.
> This should help, but if it won't, read back the status register into a dummy and/or add NOPs (__asm(''nop'');).
I already tried these suggestions; they don't do anything!
>Btw. where do you clear TIMx_SR.CCIF1? Did I miss something?
According to the reference manual, reading CCR1 automatically clears the CC1IF:current_capture = TIMx->CCR1;
Thanks for your help.
Regards,
Diego.
2016-10-12 09:55 AM
> couldn't I miss an event which could fire between the read of the SR and the write to 0?
True.uint16_t sr = TIMx->SR;
TIMx->SR = ~sr;
This is the real stuff.
> > This should help, but if it won't, read back the status register into a dummy and/or add NOPs (__asm(''nop'');).
>
> I already tried these suggestions; they don't do anything!
Hummm... That would be time to look at the disassembly, or to go straight for assembler.
>> Btw. where do you clear TIMx_SR.CCIF1? Did I miss something?
>
> According to the reference manual, reading CCR1 automatically clears the CC1IF: Uh. I never noticed before... One learns something new every day. Thanks. Jan2016-10-12 09:56 AM
Hi JW,
I'm sorry I made a big mistake!! I'm using the same code to measure two signals (TIM1_CH1 and TIM3_CH1). I wrote generic code for the two timers, and I thought I was using TIM3 to test, but I'm actually using TIM1. TIM1 has *two* interrupt vectors, one for CC and one for Update, so I redirected the two vectors to the same isr() function I copied earlier. In effect, using TIM3, there are no more spurious interrupts... I think I can't avoid the problem with TIM1, because it has two different vectors. Of course, any ideas for improvement will be appreciated, but at least now I understand the problem... I'm sorry for wasting your time. For reference, the complete (working) code:#define TOP 65536
void
isr(TIM_TypeDef *TIMx) {
static
bool
enabled;
static
uint16_t last_capture;
static
uint32_t overflow;
int
current_capture;
uint16_t sr = TIMx->SR;
TIMx->SR = ~sr;
if
(sr & TIM_SR_CC1IF) {
current_capture = TIMx->CCR1;
if
(sr & TIM_SR_CC1OF) {
enabled = FALSE;
return
;
}
if
(sr & TIM_SR_UIF) {
if
(current_capture < TOP / 2) {
overflow += TOP;
}
}
if
(enabled) {
period = current_capture - last_capture + overflow;
}
enabled = TRUE;
overflow = 0;
last_capture = current_capture;
if
(sr & TIM_SR_UIF) {
if
(current_capture >= TOP / 2) {
overflow += TOP;
}
}
}
else
if
(sr & TIM_SR_UIF) {
overflow += TOP;
}
else
{
//This path is executed only with TIM1 (redirecting both CC and
// UP interrupts to this handler).
GPIOB->BSRR.H.clear = (1 << 13);
GPIOB->BSRR.H.
set
= (1 << 13);
}
}
Sorry again and thanks for your help.
Diego.2016-10-12 11:19 AM
Jan,
> This is the real stuff.Ok, I agree.>Uh. I never noticed before... One learns something new every day. Thanks.Yes indeed. Thank you.Diego.