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