diff --git a/README.md b/README.md index 899e26f1ee..87c045de72 100644 --- a/README.md +++ b/README.md @@ -593,86 +593,87 @@ you specific needs. AD7928 ADNS9800 ADS7843 +ADS816X AMS5915 -APA102 +APA102 SPI-FLASH BME280 BMP085 BNO055 CAT24AA -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 L3GD20 LAN8720A LAWICEL LIS302DL -LIS3DSH +LIS3DSH LIS3MDL LM75 LP503X LSM303A LSM6DS33 -LTC2984 +LTC2984 MAX6966 MAX7219 MCP23X17 MCP2515 MCP7941X -MMC5603 +MMC5603 NOKIA5110 NRF24 TFT-DISPLAY PAT9125EL PCA8574 -PCA9535 +PCA9535 PCA9548A PCA9685 SH1106 SIEMENS-S65 SIEMENS-S75 -SK6812 +SK6812 SK9822 SSD1306 ST7586S STTS22H STUSB4500 -SX1276 +SX1276 TCS3414 TCS3472 TLC594X TMP102 TMP12X -TMP175 +TMP175 TOUCH2046 VL53L0 VL6180 diff --git a/src/modm/driver/adc/ads816x.hpp b/src/modm/driver/adc/ads816x.hpp new file mode 100644 index 0000000000..f4421df552 --- /dev/null +++ b/src/modm/driver/adc/ads816x.hpp @@ -0,0 +1,139 @@ +// coding: utf-8 +// ---------------------------------------------------------------------------- +/* + * Copyright (c) 2021, 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_ADS816X_HPP +#define MODM_ADS816X_HPP + +#include +#include +#include +#include +#include + +namespace modm +{ + +/// @ingroup modm_driver_ads816x +struct ads816x +{ + enum class + Register : uint16_t + { + REG_ACCESS = 0x000, + PD_CNTL = 0x004, + SDI_CNTL = 0x008, + SDO_CNTL1 = 0x00C, + SDO_CNTL2 = 0x00D, + SDO_CNTL3 = 0x00E, + SDO_CNTL4 = 0x00F, + DATA_CNTL = 0x010, + PARITY_CNTL = 0x011, + + OFST_CAL = 0x018, + REF_MRG1 = 0x019, + REF_MRG2 = 0x01A, + REFby2_MRG = 0x01B, + + AIN_CFG = 0x024, + COM_CFG = 0x027, + + DEVICE_CFG = 0x01C, + CHANNEL_ID = 0x01D, + SEQ_START = 0x01E, + SEQ_STOP = 0x01F, + ON_THE_FLY_CFG = 0x02A, + AUTO_SEQ_CFG1 = 0x080, + AUTO_SEQ_CFG2 = 0x082, + + // Alert, etc... + }; + + enum class + Command : uint8_t + { + NoOperation = 0b00000 << 3, + Write = 0b00001 << 3, + Read = 0b00010 << 3, + SetBits = 0b00011 << 3, + ClearBits = 0b00100 << 3, + }; + + enum class + Mode : uint8_t + { + Manual = 0b00, + OnTheFly = 0b01, + AutoSequence = 0b10, + CustomSequence = 0b11, + }; + + enum class + DataFormat : uint8_t + { + OnlyResult = (0b00 << 4), + ResultChannel = (0b01 << 4), + ResultChannelStatus = (0b10 << 4), + //Reserved = (0b11 << 4), + }; +}; + +/** + * @tparam SpiMaster SpiMaster interface + * @tparam Cs Chip-select pin + * + * @author Raphael Lehmann + * @ingroup modm_driver_ads816x + */ +template +class Ads816x : public ads816x, public modm::SpiDevice, protected modm::NestedResumable<3> +{ +public: + Ads816x() = default; + + /// Call this function before using the device or to change operation mode + /// \warning Only Mode::Manual is currently supported! + modm::ResumableResult + initialize(Mode mode = Mode::Manual); + + /// Initiate a single conversion and return the result of the conversion. + /// Simultanously the channel for the after next conversion will be set, + /// i.e. before reading the first valid data two dummy conversions have to + /// be executed. + modm::ResumableResult + manualModeConversion(uint8_t afterNextChannel); + + /* + // FIXME: Does not work correctly... + /// Start a "auto sequence" conversion. + /// The device will automatically cycle through the specified channels in + /// the bitmask. + template + modm::ResumableResult + autoSequenceConversion(uint8_t channelsBitmask, std::span result); + */ + +private: + modm::ResumableResult + registerAccess(Command command, Register reg, uint8_t value = 0); + + uint8_t buffer[3]; + uint8_t buffer2[3]; + + modm::ShortPreciseTimeout timeout; +}; + +} // namespace modm + +#include "ads816x_impl.hpp" + +#endif // MODM_ADS816X_HPP diff --git a/src/modm/driver/adc/ads816x.lb b/src/modm/driver/adc/ads816x.lb new file mode 100644 index 0000000000..84f75ece22 --- /dev/null +++ b/src/modm/driver/adc/ads816x.lb @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2021, 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:ads816x" + module.description = """\ +# ADS816x ADC + +The ADS8166/ADS8167/ADS8168 are, 16 bit analog-digital converters with, sample +rates of 1 MSPS (ADS8168), 500 kSPS (ADS8167), and 250 kSPS (ADS8166). +The conversion time is determined by the chip select signal. +A maximum Spi clock of 70 MHz is supported. + +This driver only implements the _manual mode_ ~~and _auto sequence mode_~~ +using the SPI interface. +Only the default multiplexer configuration with 8 single-ended inputs and no +pseudo-differential inputs is supported by now. +""" + + +def prepare(module, options): + module.depends( + ":architecture:gpio", + ":architecture:spi.device", + ":io", + ":processing:resumable", + ":processing:timer") + return True + +def build(env): + env.outbasepath = "modm/src/modm/driver/adc" + env.copy("ads816x.hpp") + env.copy("ads816x_impl.hpp") diff --git a/src/modm/driver/adc/ads816x_impl.hpp b/src/modm/driver/adc/ads816x_impl.hpp new file mode 100644 index 0000000000..04656eba18 --- /dev/null +++ b/src/modm/driver/adc/ads816x_impl.hpp @@ -0,0 +1,155 @@ +// coding: utf-8 +// ---------------------------------------------------------------------------- +/* + * Copyright (c) 2021, 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_ADS816X_HPP +# error "Don't include this file directly! Use 'ads816x.hpp' instead." +#endif + +//#include +//#include + +namespace modm +{ + +template +ResumableResult +Ads816x::initialize(Mode mode) +{ + using namespace std::chrono_literals; + RF_BEGIN(); + + // Unlock register access by writing magic byte + RF_CALL(registerAccess(Command::Write, Register::REG_ACCESS, 0b1010'1010)); + + // Power up everything except the internal REFby2 buffer + RF_CALL(registerAccess(Command::Write, Register::PD_CNTL, 0b0001'0000)); + + // Data type: Only ADC value + RF_CALL(registerAccess(Command::Write, Register::DATA_CNTL, uint8_t(DataFormat::OnlyResult))); + + // V_ref = 4.096V (internal reference) + RF_CALL(registerAccess(Command::Write, Register::OFST_CAL, 0b010)); + + // AIN0 ... AIN7 are separate channels + RF_CALL(registerAccess(Command::Write, Register::AIN_CFG, 0x00)); + + // All individual channels are single-ended inputs; connect the AIN-COM pin to GND + RF_CALL(registerAccess(Command::Write, Register::COM_CFG, 0x00)); + + // Set mode + RF_CALL(registerAccess(Command::Write, Register::DEVICE_CFG, uint8_t(mode))); + + RF_END(); +} + +template +ResumableResult +Ads816x::manualModeConversion(uint8_t afterNextChannel) +{ + RF_BEGIN(); + + RF_WAIT_UNTIL(this->acquireMaster()); + Cs::reset(); + + buffer[0] = uint8_t(Command::Write) | (uint16_t(Register::CHANNEL_ID) >> 8); + buffer[1] = uint16_t(Register::CHANNEL_ID); + buffer[2] = (afterNextChannel & 0b0000'0111); + + RF_CALL(SpiMaster::transfer(buffer, buffer2, 3)); + + if (this->releaseMaster()) { + Cs::set(); + } + + RF_END_RETURN( static_cast((static_cast(buffer2[0]) << 8) | buffer2[1]) ); +} + +/* +// FIXME: Does not work correctly... +template +template +ResumableResult +Ads816x::autoSequenceConversion(uint8_t channelsBitmask, std::span result) +{ + using namespace std::chrono_literals; + RF_BEGIN(); + + if (std::popcount(channelsBitmask) > N) { + RF_RETURN(); + } + + // Write channel bitmask config (AUTO_SEQ_CH) + RF_CALL(registerAccess(Command::Write, Register::AUTO_SEQ_CFG1, channelsBitmask)); + + timeout.restart(4us); + RF_WAIT_UNTIL(timeout.isExpired()); + + // Set SEQ_START (0b1 << 0) bit + RF_CALL(registerAccess(Command::SetBits, Register::SEQ_START, 0b1)); + + // Read conversion results + for (buffer2[0] = 0; buffer2[0] < std::popcount(channelsBitmask); buffer2[0]++) { + timeout.restart(4us); + RF_WAIT_UNTIL(timeout.isExpired()); + + RF_WAIT_UNTIL(this->acquireMaster()); + Cs::reset(); + + RF_CALL(SpiMaster::transfer(nullptr, buffer, 2)); + + if (this->releaseMaster()) { + Cs::set(); + } + + result[buffer2[0]] = buffer[0] << 8 | buffer[1]; + } + + RF_END(); +} +*/ + +template +modm::ResumableResult +Ads816x::registerAccess(Command command, Register reg, uint8_t value) +{ + using namespace std::chrono_literals; + RF_BEGIN(); + + buffer[0] = uint8_t(command) | (uint16_t(reg) >> 8); + buffer[1] = uint16_t(reg); + buffer[2] = value; + + RF_WAIT_UNTIL(this->acquireMaster()); + Cs::reset(); + + RF_CALL(SpiMaster::transfer(buffer, nullptr, 3)); + + if (command == Command::Read) { + Cs::set(); + timeout.restart(4us); + RF_WAIT_UNTIL(timeout.isExpired()); + + // To retrieve the result of the read command we issue another 3 byte + // transfer, nullptr instead of manual NoOperation (= 0b000...) command + Cs::reset(); + RF_CALL(SpiMaster::transfer(nullptr, buffer, 3)); + } + + if (this->releaseMaster()) { + Cs::set(); + } + + RF_END_RETURN(buffer[0]); +} + +} // namespace modm