Interrupt Events

The ADXL343 includes two configurable HW interrupt pins, where one or more of the following events can be 'mapped' to one of the interrupt pins:

  • Overrun: The overrun bit is set when new data replaces unread data. This can be useful in situations where it's important to know if any data samples were 'skipped', such as in sensor fusion algorithms that depend on a specific sample rate for the best possible results.
  • Watermark: This can be used with register 0x38 (FIFO_CTL) to trigger an interrupt when a user-specified number of samples are available in the internal FIFO buffer.
  • Freefall: The FREE_FALL bit is set when acceleration of less than the value stored in the THRESH_FF register (Address 0x28) is experienced for more time than is specified in the TIME_FF register (Address 0x29) on all axes (logical AND). The FREE_FALL interrupt differs from the inactivity interrupt as follows: all axes always participate and are logically AND’ed, the timer period is much smaller (1.28 sec maximum), and the mode of operation is always dc-coupled.
  • Inactivity: The inactivity bit is set when acceleration of less than the value stored in the THRESH_INACT register (Address 0x25) is experienced for more time than is specified in the TIME_INACT register (Address 0x26) on all participating axes, as set by the ACT_INACT_CTL register (Address 0x27). The maximum value for TIME_INACT is 255 sec.
  • Activity: The activity bit is set when acceleration greater than the value stored in the THRESH_ACT register (Address 0x24) is experienced on any participating axis, set by the ACT_INACT_CTL register (Address 0x27).
  • Double Tap: The DOUBLE_TAP bit is set when two acceleration events that are greater than the value in the THRESH_TAP register (Address 0x1D) occur for less time than is specified in the DUR register (Address 0x21), with the second tap starting after the time specified by the latent register (Address 0x22) but within the time specified in the window register (Address 0x23). See the Tap Detection section for more details.
  • Single Tap: The SINGLE_TAP bit is set when a single acceleration event that is greater than the value in the THRESH_TAP register (Address 0x1D) occurs for less time than is specified in the DUR register (Address 0x21).
  • Data Ready: The DATA_READY bit is set when new data is available and is cleared when no new data is available.

Mapping Interrupts to INT1/INT2

The first step to enable interrupts in your sketch is to 'map' one or more interrupt functions to either the INT1 or INT2 pins. This can be accomplished with the following function in Adafruit_ADXL343:

bool mapInterrupts(int_config cfg);

`cfg` is an 8-bit bit-field where setting the individual interrupt bit to 1 will cause the interrupt to be mapped to INT2, and setting the interrupt bit to 0 will map if to INT1. The following code shows how this works in practice (based on the `g_int_config_map` variable in the interrupts.ino example sketch):

/* Map specific interrupts to one of the two INT pins. */
g_int_config_map.bits.overrun    = ADXL343_INT1;
g_int_config_map.bits.watermark  = ADXL343_INT1;
g_int_config_map.bits.freefall   = ADXL343_INT1;
g_int_config_map.bits.inactivity = ADXL343_INT1;
g_int_config_map.bits.activity   = ADXL343_INT1;
g_int_config_map.bits.double_tap = ADXL343_INT1;
g_int_config_map.bits.single_tap = ADXL343_INT1;
g_int_config_map.bits.data_ready = ADXL343_INT2;
accel.mapInterrupts(g_int_config_map);

Enabling Interrupts

After mapping specific interrupt events to either INT1 or INT2, you need to 'enable' the interrupt via a second function:

bool enableInterrupts(int_config cfg);

An example of enabling the OVERRUN and DATA READY interrupts is shown below:

/* Enable interrupts on the accelerometer. */
g_int_config_enabled.bits.overrun    = true;    /* Set the INT1 */
g_int_config_enabled.bits.watermark  = false;
g_int_config_enabled.bits.freefall   = false;
g_int_config_enabled.bits.inactivity = false;
g_int_config_enabled.bits.activity   = false;
g_int_config_enabled.bits.double_tap = false;
g_int_config_enabled.bits.single_tap = false;
g_int_config_enabled.bits.data_ready = true;    /* Set to INT2 */
accel.enableInterrupts(g_int_config_enabled);

Connecting ADXL343 INT pins to the MCU

In order to 'detect' the interrupt generated by the ADXL, you also need to connect the INT1 and/or INT2 pins on the ADXL to an appropriate interrupt-enabled input pin on your MCU.

