--- /dev/null
+/*
+ * Copyright © 2018 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <ao.h>
+#include "ao_adxl375.h"
+
+#define DEBUG_LOW 1
+#define DEBUG_HIGH 2
+
+#define DEBUG 0
+
+#if DEBUG
+#define PRINTD(l, ...) do { if (DEBUG & (l)) { printf ("\r%5u %s: ", ao_tick_count, __func__); printf(__VA_ARGS__); flush(); } } while(0)
+#else
+#define PRINTD(l,...)
+#endif
+
+struct ao_adxl375_sample ao_adxl375_current;
+
+static void
+ao_adxl375_start(void) {
+ ao_spi_get_bit(AO_ADXL375_CS_PORT,
+ AO_ADXL375_CS_PIN,
+ AO_ADXL375_SPI_INDEX,
+ AO_ADXL375_SPI_SPEED);
+}
+
+static void
+ao_adxl375_stop(void) {
+ ao_spi_put_bit(AO_ADXL375_CS_PORT,
+ AO_ADXL375_CS_PIN,
+ AO_ADXL375_SPI_INDEX);
+}
+
+static uint8_t
+ao_adxl375_reg_read(uint8_t addr)
+{
+ uint8_t d[2];
+
+ d[0] = addr | AO_ADXL375_READ;
+ ao_adxl375_start();
+ ao_spi_duplex(d, d, 2, AO_ADXL375_SPI_INDEX);
+ ao_adxl375_stop();
+
+ PRINTD(DEBUG_LOW, "read %x = %x\n", addr, d);
+
+ return d[1];
+}
+
+static void
+ao_adxl375_reg_write(uint8_t addr, uint8_t value)
+{
+ uint8_t d[2];
+
+ PRINTD(DEBUG_LOW, "write %x %x\n", addr, value);
+ d[0] = addr;
+ d[1] = value;
+ ao_adxl375_start();
+ ao_spi_send(d, 2, AO_ADXL375_SPI_INDEX);
+ ao_adxl375_stop();
+
+#if DEBUG & DEBUG_LOW
+ d[0] = addr | AO_ADXL375_READ
+ d[1] = 0;
+ ao_adxl375_start();
+ ao_spi_duplex(d, d, 2, AO_ADXL375_SPI_INDEX);
+ ao_adxl375_stop();
+ PRINTD(DEBUG_LOW, "readback %x %x\n", d[0], d[1]);
+#endif
+}
+
+static void
+ao_adxl375_value(struct ao_adxl375_sample *value)
+{
+ uint8_t d[7];
+
+ d[0] = AO_ADXL375_DATAX0 | AO_ADXL375_READ | AO_ADXL375_MULTI_BYTE;
+ ao_adxl375_start();
+ ao_spi_duplex(d, d, 7, AO_ADXL375_SPI_INDEX);
+ ao_adxl375_stop();
+ memcpy(value, &d[1], 6);
+}
+
+struct ao_adxl375_total {
+ int32_t x;
+ int32_t y;
+ int32_t z;
+};
+
+#define AO_ADXL375_SELF_TEST_SAMPLES 16
+#define AO_ADXL375_SELF_TEST_SETTLE 6
+
+#define MIN_LSB_G 18.4
+#define MAX_LSB_G 22.6
+#define SELF_TEST_MIN_G 6.0
+#define SELF_TEST_MAX_G 6.8
+
+#define MIN_SELF_TEST ((int32_t) (MIN_LSB_G * SELF_TEST_MIN_G * AO_ADXL375_SELF_TEST_SAMPLES + 0.5))
+#define MAX_SELF_TEST ((int32_t) (MAX_LSB_G * SELF_TEST_MAX_G * AO_ADXL375_SELF_TEST_SAMPLES + 0.5))
+
+static const int32_t min_self_test = MIN_SELF_TEST;
+static const int32_t max_self_test = MAX_SELF_TEST;
+
+static void
+ao_adxl375_total_value(struct ao_adxl375_total *total, int samples)
+{
+ struct ao_adxl375_sample value;
+
+ *total = (struct ao_adxl375_total) { 0, 0, 0 };
+ for (int i = 0; i < samples; i++) {
+ ao_adxl375_value(&value);
+ total->x += value.x;
+ total->y += value.y;
+ total->z += value.z;
+ ao_delay(AO_MS_TO_TICKS(10));
+ }
+}
+
+#define AO_ADXL375_DATA_FORMAT_SETTINGS(self_test) ( \
+ AO_ADXL375_DATA_FORMAT_FIXED | \
+ (self_test << AO_ADXL375_DATA_FORMAT_SELF_TEST) | \
+ (AO_ADXL375_DATA_FORMAT_SPI_4_WIRE << AO_ADXL375_DATA_FORMAT_SPI_4_WIRE) | \
+ (0 << AO_ADXL375_DATA_FORMAT_INT_INVERT) | \
+ (0 << AO_ADXL375_DATA_FORMAT_JUSTIFY))
+
+static int32_t self_test_value;
+
+static void
+ao_adxl375_setup(void)
+{
+ /* Get the device into 4-wire SPI mode before proceeding */
+ ao_adxl375_reg_write(AO_ADXL375_DATA_FORMAT,
+ AO_ADXL375_DATA_FORMAT_SETTINGS(0));
+
+
+ uint8_t devid = ao_adxl375_reg_read(AO_ADXL375_DEVID);
+ if (devid != AO_ADXL375_DEVID_ID)
+ ao_sensor_errors = 1;
+
+ /* Set the data rate */
+ ao_adxl375_reg_write(AO_ADXL375_BW_RATE,
+ (0 << AO_ADXL375_BW_RATE_LOW_POWER) |
+ (AO_ADXL375_BW_RATE_RATE_200 << AO_ADXL375_BW_RATE_RATE));
+
+ /* Set the offsets all to zero */
+ ao_adxl375_reg_write(AO_ADXL375_OFSX, 0);
+ ao_adxl375_reg_write(AO_ADXL375_OFSY, 0);
+ ao_adxl375_reg_write(AO_ADXL375_OFSZ, 0);
+
+ /* Clear interrupts */
+ ao_adxl375_reg_write(AO_ADXL375_INT_ENABLE, 0);
+
+ /* Configure FIFO (disable) */
+ ao_adxl375_reg_write(AO_ADXL375_FIFO_CTL,
+ (AO_ADXL375_FIFO_CTL_FIFO_MODE_BYPASS << AO_ADXL375_FIFO_CTL_FIFO_MODE) |
+ (0 << AO_ADXL375_FIFO_CTL_TRIGGER) |
+ (0 << AO_ADXL375_FIFO_CTL_SAMPLES));
+
+ /* Place part in measurement mode */
+ ao_adxl375_reg_write(AO_ADXL375_POWER_CTL,
+ (0 << AO_ADXL375_POWER_CTL_LINK) |
+ (0 << AO_ADXL375_POWER_CTL_AUTO_SLEEP) |
+ (1 << AO_ADXL375_POWER_CTL_MEASURE) |
+ (0 << AO_ADXL375_POWER_CTL_SLEEP) |
+ (AO_ADXL375_POWER_CTL_WAKEUP_8 << AO_ADXL375_POWER_CTL_WAKEUP));
+
+ (void) ao_adxl375_total_value;
+ /* Perform self-test */
+
+#define AO_ADXL375_SELF_TEST_SAMPLES 16
+#define AO_ADXL375_SELF_TEST_SETTLE 6
+
+ struct ao_adxl375_total self_test_off, self_test_on;
+
+ /* Discard some samples to let it settle down */
+ ao_adxl375_total_value(&self_test_off, AO_ADXL375_SELF_TEST_SETTLE);
+
+ /* Get regular values */
+ ao_adxl375_total_value(&self_test_off, AO_ADXL375_SELF_TEST_SAMPLES);
+
+ /* Turn on self test */
+ ao_adxl375_reg_write(AO_ADXL375_DATA_FORMAT,
+ AO_ADXL375_DATA_FORMAT_SETTINGS(1));
+
+ /* Discard at least 4 samples to let the device settle */
+ ao_adxl375_total_value(&self_test_on, AO_ADXL375_SELF_TEST_SETTLE);
+
+ /* Read self test samples */
+ ao_adxl375_total_value(&self_test_on, AO_ADXL375_SELF_TEST_SAMPLES);
+
+ /* Reset back to normal mode */
+
+ ao_adxl375_reg_write(AO_ADXL375_DATA_FORMAT,
+ AO_ADXL375_DATA_FORMAT_SETTINGS(0));
+
+ /* Verify Z axis value is in range */
+
+ int32_t z_change = self_test_on.z - self_test_off.z;
+
+ self_test_value = z_change;
+
+ if (z_change < min_self_test || max_self_test < z_change)
+ ao_sensor_errors = 1;
+
+ /* Discard some samples to let it settle down */
+ ao_adxl375_total_value(&self_test_off, AO_ADXL375_SELF_TEST_SETTLE);
+}
+
+static int adxl375_count;
+
+static void
+ao_adxl375(void)
+{
+ ao_adxl375_setup();
+ for (;;) {
+ ao_adxl375_value(&ao_adxl375_current);
+ ++adxl375_count;
+ ao_arch_critical(
+ AO_DATA_PRESENT(AO_DATA_ADXL375);
+ AO_DATA_WAIT();
+ );
+ }
+}
+
+static struct ao_task ao_adxl375_task;
+
+static void
+ao_adxl375_dump(void)
+{
+ printf ("ADXL375 value %d %d %d count %d self test %d min %d max %d\n",
+ ao_adxl375_current.x,
+ ao_adxl375_current.y,
+ ao_adxl375_current.z,
+ adxl375_count,
+ self_test_value,
+ MIN_SELF_TEST,
+ MAX_SELF_TEST);
+}
+
+const struct ao_cmds ao_adxl375_cmds[] = {
+ { ao_adxl375_dump, "A\0Display ADXL375 data" },
+ { 0, NULL },
+};
+
+void
+ao_adxl375_init(void)
+{
+ ao_cmd_register(ao_adxl375_cmds);
+ ao_spi_init_cs(AO_ADXL375_CS_PORT, (1 << AO_ADXL375_CS_PIN));
+
+ ao_add_task(&ao_adxl375_task, ao_adxl375, "adxl375");
+}
--- /dev/null
+/*
+ * Copyright © 2018 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_ADXL375_H_
+#define _AO_ADXL375_H_
+
+#define AO_ADXL375_READ 0x80 /* read mode */
+#define AO_ADXL375_MULTI_BYTE 0x40 /* multi-byte mode */
+
+#define AO_ADXL375_DEVID 0x00
+#define AO_ADXL375_DEVID_ID 0xe5
+#define AO_ADXL375_THRESH_SHOCK 0x1d
+#define AO_ADXL375_OFSX 0x1e
+#define AO_ADXL375_OFSY 0x1f
+#define AO_ADXL375_OFSZ 0x20
+#define AO_ADXL375_DUR 0x21
+#define AO_ADXL375_LATENT 0x22
+#define AO_ADXL375_WINDOW 0x23
+#define AO_ADXL375_THRESH_ACT 0x24
+#define AO_ADXL375_THRESH_INACT 0x25
+#define AO_ADXL375_TIME_INACT 0x26
+#define AO_ADXL375_ACT_INACT_CTL 0x27
+#define AO_ADXL375_SHOCK_AXES 0x2a
+#define AO_ADXL375_ACT_SHOCK_STATUS 0x2b
+#define AO_ADXL375_BW_RATE 0x2c
+
+#define AO_ADXL375_BW_RATE_LOW_POWER 4
+#define AO_ADXL375_BW_RATE_RATE 0
+#define AO_ADXL375_BW_RATE_RATE_3200 0xf
+#define AO_ADXL375_BW_RATE_RATE_1600 0xe
+#define AO_ADXL375_BW_RATE_RATE_800 0xd
+#define AO_ADXL375_BW_RATE_RATE_400 0xc
+#define AO_ADXL375_BW_RATE_RATE_200 0xb
+#define AO_ADXL375_BW_RATE_RATE_100 0xa
+#define AO_ADXL375_BW_RATE_RATE_50 0x9
+#define AO_ADXL375_BW_RATE_RATE_25 0x8
+#define AO_ADXL375_BW_RATE_RATE_12_5 0x7
+#define AO_ADXL375_BW_RATE_RATE_6_25 0x6
+#define AO_ADXL375_BW_RATE_RATE_3_13 0x5
+#define AO_ADXL375_BW_RATE_RATE_1_56 0x4
+#define AO_ADXL375_BW_RATE_RATE_0_78 0x3
+#define AO_ADXL375_BW_RATE_RATE_0_39 0x2
+#define AO_ADXL375_BW_RATE_RATE_0_20 0x1
+#define AO_ADXL375_BW_RATE_RATE_0_10 0x0
+
+#define AO_ADXL375_POWER_CTL 0x2d
+#define AO_ADXL375_POWER_CTL_LINK 5
+#define AO_ADXL375_POWER_CTL_AUTO_SLEEP 4
+#define AO_ADXL375_POWER_CTL_MEASURE 3
+#define AO_ADXL375_POWER_CTL_SLEEP 2
+#define AO_ADXL375_POWER_CTL_WAKEUP 0
+#define AO_ADXL375_POWER_CTL_WAKEUP_8 0
+#define AO_ADXL375_POWER_CTL_WAKEUP_4 1
+#define AO_ADXL375_POWER_CTL_WAKEUP_2 2
+#define AO_ADXL375_POWER_CTL_WAKEUP_1 3
+
+#define AO_ADXL375_INT_ENABLE 0x2e
+#define AO_ADXL375_INT_MAP 0x2f
+#define AO_ADXL375_INT_SOURCE 0x30
+#define AO_ADXL375_DATA_FORMAT 0x31
+# define AO_ADXL375_DATA_FORMAT_FIXED 0x0b /* these bits must be set to 1 */
+# define AO_ADXL375_DATA_FORMAT_SELF_TEST 7
+# define AO_ADXL375_DATA_FORMAT_SPI 6
+# define AO_ADXL375_DATA_FORMAT_SPI_3_WIRE 0
+# define AO_ADXL375_DATA_FORMAT_SPI_4_WIRE 1
+# define AO_ADXL375_DATA_FORMAT_INT_INVERT 5
+# define AO_ADXL375_DATA_FORMAT_JUSTIFY 2
+#define AO_ADXL375_DATAX0 0x32
+#define AO_ADXL375_DATAX1 0x33
+#define AO_ADXL375_DATAY0 0x34
+#define AO_ADXL375_DATAY1 0x35
+#define AO_ADXL375_DATAZ0 0x36
+#define AO_ADXL375_DATAZ1 0x37
+#define AO_ADXL375_FIFO_CTL 0x38
+#define AO_ADXL375_FIFO_CTL_FIFO_MODE 6
+#define AO_ADXL375_FIFO_CTL_FIFO_MODE_BYPASS 0
+#define AO_ADXL375_FIFO_CTL_FIFO_MODE_FIFO 1
+#define AO_ADXL375_FIFO_CTL_FIFO_MODE_STREAM 2
+#define AO_ADXL375_FIFO_CTL_FIFO_MODE_TRIGGER 3
+#define AO_ADXL375_FIFO_CTL_TRIGGER 5
+#define AO_ADXL375_FIFO_CTL_SAMPLES 0
+
+#define AO_ADXL375_FIFO_STATUS 0x39
+
+struct ao_adxl375_sample {
+ int16_t x;
+ int16_t y;
+ int16_t z;
+};
+
+extern struct ao_adxl375_sample ao_adxl375_current;
+
+void
+ao_adxl375_init(void);
+
+#endif /* _AO_ADXL375_H_ */