diff --git a/README.md b/README.md
index 4db60bc104..8ec3c4f104 100644
--- a/README.md
+++ b/README.md
@@ -591,93 +591,95 @@ you specific needs.
AD7280A |
AD7928 |
+ADIS16470 |
ADNS9800 |
ADS7843 |
ADS816X |
-AMS5915 |
+AMS5915 |
APA102 |
SPI-FLASH |
BME280 |
BMP085 |
BNO055 |
-CAT24AA |
+CAT24AA |
DRV832X |
DS1302 |
DS1631 |
DS18B20 |
EA-DOG |
-ENCODER-INPUT |
+ENCODER-INPUT |
ENCODER-INPUT-BITBANG |
ENCODER-OUTPUT-BITBANG |
FT245 |
FT6X06 |
GPIO-SAMPLER |
-HCLAx |
+HCLAx |
HD44780 |
HMC58x |
HMC6343 |
HX711 |
I2C-EEPROM |
-ILI9341 |
+ILI9341 |
IS31FL3733 |
ITG3200 |
L3GD20 |
LAN8720A |
LAWICEL |
-LIS302DL |
+LIS302DL |
LIS3DSH |
LIS3MDL |
LM75 |
LP503X |
LSM303A |
-LSM6DS33 |
+LSM6DS33 |
LTC2984 |
MAX6966 |
MAX7219 |
MCP23X17 |
MCP2515 |
-MCP7941X |
+MCP7941X |
MMC5603 |
NOKIA5110 |
NRF24 |
TFT-DISPLAY |
PAT9125EL |
-PCA8574 |
+PCA8574 |
PCA9535 |
PCA9548A |
PCA9685 |
SH1106 |
SIEMENS-S65 |
-SIEMENS-S75 |
+SIEMENS-S75 |
SK6812 |
SK9822 |
SSD1306 |
ST7586S |
STTS22H |
-STUSB4500 |
+STUSB4500 |
SX1276 |
TCS3414 |
TCS3472 |
TLC594X |
TMP102 |
-TMP12X |
+TMP12X |
TMP175 |
TOUCH2046 |
VL53L0 |
VL6180 |
WS2812 |
+
diff --git a/src/modm/driver/inertial/adis16470.hpp b/src/modm/driver/inertial/adis16470.hpp
new file mode 100644
index 0000000000..ddd3ca7b04
--- /dev/null
+++ b/src/modm/driver/inertial/adis16470.hpp
@@ -0,0 +1,233 @@
+// coding: utf-8
+/*
+ * Copyright (c) 2022, Raphael Lehmann
+ *
+ * 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_ADIS16470_HPP
+#define MODM_ADIS16470_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace modm
+{
+
+/// @ingroup modm_driver_adis16470
+struct adis16470
+{
+ /// Available registers
+ enum class
+ Register : uint8_t
+ {
+ DIAG_STAT = 0x02,
+ X_GYRO_LOW = 0x04,
+ X_GYRO_OUT = 0x06,
+ Y_GYRO_LOW = 0x08,
+ Y_GYRO_OUT = 0x0A,
+ Z_GYRO_LOW = 0x0C,
+ Z_GYRO_OUT = 0x0E,
+ X_ACCL_LOW = 0x10,
+ X_ACCL_OUT = 0x12,
+ Y_ACCL_LOW = 0x14,
+ Y_ACCL_OUT = 0x16,
+ Z_ACCL_LOW = 0x18,
+ Z_ACCL_OUT = 0x1A,
+ TEMP_OUT = 0x1C,
+ TIME_STAMP = 0x1E,
+ DATA_CNTR = 0x22,
+ X_DELTANG_LOW = 0x24,
+ X_DELTANG_OUT = 0x26,
+ Y_DELTANG_LOW = 0x28,
+ Y_DELTANG_OUT = 0x2A,
+ Z_DELTANG_LOW = 0x2C,
+ Z_DELTANG_OUT = 0x2E,
+ X_DELTVEL_LOW = 0x30,
+ X_DELTVEL_OUT = 0x32,
+ Y_DELTVEL_LOW = 0x34,
+ Y_DELTVEL_OUT = 0x36,
+ Z_DELTVEL_LOW = 0x38,
+ Z_DELTVEL_OUT = 0x3A,
+ XG_BIAS_LOW = 0x40,
+ XG_BIAS_HIGH = 0x42,
+ YG_BIAS_LOW = 0x44,
+ YG_BIAS_HIGH = 0x46,
+ ZG_BIAS_LOW = 0x48,
+ ZG_BIAS_HIGH = 0x4A,
+ XA_BIAS_LOW = 0x4C,
+ XA_BIAS_HIGH = 0x4E,
+ YA_BIAS_LOW = 0x50,
+ YA_BIAS_HIGH = 0x52,
+ ZA_BIAS_LOW = 0x54,
+ ZA_BIAS_HIGH = 0x56,
+ FILT_CTRL = 0x5C,
+ MSC_CTRL = 0x60,
+ UP_SCALE = 0x62,
+ DEC_RATE = 0x64,
+ NULL_CNFG = 0x66,
+ GLOB_CMD = 0x68,
+ FIRM_REV = 0x6C,
+ FIRM_DM = 0x6E,
+ FIRM_Y = 0x70,
+ PROD_ID = 0x72,
+ SERIAL_NUM = 0x74,
+ USER_SCR1 = 0x76,
+ USER_SCR2 = 0x78,
+ USER_SCR3 = 0x7A,
+ FLSHCNT_LOW = 0x7C,
+ FLSHCNT_HIGH = 0x7E,
+ };
+
+ enum class
+ AccessMethod : uint8_t
+ {
+ Read = 0b01,
+ Write = 0b10,
+ ReadWrite = 0b11,
+ };
+
+ /// Get the allowed register access modes for a given Register
+ constexpr AccessMethod
+ getRegisterAccess(Register r)
+ {
+ if (r == Register::GLOB_CMD) {
+ return AccessMethod::Write;
+ }
+ if (r <= Register::Z_DELTVEL_OUT) {
+ return AccessMethod::Read;
+ }
+ if ((r >= Register::FIRM_REV) && (r != Register::USER_SCR1) && (r != Register::USER_SCR2) && (r != Register::USER_SCR3)) {
+ return AccessMethod::Read;
+ }
+ return AccessMethod::ReadWrite;
+ }
+
+ /// Status/Error Flag Indicators
+ /// DIAG_STAT register (address 0x02, 0x03) reset value is 0x0000
+ enum class
+ DiagStat : uint16_t
+ {
+ // Reserved = Bit8 to Bit15,
+ ClockError = Bit7, ///< Indicates that the internal data sampling clock does not synchronize with the external clock, which only applies when using scale sync mode.
+ MemoryFailure = Bit6, ///< Indicates a failure in the flash memory test.
+ SensorFailure = Bit5, ///< Indicates failure of at least one sensor, at the conclusion of the self test.
+ StandbyMode = Bit4, ///< Indicates that the voltage across VDD and GND is <2.8 V, which causes data processing to stop.
+ SpiCommunicationError = Bit3, ///< Indicates that the total number of SCLK cycles is not equal to an integer multiple of 16.
+ FlashUpdateFailure = Bit2, ///< Indicates that the most recent flash memory update failed.
+ DataPathOverrun = Bit1, ///< Indicates that one of the data paths have experienced an overrun condition.
+ // Reserved = Bit0,
+ };
+ MODM_FLAGS8(DiagStat);
+
+};
+
+/**
+ * \ingroup modm_driver_adis16470
+ * \author Raphael Lehmann
+ */
+template
+class Adis16470 : public adis16470,
+ public modm::SpiDevice,
+ protected modm::NestedResumable<2>
+{
+public:
+ Adis16470() = default;
+
+ /**
+ * @brief Initialize
+ *
+ * Sets used pins as output.
+ *
+ * SPI must be initialized by the user!
+ * @warning The SPI frequency must not exceed 2 MHz for this chip,
+ * or 1 MHz with burst mode.
+ */
+ modm::ResumableResult
+ initialize();
+
+ /**
+ * @brief Read a single register.
+ *
+ * @param reg The register to be read
+ * @return The register value in case of a read access, or std::nullopt if
+ * an error occured, e.g. if some register access is not permitted.
+ */
+ modm::ResumableResult>
+ readRegister(Register reg);
+
+ /**
+ * @brief Write a single register.
+ *
+ * @param reg The register to be written
+ * @param value The value to be written to the register.
+ * @return False in case of any error, e.g. if some register acces is not
+ * permitted.
+ */
+ modm::ResumableResult
+ writeRegister(Register reg, uint16_t value);
+
+ /**
+ * @brief Read a sequence of registers.
+ * Improves SPI usage efficiency by 100% compared to `accessRegister(...)`.
+ *
+ * @param sequence All registers in the std::span will be read and the
+ * results will be stored in values.
+ * @param values Results of the read operations
+ * @return False in case of any error, e.g. if some register access is not
+ * permitted.
+ */
+ modm::ResumableResult
+ readRegisterSequence(std::span sequence, std::span values);
+
+ /**
+ * @brief Read all output data registers using burst mode.
+ *
+ * In all clock modes, except when operating in scaled sync mode (Register
+ * MSC_CTRL, Bits[4:2] = 010), the burst read response includes the
+ * following registers and checksum value:
+ * DIAG_STAT, X_GYRO_OUT, Y_GYRO_OUT, Z_GYRO_OUT, X_ACCL_OUT, Y_ACCL_OUT,
+ * Z_ACCL_OUT, TEMP_OUT, DATA_CNTR, and checksum.
+ *
+ * When operating in scaled sync mode (Register MSC_CTRL, Bits[4:2] = 010),
+ * the burst read response includes the following registers and value:
+ * DIAG_STAT, X_GYRO_OUT, Y_GYRO_OUT, Z_GYRO_OUT, X_ACCL_OUT, Y_ACCL_OUT,
+ * Z_ACCL_OUT, TEMP_OUT, TIME_STMP, and checksum.
+ *
+ * This is useful with DMA accelerated SPI to retrieve the measurement
+ * data, because it only triggers a single SPI transfer.
+ *
+ * @warning The SPI frequency must not exceed 1 MHz for this mode.
+ *
+ * @param data Reference to a array of 11 uint16_t entries, the read
+ * register values are written here.
+ * data[0] is empty and the last entry (data[10]) contains a checksum which
+ * has already been checked by this driver.
+ * @return False in case of a checksum mismatch.
+ */
+ modm::ResumableResult
+ readRegisterBurst(std::array& data);
+
+private:
+ static constexpr std::size_t bufferSize = 22;
+ std::array buffer;
+ std::size_t i;
+ uint8_t checksum;
+};
+
+
+} // modm namespace
+
+#include "adis16470_impl.hpp"
+
+#endif // MODM_ADIS16470_HPP
diff --git a/src/modm/driver/inertial/adis16470.lb b/src/modm/driver/inertial/adis16470.lb
new file mode 100644
index 0000000000..ff4cbd3e25
--- /dev/null
+++ b/src/modm/driver/inertial/adis16470.lb
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2022, Raphael Lehmann
+#
+# 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:adis16470"
+ module.description = """\
+# ADIS16470 Inertial Measurement Unit
+
+[Datasheet](https://www.analog.com/media/en/technical-documentation/data-sheets/ADIS16470.pdf)
+"""
+
+def prepare(module, options):
+ module.depends(
+ ":architecture:gpio",
+ ":architecture:register",
+ ":architecture:spi.device",
+ ":processing:resumable")
+ return True
+
+def build(env):
+ env.outbasepath = "modm/src/modm/driver/inertial"
+ env.copy("adis16470.hpp")
+ env.copy("adis16470_impl.hpp")
diff --git a/src/modm/driver/inertial/adis16470_impl.hpp b/src/modm/driver/inertial/adis16470_impl.hpp
new file mode 100644
index 0000000000..63839a05dc
--- /dev/null
+++ b/src/modm/driver/inertial/adis16470_impl.hpp
@@ -0,0 +1,146 @@
+// coding: utf-8
+/*
+ * Copyright (c) 2022, Raphael Lehmann
+ *
+ * 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_ADIS16470_HPP
+ #error "Don't include this file directly, use 'adis16470.hpp' instead!"
+#endif
+
+namespace modm
+{
+
+template
+modm::ResumableResult
+Adis16470::initialize()
+{
+ RF_BEGIN();
+ Cs::setOutput(modm::Gpio::High);
+ RF_END();
+}
+
+
+template
+modm::ResumableResult>
+Adis16470::readRegister(Register reg)
+{
+ RF_BEGIN();
+
+ if (getRegisterAccess(reg) == AccessMethod::Write) {
+ // Reading this register is not permitted
+ RF_RETURN(std::nullopt);
+ }
+
+ RF_WAIT_UNTIL(this->acquireMaster());
+ Cs::reset();
+
+ buffer[0] = uint8_t(reg) & 0b0111'1111;
+ buffer[1] = 0;
+ RF_CALL(SpiMaster::transfer(buffer.data(), nullptr, 2));
+ RF_CALL(SpiMaster::transfer(buffer.data(), &buffer[2], 2));
+
+ if (this->releaseMaster()) {
+ Cs::set();
+ }
+
+ RF_END_RETURN((static_cast(buffer[2]) << 8) | buffer[3]);
+}
+
+template
+modm::ResumableResult
+Adis16470::writeRegister(Register reg, uint16_t value)
+{
+ RF_BEGIN();
+
+ if (getRegisterAccess(reg) == AccessMethod::Read) {
+ // Writing to this register is not permitted
+ RF_RETURN(false);
+ }
+
+ RF_WAIT_UNTIL(this->acquireMaster());
+ Cs::reset();
+
+ buffer[0] = (uint8_t(reg) & 0b0111'1111) | 0b1000'0000;
+ buffer[1] = static_cast(value);
+ RF_CALL(SpiMaster::transfer(buffer.data(), nullptr, 2));
+
+ buffer[0] = ((uint8_t(reg) + 1) & 0b0111'1111) | 0b1000'0000;
+ buffer[1] = static_cast(value >> 8);
+ RF_CALL(SpiMaster::transfer(buffer.data(), nullptr, 2));
+
+ if (this->releaseMaster()) {
+ Cs::set();
+ }
+ RF_END_RETURN(true);
+}
+
+template
+modm::ResumableResult
+Adis16470::readRegisterSequence(std::span sequence, std::span values)
+{
+ RF_BEGIN();
+
+ if(sequence.size() != values.size()) {
+ // Mismatching std::span sizes
+ RF_RETURN(false);
+ }
+
+ for (i = 0; i < sequence.size(); i++) {
+ if (getRegisterAccess(sequence[i]) == AccessMethod::Write) {
+ // Reading this register is not permitted
+ RF_RETURN(false);
+ }
+
+ buffer[0] = uint8_t(sequence[i]) & 0b0111'1111;
+ buffer[1] = 0;
+ RF_CALL(SpiMaster::transfer(buffer.data(), &buffer[2], 2));
+
+ if (i != 0) {
+ values[i-1] = (static_cast(buffer[2]) << 8) | buffer[3];
+ }
+ }
+
+ // one additionl transfer to retrieve value of last register
+ buffer[0] = uint8_t(sequence[0]) & 0b0111'1111;
+ buffer[1] = 0;
+ RF_CALL(SpiMaster::transfer(buffer.data(), &buffer[2], 2));
+ values[values.size()-1] = (static_cast(buffer[2]) << 8) | buffer[3];
+
+ RF_END_RETURN(true);
+}
+
+template
+modm::ResumableResult
+Adis16470::readRegisterBurst(std::array& data)
+{
+ RF_BEGIN();
+
+ buffer.fill(0);
+ buffer[0] = 0x68;
+
+ RF_WAIT_UNTIL(this->acquireMaster());
+ Cs::reset();
+
+ RF_CALL(SpiMaster::transfer(buffer.data(), reinterpret_cast(data.data()), 22));
+
+ if (this->releaseMaster()) {
+ Cs::set();
+ }
+
+ // Calulate checksum
+ checksum = 0;
+ for (i = 0; i < 18; i++) {
+ checksum += reinterpret_cast(data.data() + 2)[i];
+ }
+
+ RF_END_RETURN(checksum == reinterpret_cast(data.data())[21]);
+}
+
+} // namespace modm