The interrupt input on the MCU needs to have the following pin characteristics:

  • Must have support for interrupt mode (if you want to automatically fire an interrupt service routine when the ADXL's INT pins are triggered)
  • Must be configured as an input
  • Must be 'attached' to an interrupt service routine, which is the function that will be called when a RISING edge is detected on the MCU's interrupt input.
  • You will also need a pull down resistor on the interrupt pins

Some sample code of setting these pins up properly is shown below (assumed an Adafruit Feather M0 Basic, see the documentation for pin selection on other boards):

/** The input pins to enable the interrupt on, connected to INT1 and INT2 on the ADXL. */
#define INPUT_PIN_INT1   (5) // Uno = (2)
#define INPUT_PIN_INT2   (6) // Uno = (3)

...

/** Interrupt service routine for INT1 events. */
void int1_isr(void)
{
   /* TODO: Do something! */
}

/** Interrupt service routine for INT2 events. */
void int2_isr(void)
{
   /* TODO: Do something! */
}

...

/* Attach interrupt inputs on the MCU. */
pinMode(LED_BUILTIN, OUTPUT);
pinMode(INPUT_PIN_INT1, INPUT);
pinMode(INPUT_PIN_INT2, INPUT);
attachInterrupt(digitalPinToInterrupt(INPUT_PIN_INT1), int1_isr, RISING);
attachInterrupt(digitalPinToInterrupt(INPUT_PIN_INT2), int2_isr, RISING);

Complete Example

To see how all of these pieces fit together, you can see the code for the interrupts example that is part of the standard Adafruit driver, shown below for convenience sake.

This examples enables two interrupt events on two different pins, and tracks the number of times those interrupt handlers are fired. The main loop of the program continually checks if a new interrupt event was detected, and display some details on the interrupt source when an event it detected.

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_ADXL343.h>

/* Assign a unique ID to this sensor at the same time */
Adafruit_ADXL343 accel = Adafruit_ADXL343(12345);

/** The input pins to enable the interrupt on, connected to INT1 and INT2 on the ADXL. */
#define INPUT_PIN_INT1   (5) // Uno = (2)
#define INPUT_PIN_INT2   (6) // Uno = (3)

/**
 * This struct is used to count the number of times that specific interrutps
 * have been fired by the ADXL and detected on the MCU. They will increment
 * by one for each event associated with the specified interrupt 'bit'.
 */
struct adxl_int_stats {
    uint32_t data_ready;
    uint32_t single_tap;
    uint32_t double_tap;
    uint32_t activity;
    uint32_t inactivity;
    uint32_t freefall;
    uint32_t watermark;
    uint32_t overrun;
    uint32_t total;
};

/** Global stats block, incremented inside the interrupt handler(s). */
struct adxl_int_stats g_int_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };

/** Global counter to track the numbers of unused interrupts fired. */
uint32_t g_ints_fired = 0;

/** Global variable to determine which interrupt(s) are enabled on the ADXL343. */
int_config g_int_config_enabled = { 0 };

/** Global variables to determine which INT pin interrupt(s) are mapped to on the ADXL343. */
int_config g_int_config_map = { 0 };

/** Interrupt service routine for INT1 events. */
void int1_isr(void)
{
    /* By default, this sketch routes the OVERRUN interrupt to INT1. */
    g_int_stats.overrun++;
    g_int_stats.total++;
    g_ints_fired++;

    /* TODO: Toggle an LED! */
}

/** Interrupt service routine for INT2 events. */
void int2_isr(void)
{
    /* By default, this sketch routes the DATA_READY interrupt to INT2. */
    g_int_stats.data_ready++;
    g_int_stats.total++;
    g_ints_fired++;

    /* TODO: Toggle an LED! */
}

/** Configures the HW interrupts on the ADXL343 and the target MCU. */
void config_interrupts(void)
{
  /* NOTE: Once an interrupt fires on the ADXL you can read a register
   *  to know the source of the interrupt, but since this would likely
   *  happen in the 'interrupt context' performing an I2C read is a bad
   *  idea since it will block the device from handling other interrupts
   *  in a timely manner.
   *
   *  The best approach is to try to make use of only two interrupts on
   *  two different interrupt pins, so that when an interrupt fires, based
   *  on the 'isr' function that is called, you already know the int source.
   */

  /* Attach interrupt inputs on the MCU. */
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(INPUT_PIN_INT1, INPUT);
  pinMode(INPUT_PIN_INT2, INPUT);
  attachInterrupt(digitalPinToInterrupt(INPUT_PIN_INT1), int1_isr, RISING);
  attachInterrupt(digitalPinToInterrupt(INPUT_PIN_INT2), int2_isr, RISING);

  /* Enable interrupts on the accelerometer. */
  g_int_config_enabled.bits.overrun    = true;    /* Set the INT1 */
  g_int_config_enabled.bits.watermark  = false;
  g_int_config_enabled.bits.freefall   = false;
  g_int_config_enabled.bits.inactivity = false;
  g_int_config_enabled.bits.activity   = false;
  g_int_config_enabled.bits.double_tap = false;
  g_int_config_enabled.bits.single_tap = false;
  g_int_config_enabled.bits.data_ready = true;    /* Set to INT2 */
  accel.enableInterrupts(g_int_config_enabled);

  /* Map specific interrupts to one of the two INT pins. */
  g_int_config_map.bits.overrun    = ADXL343_INT1;
  g_int_config_map.bits.watermark  = ADXL343_INT1;
  g_int_config_map.bits.freefall   = ADXL343_INT1;
  g_int_config_map.bits.inactivity = ADXL343_INT1;
  g_int_config_map.bits.activity   = ADXL343_INT1;
  g_int_config_map.bits.double_tap = ADXL343_INT1;
  g_int_config_map.bits.single_tap = ADXL343_INT1;
  g_int_config_map.bits.data_ready = ADXL343_INT2;
  accel.mapInterrupts(g_int_config_map);
}

