Skip to content

Commit

Permalink
Loopless / low power examples.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
MacGyverNL committed May 4, 2019
1 parent 290b3b7 commit 63b43fc
Show file tree
Hide file tree
Showing 8 changed files with 644 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
Blink_loopless
Created 2019-05-03
by Pol Van Aubel
Turns on an LED on for one second, then off for one second, repeatedly.
This example is based on the original Blink sketch. The Adafruit nRF52* boards
are more advanced than "normal" Arduinos, however, and come with freeRTOS
and proper task management. This blink sketch demonstrates the use of two
SoftwareTimers to perform two repetitive delayed tasks at different frequencies
without using the energy that a continuously running loop() function has.
AdaFruit's nRF-boards have multiple on-board LEDs you can control. These are
addressable using the LED_RED, LED_BLUE, LED_BUILTIN and LED_CONN definitions.
These take care to use the correct LED pin regardless of which board is used.
If you want to know what pin the on-board LED is connected to on your model,
check the Technical Specs of your board on https://www.adafruit.com/ or the
board's variant.h at
https://github.com/adafruit/Adafruit_nRF52_Arduino/tree/master/variants
Based on the original Blink, the code of which is in the public domain.
*/

#include <Arduino.h>

// Create the two SoftwareTimers we're going to use.
SoftwareTimer bluetimer, redtimer;

void setup()
{
// LED_RED & LED_BLUE are already initialized as outputs.

// Initialize the timers at .75 seconds for blue, and 1 second for red.
bluetimer.begin(750, bluetoggle);
redtimer.begin(1000, redtoggle);
bluetimer.start();
redtimer.start();
suspendLoop();
}

void loop(void) { }

/**
* Toggle led1 every 1 second
*/
void redtoggle(TimerHandle_t _handle)
{
digitalToggle(LED_RED); // Toggle LED
}

/**
* Toggle led1 every 0.5 second
*/
void bluetoggle(TimerHandle_t _handle)
{
digitalToggle(LED_BLUE); // Toggle LED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
Blink_loopless
Created 2019-05-03
by Pol Van Aubel
Turns on an LED on for one second, then off for one second, repeatedly.
This example is based on the original Blink sketch. The Adafruit nRF52* boards
are more advanced than "normal" Arduinos, however, and come with freeRTOS
and proper task management. This blink sketch demonstrates the use of
SoftwareTimer to perform a repetitive delayed task without using the energy
that a continuously running loop() function has.
AdaFruit's nRF-boards have multiple on-board LEDs you can control. These are
addressable using the LED_RED, LED_BLUE, LED_BUILTIN and LED_CONN definitions.
These take care to use the correct LED pin regardless of which board is used.
If you want to know what pin the on-board LED is connected to on your model,
check the Technical Specs of your board on https://www.adafruit.com/ or the
board's variant.h at
https://github.com/adafruit/Adafruit_nRF52_Arduino/tree/master/variants
Based on the original Blink, the code of which is in the public domain.
*/

// Create a SoftwareTimer that will drive our LED.
SoftwareTimer led_timer;

// the setup function runs once when you press reset or power the board
void setup(void) {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_RED, OUTPUT);

// Set up a repeating timer that fires every half second (500ms) to toggle the LED.
led_timer.begin(500, timer_callback);
led_timer.start();

// Since loop() is empty, suspend its task so that the system never runs it
// and can go to sleep properly.
suspendLoop();
}

void timer_callback(TimerHandle_t _handle) {
digitalToggle(LED_RED); // toggle the red LED
}

