Page 106

External interrupt breadboard layout

Page 107 and 108

void setup() { pinMode(2, INPUT_PULLUP); // pin 2 for ext interrupt 0 pinMode(LED_BUILTIN, OUTPUT); cli(); // disable interrupts EIFR |= (1 << INTF0); // clear any existing flags EIMSK |= (1 << INT0); // Enable external interrupt INT0 EICRA |= (1 << ISC01); // Trigger request on falling edge sei(); // re-enable interrupts } ISR(INT0_vect) {     digitalWrite(LED_BUILTIN, digitalRead(LED_BUILTIN) ^ 1);    // Toggle                                                                 //LED }

const byte intPin = 2; void setup() { pinMode(intPin, INPUT_PULLUP); // pullup resistor enabled pinMode(LED_BUILTIN, OUTPUT); noInterrupts(); attachInterrupt(digitalPinToInterrupt(intPin), intHandler, FALLING); interrupts(); } void intHandler() { digitalWrite(LED_BUILTIN, digitalRead(LED_BUILTIN) ^ 1); // Toggle LED }

For a detailed explanation of switch bounce take a look at All About Circuits


Page 110

const byte intPin = 4; void setup() { pinMode(intPin, INPUT_PULLUP); pinMode(LED_BUILTIN, OUTPUT); noInterrupts(); PCMSK2 |= (1 << PCINT20); // set interrupt for pin D4 PCIFR |= (1 << PCIF2); // clear any outstanding interrupt flags PCICR |= (1 << PCIE2); // enable pin change interrupts for this port interrupts(); } ISR(PCINT2_vect) { digitalWrite(LED_BUILTIN, digitalRead(LED_BUILTIN) ^ 1); // Toggle LED }

Page 110

const byte intPin = 4; volatile byte flag = 0; byte lastFlag = flag; void setup() { Serial.begin(115200); pinMode(intPin, INPUT_PULLUP); pinMode(LED_BUILTIN, OUTPUT); noInterrupts(); PCMSK2 |= (1 << PCINT20); // set interrupt for pin D4 PCIFR |= (1 << PCIF2); // clear any outstanding interrupt flags PCICR |= (1 << PCIE2); // enable pin change interrupts for this port interrupts(); } ISR(PCINT2_vect) { digitalWrite(LED_BUILTIN, digitalRead(LED_BUILTIN) ^ 1); // Toggle LED flag = (digitalRead(intPin) == LOW) ? 1 : 2; } void loop() { if(lastFlag != flag) {     Serial.println((flag == 1) ? "LOW" : "HIGH");     lastFlag = flag; } }

Page 112

const byte intPin = 4; void setup() { Serial.begin(115200); Serial.println("Send '1' to interrupt"); pinMode(intPin, OUTPUT); // note pin set for output digitalWrite(intPin, HIGH); // and to an initial state pinMode(LED_BUILTIN, OUTPUT); noInterrupts(); PCMSK2 |= (1 << PCINT20); // set interrupt for pin D4 PCIFR |= (1 << PCIF2); // clear any outstanding interrupt flags PCICR |= (1 << PCIE2); // enable pin change interrupts for this port interrupts(); } ISR(PCINT2_vect) { digitalWrite(LED_BUILTIN, digitalRead(LED_BUILTIN) ^ 1); // Toggle LED } void loop() { if (Serial.available() > 0) {     char nChar = Serial.read();     if(nChar == '1') {      Serial.println("Going LOW");      digitalWrite(intPin, LOW); // trigger the PC interrupt      unsigned long tmr = millis() + 1000;      while (millis() < tmr) {     // wait a second      }      Serial.println("Going HIGH");      digitalWrite(intPin, HIGH); // trigger the interrupt again     } } }

Page 114

#include <util/atomic.h> volatile int wdiCount = 0; void setup() { Serial.begin(115200); Serial.println("Program Started"); noInterrupts(); wdt_reset(); // watchdog timer reset //now code to set up WDT interrupt WDTCSR = (1<<WDCE)|(1<<WDE); //set timer counter with 8s ish prescaller WDTCSR = (1<<WDIE)|(1<<WDP0)|(1<<WDP3); interrupts(); } // ISR for watchdog interrupt ISR(WDT_vect) { wdiCount++; // increment a counter for fun } void loop() { int wdCount; Serial.flush(); // just to be sure gotoSleep(); ATOMIC_BLOCK(ATOMIC_RESTORESTATE){     wdCount = wdiCount; } Serial.print("Wake up number: "); Serial.println(wdCount); } // section on sleep modes coming up later in this chapter void gotoSleep() { set_sleep_mode(SLEEP_MODE_PWR_DOWN); // full sleep mode sleep_enable(); // set SE bit sleep_mode(); // board starts sleep here // ** code resumes here on wake ** sleep_disable(); }

Page 115

#include <avr/wdt.h> #include <avr/sleep.h> #include <avr/wdt.h> void setup() { Serial.begin(115200); Serial.println("Program Started/Restarted"); pinMode(LED_BUILTIN, OUTPUT); wdt_enable(WDTO_2S); // fire watchdog interrupt every 2 seconds } void loop() { for(unsigned int i = 0; ; i++){     wdt_reset(); // restart the interrupt timer     digitalWrite(LED_BUILTIN, HIGH);     delay(i * 250);     digitalWrite(LED_BUILTIN, LOW);     delay(500); } }

Page 116

#include "MsTimer2.h" #include <util/atomic.h> volatile unsigned int volVal = 0xFF00; bool swtch = true; void setup() { Serial.begin(115200); MsTimer2::set(100, timeInterrupt); // 100ms period MsTimer2::start(); } void timeInterrupt() { //ISR just toggles the value of test, lowbyte 0x00FF or highbyte 0xFF00 volVal = (swtch)? 0x00FF : 0xFF00; swtch = !swtch; } void loop() { unsigned int loopVal; //ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {      loopVal = volVal; } //Test value of loopVal should only ever be 0x00FF or 0xFF00 if(loopVal != 0xFF00 && loopVal != 0x00FF) {     //local value incorrect due to non-atomic access of volVal     Serial.println(loopVal, HEX); } }

Page 118

#include <avr/power.h> #include <avr/sleep.h> void setup() { Serial.begin(115200); Serial.println("Send a char to wake the board"); delay(1000); //Serial activity is asynchronous so wait allows activity                 //to cease gotoSleep(); } void gotoSleep() { set_sleep_mode(SLEEP_MODE_IDLE); // gentle sleep mode sleep_enable(); // set SE bit // set the power saving extras power_adc_disable(); power_spi_disable(); power_timer0_disable(); power_timer1_disable(); power_timer2_disable(); power_twi_disable(); sleep_mode(); // put the board to sleep // code resumes here on wake sleep_disable(); power_all_enable(); Serial.println("Now Awake!"); }

Serial interface

If you take a look at the documentation for the Serial object you will see a function called serialEvent(). The documentation says thet serialEvent() is called when data is available. You might get the impression that this is an interrupt service routine (ISR). It is not unfortunately. What actually happens under the hood is that if you implement a function named serialEvent() in your code the pre-compile process will add a "hidden" line to the bottom of the loop() function*. This line reads:

if (serialEventRun) serialEventRun();

The variable serialEventRun will hold a pointer to your serialEvent() function if there is some waiting serial data.

Where your code is regularly running through the loop() function then that is a "just good enough" implementation as the function in question will be regularly called and you can add code to that function to check for and read available data from the Serial buffer.

Things are not quite so good if your code is running a lengthy process outside of the loop function but you would still like to know about data arriving over the Serial link.

The most obvious solution would be to insert that same line into that long running function's code so that a check can be made from time to time. Not quite an ISR but maybe still just good enough.

* OK that is a convenient way of thinking about it. The actual implementation is closer to what follows:
int main(void) {     init();     setup();     for (;;) {         loop();         if (serialEventRun) serialEventRun();     }     return 0; }