From c78e8cbd275ebff7cfe40eeb89575f86ac27b8a6 Mon Sep 17 00:00:00 2001
From: Kaspar Schleiser <kaspar@schleiser.de>
Date: Fri, 9 Jun 2017 17:40:44 +0200
Subject: [PATCH] drivers: mma8x5x: add motion detection interrupt support

---
 drivers/include/mma8x5x.h | 26 +++++++++++
 drivers/mma8x5x/mma8x5x.c | 93 ++++++++++++++++++++++++++++++++-------
 2 files changed, 102 insertions(+), 17 deletions(-)

diff --git a/drivers/include/mma8x5x.h b/drivers/include/mma8x5x.h
index e8ae2c97d0..7b91b3c048 100644
--- a/drivers/include/mma8x5x.h
+++ b/drivers/include/mma8x5x.h
@@ -178,6 +178,32 @@ int mma8x5x_is_ready(mma8x5x_t *dev);
  */
 void mma8x5x_read(mma8x5x_t *dev, mma8x5x_data_t *data);
 
+/**
+ * @brief   Configure motion detection interrupt
+ *
+ * User needs to configure MCU side of the selected int pin.  mma8x5x will set
+ * the pin to low on interrupt.  Before another interrupt can occur, the
+ * current interrupt must be acknowledged using @p mma8x5x_ack_int().
+ *
+ * @param[in]   dev         device descriptor of accelerometer
+ * @param[in]   int_pin     select mma8x5x int pin (1 or 2)
+ * @param[in]   threshold   motion detection threshold (see datasheet)
+ */
+void mma8x5x_set_motiondetect(mma8x5x_t *dev, uint8_t int_pin, uint8_t threshold);
+
+/**
+ * @brief   Acknowledge motion detection interrupt
+ *
+ * Acknowledges (clears) a motion detection interrupt.
+ * See @ref mma8x5x_set_motiondetect().
+ *
+ * @warning: this does incur an I2C write, thus should not be done from within
+ *           the ISR.
+ *
+ * @param[in]   dev         device descriptor of accelerometer
+ */
+void mma8x5x_ack_int(mma8x5x_t *dev);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/drivers/mma8x5x/mma8x5x.c b/drivers/mma8x5x/mma8x5x.c
index 2ea16c78c7..193353727d 100644
--- a/drivers/mma8x5x/mma8x5x.c
+++ b/drivers/mma8x5x/mma8x5x.c
@@ -105,47 +105,106 @@ void mma8x5x_set_user_offset(mma8x5x_t *dev, int8_t x, int8_t y, int8_t z)
     i2c_release(BUS);
 }
 
-void mma8x5x_set_active(mma8x5x_t *dev)
+static int _get_reg(mma8x5x_t *dev, uint8_t addr)
 {
     uint8_t reg;
 
     assert(dev);
 
-    DEBUG("[mma8x5x] put device to active mode\n");
+    DEBUG("[mma8x5x] getting reg 0x%02x\n", (unsigned)addr);
 
     i2c_acquire(BUS);
-    i2c_read_reg(BUS, ADDR, MMA8X5X_CTRL_REG1, &reg);
-    reg |= MMA8X5X_CTRL_REG1_ACTIVE;
-    i2c_write_reg(BUS, ADDR, MMA8X5X_CTRL_REG1, reg);
+    i2c_read_reg(BUS, ADDR, addr, &reg);
     i2c_release(BUS);
+
+    DEBUG("[mma8x5x] reg 0x%02x=0x%02x\n", (unsigned)addr, (unsigned)reg);
+    return reg;
 }
 
-void mma8x5x_set_standby(mma8x5x_t *dev)
+static void _reg_setbits(mma8x5x_t *dev, uint8_t reg, uint8_t val)
 {
-    uint8_t reg;
+    uint8_t tmp;
 
     assert(dev);
 
-    DEBUG("[mma8x5x] put device to standby mode\n");
-
     i2c_acquire(BUS);
-    i2c_read_reg(BUS, ADDR, MMA8X5X_CTRL_REG1, &reg);
-    reg &= ~MMA8X5X_CTRL_REG1_ACTIVE;
-    i2c_write_reg(BUS, ADDR, MMA8X5X_CTRL_REG1, reg);
+    i2c_read_reg(BUS, ADDR, reg, &tmp);
+    DEBUG("[mma8x5x] 0x%02x: 0x%02x | 0x%02x = 0x%02x\n",
+            (unsigned)reg, (unsigned)tmp, (unsigned)val, (unsigned) tmp | val);
+    tmp |= val;
+    i2c_write_reg(BUS, ADDR, reg, tmp);
     i2c_release(BUS);
 }
 
