How reliable is pin change interrupt? Is it able to raise interrupt for frequency as high as 100Khz?
From the logic diagram in the datasheet, it looks pretty certain that the PCI hardware can detect changes that are about 4 or 5 clocks long (call it 3 MHz? I couldn't find a detailed specification.)
Note that this is a lot different than any given interrupt service routine being able to EXECUTE 3MHz, or even 100kHz. That depends on how short the ISR is. 100kHz interrupts while using typical Arduino code would be very unlikely, IMO.
If you have a signal coming in at 1MHz, you'd probably see continuous interrupts limited only by the speed of the ISR (with one non-ISR instruction executed between each interrupt.)
I saw that in datasheet that it takes 3 clock cycles to move to the ISR. So, I should be able to count frequency(100Khz) with accuracy but it is not the case. I get errors of 100Hz here.
So, I should be able to count frequency(100Khz) with accuracy but it is not the case.
If only we could see the code you're using.
sodhani:
I saw that in datasheet that it takes 3 clock cycles to move to the ISR. So, I should be able to count frequency(100Khz) with accuracy but it is not the case. I get errors of 100Hz here.
3 clock cycles to initiate the ISR for every pulse, but then how many cycles does it take to execute your isr and then move back to the regular code? And how are you calculating the time, what processor, what clock speed, and oh yeah, what's your code?
My datasheet said 4 cycles to preserve the PC and status registers to the stack (e.g. automatic, no push or pop needed). It is an old datasheet so maybe it has an error.
I think the compiler adds instructions to preserve some of the other registers that may be used. I am not sure how smart the compiler is when doing this.
avr-objdump with options -h and -S can show a listing of what the compiler did. So looking at one of the interrupt vectors this is what I see.
00001a18 <__vector_16>:
#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
1a18: 1f 92 push r1
1a1a: 0f 92 push r0
1a1c: 0f b6 in r0, 0x3f ; 63
1a1e: 0f 92 push r0
1a20: 11 24 eor r1, r1
1a22: 2f 93 push r18
1a24: 3f 93 push r19
1a26: 8f 93 push r24
1a28: 9f 93 push r25
1a2a: af 93 push r26
1a2c: bf 93 push r27
// copy these to local variables so they can be stored in registers
// (volatile variables must be read from memory on every access)
unsigned long m = timer0_millis;
1a2e: 80 91 31 01 lds r24, 0x0131 ; 0x800131 <timer0_millis>
1a32: 90 91 32 01 lds r25, 0x0132 ; 0x800132 <timer0_millis+0x1>
1a36: a0 91 33 01 lds r26, 0x0133 ; 0x800133 <timer0_millis+0x2>
1a3a: b0 91 34 01 lds r27, 0x0134 ; 0x800134 <timer0_millis+0x3>
unsigned char f = timer0_fract;
1a3e: 30 91 30 01 lds r19, 0x0130 ; 0x800130 <timer0_fract>
m += MILLIS_INC;
f += FRACT_INC;
1a42: 23 e0 ldi r18, 0x03 ; 3
1a44: 23 0f add r18, r19
if (f >= FRACT_MAX) {
1a46: 2d 37 cpi r18, 0x7D ; 125
1a48: 20 f4 brcc .+8 ; 0x1a52 <__vector_16+0x3a>
// copy these to local variables so they can be stored in registers
// (volatile variables must be read from memory on every access)
unsigned long m = timer0_millis;
unsigned char f = timer0_fract;
m += MILLIS_INC;
1a4a: 01 96 adiw r24, 0x01 ; 1
1a4c: a1 1d adc r26, r1
1a4e: b1 1d adc r27, r1
1a50: 05 c0 rjmp .+10 ; 0x1a5c <__vector_16+0x44>
f += FRACT_INC;
if (f >= FRACT_MAX) {
f -= FRACT_MAX;
1a52: 26 e8 ldi r18, 0x86 ; 134
1a54: 23 0f add r18, r19
m += 1;
1a56: 02 96 adiw r24, 0x02 ; 2
1a58: a1 1d adc r26, r1
1a5a: b1 1d adc r27, r1
}
timer0_fract = f;
1a5c: 20 93 30 01 sts 0x0130, r18 ; 0x800130 <timer0_fract>
timer0_millis = m;
1a60: 80 93 31 01 sts 0x0131, r24 ; 0x800131 <timer0_millis>
1a64: 90 93 32 01 sts 0x0132, r25 ; 0x800132 <timer0_millis+0x1>
1a68: a0 93 33 01 sts 0x0133, r26 ; 0x800133 <timer0_millis+0x2>
1a6c: b0 93 34 01 sts 0x0134, r27 ; 0x800134 <timer0_millis+0x3>
timer0_overflow_count++;
1a70: 80 91 35 01 lds r24, 0x0135 ; 0x800135 <timer0_overflow_count>
1a74: 90 91 36 01 lds r25, 0x0136 ; 0x800136 <timer0_overflow_count+0x1>
1a78: a0 91 37 01 lds r26, 0x0137 ; 0x800137 <timer0_overflow_count+0x2>
1a7c: b0 91 38 01 lds r27, 0x0138 ; 0x800138 <timer0_overflow_count+0x3>
1a80: 01 96 adiw r24, 0x01 ; 1
1a82: a1 1d adc r26, r1
1a84: b1 1d adc r27, r1
1a86: 80 93 35 01 sts 0x0135, r24 ; 0x800135 <timer0_overflow_count>
1a8a: 90 93 36 01 sts 0x0136, r25 ; 0x800136 <timer0_overflow_count+0x1>
1a8e: a0 93 37 01 sts 0x0137, r26 ; 0x800137 <timer0_overflow_count+0x2>
1a92: b0 93 38 01 sts 0x0138, r27 ; 0x800138 <timer0_overflow_count+0x3>
}
1a96: bf 91 pop r27
1a98: af 91 pop r26
1a9a: 9f 91 pop r25
1a9c: 8f 91 pop r24
1a9e: 3f 91 pop r19
1aa0: 2f 91 pop r18
1aa2: 0f 90 pop r0
1aa4: 0f be out 0x3f, r0 ; 63
1aa6: 0f 90 pop r0
1aa8: 1f 90 pop r1
1aaa: 18 95 reti
00001aac <millis>:
It added twelve instructions to preserve the state of the machine before any of the ISR function code runs. Then before the reti instruction a lot of pop instructions that recover the machine state.
Anyway, I would not expect much more than about 10k events per second from these 16MHz AVRs while still having some time for other tasks.
Thinking about this, if the speed is an issue (and it sounds like we're on the edge of this) - I would probably configure a timer that supported external clock to be clocked off the input signal; that way you you could set it to a target number of counts, and use the timer overflow interrupt to grab a timestamp.... That way during the counting, the arduino isn't doing anything, the timer is just ticking away...