aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2020-07-30 01:32:17 -0400
committerKevin O'Connor <kevin@koconnor.net>2020-09-05 09:11:04 -0400
commit3405095f0af639ececb74409ac66923ca6bfe49b (patch)
tree6056016d4582eaf82f62a8f500a3734c3fe81e37 /src
parent2b2b610cffaa14167d24dd6b18ef5beefb5c2b02 (diff)
downloadkutter-3405095f0af639ececb74409ac66923ca6bfe49b.tar.gz
kutter-3405095f0af639ececb74409ac66923ca6bfe49b.tar.xz
kutter-3405095f0af639ececb74409ac66923ca6bfe49b.zip
adxl345: Add support for adxl345 accelerometer
Add support for taking measurements from an adxl345 accelerometer via SPI interface. Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
Diffstat (limited to 'src')
-rw-r--r--src/Makefile2
-rw-r--r--src/sensor_adxl345.c179
2 files changed, 180 insertions, 1 deletions
diff --git a/src/Makefile b/src/Makefile
index 92be64e2..98c9a1c1 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -7,4 +7,4 @@ src-$(CONFIG_HAVE_GPIO_SPI) += spicmds.c thermocouple.c
src-$(CONFIG_HAVE_GPIO_I2C) += i2ccmds.c
src-$(CONFIG_HAVE_GPIO_HARD_PWM) += pwmcmds.c
src-$(CONFIG_HAVE_GPIO_BITBANGING) += lcd_st7920.c lcd_hd44780.c buttons.c \
- tmcuart.c spi_software.c neopixel.c
+ tmcuart.c spi_software.c neopixel.c sensor_adxl345.c
diff --git a/src/sensor_adxl345.c b/src/sensor_adxl345.c
new file mode 100644
index 00000000..c76ab7db
--- /dev/null
+++ b/src/sensor_adxl345.c
@@ -0,0 +1,179 @@
+// Support for gathering acceleration data from ADXL345 chip
+//
+// Copyright (C) 2020 Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include <string.h> // memcpy
+#include "board/irq.h" // irq_disable
+#include "board/misc.h" // timer_read_time
+#include "basecmd.h" // oid_alloc
+#include "command.h" // DECL_COMMAND
+#include "sched.h" // DECL_TASK
+#include "spicmds.h" // spidev_transfer
+
+struct adxl345 {
+ struct timer timer;
+ uint32_t rest_ticks, stop_time;
+ struct spidev_s *spi;
+ uint16_t sequence, limit_count;
+ uint8_t flags, data_count;
+ uint8_t data[48];
+};
+
+enum {
+ AX_HAVE_START = 1<<0, AX_RUNNING = 1<<1, AX_PENDING = 1<<2,
+};
+
+static struct task_wake adxl345_wake;
+
+static uint_fast8_t
+adxl345_event(struct timer *timer)
+{
+ struct adxl345 *ax = container_of(timer, struct adxl345, timer);
+ ax->flags |= AX_PENDING;
+ sched_wake_task(&adxl345_wake);
+ return SF_DONE;
+}
+
+void
+command_config_adxl345(uint32_t *args)
+{
+ struct adxl345 *ax = oid_alloc(args[0], command_config_adxl345
+ , sizeof(*ax));
+ ax->timer.func = adxl345_event;
+ ax->spi = spidev_oid_lookup(args[1]);
+}
+DECL_COMMAND(command_config_adxl345, "config_adxl345 oid=%c spi_oid=%c");
+
+// Report local measurement buffer
+static void
+adxl_report(struct adxl345 *ax, uint8_t oid)
+{
+ sendf("adxl345_data oid=%c sequence=%hu data=%*s"
+ , oid, ax->sequence, ax->data_count, ax->data);
+ ax->data_count = 0;
+ ax->sequence++;
+}
+
+// Chip registers
+#define AR_POWER_CTL 0x2D
+#define AR_DATAX0 0x32
+#define AR_FIFO_STATUS 0x39
+#define AM_READ 0x80
+#define AM_MULTI 0x40
+
+// Query accelerometer data
+static void
+adxl_query(struct adxl345 *ax, uint8_t oid)
+{
+ uint8_t msg[9] = { AR_DATAX0 | AM_READ | AM_MULTI, 0, 0, 0, 0, 0, 0, 0, 0 };
+ spidev_transfer(ax->spi, 1, sizeof(msg), msg);
+ memcpy(&ax->data[ax->data_count], &msg[1], 6);
+ ax->data_count += 6;
+ if (ax->data_count + 6 > ARRAY_SIZE(ax->data))
+ adxl_report(ax, oid);
+ uint_fast8_t fifo_status = msg[8] & ~0x80; // Ignore trigger bit
+ if (fifo_status >= 31 && ax->limit_count != 0xffff)
+ ax->limit_count++;
+ if (fifo_status > 1 && fifo_status <= 32) {
+ // More data in fifo - wake this task again
+ sched_wake_task(&adxl345_wake);
+ } else if (ax->flags & AX_RUNNING) {
+ // Sleep until next check time
+ sched_del_timer(&ax->timer);
+ ax->flags &= ~AX_PENDING;
+ irq_disable();
+ ax->timer.waketime = timer_read_time() + ax->rest_ticks;
+ sched_add_timer(&ax->timer);
+ irq_enable();
+ }
+}
+
+// Startup measurements
+static void
+adxl_start(struct adxl345 *ax, uint8_t oid)
+{
+ sched_del_timer(&ax->timer);
+ ax->flags = AX_RUNNING;
+ uint8_t msg[2] = { AR_POWER_CTL, 0x08 };
+ uint32_t start1_time = timer_read_time();
+ spidev_transfer(ax->spi, 0, sizeof(msg), msg);
+ irq_disable();
+ uint32_t start2_time = timer_read_time();
+ ax->timer.waketime = start2_time + ax->rest_ticks;
+ sched_add_timer(&ax->timer);
+ irq_enable();
+ sendf("adxl345_start oid=%c start1_time=%u start2_time=%u"
+ , oid, start1_time, start2_time);
+}
+
+// End measurements
+static void
+adxl_stop(struct adxl345 *ax, uint8_t oid)
+{
+ // Disable measurements
+ sched_del_timer(&ax->timer);
+ ax->flags = 0;
+ uint8_t msg[2] = { AR_POWER_CTL, 0x00 };
+ uint32_t end1_time = timer_read_time();
+ spidev_transfer(ax->spi, 0, sizeof(msg), msg);
+ uint32_t end2_time = timer_read_time();
+ // Drain any measurements still in fifo
+ uint_fast8_t i;
+ for (i=0; i<33; i++) {
+ msg[0] = AR_FIFO_STATUS | AM_READ;
+ msg[1] = 0;
+ spidev_transfer(ax->spi, 1, sizeof(msg), msg);
+ if (!(msg[1] & 0x3f))
+ break;
+ adxl_query(ax, oid);
+ }
+ // Report final data
+ if (ax->data_count)
+ adxl_report(ax, oid);
+ sendf("adxl345_end oid=%c end1_time=%u end2_time=%u"
+ " limit_count=%hu sequence=%hu"
+ , oid, end1_time, end2_time, ax->limit_count, ax->sequence);
+}
+
+void
+command_query_adxl345(uint32_t *args)
+{
+ struct adxl345 *ax = oid_lookup(args[0], command_config_adxl345);
+
+ if (!args[2]) {
+ // End measurements
+ adxl_stop(ax, args[0]);
+ return;
+ }
+ // Start new measurements query
+ sched_del_timer(&ax->timer);
+ ax->timer.waketime = args[1];
+ ax->rest_ticks = args[2];
+ ax->flags = AX_HAVE_START;
+ ax->sequence = ax->limit_count = 0;
+ ax->data_count = 0;
+ sched_add_timer(&ax->timer);
+}
+DECL_COMMAND(command_query_adxl345,
+ "query_adxl345 oid=%c clock=%u rest_ticks=%u");
+
+void
+adxl345_task(void)
+{
+ if (!sched_check_wake(&adxl345_wake))
+ return;
+ uint8_t oid;
+ struct adxl345 *ax;
+ foreach_oid(oid, ax, command_config_adxl345) {
+ uint_fast8_t flags = ax->flags;
+ if (!(flags & AX_PENDING))
+ continue;
+ if (flags & AX_HAVE_START)
+ adxl_start(ax, oid);
+ else
+ adxl_query(ax, oid);
+ }
+}
+DECL_TASK(adxl345_task);