How To Use Interrupts on the Particle Photon
2019-07-01 | By Maker.io Staff
Prototyping IoT projects has never been easier thanks to the Particle Photon, its use of an online IDE, and the Arduino-style language; eventually, however, you may need to make an application that responds to hardware events immediately. In this How-To article, we will learn how to use interrupts on the Particle Photon.
The Purpose of Interrupts
Most microcontroller programs have some infinite loop where all the “main” code is held and constantly executed. This main loop could be used to monitor data from sensors, control hardware, and even transfer data to other devices over the internet. While this main loop of code can be used to read inputs and data sources to react to changes, it can only do so when a specific comparison instruction is executed.
For example, if a main loop of code checks the status of some buttons connected to I/O but also sends data to an HTTP server, then when the microcontroller sends data, any button presses will not be serviced and ignored. This is okay for simple applications, but sometimes immediate action is required (such as a safety signal or data coming in from a UART port). This is where interrupts come in!
Interrupts are special functions that are called as soon as an event occurs. When an interrupt is launched, the execution of the main loop of code is halted. Interrupt functions are often very small and fast but can instantly respond to sudden changes, no matter what the main loop of code is doing. For example, a microcontroller could be reading sensor data during its main loop, but an input button could be tied to an interrupt so that when the button is pressed the microcontroller will stop reading data from the sensor, run the interrupt service routine for the button press, and then carry on with reading data from the sensor.
Interrupt Sources
A microcontroller will have sources that can trigger an interrupt service routine, and on the Particle Photon, interrupts can come from many different sources (including the digital pins, analog pins, DAC, UART, and even the WKP pin). Some of these sources are shared with others, so some interrupt sources can only be enabled if another interrupt source is disabled. The interrupt sources on the Particle Photon are shown below.
These pins have NO interrupt sources:
- D0, A5 (shared with SETUP button)
These pins can all be used as interrupt sources with no limitations:
- D5, D6, D7, A2, A6, WKP, TX, RX
These pins have interrupts, but from each group, only one at a time can be used:
- D1, A4
- D2, A0, A3
- D3, DAC
- D4, A1
Creating Interrupts
Creating an interrupt requires two functions: the attachInterrupt() function and an interrupt service routine. The attachInterrupt() function typically takes three parameters: the interrupt source, the interrupt service routine function, and the interrupt mode. The interrupt service routine is nothing more than a simple user-defined function that contains code to handle the interrupt, which could include a value being incremented, a message sent over UART, or even a system restart.
Three possible interrupt modes exist for interrupts on the Particle Photon: CHANGE, RISING, and FALLING. CHANGE is a mode that fires the interrupt whenever a change from the interrupt source is detected, RISING is a mode that fires the interrupt when the interrupt source value rises and FALLING is a mode that fires the interrupt when the interrupt source value falls. Below is an example of a simple interrupt service routine that fires whenever a button press is detected on D2.
// Variable used to hold button press status bool buttonPressFlag = false; // Configure D2 as an input // Fire buttonPressInterrupt when button is pressed void setup() { pinMode(D2, INPUT); attachInterrupt(D2, buttonPressInterrupt, RISING); } // Do random non-important code here void loop() { } // Button press interrupt service routine void buttonPressInterrupt(){ buttonPressFlag = true; }
Deleting, Enabling, and Disabling Interrupts
Sometimes an interrupt may need to be disabled or “deleted,” and this is easily done with the detachInterrupt() function. This function takes a single parameter (the source of an interrupt) to detach, and once called, the Photon will no longer call the interrupt service routine defined in the attachInterrupt() function previously. However, in some time-critical scenarios (such as timing), an interrupt can cause problems if the system being halted detects an interrupt, so disabling interrupts can be useful. Doing this is very easy with the function noInterrupts() , which prevents the Photon from firing any interrupt. Once a time-sensitive task is completed, interrupts can be re-enabled with the function interrupts().
// Do random non-important code here void loop() { // D2 is used as an interrupt // Time sensitive code here noInterrupts(); while(digitalRead(D3) == 0) counter ++; // Re-enable interrupts interrupts(); }
Interrupt Priority
Interrupts have priority over normal code found in the main loop function, but what happens when another interrupt is detected while an interrupt is being serviced? This is where interrupt priority comes in: any interrupt whose priority is greater than another interrupt will always halt that interrupt when fired.
For example, if D2 and D3 are both configured as interrupts, but the priority of D2 is greater than D3, then an interrupt caused by D2 will halt routine services for D3, and the Photon will execute the interrupt service routine for D2. When the D2 routine is finished, the interrupt routine for D3 will then resume, and when that is completed, the main code will resume.
Priority can be set using the attachInterrupt() function and is a fourth optional parameter. The priority number ranges from 0 to 13, with lower numbers having greater priority. This means that an interrupt with a priority number of 0 has the greatest priority and will always halt all other running code when detected. Below is an example that creates two interrupts for D2 and D3 but makes D2 a higher priority than D3.
// Configure D2 and D3 as an input // Interrupt D2 will always execute even over D3 interrupt void setup() { pinMode(D2, INPUT); pinMode(D3, INPUT); attachInterrupt(D2, d2_interrupt_routine, RISING, 10); attachInterrupt(D3, d3_interrupt_routine, RISING, 11); }
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.
Visit TechForum