Reconfigurable computing
Small Embedded Systems
Unit 3.2 Arduino Interrupts
Introduction
Interrupts may be needed in order to:
Activate a piece of code when a certain period of time has gone by
Activate a piece of code when a certain external event has occurred
The Arduino functions millis() and delay() are themselves based on a timer interrupt that the Arduino environment has set up to increment a counter each millisecond
In this unit we will see how to form our own timer interrupts
We’ll also see how to use external interrupts
The code that we need to improve
We looked at this in last unit
We saw that using the Arduino function millis() gave some help, but wasn’t good enough
const int LED_PIN=13;
int LED_STATE=LOW;
void setup() { pinMode(LED_PIN, OUTPUT); }
void loop() {
digitalWrite(LED_PIN, LED_STATE);
LED_STATE = !LED_STATE;
delay(1000);
}
This holds the state of the LED
This flips the state of the LED
The code that we need to improve
We looked at this in last unit
We saw that using the Arduino function millis() gave some help, but wasn’t good enough
unsigned long int previousMillis = 0;
unsigned long int currentMillis;
void loop() {
currentMillis = millis();
if (currentMillis – previousMillis >= 1000) {
previousMillis = currentMillis;
LED_STATE = !LED_STATE;
digitalWrite(LED, LED_STATE);
} }
Get time since start
Time of last flip of LED state
void loop() {
}
void flip()
{
LED_STATE = !LED_STATE;
digitalWrite(LED, LED_STATE));
}
Now we’ll look at a different way to do this: interrupts
Interrupt has trigger condition
When trigger occurs, processor interrupts what it is currently doing, invokes the flip() function, then resumes its previous activity
Improved program using interrupt
Program for normal activity goes here
Flips LED state
Interrupt service routine
void loop() {
}
void flip()
{
LED_STATE = !LED_STATE;
digitalWrite(LED, LED_STATE));
}
In this example we will use a timer interrupt
Trigger condition occurs when timer reaches a specified value
Timer Interrupts
Program for normal activity goes here
Interrupt service routine
Flips LED state
Standard library to handle timer interrupts for ATmega processors (Arudino Uno) is TimerOne
ESP8266 (Feather Huzzah) uses different library: Ticker
Principles are the same for both:
Create an object to represent the timer
Initialize the time interval that we want to trigger the interrupt
Tell the program the identity of the interrupt service routine
Timer Interrupts in Arduino
Timer Interrupt: ATmega
#include
volatile int LED_STATE=LOW;
void setup() {
pinMode(LED, OUTPUT);
Timer1.initialize(1000000);
Timer1.attachInterrupt(flip);
}
void loop() {
}
void flip()
{
LED_STATE = !LED_STATE;
digitalWrite(LED, LED_STATE);
}
Program for normal activity goes here
Include library files
Interrupt service routine
Timer1 is object defined in TimerOne.h
When timer reaches 1000000 ms, interrupt to flip() function
Timer Interrupt: ESP8266
#include
int LED_STATE=LOW;
Ticker myTimer;
void setup() {
pinMode(LED, OUTPUT);
myTimer.attach(1.0, flip);
}
void loop() {
}
void flip()
{
LED_STATE = !LED_STATE;
digitalWrite(LED, LED_STATE);
}
Program for normal activity goes here
Include library files
Interrupt service routine
Ticker is class defined in Ticker.h
myTimer is an instance of this class
When timer reaches 1.0 seconds, interrupt to flip() function
Instruction RAM on ESP8266
void ICACHE_RAM_ATTR flip ()
{
LED_STATE = !LED_STATE;
digitalWrite(LED_PIN, LED_STATE);
}
Normal code is stored in flash memory
The ESP8266 has special high speed memory for ISRs: instruction RAM
ISRs should be stored in this memory – compilation may fail otherwise
Location is set through attribute ICACHE_RAM_ATTR when function is declared:
ISR should be placed in instruction RAM
Timer Interrupt: ATmega
#include
volatile int LED_STATE=LOW;
void setup() {
pinMode(LED, OUTPUT);
Timer1.initialize(1000000);
Timer1.attachInterrupt(flip);
}
void loop() {
}
void flip()
{
LED_STATE = !LED_STATE;
digitalWrite(LED, LED_STATE);
}
Program for normal activity goes here
Interrupt service routine
Dealing with many input devices
If we have only one input sensor (say a button) everything is quite simple
If we have many inputs that can require attention at unpredictable and uncorrelated times, then it gets more complicated
Two approaches to deal with this:
Polling
Interrupts
Dealing with many input devices
Polling
Processor takes a measurement from each port at regular time intervals.
Happens even if there has been no change in the connected device.
Predictable time slices are allocated to each device that is polled.
Simple to code, predictable timing
Inefficient because polling happens regardless of whether sensor reading has changed
Dealing with many input devices
External interrupts
interrupt function is triggered in response to what is happening at an input
only occurs when needed
more efficient than polling but timing is less predictable
External interrupt example
Let’s return to our very first program
Suppose we want the blink time to change between fast and slow depending on input button press
const int LED_PIN=13;
void setup() {
pinMode(LED_PIN, OUTPUT);
}
void loop() {
digitalWrite(LED_PIN, HIGH);
delay(1000);
digitalWrite(LED_PIN, LOW);
delay(1000);
}
Getting our example ready
const int LED=13;
const int BUTTON=2;
const int SLOW=2000;
const int FAST=100;
int delayTime=SLOW;
void setup() {
pinMode(LED, OUTPUT);
}
void loop() {
digitalWrite(LED, HIGH);
delay(delayTime);
digitalWrite(LED, LOW);
delay(delayTime);
}
We want to change this when the button is pressed
This is the pin attached to the button
These are the possible blink rate values
This is the blink rate value
We’ll introduce some variables to handle blink delay
This will be done in an ISR
In setup loop:
state what input we are monitoring
state what pin event should trigger condition:
LOW
CHANGE
RISING
FALLING
state what function to invoke when event occurs
External Interrupts in Arduino
attachInterrupt(digitalPinToInterrupt(BUTTON),
functionName,
CHANGE);
Using an External Interrupt
Now we will attach an interrupt that will change the blink delay
void changeDelay(){
if (delayTime==SLOW) {
delayTime=FAST;
} else {
delayTime=SLOW;
}
void setup() {
pinMode(LED, OUTPUT);
attachInterrupt(digitalPinToInterrupt(BUTTON),
changeDelay,
FALLING);
}
Interrupt service routine
Invoke ISR changeDelay() whenever BUTTON goes from HIGH to LOW
Instruction RAM on ESP8266
On the ESP8266, we must ensure that the ISR is stored in instruction RAM:
void ICACHE_RAM_ATTR changeDelay(){
if (delayTime==SLOW) {
delayTime=FAST;
} else {
delayTime=SLOW;
}
void setup() {
pinMode(LED, OUTPUT);
attachInterrupt(digitalPinToInterrupt(BUTTON),
changeDelay,
FALLING);
}
ISR must be placed in instruction RAM
Problem with ISRs
Interrupts create a challenge:
Processors try to be efficient:
a variable whose value is known may have a copy stored in a register
This copy in the register will not be changed unless there is an assignment to the variable
The main program doesn’t know about what the ISR has done (changed delayTime)
The program may just carry on with an old value copy stored in register, even though the real value has changed
Problem with ISRs
int delayTime=SLOW;
void loop() {
digitalWrite(LED_PIN, HIGH);
delay(delayTime);
digitalWrite(LED_PIN, LOW);
delay(delayTime);
}
Processor may use out-of-date copy of value stored in register
The value in memory may be changed by ISR
The main program doesn’t know about what the ISR has done (changed delayTime)
The program may just carry on with an old value copy stored in register, even though the real value has changed
Volatile variables
volatile int delayTime=SLOW;
void loop() {
digitalWrite(LED_PIN, HIGH);
delay(delayTime);
digitalWrite(LED_PIN, LOW);
delay(delayTime);
}
Global declarations that are shared between ISR and regular code should be declared volatile
This is solved if we use the volatile qualifier
This tells the program not to trust its temporary copies of variables as some other process may change them without warning
This code will execute correctly
Summary
The Arduino environment enables us to create our own interrupt service routines and attached them to interrupts
Interrupts could be generated from a timer or as a result of the action of an external device
/docProps/thumbnail.jpeg