diff --git a/README.md b/README.md
index f549319882..bdae9839ad 100644
--- a/README.md
+++ b/README.md
@@ -721,100 +721,101 @@ your specific needs.
ADS816x |
AMS5915 |
APA102 |
+AS5047 |
AT24MAC402 |
-SPI Flash |
+SPI Flash |
BME280 |
BMI088 |
BMP085 |
BNO055 |
CAT24AA |
-CYCLE-COUNTER |
+CYCLE-COUNTER |
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 |
IXM42XXX |
L3GD20 |
LAN8720A |
-LAWICEL |
+LAWICEL |
LIS302DL |
LIS3DSH |
LIS3MDL |
LM75 |
LP503x |
-LSM303A |
+LSM303A |
LSM6DS33 |
LSM6DSO |
LTC2984 |
MAX31855 |
MAX31865 |
-MAX6966 |
+MAX6966 |
MAX7219 |
MCP23x17 |
MCP2515 |
MCP3008 |
MCP7941x |
-MCP990X |
+MCP990X |
MMC5603 |
MS5611 |
MS5837 |
NOKIA5110 |
NRF24 |
-TFT-DISPLAY |
+TFT-DISPLAY |
PAT9125EL |
PCA8574 |
PCA9535 |
PCA9548A |
PCA9685 |
-QMC5883L |
+QMC5883L |
SH1106 |
SIEMENS-S65 |
SIEMENS-S75 |
SK6812 |
SK9822 |
-SSD1306 |
+SSD1306 |
ST7586S |
ST7789 |
STTS22H |
STUSB4500 |
SX1276 |
-SX128X |
+SX128X |
TCS3414 |
TCS3472 |
TLC594x |
TMP102 |
TMP12x |
-TMP175 |
+TMP175 |
TOUCH2046 |
VL53L0 |
VL6180 |
diff --git a/examples/nucleo_g474re/as5047/main.cpp b/examples/nucleo_g474re/as5047/main.cpp
new file mode 100644
index 0000000000..d7feb52b31
--- /dev/null
+++ b/examples/nucleo_g474re/as5047/main.cpp
@@ -0,0 +1,79 @@
+// coding: utf-8
+/*
+ * Copyright (c) 2024, Henrik Hose
+ *
+ * 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
+#include
+
+using SpiMaster = SpiMaster1;
+
+using Cs = modm::platform::GpioB10;
+using Mosi = modm::platform::GpioB5;
+using Miso = modm::platform::GpioB4;
+using Sck = modm::platform::GpioB3;
+
+using namespace Board;
+using namespace modm::literals;
+
+class EncoderThread : public modm::pt::Protothread
+{
+public:
+ EncoderThread() : encoder(data) {}
+
+ bool
+ run()
+ {
+ PT_BEGIN();
+
+ while (true)
+ {
+ PT_CALL(encoder.readout());
+
+ MODM_LOG_INFO << "\nNew readout:" << modm::endl;
+ MODM_LOG_INFO << " angle degree: " << data.getAngleDeg() << " degrees" << modm::endl;
+ MODM_LOG_INFO << " angle rad: " << data.getAngleRad() << " radians" << modm::endl;
+ MODM_LOG_INFO << " angle raw: " << data.getAngleRaw() << modm::endl;
+
+ timeout.restart(std::chrono::milliseconds(500));
+ PT_WAIT_UNTIL(timeout.isExpired());
+ }
+
+ PT_END();
+ }
+
+private:
+ modm::as5047::Data data;
+ modm::As5047 encoder;
+
+ modm::ShortTimeout timeout;
+} encoderThread;
+
+int
+main()
+{
+ Board::initialize();
+
+ Cs::setOutput(modm::Gpio::High);
+
+ SpiMaster::connect();
+ SpiMaster::initialize();
+
+ MODM_LOG_INFO << "==========AS5047 Test==========" << modm::endl;
+ MODM_LOG_DEBUG << "Debug logging here" << modm::endl;
+ MODM_LOG_INFO << "Info logging here" << modm::endl;
+ MODM_LOG_WARNING << "Warning logging here" << modm::endl;
+ MODM_LOG_ERROR << "Error logging here" << modm::endl;
+ MODM_LOG_INFO << "===============================" << modm::endl;
+
+ while (true) { encoderThread.run(); }
+
+ return 0;
+}
\ No newline at end of file
diff --git a/examples/nucleo_g474re/as5047/project.xml b/examples/nucleo_g474re/as5047/project.xml
new file mode 100644
index 0000000000..4b86fa2b25
--- /dev/null
+++ b/examples/nucleo_g474re/as5047/project.xml
@@ -0,0 +1,14 @@
+
+ modm:nucleo-g474re
+
+
+
+
+ modm:driver:as5047
+ modm:platform:gpio
+ modm:platform:spi:1
+ modm:processing:protothread
+ modm:processing:timer
+ modm:build:scons
+
+
\ No newline at end of file
diff --git a/src/modm/driver/encoder/as5047.hpp b/src/modm/driver/encoder/as5047.hpp
new file mode 100644
index 0000000000..0f597a0f01
--- /dev/null
+++ b/src/modm/driver/encoder/as5047.hpp
@@ -0,0 +1,157 @@
+// coding: utf-8
+// ----------------------------------------------------------------------------
+/*
+ * Copyright (c) 2024, Henrik Hose
+ *
+ * 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_AS5047_HPP
+#define MODM_AS5047_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace modm
+{
+
+/// @cond
+namespace detail
+{
+constexpr uint16_t
+as5047_setMsbToEvenParity(const uint16_t num)
+{
+ uint16_t par = 0x7fff & num;
+ par ^= par >> 8;
+ par ^= par >> 4;
+ par ^= par >> 2;
+ par ^= par >> 1;
+ return ((par & 1) << 15) | (0x7fff & num);
+}
+} // namespace detail
+/// @endcond
+
+/// @ingroup modm_driver_as5047
+struct as5047
+{
+ enum class Errorfl : uint16_t
+ {
+ Parerr = Bit2,
+ Invcomm = Bit1,
+ Frerr = Bit0,
+ };
+ MODM_FLAGS16(Errorfl)
+
+ enum class Prog : uint16_t
+ {
+ Progver = Bit6,
+ Progotp = Bit3,
+ Otpref = Bit2,
+ Progen = Bit0,
+ };
+ MODM_FLAGS16(Prog)
+
+ enum class Diaagc : uint16_t
+ {
+ Magl = Bit11,
+ Magh = Bit10,
+ Cof = Bit9,
+ Lf = Bit8,
+ };
+ MODM_FLAGS16(Diaagc)
+
+ enum class Register : uint16_t
+ {
+ ReadNop = detail::as5047_setMsbToEvenParity(((1 << 14) | 0x0000)),
+ ReadErrfl = detail::as5047_setMsbToEvenParity(((1 << 14) | 0x0001)),
+ ReadProg = detail::as5047_setMsbToEvenParity(((1 << 14) | 0x0003)),
+ ReadDiaagc = detail::as5047_setMsbToEvenParity(((1 << 14) | 0x3FFC)),
+ ReadMag = detail::as5047_setMsbToEvenParity(((1 << 14) | 0x3FFD)),
+ ReadAngleunc = detail::as5047_setMsbToEvenParity(((1 << 14) | 0x3FFE)),
+ ReadAnglecom = detail::as5047_setMsbToEvenParity(((1 << 14) | 0x3FFF)),
+
+ };
+
+ struct modm_packed Data
+ {
+ /// @return
+ constexpr float
+ getAngleRad() const
+ {
+ const uint16_t angle = data & 0x3fff;
+ return static_cast(angle) / 16383.f * 2.f * std::numbers::pi_v;
+ }
+
+ /// @return
+ constexpr float
+ getAngleDeg() const
+ {
+ const uint16_t angle = data & 0x3fff;
+ return static_cast(angle) / 16383.f * 360.f;
+ }
+
+ /// @return
+ constexpr uint16_t
+ getAngleRaw() const
+ {
+ const uint16_t angle = data & 0x3fff;
+ return angle;
+ }
+
+ uint16_t data;
+ };
+}; // struct as5047
+
+/**
+ * @tparam SpiMaster
+ * @tparam Cs
+ *
+ * @author Henrik Hose
+ * @ingroup modm_driver_as5047
+ */
+template
+class As5047 : public as5047, public modm::SpiDevice, protected modm::NestedResumable<5>
+{
+public:
+ using Data = as5047::Data;
+
+ /**
+ * @param data pointer to buffer of the internal data of type Data
+ */
+ As5047(Data &data);
+
+ /// Call this function once before using the device
+ modm::ResumableResult
+ initialize();
+
+ /// Read the raw data from the sensor
+ modm::ResumableResult
+ readout();
+
+ /// Get the data object for this sensor
+ inline Data &
+ getData()
+ {
+ return data;
+ }
+
+private:
+ Data &data;
+ uint8_t inBuffer[2];
+ uint8_t outBuffer[2];
+};
+
+} // namespace modm
+
+#include "as5047_impl.hpp"
+
+#endif // MODM_AS5047_HPP
\ No newline at end of file
diff --git a/src/modm/driver/encoder/as5047.lb b/src/modm/driver/encoder/as5047.lb
new file mode 100644
index 0000000000..a556ebdb74
--- /dev/null
+++ b/src/modm/driver/encoder/as5047.lb
@@ -0,0 +1,32 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2024, Henrik Hose
+#
+# 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:as5047"
+ module.description = """\
+# AS5047 14 bit Absolute Encoder SPI Driver
+
+[Datasheet](https://ams.com/documents/20143/36005/AS5047D_DS000394_2-00.pdf)
+"""
+
+def prepare(module, options):
+ module.depends(
+ ":architecture:gpio",
+ ":architecture:spi.device",
+ ":processing:resumable"
+ )
+ return True
+
+def build(env):
+ env.outbasepath = "modm/src/modm/driver/encoder"
+ env.copy("as5047.hpp")
+ env.copy("as5047_impl.hpp")
diff --git a/src/modm/driver/encoder/as5047_impl.hpp b/src/modm/driver/encoder/as5047_impl.hpp
new file mode 100644
index 0000000000..df23d7187e
--- /dev/null
+++ b/src/modm/driver/encoder/as5047_impl.hpp
@@ -0,0 +1,54 @@
+// coding: utf-8
+// ----------------------------------------------------------------------------
+/*
+ * Copyright (c) 2024, Henrik Hose
+ *
+ * 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_AS5047_HPP
+#error "Don't include this file directly, use 'as5047.hpp' instead!"
+#endif
+
+namespace modm
+{
+
+template
+As5047::As5047(Data &data) : data(data)
+{
+ this->attachConfigurationHandler([]() { SpiMaster::setDataMode(SpiMaster::DataMode::Mode1); });
+ Cs::setOutput(modm::Gpio::High);
+}
+
+template
+modm::ResumableResult
+As5047::readout()
+{
+ RF_BEGIN();
+
+ RF_WAIT_UNTIL(this->acquireMaster());
+
+ Cs::reset();
+ outBuffer[1] = static_cast(Register::ReadAngleunc);
+ outBuffer[0] = static_cast(static_cast(Register::ReadAngleunc) >> 8);
+ RF_CALL(SpiMaster::transfer(outBuffer, inBuffer, 2));
+ Cs::set();
+
+ modm::delay_us(1);
+
+ Cs::reset();
+ outBuffer[1] = 0;
+ outBuffer[0] = 0;
+ RF_CALL(SpiMaster::transfer(outBuffer, inBuffer, 2));
+ data.data = static_cast(inBuffer[1]) | (static_cast(inBuffer[0]) << 8);
+
+ if (this->releaseMaster()) { Cs::set(); }
+ RF_END();
+}
+
+} // namespace modm
\ No newline at end of file