void setup(void)
{
  Serial.begin(9600);
  while (!Serial);
  Serial.println("ADXL343 Interrupt Tester"); Serial.println("");

  /* Initialise the sensor */
  if(!accel.begin())
  {
    /* There was a problem detecting the ADXL343 ... check your connections */
    Serial.println("Ooops, no ADXL343 detected ... Check your wiring!");
    while(1);
  }

  /* Set the range to whatever is appropriate for your project */
  accel.setRange(ADXL343_RANGE_16_G);
  // displaySetRange(ADXL343_RANGE_8_G);
  // displaySetRange(ADXL343_RANGE_4_G);
  // displaySetRange(ADXL343_RANGE_2_G);


  /* Configure the HW interrupts. */
  config_interrupts();

  Serial.println("ADXL343 init complete. Waiting for INT activity.");
}

void loop(void)
{
  /* Get a new sensor event */
  sensors_event_t event;
  accel.getEvent(&event);
  delay(10);

  while (g_ints_fired) {
      Serial.println("INT detected!");
      Serial.print("\tOVERRUN Count:    "); Serial.println(g_int_stats.overrun, DEC);
      Serial.print("\tDATA_READY Count: "); Serial.println(g_int_stats.data_ready, DEC);

      /* Decrement the unhandled int counter. */
      g_ints_fired--;
  }
}

Single Tap Example

A slightly simplified example that detects single taps is shown below as well:

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_ADXL343.h>

/* Assign a unique ID to this sensor at the same time */
Adafruit_ADXL343 accel = Adafruit_ADXL343(12345);

/** The input pin to enable the interrupt on, connected to INT1 on the ADXL. */
#define INPUT_PIN_INT1   (5) // SAMD21/SAMD51 = 5 for interrupt pin

uint32_t g_tap_count = 0;
int_config g_int_config_enabled = { 0 };
int_config g_int_config_map = { 0 };

/** Interrupt service routine for INT1 events. This will be called when a single tap is detected. */
void int1_isr(void)
{
    g_tap_count++;
}

void setup(void)
{
  Serial.begin(9600);
  while (!Serial);
  Serial.println("ADXL343 Single Tap INT Tester"); Serial.println("");

  /* Initialise the sensor */
  if(!accel.begin())
  {
    /* There was a problem detecting the ADXL343 ... check your connections */
    Serial.println("Ooops, no ADXL343 detected ... Check your wiring!");
    while(1);
  }

  /* Set the range to whatever is appropriate for your project */
  accel.setRange(ADXL343_RANGE_16_G);

  /* Configure the HW interrupts. */
  pinMode(INPUT_PIN_INT1, INPUT);
  attachInterrupt(digitalPinToInterrupt(INPUT_PIN_INT1), int1_isr, RISING);

  /* Enable single tap interrupts on the accelerometer. */
  g_int_config_enabled.bits.single_tap = true;
  accel.enableInterrupts(g_int_config_enabled);

  /* Map single tap interrupts to INT1 pin. */
  g_int_config_map.bits.single_tap = ADXL343_INT1;
  accel.mapInterrupts(g_int_config_map);

  /* Reset tap counter. */
  g_tap_count = 0;

  Serial.println("ADXL343 init complete. Waiting for single tap INT activity.");
}

void loop(void)
{
  /* Get a new sensor event */
  /* Reading data clears the interrupts. */
  sensors_event_t event;
  accel.getEvent(&event);
  delay(10);

  while (g_tap_count) {
      Serial.println("Single tap detected!");
      /* Clear the interrupt as a side-effect of reading the interrupt source register.. */
      accel.checkInterrupts();
      /* Decrement the local interrupt counter. */
      g_tap_count--;
  }
}

Running this singletap demo and tapping the device gently should give the following output:

This guide was first published on Feb 24, 2019. It was last updated on Apr 15, 2024.

This page (HW Interrupts) was last updated on Apr 15, 2024.

Text editor powered by tinymce.