diff --git a/README.md b/README.md
index 7acb943065..f47cf33e07 100644
--- a/README.md
+++ b/README.md
@@ -723,96 +723,97 @@ your specific needs.
SPI Flash |
BME280 |
+BMI088 |
BMP085 |
BNO055 |
CAT24AA |
CYCLE-COUNTER |
-DRV832X |
+DRV832X |
DS1302 |
DS1631 |
DS18B20 |
EA-DOG |
Encoder Input |
-Encoder Input BitBang |
+Encoder Input BitBang |
Encoder Output BitBang |
FT245 |
FT6x06 |
Gpio Sampler |
HCLAx |
-HD44780 |
+HD44780 |
HMC58x |
HMC6343 |
HX711 |
I2C-EEPROM |
ILI9341 |
-IS31FL3733 |
+IS31FL3733 |
ITG3200 |
IXM42XXX |
L3GD20 |
LAN8720A |
LAWICEL |
-LIS302DL |
+LIS302DL |
LIS3DSH |
LIS3MDL |
LM75 |
LP503x |
LSM303A |
-LSM6DS33 |
+LSM6DS33 |
LSM6DSO |
LTC2984 |
MAX31855 |
MAX31865 |
MAX6966 |
-MAX7219 |
+MAX7219 |
MCP23x17 |
MCP2515 |
MCP3008 |
MCP7941x |
MCP990X |
-MMC5603 |
+MMC5603 |
MS5611 |
MS5837 |
NOKIA5110 |
NRF24 |
TFT-DISPLAY |
-PAT9125EL |
+PAT9125EL |
PCA8574 |
PCA9535 |
PCA9548A |
PCA9685 |
SH1106 |
-SIEMENS-S65 |
+SIEMENS-S65 |
SIEMENS-S75 |
SK6812 |
SK9822 |
SSD1306 |
ST7586S |
-ST7789 |
+ST7789 |
STTS22H |
STUSB4500 |
SX1276 |
TCS3414 |
TCS3472 |
-TLC594x |
+TLC594x |
TMP102 |
TMP12x |
TMP175 |
TOUCH2046 |
VL53L0 |
-VL6180 |
+VL6180 |
WS2812 |
diff --git a/src/modm/driver/inertial/bmi088.hpp b/src/modm/driver/inertial/bmi088.hpp
new file mode 100644
index 0000000000..8f357fdd6b
--- /dev/null
+++ b/src/modm/driver/inertial/bmi088.hpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2023, Christopher Durand
+ *
+ * This file is part of the modm project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+// ----------------------------------------------------------------------------
+
+#ifndef MODM_BMI088_HPP
+#define MODM_BMI088_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "bmi088_transport.hpp"
+
+namespace modm
+{
+
+/// @ingroup modm_driver_bmi088
+struct bmi088
+{
+ enum class AccRange : uint8_t
+ {
+ Range3g = 0x00, //< +- 3g
+ Range6g = 0x01, //< +- 6g (default)
+ Range12g = 0x02, //< +- 12g
+ Range24g = 0x03 //< +- 24g
+ };
+
+ enum class GyroRange : uint8_t
+ {
+ Range2000dps = 0x00, //< +- 2000 deg/s (default)
+ Range1000dps = 0x01, //< +- 1000 deg/s
+ Range500dps = 0x02, //< +- 500 deg/s
+ Range250dps = 0x03, //< +- 250 deg/s
+ Range125dps = 0x04 //< +- 125 deg/s
+ };
+
+ struct AccData
+ {
+ /// acceleration in milli-g
+ Vector3f getFloat() const;
+
+ Vector3i raw;
+ AccRange range;
+ };
+
+ struct GyroData
+ {
+ /// angular rate in deg/s
+ Vector3f getFloat() const;
+
+ Vector3i raw;
+ GyroRange range;
+ };
+
+ enum class AccRate : uint8_t
+ {
+ Rate12Hz_Bw5Hz = 0x5 | 0xA0,
+ Rate12Hz_Bw2Hz = 0x5 | 0x90,
+ Rate12Hz_Bw1Hz = 0x5 | 0x80,
+ Rate25Hz_Bw10Hz = 0x6 | 0xA0,
+ Rate25Hz_Bw5Hz = 0x6 | 0x90,
+ Rate25Hz_Bw3Hz = 0x6 | 0x80,
+ Rate50Hz_Bw20Hz = 0x7 | 0xA0,
+ Rate50Hz_Bw9Hz = 0x7 | 0x90,
+ Rate50Hz_Bw5Hz = 0x7 | 0x80,
+ Rate100Hz_Bw40Hz = 0x8 | 0xA0,
+ Rate100Hz_Bw19Hz = 0x8 | 0x90,
+ Rate100Hz_Bw10Hz = 0x8 | 0x80,
+ Rate200Hz_Bw80Hz = 0x9 | 0xA0,
+ Rate200Hz_Bw38Hz = 0x9 | 0x90,
+ Rate200Hz_Bw20Hz = 0x9 | 0x80,
+ Rate400Hz_Bw145Hz = 0xA | 0xA0,
+ Rate400Hz_Bw75Hz = 0xA | 0x90,
+ Rate400Hz_Bw40Hz = 0xA | 0x80,
+ Rate800Hz_Bw230Hz = 0xB | 0xA0,
+ Rate800Hz_Bw140Hz = 0xB | 0x90,
+ Rate800Hz_Bw80Hz = 0xB | 0x80,
+ Rate1600Hz_Bw280Hz = 0xC | 0xA0,
+ Rate1600Hz_Bw234Hz = 0xC | 0x90,
+ Rate1600Hz_Bw145Hz = 0xC | 0x80
+ };
+
+ enum class AccGpioConfig : uint8_t
+ {
+ ActiveHigh = Bit1,
+ OpenDrain = Bit2,
+ EnableOutput = Bit3,
+ EnableInput = Bit4
+ };
+ MODM_FLAGS8(AccGpioConfig);
+
+ enum class AccGpioMap : uint8_t
+ {
+ Int1FifoFull = Bit0,
+ Int1FifoWatermark = Bit1,
+ Int1DataReady = Bit2,
+ Int2FifoFull = Bit4,
+ Int2FifoWatermark = Bit5,
+ Int2DataReady = Bit6
+ };
+ MODM_FLAGS8(AccGpioMap);
+
+ enum class AccStatus : uint8_t
+ {
+ DataReady = Bit7
+ };
+ MODM_FLAGS8(AccStatus);
+
+ enum class AccPowerConf : uint8_t
+ {
+ Active = 0x00,
+ Suspend = 0x03
+ };
+
+ enum class AccPowerControl : uint8_t
+ {
+ Off = 0x00,
+ On = 0x04
+ };
+
+ enum class AccSelfTest : uint8_t
+ {
+ Off = 0x00,
+ Positive = 0x0D,
+ Negative = 0x09
+ };
+
+ enum class GyroRate : uint8_t
+ {
+ Rate2000Hz_Bw532Hz = 0x00,
+ Rate2000Hz_Bw230Hz = 0x01,
+ Rate1000Hz_Bw116Hz = 0x02,
+ Rate400Hz_Bw47Hz = 0x03,
+ Rate200Hz_Bw23Hz = 0x04,
+ Rate100Hz_Bw12Hz = 0x05,
+ Rate200Hz_Bw64Hz = 0x06,
+ Rate100Hz_Bw32Hz = 0x07
+ };
+
+ enum class GyroGpioConfig : uint8_t
+ {
+ Int3ActiveHigh = Bit0,
+ Int3OpenDrain = Bit1,
+ Int4ActiveHigh = Bit2,
+ Int4OpenDrain = Bit3
+ };
+ MODM_FLAGS8(GyroGpioConfig);
+
+ enum class GyroGpioMap : uint8_t
+ {
+ Int3DataReady = Bit0,
+ Int3FifoInterrupt = Bit2,
+ Int4FifoInterrupt = Bit5,
+ Int4DataReady = Bit7
+ };
+ MODM_FLAGS8(GyroGpioMap);
+
+ enum class GyroSelfTest : uint8_t
+ {
+ // read-only bits
+ RateOk = Bit4,
+ Fail = Bit2,
+ Ready = Bit1,
+ // write-only bit
+ Trigger = Bit0
+ };
+ MODM_FLAGS8(GyroSelfTest);
+
+ enum class GyroStatus : uint8_t
+ {
+ DataReady = Bit7,
+ FifoInterrupt = Bit4
+ };
+ MODM_FLAGS8(GyroStatus);
+
+ enum class GyroInterruptControl : uint8_t
+ {
+ Fifo = Bit6,
+ DataReady = Bit7
+ };
+ MODM_FLAGS8(GyroInterruptControl);
+};
+
+/// @ingroup modm_driver_bmi088
+template
+class Bmi088 : public bmi088, public Transport
+{
+public:
+ template
+ Bmi088(Args... transportArgs);
+
+ bool
+ initialize(bool runSelfTest);
+
+ // Accelerometer functions
+
+ std::optional
+ readAccData();
+
+ bool
+ readAccDataReady();
+
+ bool
+ clearAccDataReadyInterrupt();
+
+ bool
+ setAccRate(AccRate config);
+
+ bool
+ setAccRange(AccRange range);
+
+ bool
+ setAccInt1GpioConfig(AccGpioConfig_t config);
+
+ bool
+ setAccInt2GpioConfig(AccGpioConfig_t config);
+
+ bool
+ setAccGpioMap(AccGpioMap_t map);
+
+ // Gyroscope functions
+
+ std::optional
+ readGyroData();
+
+ bool
+ readGyroDataReady();
+
+ bool
+ setGyroRate(GyroRate config);
+
+ bool
+ setGyroRange(GyroRange range);
+
+ bool
+ setGyroGpioConfig(GyroGpioConfig_t config);
+
+ bool
+ setGyroGpioMap(GyroGpioMap_t map);
+
+private:
+ using AccRegister = Transport::AccRegister;
+ using GyroRegister = Transport::GyroRegister;
+
+ static constexpr std::chrono::milliseconds ResetTimeout{30};
+ static constexpr std::chrono::microseconds WriteTimeout{2};
+ static constexpr std::chrono::microseconds AccSuspendTimeout{450};
+
+ static constexpr uint8_t ResetCommand{0xB6};
+ static constexpr uint8_t AccChipId{0x1E};
+ static constexpr uint8_t GyroChipId{0x0F};
+
+ bool
+ checkChipId();
+
+ bool
+ reset();
+
+ bool
+ selfTest();
+
+ bool
+ enableAccelerometer();
+
+ void
+ timerWait();
+
+ std::optional
+ readRegister(auto reg);
+
+ modm::PreciseTimeout timer_;
+ AccRange accRange_{AccRange::Range6g};
+ GyroRange gyroRange_{GyroRange::Range2000dps};
+};
+
+
+} // modm namespace
+
+#include "bmi088_impl.hpp"
+
+#endif // MODM_ADIS16470_HPP
diff --git a/src/modm/driver/inertial/bmi088.lb b/src/modm/driver/inertial/bmi088.lb
new file mode 100644
index 0000000000..1d16099368
--- /dev/null
+++ b/src/modm/driver/inertial/bmi088.lb
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2023, Christopher Durand
+#
+# This file is part of the modm project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+# -----------------------------------------------------------------------------
+
+
+def init(module):
+ module.name = ":driver:bmi088"
+ module.description = """\
+# BMI088 Inertial Measurement Unit
+
+[Datasheet](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmi088-ds001.pdf)
+"""
+
+def prepare(module, options):
+ module.depends(
+ ":architecture:gpio",
+ ":architecture:register",
+ ":architecture:spi.device",
+ ":architecture:i2c.device",
+ ":math:geometry",
+ ":processing:fiber",
+ ":processing:timer")
+ return True
+
+def build(env):
+ env.outbasepath = "modm/src/modm/driver/inertial"
+ env.copy("bmi088.hpp")
+ env.copy("bmi088_impl.hpp")
+ env.copy("bmi088_transport.hpp")
+ env.copy("bmi088_transport_impl.hpp")
diff --git a/src/modm/driver/inertial/bmi088_impl.hpp b/src/modm/driver/inertial/bmi088_impl.hpp
new file mode 100644
index 0000000000..b94dac0604
--- /dev/null
+++ b/src/modm/driver/inertial/bmi088_impl.hpp
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2023, Christopher Durand
+ *
+ * This file is part of the modm project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+// ----------------------------------------------------------------------------
+
+#include
+#ifndef MODM_BMI088_HPP
+#error "Don't include this file directly, use 'bmi088.hpp' instead!"
+#endif
+
+#include
+
+namespace modm
+{
+
+template
+template
+Bmi088::Bmi088(Args... transportArgs)
+ : Transport{transportArgs...}
+{
+}
+
+template
+bool
+Bmi088::initialize(bool runSelfTest)
+{
+ Transport::initialize();
+
+ if (!checkChipId() or !reset() or !enableAccelerometer()) {
+ return false;
+ }
+
+ if (runSelfTest and !selfTest()) {
+ return false;
+ }
+
+ timerWait();
+ const bool ok = this->writeRegister(GyroRegister::InterruptControl,
+ uint8_t(GyroInterruptControl::DataReady));
+ timer_.restart(WriteTimeout);
+ return ok;
+}
+
+template
+std::optional
+Bmi088::readAccData()
+{
+ const auto data = this->readRegisters(AccRegister::DataXLow, 6);
+
+ if (data.empty()) {
+ return {};
+ }
+
+ return AccData {
+ .raw = Vector3i(
+ data[0] | data[1] << 8,
+ data[2] | data[3] << 8,
+ data[4] | data[5] << 8
+ ),
+ .range = accRange_
+ };
+}
+
+template
+bool
+Bmi088::readAccDataReady()
+{
+ const auto value = readRegister(AccRegister::Status).value_or(0);
+ return value & uint8_t(AccStatus::DataReady);
+}
+
+template
+bool
+Bmi088::clearAccDataReadyInterrupt()
+{
+ const auto result = readRegister(AccRegister::InterruptStatus).value_or(0);
+ return result & uint8_t(AccStatus::DataReady);
+}
+
+template
+bool
+Bmi088::setAccRate(AccRate config)
+{
+ timerWait();
+ const bool ok = this->writeRegister(AccRegister::Config, uint8_t(config));
+ timer_.restart(ResetTimeout);
+ return ok;
+}
+
+template
+bool
+Bmi088::setAccRange(AccRange range)
+{
+ timerWait();
+ const bool ok = this->writeRegister(AccRegister::Range, uint8_t(range));
+ timer_.restart(ResetTimeout);
+ if (ok) {
+ accRange_ = range;
+ return true;
+ }
+ return false;
+}
+
+template
+bool
+Bmi088::setAccInt1GpioConfig(AccGpioConfig_t config)
+{
+ timerWait();
+ const bool ok = this->writeRegister(AccRegister::Int1Control, config.value);
+ timer_.restart(ResetTimeout);
+ return ok;
+}
+
+template
+bool
+Bmi088::setAccInt2GpioConfig(AccGpioConfig_t config)
+{
+ timerWait();
+ const bool ok = this->writeRegister(AccRegister::Int2Control, config.value);
+ timer_.restart(ResetTimeout);
+ return ok;
+}
+
+template
+bool
+Bmi088::setAccGpioMap(AccGpioMap_t map)
+{
+ timerWait();
+ const bool ok = this->writeRegister(AccRegister::IntMap, map.value);
+ timer_.restart(ResetTimeout);
+ return ok;
+}
+
+template
+std::optional
+Bmi088::readGyroData()
+{
+ const auto data = this->readRegisters(GyroRegister::RateXLow, 6);
+
+ if (data.empty()) {
+ return {};
+ }
+
+ return GyroData {
+ .raw = Vector3i(
+ data[0] | data[1] << 8,
+ data[2] | data[3] << 8,
+ data[4] | data[5] << 8
+ ),
+ .range = gyroRange_
+ };
+}
+
+template
+bool
+Bmi088::readGyroDataReady()
+{
+ const auto value = readRegister(GyroRegister::InterruptStatus).value_or(0);
+ return bool(GyroStatus_t{value} & GyroStatus::DataReady);
+}
+
+template
+bool
+Bmi088::setGyroRate(GyroRate rate)
+{
+ timerWait();
+ const bool ok = this->writeRegister(GyroRegister::Bandwidth, uint8_t(rate));
+ timer_.restart(ResetTimeout);
+ return ok;
+}
+
+template
+bool
+Bmi088::setGyroRange(GyroRange range)
+{
+ timerWait();
+ const bool ok = this->writeRegister(GyroRegister::Range, uint8_t(range));
+ timer_.restart(ResetTimeout);
+ if (ok) {
+ gyroRange_ = range;
+ return true;
+ }
+ return false;
+}
+
+template
+bool
+Bmi088::setGyroGpioConfig(GyroGpioConfig_t config)
+{
+ timerWait();
+ const bool ok = this->writeRegister(GyroRegister::Int3Int4Conf, config.value);
+ timer_.restart(ResetTimeout);
+ return ok;
+}
+
+template
+bool
+Bmi088::setGyroGpioMap(GyroGpioMap_t map)
+{
+ timerWait();
+ const bool ok = this->writeRegister(GyroRegister::Int3Int4Map, map.value);
+ timer_.restart(ResetTimeout);
+ return ok;
+}
+
+template
+bool
+Bmi088::checkChipId()
+{
+ const std::optional gyroId = readRegister(GyroRegister::ChipId);
+ const std::optional accId = readRegister(AccRegister::ChipId);
+ const bool gyroIdValid = gyroId.value_or(0) == GyroChipId;
+ const bool accIdValid = accId.value_or(0) == AccChipId;
+ return gyroIdValid and accIdValid;
+}
+
+template
+void
+Bmi088::timerWait()
+{
+ while (timer_.isArmed()) {
+ modm::fiber::yield();
+ }
+}
+
+template
+std::optional
+Bmi088::readRegister(auto reg)
+{
+ const auto data = this->readRegisters(reg, 1);
+ if (data.empty()) {
+ return std::nullopt;
+ } else {
+ return data[0];
+ }
+}
+
+template
+bool
+Bmi088::reset()
+{
+ const bool gyroOk = this->writeRegister(GyroRegister::SoftReset, ResetCommand);
+ const bool accOk = this->writeRegister(AccRegister::SoftReset, ResetCommand);
+
+ if (!gyroOk or !accOk) {
+ return false;
+ }
+ accRange_ = AccRange::Range6g;
+ gyroRange_ = GyroRange::Range2000dps;
+
+ timer_.restart(ResetTimeout);
+ timerWait();
+
+ // required to switch the accelerometer to SPI mode if SPI is used
+ Transport::initialize();
+
+ return true;
+}
+
+template
+bool
+Bmi088::selfTest()
+{
+ bool ok = setAccRange(AccRange::Range24g);
+ ok &= setAccRate(AccRate::Rate1600Hz_Bw280Hz);
+ if (!ok) {
+ return false;
+ }
+ timer_.restart(2ms);
+ timerWait();
+ ok = this->writeRegister(AccRegister::SelfTest, uint8_t(AccSelfTest::Positive));
+ if (!ok) {
+ return false;
+ }
+ timer_.restart(50ms);
+ timerWait();
+ const auto positiveData = readAccData();
+ if (!positiveData) {
+ return false;
+ }
+ ok = this->writeRegister(AccRegister::SelfTest, uint8_t(AccSelfTest::Negative));
+ if (!ok) {
+ return false;
+ }
+ timer_.restart(50ms);
+ timerWait();
+ const auto negativeData = readAccData();
+ if (!negativeData) {
+ return false;
+ }
+ const Vector3f diff = positiveData->getFloat() - negativeData->getFloat();
+ const bool accOk = (diff[0] >= 1000.f) and (diff[1] >= 1000.f) and (diff[2] >= 500.f);
+ this->writeRegister(AccRegister::SelfTest, uint8_t(AccSelfTest::Off));
+ if (!accOk) {
+ return false;
+ }
+ ok = this->writeRegister(GyroRegister::SelfTest, uint8_t(GyroSelfTest::Trigger));
+ if (!ok) {
+ return false;
+ }
+ timer_.restart(30ms);
+ timerWait();
+ const auto gyroTest = GyroSelfTest_t(readRegister(GyroRegister::SelfTest).value_or(0));
+ if (gyroTest != (GyroSelfTest::Ready | GyroSelfTest::RateOk)) {
+ return false;
+ }
+ return reset() and enableAccelerometer();
+}
+
+template
+bool
+Bmi088::enableAccelerometer()
+{
+ timerWait();
+ bool ok = this->writeRegister(AccRegister::PowerConfig, uint8_t(AccPowerConf::Active));
+ timer_.restart(AccSuspendTimeout);
+ if (!ok) {
+ return false;
+ }
+ timerWait();
+
+ ok = this->writeRegister(AccRegister::PowerControl, uint8_t(AccPowerControl::On));
+ timer_.restart(WriteTimeout);
+ return ok;
+}
+
+inline Vector3f
+bmi088::AccData::getFloat() const
+{
+ const float factor = (1500 << (int(range) + 1)) * (1 / 32768.f);
+ return Vector3f {
+ raw[0] * factor,
+ raw[1] * factor,
+ raw[2] * factor
+ };
+}
+
+inline Vector3f
+bmi088::GyroData::getFloat() const
+{
+ const float factor = (2000 >> int(range)) * (1 / 32768.f);
+ return Vector3f {
+ raw[0] * factor,
+ raw[1] * factor,
+ raw[2] * factor
+ };
+}
+
+} // namespace modm
diff --git a/src/modm/driver/inertial/bmi088_transport.hpp b/src/modm/driver/inertial/bmi088_transport.hpp
new file mode 100644
index 0000000000..4e132623d1
--- /dev/null
+++ b/src/modm/driver/inertial/bmi088_transport.hpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2023, Christopher Durand
+ *
+ * This file is part of the modm project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+// ----------------------------------------------------------------------------
+
+#ifndef MODM_BMI088_TRANSPORT_HPP
+#define MODM_BMI088_TRANSPORT_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace modm
+{
+
+struct Bmi088TransportBase
+{
+ enum class
+ AccRegister : uint8_t
+ {
+ ChipId = 0x00,
+ // 0x01: reserved
+ Error = 0x02,
+ Status = 0x03,
+ // 0x04-0x11: reserved
+ DataXLow = 0x12,
+ DataXHigh = 0x13,
+ DataYLow = 0x14,
+ DataYHigh = 0x15,
+ DataZLow = 0x16,
+ DataZHigh = 0x17,
+ SensorTime0 = 0x18,
+ SensorTime1 = 0x19,
+ SensorTime2 = 0x1A,
+ // 0x1B-0x1C: reserved
+ InterruptStatus = 0x1D,
+ // 0x1E-0x21: reserved
+ TempHigh = 0x22,
+ TempLow = 0x23,
+ FifoLength0 = 0x24,
+ FifoLength1 = 0x25,
+ FifoData = 0x26,
+ // 0x27-0x3F: reserved
+ Config = 0x40,
+ Range = 0x41,
+ // 0x42-0x44: reserved
+ FifoDownsampling = 0x45,
+ FifoWatermark0 = 0x46,
+ FifoWatermark1 = 0x47,
+ FifoConfig0 = 0x48,
+ FifoConfig1 = 0x49,
+ // 0x4A-0x52: reserved
+ Int1Control = 0x53,
+ Int2Control = 0x54,
+ // 0x55-0x57: reserved
+ IntMap = 0x58,
+ // 0x59-0x6C: reserved
+ SelfTest = 0x6D,
+ // 0x6E-0x7B: reserved
+ PowerConfig = 0x7C,
+ PowerControl = 0x7D,
+ SoftReset = 0x7E
+ };
+
+ enum class
+ GyroRegister : uint8_t
+ {
+ ChipId = 0x00,
+ // 0x01: reserved
+ RateXLow = 0x02,
+ RateXHigh = 0x03,
+ RateYLow = 0x04,
+ RateYHigh = 0x05,
+ RateZLow = 0x06,
+ RateZHigh = 0x07,
+ // 0x08-0x09: reserved
+ InterruptStatus = 0x0A,
+ // 0x0B-0x0D: reserved
+ FifoStatus = 0x0E,
+ Range = 0x0F,
+ Bandwidth = 0x10,
+ LowPowerMode1 = 0x11,
+ // 0x12-0x13: reserved
+ SoftReset = 0x14,
+ InterruptControl = 0x15,
+ Int3Int4Conf = 0x16,
+ // 0x17: reserved
+ Int3Int4Map = 0x18,
+ // 0x19-0x1D: reserved
+ FifoWatermark = 0x1E,
+ // 0x1F-0x33: reserved
+ FifoExtInt = 0x34,
+ // 0x35-0x3B: reserved
+ SelfTest = 0x3C,
+ FifoConfig0 = 0x3D,
+ FifoConfig1 = 0x3E,
+ FifoData = 0x3F
+ };
+ static constexpr uint8_t MaxRegisterSequence{6};
+};
+
+template
+concept Bmi088Transport = requires(T& transport, Bmi088TransportBase::AccRegister reg1,
+ Bmi088TransportBase::GyroRegister reg2, uint8_t data)
+{
+ { transport.initialize() };
+ { transport.readRegisters(reg1, /* count= */data) } -> std::same_as>;
+ { transport.readRegisters(reg2, /* count= */data) } -> std::same_as>;
+ { transport.writeRegister(reg1, data) } -> std::same_as;
+ { transport.writeRegister(reg2, data) } -> std::same_as;
+};
+
+/// @ingroup modm_driver_bmi088
+template
+class Bmi088SpiTransport : public Bmi088TransportBase,
+ public SpiDevice
+{
+public:
+ Bmi088SpiTransport() = default;
+
+ Bmi088SpiTransport(const Bmi088SpiTransport&) = delete;
+
+ Bmi088SpiTransport&
+ operator=(const Bmi088SpiTransport&) = delete;
+
+ void
+ initialize();
+
+ std::span
+ readRegisters(AccRegister startReg, uint8_t count);
+
+ std::span
+ readRegisters(GyroRegister startReg, uint8_t count);
+
+ bool
+ writeRegister(AccRegister reg, uint8_t data);
+
+ bool
+ writeRegister(GyroRegister reg, uint8_t data);
+
+private:
+ template
+ std::span
+ readRegisters(uint8_t reg, uint8_t count, bool dummyByte);
+
+ template
+ bool
+ writeRegister(uint8_t reg, uint8_t data);
+
+ static constexpr uint8_t ReadFlag{0b1000'0000};
+
+ std::array rxBuffer_{};
+ std::array txBuffer_{};
+};
+
+/// @ingroup modm_driver_bmi088
+template
+class Bmi088I2cTransport : public Bmi088TransportBase, public I2cDevice
+{
+public:
+ Bmi088I2cTransport(uint8_t accAddress, uint8_t gyroAddress);
+
+ Bmi088I2cTransport(const Bmi088I2cTransport&) = delete;
+
+ Bmi088I2cTransport&
+ operator=(const Bmi088I2cTransport&) = delete;
+
+ void
+ initialize();
+
+ std::span
+ readRegisters(AccRegister startReg, uint8_t count);
+
+ std::span
+ readRegisters(GyroRegister startReg, uint8_t count);
+
+ bool
+ writeRegister(AccRegister reg, uint8_t data);
+
+ bool
+ writeRegister(GyroRegister reg, uint8_t data);
+
+private:
+ std::span
+ readRegisters(uint8_t reg, uint8_t count);
+
+ bool
+ writeRegister(uint8_t reg, uint8_t data);
+
+ uint8_t accAddress_;
+ uint8_t gyroAddress_;
+ std::array buffer_{};
+};
+
+} // modm namespace
+
+#include "bmi088_transport_impl.hpp"
+
+#endif // MODM_BMI088_TRANSPORT_HPP
diff --git a/src/modm/driver/inertial/bmi088_transport_impl.hpp b/src/modm/driver/inertial/bmi088_transport_impl.hpp
new file mode 100644
index 0000000000..c44abf83ec
--- /dev/null
+++ b/src/modm/driver/inertial/bmi088_transport_impl.hpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2023, Christopher Durand
+ *
+ * This file is part of the modm project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+// ----------------------------------------------------------------------------
+
+#ifndef MODM_BMI088_TRANSPORT_HPP
+#error "Don't include this file directly, use 'bmi088_transport.hpp' instead!"
+#endif
+
+#include
+
+namespace modm
+{
+
+template
+void
+Bmi088SpiTransport::initialize()
+{
+ // rising edge on CS required to enable accelerometer SPI mode
+ AccCs::setOutput(false);
+ modm::delay_ns(100);
+ AccCs::setOutput(true);
+
+ GyroCs::setOutput(true);
+}
+
+template
+std::span
+Bmi088SpiTransport::readRegisters(AccRegister startReg,
+ uint8_t count)
+{
+ return readRegisters(static_cast(startReg), count, true);
+}
+
+template
+std::span
+Bmi088SpiTransport::readRegisters(GyroRegister startReg,
+ uint8_t count)
+{
+ return readRegisters(static_cast(startReg), count, false);
+}
+
+template
+template
+std::span
+Bmi088SpiTransport::readRegisters(uint8_t startReg,
+ uint8_t count, bool dummyByte)
+{
+ if (count > MaxRegisterSequence) {
+ return {};
+ }
+
+ while (!this->acquireMaster()) {
+ modm::fiber::yield();
+ }
+ Cs::reset();
+
+ const uint8_t dataOffset = (dummyByte ? 2 : 1);
+
+ txBuffer_[0] = startReg | ReadFlag;
+ txBuffer_[1] = 0;
+
+ SpiMaster::transfer(&txBuffer_[0], &rxBuffer_[0], count + dataOffset);
+
+ if (this->releaseMaster()) {
+ Cs::set();
+ }
+
+ return std::span{&rxBuffer_[dataOffset], count};
+}
+
+template
+bool
+Bmi088SpiTransport::writeRegister(AccRegister reg, uint8_t data)
+{
+ return writeRegister(static_cast(reg), data);
+}
+
+template
+bool
+Bmi088SpiTransport::writeRegister(GyroRegister reg, uint8_t data)
+{
+ return writeRegister(static_cast(reg), data);
+}
+
+template
+template
+bool
+Bmi088SpiTransport::writeRegister(uint8_t reg, uint8_t data)
+{
+ while (!this->acquireMaster()) {
+ modm::fiber::yield();
+ }
+ Cs::reset();
+
+ txBuffer_[0] = reg;
+ txBuffer_[1] = data;
+ SpiMaster::transfer(&txBuffer_[0], nullptr, 2);
+
+ if (this->releaseMaster()) {
+ Cs::set();
+ }
+
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+
+template
+void
+Bmi088I2cTransport::initialize()
+{
+}
+
+template
+std::span
+Bmi088I2cTransport::readRegisters(AccRegister startReg, uint8_t count)
+{
+ this->transaction.address = accAddress_;
+ return readRegisters(static_cast(startReg), count);
+}
+
+template
+std::span
+Bmi088I2cTransport::readRegisters(GyroRegister startReg, uint8_t count)
+{
+ this->transaction.address = gyroAddress_;
+ return readRegisters(static_cast(startReg), count);
+}
+
+template
+std::span
+Bmi088I2cTransport::readRegisters(uint8_t startReg, uint8_t count)
+{
+ if (count > MaxRegisterSequence) {
+ return {};
+ }
+
+ this->transaction.configureWriteRead(&startReg, 1, &buffer_[0], count);
+ const bool success = this->runTransaction();
+
+ if (success) {
+ return std::span{&buffer_[0], count};
+ } else {
+ return {};
+ }
+}
+
+template
+bool
+Bmi088I2cTransport::writeRegister(AccRegister reg, uint8_t data)
+{
+ this->transaction.address = accAddress_;
+ return writeRegister(static_cast(reg), data);
+}
+
+template
+bool
+Bmi088I2cTransport::writeRegister(GyroRegister reg, uint8_t data)
+{
+ this->transaction.address = gyroAddress_;
+ return writeRegister(static_cast(reg), data);
+}
+
+template
+bool
+Bmi088I2cTransport::writeRegister(uint8_t reg, uint8_t data)
+{
+ buffer_[0] = reg;
+ buffer_[1] = data;
+ this->transaction.configureWrite(&buffer_[0], 2);
+
+ return this->runTransaction();
+}
+
+} // namespace modm