aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Hansel <github@paulhansel.com>2025-03-04 14:12:26 -0800
committerGitHub <noreply@github.com>2025-03-04 17:12:26 -0500
commit75a10bfcafc0655e36e5ecf01c3f2033a14ef2c7 (patch)
treed297ab7eee95a3500d28b6cd1532209c10543cde /src
parent730e5951bc453d7c08b9b2a066479f2c0cd25842 (diff)
downloadkutter-75a10bfcafc0655e36e5ecf01c3f2033a14ef2c7.tar.gz
kutter-75a10bfcafc0655e36e5ecf01c3f2033a14ef2c7.tar.xz
kutter-75a10bfcafc0655e36e5ecf01c3f2033a14ef2c7.zip
icm20948: Add support for ICM20948 accelerometer (#6756)
Signed-off-by: Paul Hansel <github@paulhansel.com>
Diffstat (limited to 'src')
-rw-r--r--src/Kconfig9
-rw-r--r--src/Makefile1
-rw-r--r--src/sensor_icm20948.c184
3 files changed, 193 insertions, 1 deletions
diff --git a/src/Kconfig b/src/Kconfig
index 16857272..9923ed7f 100644
--- a/src/Kconfig
+++ b/src/Kconfig
@@ -112,6 +112,10 @@ config WANT_MPU9250
bool
depends on HAVE_GPIO_I2C
default y
+config WANT_ICM20948
+ bool
+ depends on HAVE_GPIO_I2C
+ default y
config WANT_HX71X
bool
depends on WANT_GPIO_BITBANGING
@@ -138,7 +142,7 @@ config WANT_SOFTWARE_SPI
default y
config NEED_SENSOR_BULK
bool
- depends on WANT_ADXL345 || WANT_LIS2DW || WANT_MPU9250 \
+ depends on WANT_ADXL345 || WANT_LIS2DW || WANT_MPU9250 || WANT_ICM20948 \
|| WANT_HX71X || WANT_ADS1220 || WANT_LDC1612 || WANT_SENSOR_ANGLE
default y
menu "Optional features (to reduce code size)"
@@ -161,6 +165,9 @@ config WANT_LIS2DW
config WANT_MPU9250
bool "Support MPU accelerometers"
depends on HAVE_GPIO_I2C
+config WANT_ICM20948
+ bool "Support ICM20948 accelerometer"
+ depends on HAVE_GPIO_I2C
config WANT_HX71X
bool "Support HX711 and HX717 ADC chips"
depends on WANT_GPIO_BITBANGING
diff --git a/src/Makefile b/src/Makefile
index a0ca02b9..a7343ffc 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -18,6 +18,7 @@ src-$(CONFIG_WANT_THERMOCOUPLE) += thermocouple.c
src-$(CONFIG_WANT_ADXL345) += sensor_adxl345.c
src-$(CONFIG_WANT_LIS2DW) += sensor_lis2dw.c
src-$(CONFIG_WANT_MPU9250) += sensor_mpu9250.c
+src-$(CONFIG_WANT_ICM20948) += sensor_icm20948.c
src-$(CONFIG_WANT_HX71X) += sensor_hx71x.c
src-$(CONFIG_WANT_ADS1220) += sensor_ads1220.c
src-$(CONFIG_WANT_LDC1612) += sensor_ldc1612.c
diff --git a/src/sensor_icm20948.c b/src/sensor_icm20948.c
new file mode 100644
index 00000000..eb73a52c
--- /dev/null
+++ b/src/sensor_icm20948.c
@@ -0,0 +1,184 @@
+// Support for gathering acceleration data from icm20948 chip
+//
+// Copyright (C) 2024 Paul Hansel <github@paulhansel.com>
+// Copyright (C) 2023 Matthew Swabey <matthew@swabey.org>
+// Copyright (C) 2022 Harry Beyel <harry3b9@gmail.com>
+// Copyright (C) 2020-2023 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 "sensor_bulk.h" // sensor_bulk_report
+#include "i2ccmds.h" // i2cdev_oid_lookup
+
+// Chip registers
+#define AR_FIFO_COUNT_H 0x70
+#define AR_FIFO 0x72
+#define AR_INT_STATUS 0x1B // INT_STATUS_2
+
+#define FIFO_OVERFLOW_INT 0x0F // from datasheet
+
+#define BYTES_PER_FIFO_ENTRY 6
+#define BYTES_PER_BLOCK 48
+
+struct icm20948 {
+ struct timer timer;
+ uint32_t rest_ticks;
+ struct i2cdev_s *i2c;
+ uint16_t fifo_max, fifo_pkts_bytes;
+ uint8_t flags;
+ struct sensor_bulk sb;
+};
+
+enum {
+ AX_PENDING = 1<<0,
+};
+
+static struct task_wake icm20948_wake;
+
+// Event handler that wakes icm20948_task() periodically
+static uint_fast8_t
+icm20948_event(struct timer *timer)
+{
+ struct icm20948 *ax = container_of(timer, struct icm20948, timer);
+ ax->flags |= AX_PENDING;
+ sched_wake_task(&icm20948_wake);
+ return SF_DONE;
+}
+
+void
+command_config_icm20948(uint32_t *args)
+{
+ struct icm20948 *ic = oid_alloc(args[0], command_config_icm20948
+ , sizeof(*ic));
+ ic->timer.func = icm20948_event;
+ ic->i2c = i2cdev_oid_lookup(args[1]);
+}
+DECL_COMMAND(command_config_icm20948, "config_icm20948 oid=%c i2c_oid=%c");
+
+// Helper code to reschedule the icm20948_event() timer
+static void
+ic20948_reschedule_timer(struct icm20948 *ic)
+{
+ irq_disable();
+ ic->timer.waketime = timer_read_time() + ic->rest_ticks;
+ sched_add_timer(&ic->timer);
+ irq_enable();
+}
+
+static void
+read_mpu(struct i2cdev_s *i2c, uint8_t reg_len, uint8_t *reg
+ , uint8_t read_len, uint8_t *read)
+{
+ int ret = i2c_dev_read(i2c, reg_len, reg, read_len, read);
+ i2c_shutdown_on_err(ret);
+}
+
+// Reads the fifo byte count from the device.
+static uint16_t
+get_fifo_status(struct icm20948 *ic)
+{
+ uint8_t reg[] = {AR_FIFO_COUNT_H};
+ uint8_t msg[2];
+ read_mpu(ic->i2c, sizeof(reg), reg, sizeof(msg), msg);
+ uint16_t fifo_bytes = ((msg[0] & 0x1f) << 8) | msg[1];
+ if (fifo_bytes > ic->fifo_max)
+ ic->fifo_max = fifo_bytes;
+ return fifo_bytes;
+}
+
+// Query accelerometer data
+static void
+ic20948_query(struct icm20948 *ic, uint8_t oid)
+{
+ // If not enough bytes to fill report read MPU FIFO's fill
+ if (ic->fifo_pkts_bytes < BYTES_PER_BLOCK)
+ ic->fifo_pkts_bytes = get_fifo_status(ic);
+
+ // If we have enough bytes to fill the buffer do it and send report
+ if (ic->fifo_pkts_bytes >= BYTES_PER_BLOCK) {
+ uint8_t reg = AR_FIFO;
+ read_mpu(ic->i2c, sizeof(reg), &reg, BYTES_PER_BLOCK, &ic->sb.data[0]);
+ ic->sb.data_count = BYTES_PER_BLOCK;
+ ic->fifo_pkts_bytes -= BYTES_PER_BLOCK;
+ sensor_bulk_report(&ic->sb, oid);
+ }
+
+ // If we have enough bytes remaining to fill another report wake again
+ // otherwise schedule timed wakeup
+ if (ic->fifo_pkts_bytes >= BYTES_PER_BLOCK) {
+ sched_wake_task(&icm20948_wake);
+ } else {
+ ic->flags &= ~AX_PENDING;
+ ic20948_reschedule_timer(ic);
+ }
+}
+
+void
+command_query_icm20948(uint32_t *args)
+{
+ struct icm20948 *ic = oid_lookup(args[0], command_config_icm20948);
+
+ sched_del_timer(&ic->timer);
+ ic->flags = 0;
+ if (!args[1]) {
+ // End measurements
+
+ // Uncomment and rebuilt to check for FIFO overruns when tuning
+ //output("mpu9240 fifo_max=%u", ic->fifo_max);
+ return;
+ }
+
+ // Start new measurements query
+ ic->rest_ticks = args[1];
+ sensor_bulk_reset(&ic->sb);
+ ic->fifo_max = 0;
+ ic->fifo_pkts_bytes = 0;
+ ic20948_reschedule_timer(ic);
+}
+DECL_COMMAND(command_query_icm20948, "query_icm20948 oid=%c rest_ticks=%u");
+
+void
+command_query_icm20948_status(uint32_t *args)
+{
+ struct icm20948 *ic = oid_lookup(args[0], command_config_icm20948);
+
+ // Detect if a FIFO overrun occurred
+ uint8_t int_reg[] = {AR_INT_STATUS};
+ uint8_t int_msg;
+ read_mpu(ic->i2c, sizeof(int_reg), int_reg, sizeof(int_msg), &int_msg);
+ if (int_msg & FIFO_OVERFLOW_INT)
+ ic->sb.possible_overflows++;
+
+ // Read latest FIFO count (with precise timing)
+ uint8_t reg[] = {AR_FIFO_COUNT_H};
+ uint8_t msg[2];
+ uint32_t time1 = timer_read_time();
+ read_mpu(ic->i2c, sizeof(reg), reg, sizeof(msg), msg);
+ uint32_t time2 = timer_read_time();
+ uint16_t fifo_bytes = ((msg[0] & 0x1f) << 8) | msg[1];
+
+ // Report status
+ sensor_bulk_status(&ic->sb, args[0], time1, time2-time1, fifo_bytes);
+}
+DECL_COMMAND(command_query_icm20948_status, "query_icm20948_status oid=%c");
+
+void
+icm20948_task(void)
+{
+ if (!sched_check_wake(&icm20948_wake))
+ return;
+ uint8_t oid;
+ struct icm20948 *ic;
+ foreach_oid(oid, ic, command_config_icm20948) {
+ uint_fast8_t flags = ic->flags;
+ if (flags & AX_PENDING)
+ ic20948_query(ic, oid);
+ }
+}
+DECL_TASK(icm20948_task);