-int mma8x5x_is_ready(mma8x5x_t *dev)
+static void _reg_clearbits(mma8x5x_t *dev, uint8_t reg, uint8_t val)
 {
-    uint8_t reg;
+    uint8_t tmp;
 
     assert(dev);
 
-    DEBUG("[mma8x5x] checking for new available data\n");
-
     i2c_acquire(BUS);
-    i2c_read_reg(BUS, ADDR, MMA8X5X_STATUS, &reg);
+    i2c_read_reg(BUS, ADDR, reg, &tmp);
+    DEBUG("[mma8x5x] 0x%02x: 0x%02x &= ~0x%02x = 0x%02x\n",
+            (unsigned)reg, (unsigned)tmp, (unsigned)val, (unsigned) tmp & ~val);
+    tmp &= ~val;
+    i2c_write_reg(BUS, ADDR, reg, tmp);
     i2c_release(BUS);
+}
+
+void mma8x5x_set_active(mma8x5x_t *dev)
+{
+    DEBUG("[mma8x5x] put device to active mode\n");
+    _reg_setbits(dev, MMA8X5X_CTRL_REG1, MMA8X5X_CTRL_REG1_ACTIVE);
+}
+
+void mma8x5x_set_standby(mma8x5x_t *dev)
+{
+    DEBUG("[mma8x5x] put device to standby mode\n");
+    _reg_clearbits(dev, MMA8X5X_CTRL_REG1, MMA8X5X_CTRL_REG1_ACTIVE);
+}
+
+void mma8x5x_set_motiondetect(mma8x5x_t *dev, uint8_t int_pin, uint8_t threshold)
+{
+    DEBUG("[mma8x5x] put device to motion detect mode (ELE=1, OAE=1)\n");
+    assert(int_pin < 3);
+
+    mma8x5x_set_standby(dev);
+
+    _reg_setbits(dev, MMA8X5X_FF_MT_CFG, \
+            MMA8X5X_FF_MT_CFG_XEFE | \
+            MMA8X5X_FF_MT_CFG_YEFE | \
+            MMA8X5X_FF_MT_CFG_ZEFE | \
+            MMA8X5X_FF_MT_CFG_OAE | MMA8X5X_FF_MT_CFG_ELE);
+
+    switch(int_pin) {
+        case 0:
+            _reg_clearbits(dev, MMA8X5X_CTRL_REG4, MMA8X5X_CTRL_REG4_INT_EN_FF_MT);
+            goto out;
+        case 1:
+            _reg_setbits(dev, MMA8X5X_CTRL_REG5, MMA8X5X_CTRL_REG5_INT_CFG_FF_MT);
+            break;
+        case 2:
+            _reg_clearbits(dev, MMA8X5X_CTRL_REG5, MMA8X5X_CTRL_REG5_INT_CFG_FF_MT);
+            break;
+    }
+
+    _reg_setbits(dev, MMA8X5X_FF_MT_THS, threshold & 0x7f);
+    _reg_setbits(dev, MMA8X5X_CTRL_REG4, MMA8X5X_CTRL_REG4_INT_EN_FF_MT);
+
+out:
+    mma8x5x_set_active(dev);
+}
+
+void mma8x5x_ack_int(mma8x5x_t *dev)
+{
+    _get_reg(dev, MMA8X5X_FF_MT_SRC);
+}
+
+int mma8x5x_is_ready(mma8x5x_t *dev)
+{
+    DEBUG("[mma8x5x] checking for new available data\n");
+
+    uint8_t reg = _get_reg(dev, MMA8X5X_STATUS);
 
     if (reg & MMA8X5X_STATUS_ZYXDR) {
         return MMA8X5X_DATA_READY;
-- 
GitLab