// the loop function is empty and should never run.
void loop(void) { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
Button_interrupt
Created 2019-05-04
by Pol Van Aubel
Uses an interrupt to listen for a button input. On the nRF52840, it can listen
on pin 7 to the UserSW. On other boards, an external switch needs to be provided.
The Adafruit nRF52* boards are more advanced than "normal" Arduinos and come
with freeRTOS, interrupts available on every pin, and proper task management.
This button sketch demonstrates the use of interrupts to act on an input such
as a switch, without having to poll it in a continuously scanning loop() function.
Even though human button presses are slow, and therefore unlikely to be missed
using a scanning loop, the interrupt technique allows the board to go into sleep
mode while waiting for an input, thereby saving a lot of energy.
AdaFruit's nRF-boards have multiple on-board LEDs you can control. These are
addressable using the LED_RED, LED_BLUE, LED_BUILTIN and LED_CONN definitions.
These take care to use the correct LED pin regardless of which board is used.
If you want to know what pin the on-board LED is connected to on your model,
check the Technical Specs of your board on https://www.adafruit.com/ or the
board's variant.h at
https://github.com/adafruit/Adafruit_nRF52_Arduino/tree/master/variants
This code is licensed under CC0, effectively putting it in the Public Domain
where possible.
*/

// On the nRF52840, pin 7 is the UserSW/DFU switch.
int interruptPin = 7;

// the setup function runs once when you press reset or power the board
void setup(void) {
pinMode(LED_RED, OUTPUT);
pinMode(interruptPin, INPUT_PULLUP); // Configure the switch pin as an input with internal pull-up register enabled.

// Configure it to call switch_callback if the switch pin transitions from HIGH to LOW, i.e. when it is pressed.
// On the nRF52840, RISING and CHANGE are also valid options. Depending on your platform, LOW may also be available.
attachInterrupt(interruptPin, switch_callback, FALLING);

// Since loop() is empty, suspend its task so that the system never runs it
// and can go to sleep properly.
suspendLoop();
}

// the loop function is empty and should never run.
void loop(void) { }

// The switch_callback function is the function we attached to the interrupt on line 42.
// This is known as an Interrupt Service Routine, or ISR.
// It gets run every time the interrupt fires, in the interrupt context. Nothing else
// happens while this is running. Therefore, it should be fast and simple.
// In particular, it should never block, nor do Serial communication or use the Bluefruit API.
// There are a few freeRTOS functions specifically designed to be run from an ISR.
// If you need a longer ISR, other examples show how to do this.
void switch_callback(void) {
digitalToggle(LED_RED); // toggle the red LED

// You may notice that the LED does not reliably turn on on one press of the switch,
// then off on the next. This is likely due to a phenomenon physical switches exhibit called "bounce".
// Effectively this means that the pin goes through multiple transitions of HIGH to LOW,
// back to HIGH, to LOW again, for a single press of the button.
// Some debouncing techniques using interrupts are shown in another example.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
Button_interrupt_ada_callback
Created 2019-05-04
by Pol Van Aubel
Uses an interrupt to listen for a button input. On the nRF52840, it can listen
on pin 7 to the UserSW. On other boards, an external switch needs to be provided.
The Adafruit nRF52* boards are more advanced than "normal" Arduinos and come
with freeRTOS, interrupts available on every pin, and proper task management.
This button sketch demonstrates the use of interrupts to act on an input such
as a switch, without having to poll it in a continuously scanning loop() function.
Even though human button presses are slow, and therefore unlikely to be missed
using a scanning loop, the interrupt technique allows the board to go into sleep
mode while waiting for an input, thereby saving a lot of energy.
However, because interrupts need to be fast, you must not do Serial communication
or long tasks directly in an ISR. This is where deferred ISRs come in. However,
deferred ISRs cannot have a single time-critical component. By using
ada_callback_fromISR, you can run a fast time-critical component in a normal
ISR, and then queue up a callback to process the rest.
AdaFruit's nRF-boards have multiple on-board LEDs you can control. These are
addressable using the LED_RED, LED_BLUE, LED_BUILTIN and LED_CONN definitions.
These take care to use the correct LED pin regardless of which board is used.
If you want to know what pin the on-board LED is connected to on your model,
check the Technical Specs of your board on https://www.adafruit.com/ or the
board's variant.h at
https://github.com/adafruit/Adafruit_nRF52_Arduino/tree/master/variants
This code is licensed under CC0, effectively putting it in the Public Domain
where possible.
*/

// On the nRF52840, pin 7 is the UserSW/DFU switch.
int interruptPin = 7;

// Because this variable is modified inside a non-deferred ISR, and used outside it,
// it must be declared volatile to ensure that the most recently written value is
// always used.
volatile int numinterrupts;

// the setup function runs once when you press reset or power the board
void setup(void) {
Serial.begin(115200); // Not required on the nRF52840 with native USB.

while (!Serial) { // Stalls the nRF52840 until the USB serial console has been opened on the host PC.
delay(10);
}
Serial.println("Press the button!");
Serial.flush(); // Because this sketch does nothing else, it will go to sleep rather than send the
// entire Serial buffer to the host PC. So, use Serial.flush() to make sure it does that
// before allowing it to sleep.

pinMode(LED_RED, OUTPUT);
pinMode(interruptPin, INPUT_PULLUP); // Configure the switch pin as an input with internal pull-up register enabled.

// Configure it to call switch_callback if the switch pin transitions from HIGH to LOW, i.e. when it is pressed.
// On the nRF52840, RISING and CHANGE are also valid options. Depending on your platform, LOW may also be available.
attachInterrupt(interruptPin, switch_isr, FALLING);

// Since loop() is empty, suspend its task so that the system never runs it
// and can go to sleep properly.
suspendLoop();
}

// the loop function is empty and should never run.
void loop(void) { }

// The switch_isr function is the function we attached to the interrupt on line 63.
// This is known as an Interrupt Service Routine, or ISR.
// It gets run every time the interrupt fires, in the interrupt context. Nothing else
// happens while this is running. Therefore, it should be fast and simple.
// In particular, it should never block, nor do Serial communication or use the Bluefruit API.
// There are a few freeRTOS functions specifically designed to be run from an ISR.
void switch_isr(void) {
// Do short time-critical processing (setting flags, queueing up tasks, etc) here.
digitalToggle(LED_RED);

++numinterrupts;
ada_callback_fromISR(NULL, 0, switch_isr_callback); // Queue up a task with no extra variables and no arguments.
// Every single interrupt is serviced, because internally, a
// queue is used.
}


// The switch_isr_callback function is the function we set up as the ada_callback on line 84.
// It gets run once for every interrupt that switch_isr ran for, but with no guarantees about
// when that happens. However, the advantage is you have the full range of Serial, Bluefruit,
// and freeRTOS API to use now.
// Bear in mind, however, that if this function takes longer to run than the average time between
// interrupts, at some point your ada_callback_queue will overflow. For that scenario, consider
// batch-processing interrupts by using direct-to-task notifications.
void switch_isr_callback(void) {
Serial.println("Interrupt serviced!");
Serial.print("The number of interrupts received at this point was ");
Serial.println(numinterrupts);
Serial.flush(); // Because this sketch does nothing else, it will go to sleep rather than send the
// entire Serial buffer to the host PC. However, here, Serial.flush() doesn't seem
// to work either. Maybe something to do with the task context?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
Button_interrupt_deferred
Created 2019-05-04
by Pol Van Aubel
Uses an interrupt to listen for a button input. On the nRF52840, it can listen
on pin 7 to the UserSW. On other boards, an external switch needs to be provided.
The Adafruit nRF52* boards are more advanced than "normal" Arduinos and come
with freeRTOS, interrupts available on every pin, and proper task management.
This button sketch demonstrates the use of interrupts to act on an input such
as a switch, without having to poll it in a continuously scanning loop() function.
Even though human button presses are slow, and therefore unlikely to be missed
using a scanning loop, the interrupt technique allows the board to go into sleep
mode while waiting for an input, thereby saving a lot of energy.
However, because interrupts need to be fast, you must not do Serial communication
or long tasks directly in an ISR. This is where deferred ISRs come in.
AdaFruit's nRF-boards have multiple on-board LEDs you can control. These are
addressable using the LED_RED, LED_BLUE, LED_BUILTIN and LED_CONN definitions.
These take care to use the correct LED pin regardless of which board is used.
If you want to know what pin the on-board LED is connected to on your model,
check the Technical Specs of your board on https://www.adafruit.com/ or the
board's variant.h at
https://github.com/adafruit/Adafruit_nRF52_Arduino/tree/master/variants
This code is licensed under CC0, effectively putting it in the Public Domain
where possible.
*/

// On the nRF52840, pin 7 is the UserSW/DFU switch.
int interruptPin = 7;

// the setup function runs once when you press reset or power the board
void setup(void) {
Serial.begin(115200); // Not required on the nRF52840 with native USB.

while (!Serial) { // Stalls the nRF52840 until the USB serial console has been opened on the host PC.
delay(10);
}
Serial.println("Press the button!");

pinMode(LED_RED, OUTPUT);
pinMode(interruptPin, INPUT_PULLUP); // Configure the switch pin as an input with internal pull-up register enabled.

// Configure it to call switch_callback if the switch pin transitions from HIGH to LOW, i.e. when it is pressed.
// On the nRF52840, RISING and CHANGE are also valid options. Depending on your platform, LOW may also be available.
// The ISR_DEFERRED flag ensures that the interrupt is serviced by a scheduled operating system task.
// What this means is that the ISR is allowed to perform slow functionality, including Serial communication,
// using the Bluefruit API, and the entire freeRTOS API rather than just the *FromISR functions.
// Use this if your interrupt servicing is not time-critical.
attachInterrupt(interruptPin, switch_deferred, ISR_DEFERRED | FALLING);

// Since loop() is empty, suspend its task so that the system never runs it
// and can go to sleep properly.
suspendLoop();
}

// the loop function is empty and should never run.
void loop(void) { }

// The switch_deferred function is the function we attached to the interrupt on line 56.
// This is known as an Interrupt Service Routine, or ISR.
// Because it is a deferred ISR, it is not guaranteed to run immediately after the interrupt
// happens. However, the advantage is you have the full range of Serial, Bluefruit,
// and freeRTOS API to use now.
void switch_deferred(void) {
digitalToggle(LED_RED);
Serial.println("Interrupt serviced!");
Serial.flush(); // Because this sketch does nothing else, it will go to sleep rather than send the
// entire Serial buffer to the host PC. So, use Serial.flush() to make sure it does that
// before allowing it to sleep.

// You may notice that you receive multiple messages per press of the switch,
// This is likely due to a phenomenon physical switches exhibit called "bounce".
// Effectively this means that the pin goes through multiple transitions of HIGH to LOW,
// back to HIGH, to LOW again, for a single press of the button.
// Some debouncing techniques using interrupts are shown in another example.
}
Loading

0 comments on commit 63b43fc

Please sign in to comment.