## Input Capture and Vector Priority

11 posts / 0 new
Author
Message

I am using the input capture on a ATMEGA644p to measure the pulse width of an encoder signal.

The problem occurs in this sequence of events:

1) The program disables interrupts
2) A timer overflow occurs
3) An capture event occurs

In this scenario the input capture interrupt has priority over the timer overflow interrupt and will be serviced before the timer overflow interrupt. This creates a problem for me because when I update the motor speed in the input capture interrupt I need to know how many times the counter has overflowed.

If only I could swap the vector priorities this wouldn't happen.

Has anyone else run into this issue?

While inside the capture ISR, check for a timer overflow?

Couldn't you have a look at the timer overflow flag in the input capture interrupt to determine if this happened?

JW

Yeah, I was afraid of this. I would hoping to avoid the extra clock cycles, but after searching around I think this is the only solution:

```ISR(TIMER1_CAPT_vect){  //On 8-18-10 the duration of this interrupt was measured at 13 us,  I have not measured the new duration since I changed this.
uint16_t new_stamp;
uint8_t edge;
static uint16_t old_stamp;
static uint8_t old_dir;
uint16_t local_overflow;
uint16_t local_iv;
uint8_t local_rev_f;

//The highest frequency of this interupt is approximately 6.25 kHz, which gives 160 us between interrupts

new_stamp = ICR1;
if((TIFR1 & (1<<TOV1)) && (new_stamp < 750)){//Make sure an overflow wasnt pending when the ICP triggered
new_stamp += 1500;
slow_counter.word++;
TIFR1 = 1<<TOV1;
}

//Is this a rising edge interrupt?
if(PIND & (1<<PD6)){
edge = (1<<PD5);
TCCR1B &= ~(1<<ICES1); //Switch to falling edge input capture
}
else{
edge = 0;
TCCR1B |= 1<<ICES1;//Switch to rising edge input capture
}

local_rev_f = encoder.reverse_f;
//Check the second quadrature channel and increment or decrement without overflowing the variable
if((PIND & (1<<PD5)) ^ (edge)){
encoder.pos++;
local_rev_f = 0;
}
else{
encoder.pos--;
local_rev_f = 1;
}

local_overflow = inverse_velocity_overflow;

//calculate the period of the signal
if(old_dir == local_rev_f)
encoder.inverse_vel = ((new_stamp + local_overflow) - old_stamp);
else{
local_iv = encoder.inverse_vel;
local_iv += ((new_stamp + local_overflow) - old_stamp);
if(local_iv > 15000)
local_iv = 15000;
encoder.inverse_vel = local_iv;
}

//save the time stamp for the next calculation
old_stamp = new_stamp;
old_dir = local_rev_f;
encoder.reverse_f = local_rev_f;

//clear the overflow variable
inverse_velocity_overflow = 0;

}

ISR(TIMER1_OVF_vect){
uint16_t local_overflow = inverse_velocity_overflow;
//This is used for long delays in main
slow_counter.word++;

if(local_overflow < 13500){		//13500 is the max, this is essentially stopped
local_overflow += 1500;		//if its less than max increment
if((local_overflow >= 3000) && ((local_overflow - 1500) > encoder.inverse_vel))
encoder.inverse_vel = local_overflow - 1500;
}
else encoder.inverse_vel = local_overflow;

inverse_velocity_overflow =  local_overflow;
}```

If you want hear some tricks, then run Timer2 synchronously with Timer1 and use T2 overflow instead of T1 overflow. But I am afraid you did not want to hear that.

JW

Using another timer does not really help. The problem is that the may be two interrupts pending, and there is no simple priority that can decide which ISR has to be serviced first. If the timer overflow has the higher priority one may get the problem the other way around, overfow ISR serviced to early instead of to late.

dpaulsen allready found the correct way to handle the problem of nearly simutaneous interrupts. The main part is there, but i Don't think all the details are correct: if the wrong sequence is detected, the overflow ISR code should be run first. Here the code inside the IF and the the one in the normal ISR looks quite different.

Kleinstein wrote:
Using another timer does not really help.
Indeed. This is just another incarnation of the dam'd timer extension problem (in fact a special atomicity problem).
Kleinstein wrote:
Here the code inside the IF and the the one in the normal ISR looks quite different.
Calling a common function from both ISRs can be disastrous time-wise. In C, that is; so it's maybe time to fall back to asm.

JW

Quote:

Calling a common function from both ISRs can be disastrous time-wise. In C, that is; so it's maybe time to fall back to asm.

Unless the "call'd" function is static inline.

wek wrote:
Calling a common function from both ISRs can be disastrous time-wise. In C, that is; so it's maybe time to fall back to asm.

`asm (" CALL vect_common_vect");`

Make the common function an "ISR".
Also, if I just want to know what time it is,
I don't bother with interrupts.

```new_tcnt1=TCNT1;
if(new_tcnt1< old_tcnt1) {
++overflows1;
}
old_tcnt1=new_tcnt1;
time=overflows1*0x10000+new_tcnt1;```

IIRC there have been threads on how to make sure that the
overflow flag value one uses goes with the count value one uses.
My code sidesteps that problem by not using the overflow flag.

Also, both ISRs could be the same function.

Iluvatar is the better part of Valar.

Kleinstein wrote:
Using another timer does not really help. The problem is that the may be two interrupts pending, and there is no simple priority that can decide which ISR has to be serviced first. If the timer overflow has the higher priority one may get the problem the other way around, overfow ISR serviced to early instead of to late.

dpaulsen allready found the correct way to handle the problem of nearly simutaneous interrupts. The main part is there, but i Don't think all the details are correct: if the wrong sequence is detected, the overflow ISR code should be run first. Here the code inside the IF and the the one in the normal ISR looks quite different.

Now that I think about it you are quite correct, the problem is that you do not know the order that the ISRs triggered. To be automatic the AVR would have to prioritize according to which triggered first, which currently isnt an option.

Yes I know the code is different in the if statement. I left out the parts that aren't needed.

I guess I was getting spoiled with all the automatic modules like the ADC and the UART. To bad Atmel doesn't make a encoder module.

dpaulsen wrote:
Now that I think about it you are quite correct, the problem is that you do not know the order that the ISRs triggered. To be automatic the AVR would have to prioritize according to which triggered first, which currently isnt an option.
Actually, you test for it correctly.
dpaulsen wrote:

```ISR(TIMER1_CAPT_vect){
...
if((TIFR1 & (1<<TOV1)) && (new_stamp < 750)){//Make sure an overflow wasnt pending when the ICP triggered
new_stamp += 1500;
slow_counter.word++;
TIFR1 = 1<<TOV1;
}
...```

The small stamp implies that the overflow occurred first.
I'll admit I don't understand the significance of the 1500.
Does your device overflow at 1500?
Quote:
Yes I know the code is different in the if statement. I left out the parts that aren't needed.
'Tain't at all obvious why those parts would
be needed in the ISR, but not in the if.
I'd have been inclined to use something like

```ISR(TIMER1_CAPT_vect){
...
if((TIFR1 & (1<<TOV1)) && (new_stamp < 750)){//Make sure an overflow wasnt pending when the ICP triggered
asm (" CALL TIMER1_OVF_vect");
TIFR1 = 1<<TOV1;
}
...```

Iluvatar is the better part of Valar.