Skip to content

Commit 63b43fc

Browse files
committed
Loopless / low power examples.
A set of examples that demonstrate basic use of timers, interrupts, and tasks. Based on and tested on the nRF52840, but also compiles on the nRF52832 -- although you'll need an external button there.
1 parent 290b3b7 commit 63b43fc

File tree

8 files changed

+644
-0
lines changed

8 files changed

+644
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
Blink_loopless
3+
4+
Created 2019-05-03
5+
by Pol Van Aubel
6+
7+
Turns on an LED on for one second, then off for one second, repeatedly.
8+
9+
This example is based on the original Blink sketch. The Adafruit nRF52* boards
10+
are more advanced than "normal" Arduinos, however, and come with freeRTOS
11+
and proper task management. This blink sketch demonstrates the use of two
12+
SoftwareTimers to perform two repetitive delayed tasks at different frequencies
13+
without using the energy that a continuously running loop() function has.
14+
15+
AdaFruit's nRF-boards have multiple on-board LEDs you can control. These are
16+
addressable using the LED_RED, LED_BLUE, LED_BUILTIN and LED_CONN definitions.
17+
These take care to use the correct LED pin regardless of which board is used.
18+
If you want to know what pin the on-board LED is connected to on your model,
19+
check the Technical Specs of your board on https://www.adafruit.com/ or the
20+
board's variant.h at
21+
https://github.com/adafruit/Adafruit_nRF52_Arduino/tree/master/variants
22+
23+
24+
Based on the original Blink, the code of which is in the public domain.
25+
*/
26+
27+
#include <Arduino.h>
28+
29+
// Create the two SoftwareTimers we're going to use.
30+
SoftwareTimer bluetimer, redtimer;
31+
32+
void setup()
33+
{
34+
// LED_RED & LED_BLUE are already initialized as outputs.
35+
36+
// Initialize the timers at .75 seconds for blue, and 1 second for red.
37+
bluetimer.begin(750, bluetoggle);
38+
redtimer.begin(1000, redtoggle);
39+
bluetimer.start();
40+
redtimer.start();
41+
suspendLoop();
42+
}
43+
44+
void loop(void) { }
45+
46+
/**
47+
* Toggle led1 every 1 second
48+
*/
49+
void redtoggle(TimerHandle_t _handle)
50+
{
51+
digitalToggle(LED_RED); // Toggle LED
52+
}
53+
54+
/**
55+
* Toggle led1 every 0.5 second
56+
*/
57+
void bluetoggle(TimerHandle_t _handle)
58+
{
59+
digitalToggle(LED_BLUE); // Toggle LED
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
Blink_loopless
3+
4+
Created 2019-05-03
5+
by Pol Van Aubel
6+
7+
Turns on an LED on for one second, then off for one second, repeatedly.
8+
9+
This example is based on the original Blink sketch. The Adafruit nRF52* boards
10+
are more advanced than "normal" Arduinos, however, and come with freeRTOS
11+
and proper task management. This blink sketch demonstrates the use of
12+
SoftwareTimer to perform a repetitive delayed task without using the energy
13+
that a continuously running loop() function has.
14+
15+
AdaFruit's nRF-boards have multiple on-board LEDs you can control. These are
16+
addressable using the LED_RED, LED_BLUE, LED_BUILTIN and LED_CONN definitions.
17+
These take care to use the correct LED pin regardless of which board is used.
18+
If you want to know what pin the on-board LED is connected to on your model,
19+
check the Technical Specs of your board on https://www.adafruit.com/ or the
20+
board's variant.h at
21+
https://github.com/adafruit/Adafruit_nRF52_Arduino/tree/master/variants
22+
23+
24+
Based on the original Blink, the code of which is in the public domain.
25+
26+
*/
27+
28+
// Create a SoftwareTimer that will drive our LED.
29+
SoftwareTimer led_timer;
30+
31+
// the setup function runs once when you press reset or power the board
32+
void setup(void) {
33+
// initialize digital pin LED_BUILTIN as an output.
34+
pinMode(LED_RED, OUTPUT);
35+
36+
// Set up a repeating timer that fires every half second (500ms) to toggle the LED.
37+
led_timer.begin(500, timer_callback);
38+
led_timer.start();
39+
40+
// Since loop() is empty, suspend its task so that the system never runs it
41+
// and can go to sleep properly.
42+
suspendLoop();
43+
}
44+
45+
void timer_callback(TimerHandle_t _handle) {
46+
digitalToggle(LED_RED); // toggle the red LED
47+
}
48+
49+
// the loop function is empty and should never run.
50+
void loop(void) { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
Button_interrupt
3+
4+
Created 2019-05-04
5+
by Pol Van Aubel
6+
7+
Uses an interrupt to listen for a button input. On the nRF52840, it can listen
8+
on pin 7 to the UserSW. On other boards, an external switch needs to be provided.
9+
10+
The Adafruit nRF52* boards are more advanced than "normal" Arduinos and come
11+
with freeRTOS, interrupts available on every pin, and proper task management.
12+
This button sketch demonstrates the use of interrupts to act on an input such
13+
as a switch, without having to poll it in a continuously scanning loop() function.
14+
Even though human button presses are slow, and therefore unlikely to be missed
15+
using a scanning loop, the interrupt technique allows the board to go into sleep
16+
mode while waiting for an input, thereby saving a lot of energy.
17+
18+
AdaFruit's nRF-boards have multiple on-board LEDs you can control. These are
19+
addressable using the LED_RED, LED_BLUE, LED_BUILTIN and LED_CONN definitions.
20+
These take care to use the correct LED pin regardless of which board is used.
21+
If you want to know what pin the on-board LED is connected to on your model,
22+
check the Technical Specs of your board on https://www.adafruit.com/ or the
23+
board's variant.h at
24+
https://github.com/adafruit/Adafruit_nRF52_Arduino/tree/master/variants
25+
26+
27+
This code is licensed under CC0, effectively putting it in the Public Domain
28+
where possible.
29+
30+
*/
31+
32+
// On the nRF52840, pin 7 is the UserSW/DFU switch.
33+
int interruptPin = 7;
34+
35+
// the setup function runs once when you press reset or power the board
36+
void setup(void) {
37+
pinMode(LED_RED, OUTPUT);
38+
pinMode(interruptPin, INPUT_PULLUP); // Configure the switch pin as an input with internal pull-up register enabled.
39+
40+
// Configure it to call switch_callback if the switch pin transitions from HIGH to LOW, i.e. when it is pressed.
41+
// On the nRF52840, RISING and CHANGE are also valid options. Depending on your platform, LOW may also be available.
42+
attachInterrupt(interruptPin, switch_callback, FALLING);
43+
44+
// Since loop() is empty, suspend its task so that the system never runs it
45+
// and can go to sleep properly.
46+
suspendLoop();
47+
}
48+
49+
// the loop function is empty and should never run.
50+
void loop(void) { }
51+
52+
// The switch_callback function is the function we attached to the interrupt on line 42.
53+
// This is known as an Interrupt Service Routine, or ISR.
54+
// It gets run every time the interrupt fires, in the interrupt context. Nothing else
55+
// happens while this is running. Therefore, it should be fast and simple.
56+
// In particular, it should never block, nor do Serial communication or use the Bluefruit API.
57+
// There are a few freeRTOS functions specifically designed to be run from an ISR.
58+
// If you need a longer ISR, other examples show how to do this.
59+
void switch_callback(void) {
60+
digitalToggle(LED_RED); // toggle the red LED
61+
62+
// You may notice that the LED does not reliably turn on on one press of the switch,
63+
// then off on the next. This is likely due to a phenomenon physical switches exhibit called "bounce".
64+
// Effectively this means that the pin goes through multiple transitions of HIGH to LOW,
65+
// back to HIGH, to LOW again, for a single press of the button.
66+
// Some debouncing techniques using interrupts are shown in another example.
67+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
Button_interrupt_ada_callback
3+
4+
Created 2019-05-04
5+
by Pol Van Aubel
6+
7+
Uses an interrupt to listen for a button input. On the nRF52840, it can listen
8+
on pin 7 to the UserSW. On other boards, an external switch needs to be provided.
9+
10+
The Adafruit nRF52* boards are more advanced than "normal" Arduinos and come
11+
with freeRTOS, interrupts available on every pin, and proper task management.
12+
This button sketch demonstrates the use of interrupts to act on an input such
13+
as a switch, without having to poll it in a continuously scanning loop() function.
14+
Even though human button presses are slow, and therefore unlikely to be missed
15+
using a scanning loop, the interrupt technique allows the board to go into sleep
16+
mode while waiting for an input, thereby saving a lot of energy.
17+
18+
However, because interrupts need to be fast, you must not do Serial communication
19+
or long tasks directly in an ISR. This is where deferred ISRs come in. However,
20+
deferred ISRs cannot have a single time-critical component. By using
21+
ada_callback_fromISR, you can run a fast time-critical component in a normal
22+
ISR, and then queue up a callback to process the rest.
23+
24+
AdaFruit's nRF-boards have multiple on-board LEDs you can control. These are
25+
addressable using the LED_RED, LED_BLUE, LED_BUILTIN and LED_CONN definitions.
26+
These take care to use the correct LED pin regardless of which board is used.
27+
If you want to know what pin the on-board LED is connected to on your model,
28+
check the Technical Specs of your board on https://www.adafruit.com/ or the
29+
board's variant.h at
30+
https://github.com/adafruit/Adafruit_nRF52_Arduino/tree/master/variants
31+
32+
33+
This code is licensed under CC0, effectively putting it in the Public Domain
34+
where possible.
35+
36+
*/
37+
38+
// On the nRF52840, pin 7 is the UserSW/DFU switch.
39+
int interruptPin = 7;
40+
41+
// Because this variable is modified inside a non-deferred ISR, and used outside it,
42+
// it must be declared volatile to ensure that the most recently written value is
43+
// always used.
44+
volatile int numinterrupts;
45+
46+
// the setup function runs once when you press reset or power the board
47+
void setup(void) {
48+
Serial.begin(115200); // Not required on the nRF52840 with native USB.
49+
50+
while (!Serial) { // Stalls the nRF52840 until the USB serial console has been opened on the host PC.
51+
delay(10);
52+
}
53+
Serial.println("Press the button!");
54+
Serial.flush(); // Because this sketch does nothing else, it will go to sleep rather than send the
55+
// entire Serial buffer to the host PC. So, use Serial.flush() to make sure it does that
56+
// before allowing it to sleep.
57+
58+
pinMode(LED_RED, OUTPUT);
59+
pinMode(interruptPin, INPUT_PULLUP); // Configure the switch pin as an input with internal pull-up register enabled.
60+
61+
// Configure it to call switch_callback if the switch pin transitions from HIGH to LOW, i.e. when it is pressed.
62+
// On the nRF52840, RISING and CHANGE are also valid options. Depending on your platform, LOW may also be available.
63+
attachInterrupt(interruptPin, switch_isr, FALLING);
64+
65+
// Since loop() is empty, suspend its task so that the system never runs it
66+
// and can go to sleep properly.
67+
suspendLoop();
68+
}
69+
70+
// the loop function is empty and should never run.
71+
void loop(void) { }
72+
73+
// The switch_isr function is the function we attached to the interrupt on line 63.
74+
// This is known as an Interrupt Service Routine, or ISR.
75+
// It gets run every time the interrupt fires, in the interrupt context. Nothing else
76+
// happens while this is running. Therefore, it should be fast and simple.
77+
// In particular, it should never block, nor do Serial communication or use the Bluefruit API.
78+
// There are a few freeRTOS functions specifically designed to be run from an ISR.
79+
void switch_isr(void) {
80+
// Do short time-critical processing (setting flags, queueing up tasks, etc) here.
81+
digitalToggle(LED_RED);
82+
83+
++numinterrupts;
84+
ada_callback_fromISR(NULL, 0, switch_isr_callback); // Queue up a task with no extra variables and no arguments.
85+
// Every single interrupt is serviced, because internally, a
86+
// queue is used.
87+
}
88+
89+
90+
// The switch_isr_callback function is the function we set up as the ada_callback on line 84.
91+
// It gets run once for every interrupt that switch_isr ran for, but with no guarantees about
92+
// when that happens. However, the advantage is you have the full range of Serial, Bluefruit,
93+
// and freeRTOS API to use now.
94+
// Bear in mind, however, that if this function takes longer to run than the average time between
95+
// interrupts, at some point your ada_callback_queue will overflow. For that scenario, consider
96+
// batch-processing interrupts by using direct-to-task notifications.
97+
void switch_isr_callback(void) {
98+
Serial.println("Interrupt serviced!");
99+
Serial.print("The number of interrupts received at this point was ");
100+
Serial.println(numinterrupts);
101+
Serial.flush(); // Because this sketch does nothing else, it will go to sleep rather than send the
102+
// entire Serial buffer to the host PC. However, here, Serial.flush() doesn't seem
103+
// to work either. Maybe something to do with the task context?
104+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
Button_interrupt_deferred
3+
4+
Created 2019-05-04
5+
by Pol Van Aubel
6+
7+
Uses an interrupt to listen for a button input. On the nRF52840, it can listen
8+
on pin 7 to the UserSW. On other boards, an external switch needs to be provided.
9+
10+
The Adafruit nRF52* boards are more advanced than "normal" Arduinos and come
11+
with freeRTOS, interrupts available on every pin, and proper task management.
12+
This button sketch demonstrates the use of interrupts to act on an input such
13+
as a switch, without having to poll it in a continuously scanning loop() function.
14+
Even though human button presses are slow, and therefore unlikely to be missed
15+
using a scanning loop, the interrupt technique allows the board to go into sleep
16+
mode while waiting for an input, thereby saving a lot of energy.
17+
18+
However, because interrupts need to be fast, you must not do Serial communication
19+
or long tasks directly in an ISR. This is where deferred ISRs come in.
20+
21+
AdaFruit's nRF-boards have multiple on-board LEDs you can control. These are
22+
addressable using the LED_RED, LED_BLUE, LED_BUILTIN and LED_CONN definitions.
23+
These take care to use the correct LED pin regardless of which board is used.
24+
If you want to know what pin the on-board LED is connected to on your model,
25+
check the Technical Specs of your board on https://www.adafruit.com/ or the
26+
board's variant.h at
27+
https://github.com/adafruit/Adafruit_nRF52_Arduino/tree/master/variants
28+
29+
30+
This code is licensed under CC0, effectively putting it in the Public Domain
31+
where possible.
32+
33+
*/
34+
35+
// On the nRF52840, pin 7 is the UserSW/DFU switch.
36+
int interruptPin = 7;
37+
38+
// the setup function runs once when you press reset or power the board
39+
void setup(void) {
40+
Serial.begin(115200); // Not required on the nRF52840 with native USB.
41+
42+
while (!Serial) { // Stalls the nRF52840 until the USB serial console has been opened on the host PC.
43+
delay(10);
44+
}
45+
Serial.println("Press the button!");
46+
47+
pinMode(LED_RED, OUTPUT);
48+
pinMode(interruptPin, INPUT_PULLUP); // Configure the switch pin as an input with internal pull-up register enabled.
49+
50+
// Configure it to call switch_callback if the switch pin transitions from HIGH to LOW, i.e. when it is pressed.
51+
// On the nRF52840, RISING and CHANGE are also valid options. Depending on your platform, LOW may also be available.
52+
// The ISR_DEFERRED flag ensures that the interrupt is serviced by a scheduled operating system task.
53+
// What this means is that the ISR is allowed to perform slow functionality, including Serial communication,
54+
// using the Bluefruit API, and the entire freeRTOS API rather than just the *FromISR functions.
55+
// Use this if your interrupt servicing is not time-critical.
56+
attachInterrupt(interruptPin, switch_deferred, ISR_DEFERRED | FALLING);
57+
58+
// Since loop() is empty, suspend its task so that the system never runs it
59+
// and can go to sleep properly.
60+
suspendLoop();
61+
}
62+
63+
// the loop function is empty and should never run.
64+
void loop(void) { }
65+
66+
// The switch_deferred function is the function we attached to the interrupt on line 56.
67+
// This is known as an Interrupt Service Routine, or ISR.
68+
// Because it is a deferred ISR, it is not guaranteed to run immediately after the interrupt
69+
// happens. However, the advantage is you have the full range of Serial, Bluefruit,
70+
// and freeRTOS API to use now.
71+
void switch_deferred(void) {
72+
digitalToggle(LED_RED);
73+
Serial.println("Interrupt serviced!");
74+
Serial.flush(); // Because this sketch does nothing else, it will go to sleep rather than send the
75+
// entire Serial buffer to the host PC. So, use Serial.flush() to make sure it does that
76+
// before allowing it to sleep.
77+
78+
// You may notice that you receive multiple messages per press of the switch,
79+
// This is likely due to a phenomenon physical switches exhibit called "bounce".
80+
// Effectively this means that the pin goes through multiple transitions of HIGH to LOW,
81+
// back to HIGH, to LOW again, for a single press of the button.
82+
// Some debouncing techniques using interrupts are shown in another example.
83+
}

0 commit comments

Comments
 (0)