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