// in vim, :set ts=2 sts=2 sw=2 et // Enable Interrupts ===================================================================================================== ===================================================================================================== ATmega328 Support ===================================================================================================== ===================================================================================================== Interrupt pins: Pin External Pin Pin Change Pin Pin Change Interrupt Interrupt Interrupt 3 INT1 PD3 0 PCINT16 PD0 A0 PCINT8 PC0 2 INT0 PD2 1 PCINT17 PD1 A1 PCINT9 PC1 2 PCINT18 PD2 A2 PCINT10 PC2 3 PCINT19 PD3 A3 PCINT11 PC3 4 PCINT20 PD4 A4 PCINT12 PC4 5 PCINT21 PD5 A5 PCINT13 PC5 6 PCINT22 PD6 7 PCINT23 PD7 8 PCINT0 PB0 9 PCINT1 PB1 10 PCINT2 PB2 11 PCINT3 PB3 12 PCINT4 PB4 13 PCINT5 PB5 // The External Interrupts are triggered by the INT0 and INT1 pins or any of the PCINT23...0 pins. // PIN CHANGE INTERRUPT REGISTER BESTIARY // ATmega328p and similar (328, 168) The pin change interrupt PCI2 will trigger if any enabled PCINT[23:16] pin toggles. The pin change interrupt PCI1 will trigger if any enabled PCINT[14:8] pin toggles. The pin change interrupt PCI0 will trigger if any enabled PCINT[7:0] pin toggles. The PCMSK2, PCMSK1 and PCMSK0 Registers control which pins contribute to the pin change interrupts. Pin change interrupts on PCINT23...0 are detected asynchronously. This implies that these interrupts can be used for waking the part also from sleep modes other than Idle mode. // GLOBALLY SREG: 7 6 5 4 3 2 1 0 (AVR Status Register) I -Global Interrupt Enable bit Set to enable interrupts. rw // PIN CHANGE INTERRUPTS REGISTER BESTIARY PCICR: 7 6 5 4 3 2 1 0 (When PCIE2 is set and GIE is set, PCI2 is enabled) PCIE2 1 0 Likewise for 1 and 0. - - - - - rw rw rw PCMSK2:7 6 5 4 3 2 1 0 (Selects whether pin change interrupt is enabled on PCINT23:16 PCINT23 ... PCINT16 If PCIE2 in PCICR and this bit is set, it is enabled on that rw rw rw rw rw rw rw rw pin.) PD7 PD6 PD5 PD4 PD3 PD2 PD1 PD0 =PORTD PCMSK1:7 6 5 4 3 2 1 0 (Selects whether pin change interrupt is enabled on PCINT14:8 - PCINT14 ... PCINT8 If PCIE1 in PCICR and this bit is set, it is enabled on that r rw rw rw rw rw rw rw pin.) PC6 PC5 PC4 PC3 PC2 PC1 PC0 =PORTC PCMSK0:7 6 5 4 3 2 1 0 (Selects whether pin change interrupt is enabled on PCINT7:0 PCINT7 ... PCINT0 If PCIE0 in PCICR and this bit is set, it is enabled on that rw rw rw rw rw rw rw rw pin.) PB7 PB6 PB5 PB4 PB3 PB2 PB1 PB0 =PORTB // set... PCIFR: 7 6 5 4 3 2 1 0 (When logic change triggers IRQ, bececomes set. Cleared when PCIF2 1 0 IRQ is executed. PCIF2 == PCIE2, etc.) - - - - - rw rw rw // EXTERNAL INTERRUPTS REGISTER BESTIARY For ATmega328p and the like. INT0=pin2, INT1=pin3 EICRA: 7 6 5 4 3 2 1 0 (External Interrupt Control Register A) ISC 11 10 01 00 Interrupt Sense Control Bits - - - - rw rw rw rw ISC11 / ISC10 : INT1 ISC01 / ISC00 0 0 Low (similar to ISC11/10) 0 1 Change 1 0 Falling 1 1 Rising EIMSK: 7 6 5 4 3 2 1 0 (External Interrupt Mask Register) INT1 INT0 - - - - - - rw rw set to 1 to enable this interrupt // set... EIFR: INTF1: Bit 1: set when edge/logic chg on INT1 triggers IRQ. Cleared when IRQ executed. INTF0: Bit 0: set when edge/logic chg on INT0 triggers IRQ. Cleared when IRQ executed. The INT0 and INT1 interrupts can be triggered by a falling or rising edge or a low level. This is set up as indicated in the specification for the External Interrupt Control Register A – EICRA. When the INT0 or INT1 interrupts are enabled and are configured as level triggered, the inter- rupts will trigger as long as the pin is held low. Low level interrupt on INT0 and INT1 is detected asynchronously. This implies that this interrupt can be used for waking the part also from sleep modes other than Idle mode. The I/O clock is halted in all sleep modes except Idle mode. ============================================================================================= Fri Jan 23 21:51:01 CST 2015 PROBLEM: I have a "dirty" switch: it bounces a lot when pressed. The bounces can occur on the order of microseconds apart. So pretend I have turned on a port as an input port, and the pullup resistor is on. I have enabled a Pin Change Interrupt on the pin. It will trigger on any level change. So when I press the switch, the signal goes from high to low and the interrupt is triggered. Notice that for Pin Change Interrupts, the interrupt can take place when any pin on the port is interrupted. If you have the luxury of knowing ahead of time which pin(s) are interrupting, you can design fast, custom code that will react to your simple situation. But remember that I am writing a library: I don't know which pin may be doing the interrupting. So I have to survey the pins to figure out which one(s) changed, and had triggered the interrupt. Furthermore, there is an appreciable amount of time that it takes from the moment the triggering event happened to when I enter the interrupt subroutine (ISR) and have gone through the logic to figure out which pins did the triggering. ...How much time? That I aim to find out. Why is this a big deal? Remember my bouncy switch? ...The interrupt triggers, the ISR starts up, and the first thing I need to do is query the port to see the state of its pins. Well, some time has elapsed since the triggering event and this query. In the course of that time, it's entirely possible- and I'm writing this because it's not only possible, but it can happen quite readily- that the state of the pin changes before I get a chance to sample it. So I get an interrupt but it looks like a false alarm! The ISR never calls the user's function because none of the user's interrupt pins appear to have changed. There is no complete solution to this problem, because of the nature of Pin Change Interrupts. All you can do is mitigate the situation. I will attempt to do so by capturing the state of the port as early as possible in the ISR. The question is, how early is that? I attempt a test: my ISR looks like this; this will turn on and off the Arduino Uno's pin 13 LED: ISR(PORTC_VECT, ISR_NAKED) { uint8_t interruptMask; uint8_t ledon, ledoff; ledon=0b00100000; ledoff=0b0; PORTB=ledoff; // LOW PORTB=ledon; // HIGH PORTB=ledoff; // LOW PORTB=ledon; // HIGH PORTB=ledoff; // LOW (...) } The generated assembly code looks like this: 00000292 <__vector_4>: ledon=0b00100000; ledoff=0b0; PORTB=ledoff; // LOW 292: 15 b8 out 0x05, r1 ; 5 PORTB=ledon; // HIGH 294: 80 e2 ldi r24, 0x20 ; 32 296: 85 b9 out 0x05, r24 ; 5 PORTB=ledoff; // LOW 298: 15 b8 out 0x05, r1 ; 5 PORTB=ledon; // HIGH 29a: 85 b9 out 0x05, r24 ; 5 PORTB=ledoff; // LOW 29c: 15 b8 out 0x05, r1 ; 5 Notice a little optimization here: r1 is defined to always contain 0, so we don't even have to load a value from memory. 0 is an important number! This makes the initial command very quick, and by using an oscilloscope we can see just how quickly the chip reacts after receiving the signal. see just how fast ***** MACRO vs. INLINE the ISR frontend ************************** The code compiles to very similar assembler. However, the inline is better looking C code. Refer to the "testing0" branch for comparison. I will use the INLINE method in the production code. ***** MACRO vs. INLINE the ISR frontend ************************** A lot of the basic Pin and Port definitions are in /usr/avr/include/avr/iom328p.h ================== ================== ================== ================== ================== ===================================================================================================== ===================================================================================================== Leonardo Support LEONARDO ===================================================================================================== ===================================================================================================== // Map SPI port to 'new' pins D14..D17 static const uint8_t SS = 17; static const uint8_t MOSI = 16; static const uint8_t MISO = 14; static const uint8_t SCK = 15; // A0 starts at 18 Interrupt pins: Pin External Pin Pin Change Interrupt Interrupt 3 INT0 PD0 8 PCINT4 PB4 2 INT1 PD1 9 PCINT5 PB5 0 INT2 PD2 10 PCINT6 PB6 1 INT3 PD3 11 PCINT7 PB7 7 INT6 PE6 SCK/15 PCINT1 PB1 MOSI/16 PCINT2 PB2 MISO/14 PCINT3 PB3 SS/17 PCINT0 PB0 (on 3rd party boards) on ICSP: SCK/15: PCINT1 (PB1) MOSI/16: PCINT2 (PB2) MISO/14: PCINT3 (PB3) PCINT0 (PB0) is RXLED and is not exposed as a pin on the Leonardo board. External Interrupts ------------------------------------------------------------------------------ ...it is recommended to first disable INTn by clearing its Interrupt Enable bit in the EIMSK Register. Then, the ISCn bit can be changed. Finally, the INTn interrupt flag should be cleared by writing a logical one to its Interrupt Flag bit (INTFn) in the EIFR Register before the interrupt is re-enabled. EICRA: 7 6 5 4 3 2 1 0 (External Interrupt Control Register A) ISC: 31 30 21 20 11 10 01 00 Interrupt Sense Control Bits rw rw rw rw rw rw rw rw EICRB: 7 6 5 4 3 2 1 0 (External Interrupt Control Register A) ISC: - - 61 60 - - - - Interrupt Sense Control Bits rw rw rw rw rw rw rw rw ISCx1 / ISCx0 : INTx 0 0 Low 0 1 Change 1 0 Falling 1 1 Rising EIMSK: 7 6 5 4 3 2 1 0 (External Interrupt Mask Register) n n n n n - rw - - rw rw rw rw set to 1 to enable this interrupt n= INTn number External Interrupt Flag Register is set to 1 when a signal generates an IRQ. Cleared upon entering the ISR. Can be cleared by writing a 1 to it. EIFR: 7 6 5 4 3 2 1 0 (External Interrupt Flag Register) n n n n n - rw - - rw rw rw rw n= INTn number Pin Change Interrupts --------------------------------------------------------------------------- // PIN CHANGE INTERRUPTS REGISTER BESTIARY PCICR: 7 6 5 4 3 2 1 0 (When PCIE0 is set and GIE is set, PCI0 is enabled) PCIE0 - - - - - - - rw // set... PCIFR: 7 6 5 4 3 2 1 0 (When logic change triggers IRQ, bececomes set. Cleared when PCIF0 IRQ is executed. PCIF0 == PCIE0.) - - - - - - - rw Can be cleared by writing 1 to it. PCMSK0:7 6 5 4 3 2 1 0 (Selects whether pin change interrupt is enabled on PCINT7:0 PCINT7 ... PCINT0 If PCIE0 in PCICR and this bit is set, it is enabled on that rw rw rw rw rw rw rw rw pin.) PB7 PB6 PB5 PB4 PB3 PB2 PB1 PB0 =PORTB ===================================================================================================== ===================================================================================================== ATmega2560 Support ===================================================================================================== ===================================================================================================== External Interrupts ------------------------------------------------------------------------------ The following External Interrupts are available on the Arduino: Arduino Pin* PORT INT ATmega2560 pin 21 PD0 0 43 20 PD1 1 44 19 PD2 2 45 18 PD3 3 46 2 PE4 4 6 3 PE5 5 7 n/c PE6 6 8 (fake pin 75) n/c PE7 7 9 (fake pin 76) ...it is recommended to first disable INTn by clearing its Interrupt Enable bit in the EIMSK Register. Then, the ISCn bit can be changed. Finally, the INTn interrupt flag should be cleared by writing a logical one to its Interrupt Flag bit (INTFn) in the EIFR Register before the interrupt is re-enabled. EICRA: 7 6 5 4 3 2 1 0 (External Interrupt Control Register A) ISC: 31 30 21 20 11 10 01 00 Interrupt Sense Control Bits rw rw rw rw rw rw rw rw EICRB: 7 6 5 4 3 2 1 0 (External Interrupt Control Register B) ISC: 71 70 61 60 51 50 41 40 Interrupt Sense Control Bits rw rw rw rw rw rw rw rw NOTE: NO CONNECTION for INT6 and INT7 on ATmega2560** ISCx1 / ISCx0 : INTx 0 0 Low 0 1 Change 1 0 Falling 1 1 Rising EIMSK: 7 6 5 4 3 2 1 0 (External Interrupt Mask Register) n n n n n n n n NOTE: NO CONNECTION FOR INT6 and INT7 on ATmega2560** rw rw rw rw rw rw rw rw n= INTn number External Interrupt Flag Register is set to 1 when a signal generates an IRQ. Cleared upon entering the ISR. Can be cleared by writing a 1 to it. EIFR: 7 6 5 4 3 2 1 0 (External Interrupt Flag Register) n n n n n n n n NOTE: NO CONNECTION FOR INT6 and INT7 on ATmega2560** rw rw rw rw rw rw rw rw n= INTn number ** But that doesn't mean they wouldn't make an excellent resource for software interrupts! ;-) ) Pin Change Interrupts --------------------------------------------------------------------------- ATMEGA2560 Pin Change Interrupts Arduino Arduino Arduino Pin* PORT PCINT Pin PORT PCINT Pin PORT PCINT A8 PK0 16 10 PB4 4 SS PB0 0 A9 PK1 17 11 PB5 5 SCK PB1 1 A10 PK2 18 12 PB6 6 MOSI PB2 2 A11 PK3 19 13 PB7 7 MISO PB3 3 A12 PK4 20 14 PJ1 10 A13 PK5 21 15 PJ0 9 A14 PK6 22 0 PE0 8 - this one is a little odd.* A15 PK7 23 ...indeed, the ATmega2560 chip supports many more Pin Change Interrupt pins but they are unavailable on the Arduino, unless you want to solder teeny tiny wires. (However, that doesn't mean they wouldn't make an excellent resource for software interrupts! :-) ) * Note: Arduino Pin 0 is PE0 (PCINT8), which is RX0. Also, it is the only other pin on another port on PCI1. This would make it very costly to integrate with the library's code and thus is not supported by this library. It is the same pin the Arduino uses to upload sketches, and it is connected to the FT232RL USB-to-Serial chip (ATmega16U2 on the R3). static const uint8_t SS = 53; static const uint8_t MOSI = 51; static const uint8_t MISO = 50; static const uint8_t SCK = 52; static const uint8_t A8 = 62; static const uint8_t A9 = 63; static const uint8_t A10 = 64; static const uint8_t A11 = 65; static const uint8_t A12 = 66; static const uint8_t A13 = 67; static const uint8_t A14 = 68; static const uint8_t A15 = 69; // PIN CHANGE INTERRUPTS REGISTER BESTIARY for the ATmega2560 PCICR: 7 6 5 4 3 2 1 0 (When PCIE2 is set and GIE is set, PCI2 is enabled) PCIE2 1 0 Likewise for 1 and 0. - - - - - rw rw rw // set... PCIFR: 7 6 5 4 3 2 1 0 (When logic change triggers IRQ, bececomes set. Cleared when PCIF2 1 0 IRQ is executed. PCIF2 == PCIE2, etc.) - - - - - rw rw rw PCMSK2:7 6 5 4 3 2 1 0 (Selects whether pin change interrupt is enabled on PCINT23:16 PCINT23 ... PCINT16 If PCIE2 in PCICR and this bit is set, it is enabled on that rw rw rw rw rw rw rw rw pin.) PK7 PK6 PK5 PK4 PK3 PK2 PK1 PK0 =PORTK + + + + + + + + + == available on the Arduino board. PCMSK1:7 6 5 4 3 2 1 0 (Selects whether pin change interrupt is enabled on PCINT15:8 PCINT15 ... PCINT8 If PCIE1 in PCICR and this bit is set, it is enabled on that rw rw rw rw rw rw rw rw pin.) PJ6 PJ5 PJ4 PJ3 PJ2 PJ1 PJ0 PE0 =PORTJ/E Note pin PE0 is the Arduino's RX pin for programming. + + + PCMSK0:7 6 5 4 3 2 1 0 (Selects whether pin change interrupt is enabled o2560n PCINT7:0 PCINT7 ... PCINT0 If PCIE0 in PCICR and this bit is set, it is enabled on that rw rw rw rw rw rw rw rw pin.) PB7 PB6 PB5 PB4 PB3 PB2 PB1 PB0 =PORTB + + + + + + + + ===================================================================================================== ===================================================================================================== ATmega644 / 1284 Support (MIGHTY1284 used as a reference, NOT Sanguino) ===================================================================================================== ===================================================================================================== // Sanguino, Mosquino uino bobino bonanafannafofino, me my momino... #if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) || defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) // Here I use the mighty-1284p pinout from https://maniacbug.wordpress.com/2011/11/27/arduino-on-atmega1284p-4/ +---\/---+ (D 0) PB0 |1 40| PA0 (AI 0 / D24) (D 1) PB1 |2 39| PA1 (AI 1 / D25) INT2 (D 2) PB2 |3 38| PA2 (AI 2 / D26) PWM (D 3) PB3 |4 37| PA3 (AI 3 / D27) PWM SS (D 4) PB4 |5 36| PA4 (AI 4 / D28) MOSI (D 5) PB5 |6 35| PA5 (AI 5 / D29) PWM MISO (D 6) PB6 |7 34| PA6 (AI 6 / D30) PWM SCK (D 7) PB7 |8 33| PA7 (AI 7 / D31) RST |9 32| AREF VCC |10 31| GND GND |11 30| AVCC XTAL2 |12 29| PC7 (D 23) XTAL1 |13 28| PC6 (D 22) RX0 (D 8) PD0 |14 27| PC5 (D 21) TDI TX0 (D 9) PD1 |15 26| PC4 (D 20) TDO INT0 RX1 (D 10) PD2 |16 25| PC3 (D 19) TMS INT1 TX1 (D 11) PD3 |17 24| PC2 (D 18) TCK PWM (D 12) PD4 |18 23| PC1 (D 17) SDA PWM (D 13) PD5 |19 22| PC0 (D 16) SDL PWM (D 14) PD6 |20 21| PD7 (D 15) PWM +--------+ External Interrupts ------------------------------------------------------------------------------ The following External Interrupts are available on the Sanguino: Sanguino Pin* PORT INT ATmega644/1284 pin 2 PB2 2 3 10 PD2 0 16 11 PD3 1 17 The following Pin Change Interrupts are available on the ATmega1284p: Mighty Mighty Pin* PORT PCINT ATmega644/1284 pin Pin* PORT PCINT ATmega644/1284 pin 0 PB0 8 1 15 PD7 31 21 1 PB1 9 2 16 PC0 16 22 2 PB2 2 3 17 PC1 17 23 3 PB3 11 4 18 PC2 18 24 4 PB4 12 5 19 PC3 19 25 5 PB5 13 6 20 PC4 20 26 6 PB6 14 7 21 PC5 21 27 7 PB7 15 8 22 PC6 22 28 8 PD0 24 14 23 PC7 23 29 9 PD1 25 15 31/A7 PA7 7 33 10 PD2 26 16 30/A6 PA6 6 34 11 PD3 27 17 29/A5 PA5 5 35 12 PD4 28 18 28/A4 PA4 4 36 13 PD5 29 19 27/A3 PA3 3 37 14 PD6 30 20 26/A2 PA2 2 38 25/A1 PA1 1 39 24/A0 PA0 0 40 EICRA: 7 6 5 4 3 2 1 0 (External Interrupt Control Register A) ISC: - - 21 20 11 10 01 00 Interrupt Sense Control Bits (ISCxx) r r rw rw rw rw rw rw EIMSK: 7 6 5 4 3 2 1 0 (External Interrupt Mask Register) - - - - - n n n NOTE: NO CONNECTION FOR INT6 and INT7 on ATmega2560** r r r r r rw rw rw n= INTn number EIFR: External Interrupt Flag Register is set to 1 when a signal generates an IRQ. Cleared upon entering the ISR. Can be cleared by writing a 1 to it. EIFR: 7 6 5 4 3 2 1 0 (External Interrupt Flag Register) - - - - - n n n NOTE: NO CONNECTION FOR INT6 and INT7 on ATmega2560** rw r r r rw rw rw rw n= INTn number PCICR: 7 6 5 4 3 2 1 0 (When PCIE2 is set and GIE is set, PCI2 is enabled) PCIE3 2 1 0 Likewise for 1 and 0. r r r r rw rw rw rw PCIFR: 7 6 5 4 3 2 1 0 (When logic change triggers IRQ, becomes set. Cleared when PCIF3 2 1 0 IRQ is executed. PCIF2 == PCIE2, etc.) r r r r rw rw rw rw 0x73 PCMSK3:7 6 5 4 3 2 1 0 (Selects whether pin change interrupt is enabled on PCINT23:16 PCINT31 ... PCINT24 If PCIE2 in PCICR and this bit is set, it is enabled on that rw rw rw rw rw rw rw rw pin.) PD7 PD6 PD5 PD4 PD3 PD2 PD1 PD0 =PORTD 15 14 13 12 11 10 09 08 -->Sanguino Pins, DIP40 package 0x6D PCMSK2:7 6 5 4 3 2 1 0 (Selects whether pin change interrupt is enabled on PCINT23:16 PCINT23 ... PCINT16 If PCIE2 in PCICR and this bit is set, it is enabled on that rw rw rw rw rw rw rw rw pin.) PC7 PC6 PC5 PC4 PC3 PC2 PC1 PC0 =PORTC 23 22 21 20 19 18 17 16 -->Sanguino Pins, DIP40 package PCMSK1:7 6 5 4 3 2 1 0 (Selects whether pin change interrupt is enabled on PCINT15:8 PCINT15 ... PCINT8 If PCIE1 in PCICR and this bit is set, it is enabled on that rw rw rw rw rw rw rw rw pin.) PB7 PB6 PB5 PB4 PB3 PB2 PB1 PB0 =PORTB 7 6 5 4 3 2 1 0 -->Sanguino Pins, DIP40 package PCMSK0:7 6 5 4 3 2 1 0 (Selects whether pin change interrupt is enabled o2560n PCINT7:0 PCINT7 ... PCINT0 If PCIE0 in PCICR and this bit is set, it is enabled on that rw rw rw rw rw rw rw rw pin.) PA7 PA6 PA5 PA4 PA3 PA2 PA1 PA0 =PORTB 24 25 26 27 28 29 30 31 -->Sanguino Pins, DIP40 package ===================================================================================================== ===================================================================================================== ATtiny 24/44/84 support ATTINY24 ===================================================================================================== ===================================================================================================== Only the 44/84 are supported. Support for this chip is based on David Mellis' work at https://github.com/damellis/attiny This is an ATmega that comes in a 14-pin configuration. There are two ports, A and B. PCint pins are configured on both ports. The following is the pinout, and shows the Arduino pin numbering scheme: // ATtiny24/44/84 / ARDUINO // // +-\/-+ // VCC 1| |14 GND // (D 10) PB0 2| |13 PA0 (D 0) == AREF // (D 9) PB1 3| |12 PA1 (D 1) // PB3 4| |11 PA2 (D 2) // PWM INT0 (D 8) PB2 5| |10 PA3 (D 3) // PWM (D 7) PA7 6| |9 PA4 (D 4) // PWM (D 6) PA6 7| |8 PA5 (D 5) PWM // +----+ gcc CPU designations are as follows: __AVR_ATtiny24__ __AVR_ATtiny24A__ __AVR_ATtiny44__ __AVR_ATtiny44A__ __AVR_ATtiny84__ __AVR_ATtiny84A__ The following External Interrupts are available on the ATtiny24/44/84: Arduino(tiny) Pin* PORT INT ATtiny44/84 pin 8 PB2 0 5 The following Pin Change Interrupts are available on the ATmega24/44/84: Arduino(tiny) Arduino(tiny) Pin* PORT PCINT ATmega24/44/84 pin Pin* PORT PCINT ATmega24/44/84 pin 0 PA0 0 13 5 PA5 5 8 1 PA1 1 12 6 PA6 6 7 2 PA2 2 11 7 PA7 7 6 3 PA3 3 10 8 PB2 10 5 4 PA4 4 9 9 PB1 9 3 10 PB0 8 2 "fake" 11 PB3 11 4 Interrupt registers are different on the Tiny series. Interrupts are set by MCUCR, GIMSK, GIFR, PCMSK0, and PCMSK1. EXTERNAL INTERRUPTS ------------------- MCUCR: ISC: - - - - - - 01 00 Interrupt Sense Control Bits (ISCxx) rw rw rw rw rw rw rw rw ISC01 00 INT0 "is activated if the SREG I-flag and the MCUCR mask is set" (but see GIMSK, below). ISC01 ISC00 0 0 INT0 interrupted on LOW level 1 1 INT0 interrupted on CHANGE 0 0 INT0 interrupted on FALLING 1 1 INT0 interrupted on RISING GIMSK: When INT0 == 1 and SREG I-flag set, External Interrupt enabled. When PCIE1 == 1 and SREG I-flag set, pin change Interrupt PCINT11:8 enabled. When PCIE0 == 1 and SREG I-flag set, pin change Interrupt PCINT7:0 enabled. - INT0 PCIE0 - - - - PCIE1 rw rw rw rw rw rw rw rw -------------------------------- GIFR: 7 6 5 4 3 2 1 0 (When logic change triggers IRQ, becomes set. Cleared when INTF0 PCIF0 2 1 0 IRQ is executed. INTF0 == INT0, etc.) - PCIF1 - - - - INT0 is always cleared when INT0 is a LOW interrupt r rw rw rw r r r r PCMSK1:7 6 5 4 3 2 1 0 (Selects whether pin change interrupt is enabled on PCINT15:8 PCINT11 ... PCINT8 If PCIE1 in GIMSK and this bit is set, it is enabled on that r r r r rw rw rw rw pin.) - - - - PB3 PB2 PB1 PB0 =PORTB - - - - 4 5 3 2 -->ATtiny 24/44/84 Pins, DIP14 package - - - - 11* 8 9 10 -->Arduino Pins, DIP14 package. * - "Fake" Arduino pin. PCMSK0:7 6 5 4 3 2 1 0 (Selects whether pin change interrupt is enabled on PCINT7:0 PCINT7 ... PCINT0 If PCIE0 in GIMSK and this bit is set, it is enabled on that rw rw rw rw rw rw rw rw pin.) PA7 PA6 PA5 PA4 PA3 PA2 PA1 PA0 =PORTA 7 6 5 4 3 2 1 1 -->ATtiny 24/44/84 Pins, DIP14 package 6 7 8 9 10 11 12 13 -->Arduino Pins, DIP14 package. * - "Fake" Arduino pin. ===================================================================================================== ===================================================================================================== ATtiny 25/45/85 support ATTINY25 ===================================================================================================== ===================================================================================================== Only the ATtiny 45/85 are supported. Support for this chip is based on David Mellis' work at https://github.com/damellis/attiny This is an ATmega that comes in an 8-pin configuration. There is a single port, B. gcc CPU designations are as follows: __AVR_ATtiny25__ __AVR_ATtiny45__ __AVR_ATtiny85__ The following is the pinout, and shows the Arduino pin numbering scheme: // ATtiny25/45/85 / ARDUINO // // +-\/-+ // (D 5) PB5 1| |8 Vcc // (D 3) PB3 2| |7 PB2 (D 2) (INT0) (== Gemma B2) // (D 4) PB4 3| |6 PB1 (D 1) (== Gemma B1) // GND 4| |5 PB0 (D 0) (== Gemma B0) // +----+ The following External Interrupts are available on the ATtiny25/45/85: Arduino(tiny) Pin* PORT INT ATtiny25/45/85 pin 2 PB2 0 7 The following Pin Change Interrupts are available on the ATmega25/45/85: Arduino(tiny) Arduino(tiny) Pin* PORT PCINT ATmega25/45/85 pin Pin* PORT PCINT ATmega25/45/85 pin 0 PB0 0 5 3 PB3 3 2 1 PB1 1 6 4 PB4 4 3 2 PB2 2 7 5 PB5 5 1 Interrupt registers are different on the Tiny series. Interrupts are set by MCUCR, GIMSK, GIFR, PCMSK0, and PCMSK1. MCUCR: ISC: - - - - - - 01 00 Interrupt Sense Control Bits (ISCxx) rw rw rw rw rw rw rw rw ISC01 00 INT0 "is activated if the SREG I-flag and the MCUCR mask is set" (but see GIMSK, below). ISC01 ISC00 0 0 INT0 interrupted on LOW level 1 1 INT0 interrupted on CHANGE 0 0 INT0 interrupted on FALLING 1 1 INT0 interrupted on RISING GIMSK: When INT0 == 1 and SREG I-flag set, External Interrupt enabled. When PCIE == 1 and SREG I-flag set, pin change Interrupt PCINT5:0 enabled. - INT0 - - - - - PCIE r rw rw r r r r r -------------------------------- GIFR: 7 6 5 4 3 2 1 0 (When logic change triggers IRQ, becomes set. Cleared when INT0 IRQ is executed. INT0 == INT0, etc.) - PCIE - - - - - INT0 is always cleared when INT0 is a LOW interrupt r rw rw r r r r r PCMSK :7 6 5 4 3 2 1 0 (Selects whether pin change interrupt is enabled on PCINT7:0 PCINT5 ... PCINT0 If PCIE0 in GIMSK and this bit is set, it is enabled on that rw rw rw rw rw rw rw rw pin.) - - PB5 PB4 PB3 PB2 PB1 PB0 =PORTB 1 3 2 7 6 5 -->ATtiny 25/45/85 Pins, DIP8 package ================================================================================================================= From http://www.atmel.com/Images/doc8468.pdf: External interrupts can be sensed and registered either synchronously or asynchronously. Synchronous sensing requires I/O clock whereas asynchronous sensing does not requires I/O clock. This implies that the interrupts that are detected asynchronously can be used for waking the device from sleep modes other than idle mode because the I/O clock is halted in all sleep modes except idle mode. The sense configuration for external interrupts and pin change interrupts for Atmel ATmega2560 is given in Table 2-2. For device specific sense configuration, please refer to the respective datasheet. Table 2-2. External interrupts sense configuration. Program address Interrupt source Sensing $0002 INT0 Asynchronous (Edges and level) $0004 INT1 Asynchronous (Edges and level) $0006 INT2 Asynchronous (Edges and level) $0008 INT3 Asynchronous (Edges and level) $000A INT4 Synchronous (Edges and level) $000C INT5 Synchronous (Edges and level) $000E INT6 Synchronous (Edges and level) $0010 INT7 Synchronous (Edges and level) $0012 PCINT0 Asynchronous $0014 PCINT1 Asynchronous $0016 PCINT2 Asynchronous From Table 2-2, all the pin change interrupts are detected asynchronously. ... The interrupt execution response for all the enabled AVR interrupts is four/five clock cycle’s minimum. This four/five clock cycles depends on the program counter width. If the program counter width is not more than two bytes, then the interrupt response time will be four clock cycles minimum and if the program counter width is more than two bytes, then the interrupt response time will be minimum five clock cycles. These four/five clock cycles include: 1. Two/Three cycles for pushing the Program Counter (PC) value into the stack. 2. One cycle for updating the stack pointer. 3. One cycle for clearing the Global interrupt enable (I) bit. If an interrupt occurs when the MCU is in sleep mode, the interrupt execution response time is increased by five clock cycles. This increase comes in addition to the start-up time from the selected sleep mode. This start up time is the time it will take to start the clock source. ===================================================================================================== ARDUINO CPU BESTIARY ===================================================================================================== See /usr/avr/include/avr/io.h for the actual #defines for the different CPUs. The CPUs are converted to the #defined macros (eg., __AVR_ATmega169__) by the compiler from the source file "avr-mcus.def". See https://github.com/gcc-mirror/gcc/blob/master/gcc/config/avr/avr-mcus.def $ grep mcu /usr/share/arduino/hardware/arduino/boards.txt uno.build.mcu=atmega328p atmega328.build.mcu=atmega328p diecimila.build.mcu=atmega168 nano328.build.mcu=atmega328p nano.build.mcu=atmega168 mega2560.build.mcu=atmega2560 mega.build.mcu=atmega1280 leonardo.build.mcu=atmega32u4 esplora.build.mcu=atmega32u4 micro.build.mcu=atmega32u4 mini328.build.mcu=atmega328p mini.build.mcu=atmega168 ethernet.build.mcu=atmega328p fio.build.mcu=atmega328p bt328.build.mcu=atmega328p bt.build.mcu=atmega168 LilyPadUSB.build.mcu=atmega32u4 lilypad328.build.mcu=atmega328p lilypad.build.mcu=atmega168 pro5v328.build.mcu=atmega328p pro5v.build.mcu=atmega168 pro328.build.mcu=atmega328p pro.build.mcu=atmega168 atmega168.build.mcu=atmega168 atmega8.build.mcu=atmega8 robotControl.build.mcu=atmega32u4 robotMotor.build.mcu=atmega32u4 ISR Register handling Tested the following code to ensure that all registers would be pushed/popped correctly, even if I was using NAKED_ISR. Note that I did not run code, I merely eyeballed the output to ensure it was doing what I expected: volatile uint8_t storage0; volatile uint8_t storage1; volatile uint8_t storage2; volatile uint8_t storage3; volatile uint8_t storage4; volatile uint8_t storage5; volatile uint8_t storage6; volatile uint8_t storage7; volatile uint8_t storage8; volatile uint8_t storage9; volatile uint8_t storage10; volatile uint8_t storage11; volatile uint8_t storage12; volatile uint8_t storage13; volatile uint8_t storage14; volatile uint8_t storage15; volatile uint8_t storage16; volatile uint8_t storage17; volatile uint8_t storage18; volatile uint8_t storage19; void test_ISR(uint8_t current) { uint8_t local0=storage0; uint8_t local1=storage1; uint8_t local2=storage2; uint8_t local3=storage3; uint8_t local4=storage4; uint8_t local5=storage5; uint8_t local6=storage6; uint8_t local7=storage7; uint8_t local8=storage8; uint8_t local9=storage9; uint8_t local10=storage10; uint8_t local11=storage11; uint8_t local12=storage12; uint8_t local13=storage13; uint8_t local14=storage14; uint8_t local15=storage15; uint8_t local16=storage16; uint8_t local17=storage17; uint8_t local18=storage18; uint8_t local19=storage19; local0+=current; local1+=current; local2+=current; local3+=current; local4+=current; local5+=current; local6+=current; local7+=current; local8+=current; local9+=current; local10+=current; local11+=current; local12+=current; local13+=current; local14+=current; local15+=current; local16+=current; local17+=current; local18+=current; local19+=current; storage0=local0; storage1=local1; storage2=local2; storage3=local3; storage4=local4; storage5=local5; storage6=local6; storage7=local7; storage8=local8; storage9=local9; storage10=local10; storage11=local11; storage12=local12; storage13=local13; storage14=local14; storage15=local15; storage16=local16; storage17=local17; storage18=local18; storage19=local19; } ISR(PORTD_VECT, ISR_NAKED) { register uint8_t current asm("r18"); EI_ASM_PREFIX(PIND); test_ISR(current); EI_ASM_SUFFIX; } MISCELLANEOUS: STORING FUNCTION POINTERS IN PROGMEM: http://stackoverflow.com/questions/28261595/put-progmem-function-pointer-array-into-another-progmem-array Calling the function pointers by checking each pin's bitmask individually: if (interruptMask & _BV(0)) functionPointerB0(); Cycles 8b8: c0 ff sbrs r28, 0 1 if move on, 2 if skip (to 8bc) 8ba: 05 c0 rjmp .+10 2 (move on) 8bc: e0 91 60 02 lds r30, 0x0260 2 8c0: f0 91 61 02 lds r31, 0x0261 2 8c4: 19 95 eicall 4 8c6: (move on) TOTAL: 10 if matches, 3 if not == 21 cycles till get to last function call, then 10 cycles to execute it: 31 cycles If an interrupt on third pin: 2 x 3 (not matched) + 10 == 16, plus 4 x 3 (not matched) = 28 total. Calling the function pointers referred to in an array, using while loop: 8c6: d0 e0 ldi r29, 0x00 ; 0 this is the "i" variable while (1) { if (interruptMask & 0x01) { 8c8: c0 ff sbrs r28, 0 1 if it's not set, 2 if skip 8ca: 0a c0 rjmp .+20 2 (move on to try next bit at 0x8e0) (*functionPointerArray[i])(); 8cc: ed 2f mov r30, r29 1 (it's set, execute the function) 8ce: f0 e0 ldi r31, 0x00 1 ; loads 0 8d0: ee 0f add r30, r30 1 8d2: ff 1f adc r31, r31 1 8d4: e7 57 subi r30, 0x77 1 ; 119 8d6: fd 4f sbci r31, 0xFD 1 ; 253 8d8: 01 90 ld r0, Z+ 1 load from data memory 8da: f0 81 ld r31, Z 1 8dc: e0 2d mov r30, r0 1 8de: 19 95 eicall 4 } interruptMask=interruptMask >> 1; 8e0: c6 95 lsr r28 1 if (interruptMask == 0) goto exitISR; 8e2: 11 f0 breq .+4 1 if not zero, 2 if zero (jumps out...) ; 0x8e8 <__vector_9+0x86> i++; 8e4: df 5f subi r29, 0xFF 1 ; 255 8e6: f0 cf rjmp .-32 2 ; 0x8c8 <__vector_9+0x66> TOTAL: 14 if matches, 9 if not == 63 cycles to get to last function call, then 14 cycles to execute it: 77 cycles If an interrupt on third pin: 2 x 7 (not matched) == 14 + 14 == 28 total. Then a couple of cycles to get out. =========================================================================================================== Space Optimizations =========================================================================================================== Compile Simple.ino, using pin 10, compile for ATmega2560: Program: 6402 bytes (2.4% Full) (.text + .data + .bootloader) Data: 859 bytes (10.5% Full) (.data + .bss + .noinit) Compile Simple.ino, using pin 10, define EI_NOTEXTERNAL: Program: 5328 bytes (2.0% Full) (.text + .data + .bootloader) Data: 843 bytes (10.3% Full) (.data + .bss + .noinit) Compile Simple.ino, using pin 10, define EI_NOTPORTJ: Program: 5006 bytes (1.9% Full) (.text + .data + .bootloader) Data: 825 bytes (10.1% Full) (.data + .bss + .noinit) Compile Simple.ino, using pin 10, define EI_NOTPORTJ EI_NOTPORTK: Program: 4686 bytes (1.8% Full) (.text + .data + .bootloader) Data: 806 bytes (9.8% Full) (.data + .bss + .noinit) Compile Simple.ino, using pin 10, define EI_NOTPORTJ EI_NOTPORTK EI_NOTEXTERNAL: Program: 4674 bytes (1.8% Full) (.text + .data + .bootloader) Data: 806 bytes (9.8% Full) (.data + .bss + .noinit)