Callback
A callback is a user provided function that a user may pass to an API. The callback allows the API to execute the user’s code in its own context.
This is the technical reference for callbacks. You should read the Callbacks section of the Platform Overview first for deeper insight into their use.
Calling callbacks
Callbacks overload the function call operator, so you can call a Callback like you would a normal function:
void run_timer_event(Callback<void(float)> on_timer) {
on_timer(1.0f);
}
The only thing to watch out for is that the Callback type has an empty Callback, just like a null function pointer. Default initialized callbacks are empty and assert if you call them. If a callback may be empty, you need to check if it is empty before calling it.
void run_timer_event(Callback<void(float)> on_timer) {
if (on_timer) {
on_timer(1.0f);
}
}
You can reset Callbacks to empty by assigning nullptr
.
The Callback class is what’s known in C++ as a “Concrete Type”. That is, the Callback class is lightweight enough to be passed around like an int, pointer or other primitive type.
Configuration
Two system configuration options permit trade-offs between image size and flexibility of the Callback class.
-
platform.callback-nontrivial
controls whether Callbacks can store non-trivially-copyable function objects. Having this setting off saves significant code size, as it makes Callback itself trivially-copyable, so all Callback assignments and copies are simpler. Almost all users use Callback only with function pointers, member function pointers or lambdas with trivial captures, so this setting can almost always be set to false. A compile-time error will indicate that this setting needs to be set to true if any code attempts to assign a non-trivially-copyable object to a Callback. -
platform.callback-comparable
controls whether two Callbacks can be compared to each other. The ability to support this comparison increases code size whenever a Callback is assigned, whether or not any such comparison occurs. Turning the option off removes the comparison operator and saves a little image size.
Tip: See the documentation of the Arm Mbed configuration system for more details about mbed_app.json
.
Callback class reference
Serial passthrough example with callbacks
/*
* Copyright (c) 2020 Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*/
#include "mbed.h"
static UnbufferedSerial pc(USBTX, USBRX);
static UnbufferedSerial uart(D1, D0);
DigitalOut led1(LED1);
DigitalOut led4(LED4);
char *pc2uart = new char[1];
char *uart2pc = new char[1];
void uart_recv()
{
led1 = !led1;
while (uart.readable()) {
uart.read(uart2pc, sizeof(uart2pc));
pc.write(uart2pc, sizeof(uart2pc));
}
}
void pc_recv()
{
while (pc.readable()) {
led4 = !led4;
pc.read(pc2uart, sizeof(pc2uart));
uart.write(pc2uart, sizeof(pc2uart));
}
}
int main()
{
pc.attach(&pc_recv, UnbufferedSerial::RxIrq);
uart.attach(&uart_recv, UnbufferedSerial::RxIrq);
while (1) {
sleep();
}
}
Thread example with callbacks
The Callback API provides a convenient way to pass arguments to spawned threads. This example uses a C function pointer in the Callback.
/*
* Copyright (c) 2020 Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*/
#include "mbed.h"
Thread thread;
DigitalOut led1(LED1);
volatile bool running = true;
// Blink function toggles the led in a long running loop
void blink(DigitalOut *led)
{
while (running) {
*led = !*led;
ThisThread::sleep_for(1000);
}
}
// Spawns a thread to run blink for 5 seconds
int main()
{
thread.start(callback(blink, &led1));
ThisThread::sleep_for(5000);
running = false;
thread.join();
}
Sonar example
Here is an example that uses everything discussed in the introduction to callbacks document in the form of a minimal Sonar class. This example uses a C++ class and method in the Callback.
/*
* Copyright (c) 2006-2020 Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*/
#include "mbed.h"
/**
* Sonar class for the HC-SR04
*/
class Sonar {
DigitalOut trigger;
InterruptIn echo; // calls a callback when a pin changes
Timer timer;
Timeout timeout; // calls a callback once when a timeout expires
Ticker ticker; // calls a callback repeatedly with a timeout
int32_t begin;
int32_t end;
float distance;
public:
/**
* Sonar constructor
* Creates a sonar object on a set of provided pins
* @param trigger_pin Pin used to trigger reads from the sonar device
* @param echo_pin Pin used to receive the sonar's distance measurement
*/
Sonar(PinName trigger_pin, PinName echo_pin) : trigger(trigger_pin), echo(echo_pin)
{
trigger = 0;
distance = -1;
echo.rise(callback(this, &Sonar::echo_in)); // Attach handler to the rising interruptIn edge
echo.fall(callback(this, &Sonar::echo_fall)); // Attach handler to the falling interruptIn edge
}
/**
* Start the background task to trigger sonar reads every 100ms
*/
void start(void)
{
ticker.attach(callback(this, &Sonar::background_read), 0.01f);
}
/**
* Stop the background task that triggers sonar reads
*/
void stop(void)
{
ticker.detach();
}
/**
* Interrupt pin rising edge interrupt handler. Reset and start timer
*/
void echo_in(void)
{
timer.reset();
timer.start();
begin = timer.read_us();
}
/**
* Interrupt pin falling edge interrupt handler. Read and disengage timer.
* Calculate raw echo pulse length
*/
void echo_fall(void)
{
end = timer.read_us();
timer.stop();
distance = end - begin;
}
/**
* Wrapper function to set the trigger pin low. Callbacks cannot take in both object and argument pointers.
* See use of this function in background_read().
*/
void trigger_toggle(void)
{
trigger = 0;
}
/**
* Background callback task attached to the periodic ticker that kicks off sonar reads
*/
void background_read(void)
{
trigger = 1;
timeout.attach(callback(this, &Sonar::trigger_toggle), 10.0e-6);
}
/**
* Public read function that returns the scaled distance result in cm
*/
float read(void)
{
return distance / 58.0f;
}
};
int main()
{
// Create sonar object on pins D5 and D6
Sonar sonar(D5, D6);
// Begin background thread sonar acquires
sonar.start();
while (1) {
ThisThread::sleep_for(100);
// Periodically print results from sonar object
printf("%f\r\n", sonar.read());
}
}