diff --git a/README.md b/README.md
index a9c292b79e..0001ff98e9 100644
--- a/README.md
+++ b/README.md
@@ -768,109 +768,110 @@ your specific needs.
| ADS7843 |
ADS816x |
+ADS868X |
AMS5915 |
APA102 |
AS5047 |
-AS5600 |
+| AS5600 |
AT24MAC402 |
SPI Flash |
BME280 |
BMI088 |
BMP085 |
-BNO055 |
+| BNO055 |
CAT24AA |
CYCLE-COUNTER |
DRV832X |
DS1302 |
DS1631 |
-DS18B20 |
+| DS18B20 |
DW3110 |
EA-DOG |
Encoder Input |
Encoder Input BitBang |
Encoder Output BitBang |
-FT245 |
+| FT245 |
FT6x06 |
Gpio Sampler |
HCLAx |
HD44780 |
HMC58x |
-HMC6343 |
+| HMC6343 |
HX711 |
I2C-EEPROM |
ILI9341 |
IS31FL3733 |
ITG3200 |
-IXM42XXX |
+| IXM42XXX |
L3GD20 |
LAN8720A |
LAWICEL |
LIS302DL |
LIS3DSH |
-LIS3MDL |
+| LIS3MDL |
LM75 |
LP503x |
LSM303A |
LSM6DS33 |
LSM6DSO |
-LTC2984 |
+| LTC2984 |
MAX31855 |
MAX31865 |
MAX6966 |
MAX7219 |
MCP23x17 |
-MCP2515 |
+| MCP2515 |
MCP3008 |
MCP7941x |
MCP990X |
MMC5603 |
MS5611 |
-MS5837 |
+| MS5837 |
NOKIA5110 |
NRF24 |
TFT-DISPLAY |
PAT9125EL |
PCA8574 |
-PCA9535 |
+| PCA9535 |
PCA9548A |
PCA9685 |
QMC5883L |
SH1106 |
SIEMENS-S65 |
-SIEMENS-S75 |
+| SIEMENS-S75 |
SK6812 |
SK9822 |
SSD1306 |
ST7586S |
ST7789 |
-STTS22H |
+| STTS22H |
STUSB4500 |
SX1276 |
SX128X |
TCS3414 |
TCS3472 |
-TLC594x |
+| TLC594x |
TMP102 |
TMP12x |
TMP175 |
TOUCH2046 |
VL53L0 |
-VL6180 |
+| VL6180 |
WS2812 |
diff --git a/examples/nucleo_f429zi/adc_ads868x/main.cpp b/examples/nucleo_f429zi/adc_ads868x/main.cpp
new file mode 100644
index 0000000000..7aa50084c6
--- /dev/null
+++ b/examples/nucleo_f429zi/adc_ads868x/main.cpp
@@ -0,0 +1,58 @@
+/*
+ * 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/.
+ */
+// ----------------------------------------------------------------------------
+
+#include
+#include
+
+using namespace Board;
+
+using SpiMaster = SpiMaster1;
+using Mosi = GpioB5;
+using Miso = GpioB4;
+using Sck = GpioB3;
+using Cs = GpioA4;
+using Rst = GpioF12;
+
+using Ads868x = modm::Ads868x;
+
+Ads868x adc{};
+
+int
+main()
+{
+ Board::initialize();
+ Leds::setOutput();
+
+ MODM_LOG_INFO << "ADS868X Demo on Nucleo-F429ZI\n" << modm::endl;
+
+ SpiMaster::initialize();
+ SpiMaster::connect();
+
+ Rst::setOutput();
+ Cs::setOutput(true);
+
+ MODM_LOG_INFO << "Initializing ADC..." << modm::endl;
+ adc.initialize();
+
+ uint32_t counter(0);
+
+ while (true)
+ {
+ Leds::write(counter % ((1 << (Leds::width+1)) - 1));
+
+ uint16_t result = adc.singleConversion();
+ MODM_LOG_INFO.printf("ADC Manual: %05u\n", result);
+
+ modm::delay(Button::read() ? 1ms : 500ms);
+ }
+
+ return 0;
+}
diff --git a/examples/nucleo_f429zi/adc_ads868x/project.xml b/examples/nucleo_f429zi/adc_ads868x/project.xml
new file mode 100644
index 0000000000..365e55e9b7
--- /dev/null
+++ b/examples/nucleo_f429zi/adc_ads868x/project.xml
@@ -0,0 +1,11 @@
+
+ modm:nucleo-f429zi
+
+
+
+
+ modm:build:scons
+ modm:platform:spi:1
+ modm:driver:ads868x
+
+
diff --git a/src/modm/driver/adc/ads868x.hpp b/src/modm/driver/adc/ads868x.hpp
new file mode 100644
index 0000000000..02a48c112c
--- /dev/null
+++ b/src/modm/driver/adc/ads868x.hpp
@@ -0,0 +1,274 @@
+// coding: utf-8
+// ----------------------------------------------------------------------------
+/*
+ * Copyright (c) 2020, Vivien Henry
+ * Copyright (c) 2023, Niklas Hauser
+ *
+ * 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_ADS868x_HPP
+#define MODM_ADS868x_HPP
+
+#include
+#include
+#include
+#include
+
+namespace modm
+{
+
+/// @ingroup modm_driver_ads868x
+struct ads868x
+{
+ enum class
+ Register: uint8_t
+ {
+ DEV_ID_REG = 0x00,
+ RST_PWRCTRL_REG = 0x04,
+ SDI_CTL_REG = 0x08,
+ SDO_CTL_REG = 0x0C,
+ DATAOUT_CTL_REG = 0x10,
+ RANGE_CTL_REG = 0x14,
+ ALARM_REG = 0x20,
+ ALARM_H_TH_REG = 0x24,
+ ALARM_L_TH_REG = 0x28,
+ };
+
+
+ /// Device ID register
+ enum class
+ DeviceIDRegister : uint32_t
+ {
+ Addr0 = Bit16,
+ Addr1 = Bit17,
+ Addr2 = Bit18,
+ Addr3 = Bit19
+ };
+ MODM_FLAGS32(DeviceIDRegister);
+
+ typedef modm::Value DeviceID_t;
+
+
+ // Reset and Power Control register
+ enum class
+ ResetPowerControlRegister: uint32_t
+ {
+ WKey0 = Bit8,
+ WKey1 = Bit9,
+ WKey2 = Bit10,
+ WKey3 = Bit11,
+ WKey4 = Bit12,
+ WKey5 = Bit13,
+ WKey6 = Bit14,
+ WKey7 = Bit15,
+
+ VDDAlarmDisable = Bit5,
+
+ InputAlarmDisable = Bit4,
+ RSTn_ApplicationReset = Bit2,
+ NapModeEnable = Bit1,
+ PowerDownEnable = Bit0
+ };
+ MODM_FLAGS32(ResetPowerControlRegister);
+
+ typedef modm::Value WriteKey_t;
+
+ // SDI Data Input Control register
+ enum class
+ SDIControlRegister: uint32_t
+ {
+ SDIMode0 = Bit0,
+ SDIMode1 = Bit1
+ };
+ MODM_FLAGS32(SDIControlRegister);
+
+ enum class
+ SDIMode : uint8_t
+ {
+ Std_Pol0_Phase0 = 0,
+ Std_Pol0_Phase1 = 1,
+ Std_Pol1_Phase0 = 2,
+ Std_Pol1_Phase1 = 3
+ };
+ typedef modm::Configuration SDIMode_t;
+
+ // SDO Data Output Control Register
+ enum class
+ SDOControlRegister: uint32_t
+ {
+ SDOMode0 = Bit0,
+ SDOMode1 = Bit1,
+
+ SSyncClock = Bit6,
+
+ SDO1Config0 = Bit8,
+ SDO1Config1 = Bit9,
+
+ GPOValue = Bit12
+ };
+ MODM_FLAGS32(SDOControlRegister);
+
+ enum class
+ SDOMode: uint8_t
+ {
+ SameAsSDI0 = 0b00,
+ SameAsSDI1 = 0b01,
+ Invalid = 0b10,
+ ADCMasterClk_SourcSync = 0b11
+ };
+ typedef modm::Configuration SDOMode_t;
+
+
+ enum class
+ SourceSyncClock: uint8_t
+ {
+ External = 0,
+ Internal = 1
+ };
+ typedef modm::Configuration SourceSyncClock_t;
+
+ enum class
+ SDO1Config: uint8_t
+ {
+ SDO1_Tristated = 0b00,
+ SDO1_Alarm = 0b01,
+ SDO1_GPO = 0b10,
+ SDO1_2BitsSDO = 0b11
+ };
+ typedef modm::Configuration SDO1Config_t;
+
+
+ enum class
+ DataOutControlRegister: uint32_t
+ {
+ DataVal0 = Bit0,
+ DataVal1 = Bit1,
+ DataVal2 = Bit2,
+
+ ParityEnable = Bit3,
+
+ Inc_Range = Bit8,
+
+ Inc_InActiveAlarm_High = Bit10,
+ Inc_InActiveAlarm1_Low = Bit11,
+
+ Inc_VDDActiveAlarm0_High = Bit12,
+ Inc_VDDActiveAlarm1_Low = Bit13,
+
+ Inc_DeviceAddr = Bit14
+ };
+ MODM_FLAGS32(DataOutControlRegister);
+
+ enum class
+ DataValue: uint8_t
+ {
+ ConversionData = 0b000,
+ All0 = 0b100,
+ All1 = 0b101,
+ Seq01 = 0b110,
+ Seq0011 = 0b111
+ };
+ typedef modm::Configuration DataValue_t;
+
+
+ enum class
+ RangeSelectionRegister: uint32_t
+ {
+ RangeSel0 = Bit0,
+ RangeSel1 = Bit1,
+ RangeSel2 = Bit2,
+ RangeSel3 = Bit3,
+
+ InternalRefDisabled = Bit6
+ };
+ MODM_FLAGS32(RangeSelectionRegister);
+
+ enum class
+ RangeSel: uint8_t
+ {
+ Range_Bipolar_3_000_VRef = 0b0000,
+ Range_Bipolar_2_500_VRef = 0b0001,
+ Range_Bipolar_1_500_VRef = 0b0010,
+ Range_Bipolar_1_250_VRef = 0b0011,
+ Range_Bipolar_0_625_VRef = 0b0100,
+ Range_Unipolar_3_00_VRef = 0b1000,
+ Range_Unipolar_2_50_VRef = 0b1001,
+ Range_Unipolar_1_50_VRef = 0b1010,
+ Range_Unipolar_1_25_VRef = 0b1011
+ };
+ typedef modm::Configuration RangeSel_t;
+
+}; // struct ads868x
+
+/**
+ * @tparam SpiMaster SpiMaster interface
+ * @tparam Cs Chip-select pin
+ *
+ * @author Vivien Henry
+ * @ingroup modm_driver_ads868x
+ */
+template
+class Ads868x : public ads868x, public modm::SpiDevice
+{
+public:
+ /// Call this function once before using the device
+ void
+ initialize();
+
+ uint16_t
+ singleConversion();
+
+ void
+ setDeviceAddress(uint8_t devID)
+ {
+ writeRegister(Register::DEV_ID_REG, DeviceID_t(devID).value);
+ };
+
+ uint8_t
+ getDeviceAddress()
+ {
+ const uint32_t devID = readRegister(Register::DEV_ID_REG);
+ return DeviceID_t::get(DeviceIDRegister_t(devID));
+ }
+
+ void
+ setOutputProtocol(SDOMode mode, SourceSyncClock syncClock, SDO1Config sdo1Config)
+ {
+ SDOControlRegister_t sdo_reg = SDOMode_t(mode) | SourceSyncClock_t(syncClock) | SDO1Config_t(sdo1Config);
+ writeRegister(Register::SDO_CTL_REG, sdo_reg.value);
+ }
+
+ void
+ setRange(RangeSel range)
+ {
+ writeRegister(Register::RANGE_CTL_REG, RangeSel_t(range).value);
+ }
+
+ RangeSel
+ getRange()
+ {
+ const uint32_t range = readRegister(Register::RANGE_CTL_REG);
+ return RangeSel_t::get(RangeSelectionRegister_t(range));
+ }
+
+private:
+ void
+ writeRegister(Register reg, uint32_t data);
+
+ uint32_t
+ readRegister(Register reg);
+
+ uint8_t buffer[4];
+};
+
+} // namespace modm
+
+#include "ads868x_impl.hpp"
+
+#endif // MODM_ADS868x_HPP
diff --git a/src/modm/driver/adc/ads868x.lb b/src/modm/driver/adc/ads868x.lb
new file mode 100644
index 0000000000..ba79f566e6
--- /dev/null
+++ b/src/modm/driver/adc/ads868x.lb
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2018, Niklas Hauser
+#
+# 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:ads868x"
+ module.description = """
+# ADS868x ADC
+
+ADS868x 16-Bit, High-Speed, Single-Supply, SAR ADC Data Acquisition System with
+Programmable, Bipolar Input Ranges.
+
+This driver supports these three devices:
+- ADS8681
+- ADS8685
+- ADS8689
+
+[Datasheet](https://www.ti.com/lit/ds/symlink/ads8681.pdf)
+"""
+
+
+def prepare(module, options):
+ module.depends(
+ ":architecture:gpio",
+ ":architecture:register",
+ ":architecture:spi.device",
+ ":architecture:fiber")
+ return True
+
+
+def build(env):
+ env.outbasepath = "modm/src/modm/driver/adc"
+ env.copy("ads868x.hpp")
+ env.copy("ads868x_impl.hpp")
diff --git a/src/modm/driver/adc/ads868x_impl.hpp b/src/modm/driver/adc/ads868x_impl.hpp
new file mode 100644
index 0000000000..31268895d2
--- /dev/null
+++ b/src/modm/driver/adc/ads868x_impl.hpp
@@ -0,0 +1,132 @@
+// coding: utf-8
+// ----------------------------------------------------------------------------
+/*
+ * Copyright (c) 2020, Vivien Henry
+ * Copyright (c) 2023, Niklas Hauser
+ *
+ * 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_ADS868x_HPP
+# error "Don't include this file directly! Use 'ads868x.hpp' instead."
+#endif
+
+// ----------------------------------------------------------------------------
+namespace modm
+{
+
+
+template
+void
+Ads868x::initialize()
+{
+ Cs::setOutput(modm::Gpio::Low);
+ nReset::setOutput(modm::Gpio::Low);
+
+ modm::this_fiber::sleep_for(1ms);
+
+ nReset::set();
+ Cs::set();
+}
+
+
+template
+uint16_t
+Ads868x::singleConversion()
+{
+ modm::this_fiber::poll([&]{ return this->acquireMaster(); });
+ Cs::reset();
+
+ SpiMaster::transfer(nullptr, buffer, 2);
+
+ if (this->releaseMaster()) Cs::set();
+
+ return uint16_t((buffer[0] << 8) | buffer[1]);
+}
+
+
+template
+void
+Ads868x::writeRegister(Register reg, uint32_t data)
+{
+ // LSB (0-15)
+ buffer[0] = 0b1101'0000;
+ buffer[1] = uint8_t(reg);
+ buffer[2] = data >> 8;
+ buffer[3] = data;
+
+ modm::this_fiber::poll([&]{ return this->acquireMaster(); });
+ Cs::reset();
+
+ SpiMaster::transfer(buffer, nullptr, 4);
+
+ Cs::set();
+
+ // MSB (16-31)
+ buffer[0] = 0b1101'0000;
+ buffer[1] = uint8_t(reg) + 2;
+ buffer[2] = data >> 24;
+ buffer[3] = data >> 16;
+
+ modm::this_fiber::sleep_for(1us);
+ Cs::reset();
+
+ SpiMaster::transfer(buffer, nullptr, 4);
+
+ if (this->releaseMaster()) Cs::set();
+}
+
+template
+uint32_t
+Ads868x::readRegister(Register reg)
+{
+ uint32_t result{};
+
+ modm::this_fiber::poll([&]{ return this->acquireMaster(); });
+ Cs::reset();
+
+ // MSB (31-16)
+ buffer[0] = 0b1100'1000;
+ buffer[1] = uint8_t(reg) + 2;
+ buffer[2] = 0;
+ buffer[3] = 0;
+
+ SpiMaster::transfer(buffer, nullptr, 4);
+
+ Cs::set();
+ modm::this_fiber::sleep_for(1us);
+ Cs::reset();
+
+ SpiMaster::transfer(nullptr, buffer, 4);
+ result = (uint32_t(buffer[0]) << 24) | (uint32_t(buffer[1]) << 16);
+
+ Cs::set();
+ modm::this_fiber::sleep_for(1us);
+ Cs::reset();
+
+ // LSB (0-15)
+ buffer[0] = 0b1100'1000;
+ buffer[1] = uint8_t(reg);
+ buffer[2] = 0;
+ buffer[3] = 0;
+
+ SpiMaster::transfer(buffer, nullptr, 4);
+
+ Cs::set();
+ modm::this_fiber::sleep_for(1us);
+ Cs::reset();
+
+ SpiMaster::transfer(nullptr, buffer, 4);
+ result |= (uint32_t(buffer[0]) << 8) | uint32_t(buffer[1]);
+
+ if (this->releaseMaster()) Cs::set();
+
+ return result;
+}
+
+} // namespace modm