Graynomad:
You will never do high speed using interrupts.
You should be able to do it with some tight polling, otherwise AFAIK you have to add a small delay when transmitting bytes.
I was curious if that was true, so I did some careful measuring.
Test 1, using interrupts:
void isr () {
PORTB = 0x20; // LED on
}
void setup() {
pinMode(13, OUTPUT);
attachInterrupt(0, isr, FALLING);
}
void loop() {
PORTB = 0; // LED off
}
To reduce overhead, I used direct port manipulation to turn the LED on pin 13. I then pumped through gradually increasing square waves into D2 to see what would happen. The bottom line was that it took around 3.5 uS for the LED to turn on after the trailing edge on D2. Also, you couldn't handle a frequency much higher than 150 kHz before some edges were missed (ie. around 6.7 uS all-round response).
This was somewhat poorer response than I expected, as the datasheet says the processor responds to interrupts "in 4 clock cycles". However a bit of research shows that the internal interrupt routine is actually this:
SIGNAL(INT0_vect) {
166: 1f 92 push r1
168: 0f 92 push r0
16a: 0f b6 in r0, 0x3f ; 63
16c: 0f 92 push r0
16e: 11 24 eor r1, r1
170: 2f 93 push r18
172: 3f 93 push r19
174: 4f 93 push r20
176: 5f 93 push r21
178: 6f 93 push r22
17a: 7f 93 push r23
17c: 8f 93 push r24
17e: 9f 93 push r25
180: af 93 push r26
182: bf 93 push r27
184: ef 93 push r30
186: ff 93 push r31
if(intFunc[EXTERNAL_INT_0])
188: 80 91 00 01 lds r24, 0x0100
18c: 90 91 01 01 lds r25, 0x0101
190: 89 2b or r24, r25
192: 29 f0 breq .+10 ; 0x19e <__vector_1+0x38>
intFunc[EXTERNAL_INT_0]();
194: e0 91 00 01 lds r30, 0x0100
198: f0 91 01 01 lds r31, 0x0101
19c: 09 95 icall
}
19e: ff 91 pop r31
1a0: ef 91 pop r30
1a2: bf 91 pop r27
1a4: af 91 pop r26
1a6: 9f 91 pop r25
1a8: 8f 91 pop r24
1aa: 7f 91 pop r23
1ac: 6f 91 pop r22
1ae: 5f 91 pop r21
1b0: 4f 91 pop r20
1b2: 3f 91 pop r19
1b4: 2f 91 pop r18
1b6: 0f 90 pop r0
1b8: 0f be out 0x3f, r0 ; 63
1ba: 0f 90 pop r0
1bc: 1f 90 pop r1
1be: 18 95 reti
Now adding up the clock cycles for those instructions (the push alone takes two cycles and there are 15 of them) it comes to 45, plus 4 to enter the interrupt, plus 3 for the JMP from the interrupt vector table. Then there is this in "my" interrupt routine:
void isr () {
PORTB = 0x20;
100: 80 e2 ldi r24, 0x20 ; 32
102: 85 b9 out 0x05, r24 ; 5
}
104: 08 95 ret
That's another couple of cycles to turn on the LED. Total being 45 + 4 + 3 + 2 = 54 cycles. That accounts for 3.38 uS response time, which is pretty close to the measured one. Then there is all the stuff to exit interrupts (ret = 4 + 35 others) and that would account for another 2.4 uS.
Test 2, using a tight loop:
void setup() {
pinMode(13, OUTPUT);
noInterrupts();
}
void loop() {
while (true)
{
if (PIND & 0x04)
PORTB = 0;
else
PORTB = 0x20;
}
}
This time I measured something like 480 nS for the LED to light, which indicates it was noticed within about 7 clock cycles. The relevant code is:
while (true)
{
if (PIND & 0x04)
102: 4a 9b sbis 0x09, 2 ; 9
104: 02 c0 rjmp .+4 ; 0x10a <loop+0xa>
PORTB = 0;
106: 15 b8 out 0x05, r1 ; 5
108: fc cf rjmp .-8 ; 0x102 <loop+0x2>
else
PORTB = 0x20;
10a: 85 b9 out 0x05, r24 ; 5
10c: fa cf rjmp .-12 ; 0x102 <loop+0x2>
Clearly there are less instructions there than in the interrupt routine, so taking something like 6 cycles to notice the pin change would be about right.
So I'll have to agree, whilst interrupts are pretty useful, they can't respond as fast as a tight loop. But on the other hand, if you are using a tight loop you aren't doing anything else useful, so it would depend on the application somewhat.