Accelerometer Interrupt Sample
Introduction
The Accelerometer Interrupt sample detects board orientation using the LIS2DH12 accelerometer's data-ready interrupt. Unlike the polling-based Accelerometer sample, this sample uses hardware interrupts and only prints when the orientation changes. Detected orientations include Face Up, Face Down, Landscape, Portrait, and their inverted variants. This sample is compatible with the following boards:
Download
Accelerometer InterruptNCS v3.2.1Project configuration
The application is configured in the prj.conf file:
CONFIG_NEWLIB_LIBC=y
CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y
CONFIG_GPIO=y
CONFIG_I2C=y
CONFIG_SENSOR=y
CONFIG_LIS2DH=y
CONFIG_LIS2DH_ACCEL_RANGE_2G=y
CONFIG_LIS2DH_ODR_4=y
CONFIG_LIS2DH_TRIGGER_GLOBAL_THREAD=y
CONFIG_NEWLIB_LIBC and CONFIG_NEWLIB_LIBC_FLOAT_PRINTF enable the Newlib C library with floating-point printf support.
CONFIG_GPIO enables the GPIO driver, needed for the interrupt pin. CONFIG_I2C enables the I2C bus driver for communicating with the accelerometer.
CONFIG_SENSOR and CONFIG_LIS2DH enable the sensor subsystem and the LIS2DH driver.
CONFIG_LIS2DH_ACCEL_RANGE_2G sets the measuring range to +/- 2G. A lower range provides higher sensitivity for detecting orientation.
CONFIG_LIS2DH_ODR_4 sets the output data rate to 25 Hz.
CONFIG_LIS2DH_TRIGGER_GLOBAL_THREAD enables interrupt-based triggering using Zephyr's global system work queue. This is what allows the sample to react to accelerometer events without polling.
The sysbuild.conf file enables MCUBoot so the sample can be flashed over USB:
SB_CONFIG_BOOTLOADER_MCUBOOT=y
Code explanation
Orientation detection
The sample defines seven orientations as an enum:
typedef enum {
ORIENTATION_UNKNOWN,
ORIENTATION_FACE_UP,
ORIENTATION_FACE_DOWN,
ORIENTATION_LANDSCAPE,
ORIENTATION_LANDSCAPE_INVERTED,
ORIENTATION_PORTRAIT_LEFT,
ORIENTATION_PORTRAIT_RIGHT,
} orientation_t;
The calculate_orientation function determines the orientation by checking which axis exceeds a threshold of approximately 0.7g (7.0 m/s²):
static orientation_t calculate_orientation(double x, double y, double z)
{
const double threshold = 7.0;
if (z > threshold) {
return ORIENTATION_FACE_UP;
} else if (z < -threshold) {
return ORIENTATION_FACE_DOWN;
} else if (y > threshold) {
return ORIENTATION_LANDSCAPE;
} else if (y < -threshold) {
return ORIENTATION_LANDSCAPE_INVERTED;
} else if (x > threshold) {
return ORIENTATION_PORTRAIT_LEFT;
} else if (x < -threshold) {
return ORIENTATION_PORTRAIT_RIGHT;
}
return ORIENTATION_UNKNOWN;
}
Trigger handler
When the accelerometer detects motion, it fires an interrupt that invokes accel_trigger_handler. The handler reads the acceleration values, calculates the orientation, and prints it only if it has changed:
static void accel_trigger_handler(const struct device *dev,
const struct sensor_trigger *trig)
{
struct sensor_value val_x, val_y, val_z;
double x, y, z;
orientation_t new_orientation;
if (sensor_sample_fetch(dev) < 0) {
printk("Failed to fetch sample\n");
return;
}
sensor_channel_get(dev, SENSOR_CHAN_ACCEL_X, &val_x);
sensor_channel_get(dev, SENSOR_CHAN_ACCEL_Y, &val_y);
sensor_channel_get(dev, SENSOR_CHAN_ACCEL_Z, &val_z);
x = sensor_value_to_double(&val_x);
y = sensor_value_to_double(&val_y);
z = sensor_value_to_double(&val_z);
new_orientation = calculate_orientation(x, y, z);
if (new_orientation != ORIENTATION_UNKNOWN &&
new_orientation != current_orientation) {
current_orientation = new_orientation;
printk("Orientation: %s\n", orientation_to_string(current_orientation));
printf(" (X: %.2f, Y: %.2f, Z: %.2f m/s^2)\n", x, y, z);
}
}
Trigger configuration
In main, the accelerometer is initialized and the motion detection trigger is configured. The slope threshold and duration control how much motion is needed to fire an interrupt:
accel = DEVICE_DT_GET(ACCEL_NODE);
if (!device_is_ready(accel)) {
printk("Error: accelerometer device not ready\n");
return -1;
}
struct sensor_value slope_th = { .val1 = 2, .val2 = 0 };
struct sensor_value slope_dur = { .val1 = 1, .val2 = 0 };
sensor_attr_set(accel, SENSOR_CHAN_ACCEL_XYZ,
SENSOR_ATTR_SLOPE_TH, &slope_th);
sensor_attr_set(accel, SENSOR_CHAN_ACCEL_XYZ,
SENSOR_ATTR_SLOPE_DUR, &slope_dur);
trig.type = SENSOR_TRIG_DELTA;
trig.chan = SENSOR_CHAN_ACCEL_XYZ;
err = sensor_trigger_set(accel, &trig, accel_trigger_handler);
if (err) {
printk("Failed to set trigger: %d\n", err);
return -1;
}
SENSOR_TRIG_DELTA is an any-motion trigger — it fires when the acceleration change exceeds the configured slope threshold. After the trigger is set, the main loop simply sleeps with k_sleep(K_FOREVER) since all work is handled in the interrupt callback.