diff --git a/README.md b/README.md
index 80bf649f33..2f33b19e22 100644
--- a/README.md
+++ b/README.md
@@ -454,7 +454,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅ |
✅ |
✅ |
-○ |
+✅ |
✅ |
✅ |
✅ |
diff --git a/src/modm/platform/spi/stm32h7/module.lb b/src/modm/platform/spi/stm32h7/module.lb
new file mode 100644
index 0000000000..6056bc2c83
--- /dev/null
+++ b/src/modm/platform/spi/stm32h7/module.lb
@@ -0,0 +1,93 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2016-2018, Niklas Hauser
+# Copyright (c) 2017, Fabian Greif
+# 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 get_properties(env):
+ device = env[":target"]
+ driver = device.get_driver("spi")
+ properties = {}
+ properties["use_fiber"] = env.query(":processing:fiber:__enabled", False)
+ properties["target"] = device.identifier
+
+ return properties
+
+class Instance(Module):
+ def __init__(self, driver, instance):
+ self.instance = int(instance)
+ self.driver = driver
+
+ def init(self, module):
+ module.name = str(self.instance)
+ module.description = "Instance {}".format(self.instance)
+
+ def prepare(self, module, options):
+ module.depends(":platform:spi")
+ return True
+
+ def build(self, env):
+ properties = get_properties(env)
+ properties["id"] = self.instance
+
+ device = env[":target"]
+ if device.identifier.family in ["h7"]:
+ properties["fifo_size"] = 16 if self.instance in (1, 2, 3) else 8
+ properties["max_data_bits"] = 32 if self.instance in (1, 2, 3) else 16
+ properties["max_crc_bits"] = properties["max_data_bits"]
+ properties["max_transfer_size"] = 2**16 - 1 # is smaller for some instances on U5
+ properties["i2s"] = self.instance in (1, 2, 3, 6)
+ else:
+ raise NotImplementedError("Unsupported device")
+
+ env.substitutions = properties
+ env.outbasepath = "modm/src/modm/platform/spi"
+
+ env.template("spi_hal.hpp.in", "spi_hal_{}.hpp".format(self.instance))
+ env.template("spi_hal_impl.hpp.in", "spi_hal_{}_impl.hpp".format(self.instance))
+ env.template("spi_master.hpp.in", "spi_master_{}.hpp".format(self.instance))
+ env.template("spi_master.cpp.in", "spi_master_{}.cpp".format(self.instance))
+ if env.has_module(":platform:dma"):
+ env.template("spi_master_dma.hpp.in", "spi_master_{}_dma.hpp".format(self.instance))
+ env.template("spi_master_dma_impl.hpp.in", "spi_master_{}_dma_impl.hpp".format(self.instance))
+
+
+def init(module):
+ module.name = ":platform:spi"
+ module.description = "Serial Peripheral Interface (SPI)"
+
+def prepare(module, options):
+ device = options[":target"]
+ if not device.has_driver("spi:stm32-extended"):
+ return False
+ if device.identifier.family not in ["h7"]:
+ # U5 is not supported yet
+ return False
+
+ module.depends(
+ ":architecture:register",
+ ":architecture:spi",
+ ":cmsis:device",
+ ":math:algorithm",
+ ":platform:gpio",
+ ":platform:rcc")
+
+ for driver in device.get_all_drivers("spi:stm32-extended"):
+ for instance in driver["instance"]:
+ module.add_submodule(Instance(driver, instance))
+
+ return True
+
+def build(env):
+ env.substitutions = get_properties(env)
+ env.outbasepath = "modm/src/modm/platform/spi"
+
+ env.template("spi_base.hpp.in")
diff --git a/src/modm/platform/spi/stm32h7/spi_base.hpp.in b/src/modm/platform/spi/stm32h7/spi_base.hpp.in
new file mode 100644
index 0000000000..2c6a1e8480
--- /dev/null
+++ b/src/modm/platform/spi/stm32h7/spi_base.hpp.in
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2013, Kevin Läufer
+ * Copyright (c) 2013-2017, Niklas Hauser
+ * Copyright (c) 2014, Daniel Krebs
+ * Copyright (c) 2020, Mike Wolfram
+ * 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_STM32H7_SPI_BASE_HPP
+#define MODM_STM32H7_SPI_BASE_HPP
+
+#include
+#include "../device.hpp"
+#include
+
+namespace modm::platform
+{
+
+/**
+ * Base class for the SPI classes
+ *
+ * Provides common definitions that do not depend on the specific SPI instance.
+ *
+ * @ingroup modm_platform_spi
+ */
+class SpiBase
+{
+public:
+ enum class
+ Interrupt : uint32_t
+ {
+ RxPacketAvailable = SPI_IER_RXPIE,
+ TxPacketSpaceAvailable = SPI_IER_TXPIE,
+ DuplexPacket = SPI_IER_DXPIE,
+ EndOfTransfer = SPI_IER_EOTIE,
+ TxTransferFilled = SPI_IER_TXTFIE,
+ Underrun = SPI_IER_UDRIE,
+ Overrun = SPI_IER_OVRIE,
+ CrcError = SPI_IER_CRCEIE,
+ TiFrameError = SPI_IER_TIFREIE,
+ ModeFault = SPI_IER_MODFIE,
+ Reload = SPI_IER_TSERFIE
+ };
+ MODM_FLAGS32(Interrupt);
+
+ enum class
+ StatusFlag : uint32_t
+ {
+ RxPacketAvailable = SPI_SR_RXP,
+ TxPacketSpaceAvailable = SPI_SR_TXP,
+ DuplexPacket = SPI_SR_DXP,
+ EndOfTransfer = SPI_SR_EOT,
+ TxTransferFilled = SPI_SR_TXTF,
+ Underrun = SPI_SR_UDR,
+ Overrun = SPI_SR_OVR,
+ CrcError = SPI_SR_CRCE,
+ TiFrameError = SPI_SR_TIFRE,
+ ModeFault = SPI_SR_MODF,
+ Reload = SPI_SR_TSERF,
+ Suspension = SPI_SR_SUSP,
+ TxTransferComplete = SPI_SR_TXC,
+ RxFifoLevel1 = SPI_SR_RXPLVL_1,
+ RxFifoLevel0 = SPI_SR_RXPLVL_0,
+ RxFifoWordNotEmpty = SPI_SR_RXWNE
+ };
+ MODM_FLAGS32(StatusFlag);
+
+ enum class
+ MasterSelection : uint32_t
+ {
+ Slave = 0,
+ Master = SPI_CFG2_MASTER,
+ Mask = Master,
+ };
+
+ enum class
+ DataMode : uint32_t
+ {
+ Mode0 = 0b00, ///< clock normal, sample on rising edge
+ Mode1 = SPI_CFG2_CPHA, ///< clock normal, sample on falling edge
+ Mode2 = SPI_CFG2_CPOL, ///< clock inverted, sample on falling edge
+ /// clock inverted, sample on rising edge
+ Mode3 = SPI_CFG2_CPOL | SPI_CFG2_CPHA,
+ Mask = Mode3
+ };
+
+ enum class
+ DataOrder : uint32_t
+ {
+ MsbFirst = 0b0,
+ LsbFirst = SPI_CFG2_LSBFRST,
+ Mask = LsbFirst,
+ };
+
+ enum class
+ Prescaler : uint32_t
+ {
+ Div2 = 0,
+ Div4 = SPI_CFG1_MBR_0,
+ Div8 = SPI_CFG1_MBR_1,
+ Div16 = SPI_CFG1_MBR_1 | SPI_CFG1_MBR_0,
+ Div32 = SPI_CFG1_MBR_2,
+ Div64 = SPI_CFG1_MBR_2 | SPI_CFG1_MBR_0,
+ Div128 = SPI_CFG1_MBR_2 | SPI_CFG1_MBR_1,
+ Div256 = SPI_CFG1_MBR_2 | SPI_CFG1_MBR_1 | SPI_CFG1_MBR_0
+ };
+
+ enum class
+ DataSize : uint32_t
+ {
+ Bit4 = 3,
+ Bit5 = 4,
+ Bit6 = 5,
+ Bit7 = 6,
+ Bit8 = 7,
+ Bit9 = 8,
+ Bit10 = 9,
+ Bit11 = 10,
+ Bit12 = 11,
+ Bit13 = 12,
+ Bit14 = 13,
+ Bit15 = 14,
+ Bit16 = 15,
+ Bit17 = 16,
+ Bit18 = 17,
+ Bit19 = 18,
+ Bit20 = 19,
+ Bit21 = 20,
+ Bit22 = 21,
+ Bit23 = 22,
+ Bit24 = 23,
+ Bit25 = 24,
+ Bit26 = 25,
+ Bit27 = 26,
+ Bit28 = 27,
+ Bit29 = 28,
+ Bit30 = 29,
+ Bit31 = 30,
+ Bit32 = 31
+ };
+ static_assert(SPI_CFG1_DSIZE_Pos == 0);
+
+ enum class DmaMode : uint32_t
+ {
+ None = 0,
+ Tx = SPI_CFG1_TXDMAEN,
+ Rx = SPI_CFG1_RXDMAEN,
+ Mask = SPI_CFG1_TXDMAEN | SPI_CFG1_RXDMAEN
+ };
+ MODM_FLAGS32(DmaMode);
+
+ enum class DuplexMode : uint32_t
+ {
+ FullDuplex = 0,
+ TransmitOnly = SPI_CFG2_COMM_0,
+ ReceiveOnly = SPI_CFG2_COMM_1,
+ HalfDuplex = SPI_CFG2_COMM_1 | SPI_CFG2_COMM_0,
+ Mask = HalfDuplex
+ };
+
+ enum class SlaveSelectMode
+ {
+ HardwareGpio,
+ Software = SPI_CFG2_SSM
+ };
+
+ enum class SlaveSelectPolarity
+ {
+ ActiveLow = 0,
+ ActiveHigh = SPI_CFG2_SSIOP
+ };
+
+ enum class CrcInit : uint32_t
+ {
+ AllZeros = 0,
+ AllOnes = SPI_CR1_TCRCINI | SPI_CR1_RCRCINI,
+ Mask = AllOnes
+ };
+};
+
+constexpr auto
+operator<=>(SpiBase::DataSize s0, SpiBase::DataSize s1)
+{
+ const auto v0 = static_cast(s0);
+ const auto v1 = static_cast(s1);
+ if (v0 < v1) {
+ return std::strong_ordering::less;
+ } else if (v0 > v1) {
+ return std::strong_ordering::greater;
+ } else {
+ return std::strong_ordering::equal;
+ }
+}
+
+} // namespace modm::platform
+
+#endif // MODM_STM32H7_SPI_BASE_HPP
diff --git a/src/modm/platform/spi/stm32h7/spi_hal.hpp.in b/src/modm/platform/spi/stm32h7/spi_hal.hpp.in
new file mode 100644
index 0000000000..214cb1d296
--- /dev/null
+++ b/src/modm/platform/spi/stm32h7/spi_hal.hpp.in
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2013, Kevin Läufer
+ * Copyright (c) 2013-2018, Niklas Hauser
+ * Copyright (c) 2014, Daniel Krebs
+ * Copyright (c) 2020, Mike Wolfram
+ * 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_STM32H7_SPI_HAL{{ id }}_HPP
+#define MODM_STM32H7_SPI_HAL{{ id }}_HPP
+
+#include "spi_base.hpp"
+
+namespace modm::platform
+{
+
+/**
+ * Serial peripheral interface (SPI{{ id }})
+ *
+ * @ingroup modm_platform_spi modm_platform_spi_{{id}}
+ */
+class SpiHal{{ id }} : public SpiBase
+{
+public:
+ static constexpr auto peripheral = Peripheral::Spi{{ id }};
+
+ static constexpr DataSize MaxDataBits = DataSize::Bit{{ max_data_bits }};
+ static constexpr DataSize MaxCrcBits = DataSize::Bit{{ max_crc_bits }};
+
+ /// Maximum permitted size for fixed size transfers
+ static constexpr std::size_t MaxTransferSize = {{ max_transfer_size }};
+
+ /// Size of FIFO in bytes
+ static constexpr std::size_t FifoSize = {{ fifo_size }};
+
+ static void
+ enableClock();
+
+ static void
+ disableClock();
+
+ static void
+ initialize(Prescaler prescaler,
+ MasterSelection masterSelection = MasterSelection::Master,
+ DataMode dataMode = DataMode::Mode0,
+ DataOrder dataOrder = DataOrder::MsbFirst,
+ DataSize dataSize = DataSize::Bit8);
+
+ static void
+ enableTransfer();
+
+ static void
+ disableTransfer();
+
+ static bool
+ isTransferEnabled();
+
+ static void
+ startMasterTransfer();
+
+ static void
+ suspendMasterTransfer();
+
+ static void
+ setDataMode(DataMode dataMode);
+
+ static void
+ setDataOrder(DataOrder dataOrder);
+
+ static void
+ setDataSize(DataSize dataSize);
+
+ static void
+ setMasterSelection(MasterSelection masterSelection);
+
+ static void
+ setDuplexMode(DuplexMode mode);
+
+ static void
+ setCrcEnabled(bool enabled);
+
+ static void
+ setCrcSize(DataSize crcSize);
+
+ static void
+ setCrcPolynomial(uint32_t poly);
+
+ static void
+ setCrcInitialValue(CrcInit init);
+
+ static void
+ setDmaMode(DmaMode_t mode);
+
+ /**
+ * Configure total amount of data items of transfer
+ * Set size to 0 for unlimited transfers.
+ * @param reload Next transfer size after reload
+ */
+ static void
+ setTransferSize(uint16_t size, uint16_t reload = 0);
+
+ static void
+ setSlaveSelectMode(SlaveSelectMode mode);
+
+ static void
+ setSlaveSelectPolarity(SlaveSelectPolarity polarity);
+
+ static void
+ setSlaveSelectState(bool state);
+
+ static uint32_t
+ transmitCrc();
+
+ static uint32_t
+ receiveCrc();
+
+ static volatile uint32_t*
+ transmitRegister();
+
+ static const volatile uint32_t*
+ receiveRegister();
+
+ static StatusFlag_t
+ status();
+
+ /// @return true if SPI_SR_EOT is set
+ static bool
+ isTransferCompleted();
+
+ /// @return true if SPI_SR_TXC is set
+ static bool
+ isTxCompleted();
+
+ /// @return true if SPI_SR_TXP is not set
+ static bool
+ isTxFifoFull();
+
+ /// @return true if SPI_SR_RXP is set
+ static bool
+ isRxDataAvailable();
+
+ /**
+ * Write up to 8 Bit to the transmit data register
+ *
+ * @warning Writing with a size smaller than the configured data size is not allowed.
+ */
+ static void
+ write(uint8_t data);
+
+ /**
+ * Write up to 16 Bit to the data register
+ * @warning Writing with a size smaller than the configured data size is not allowed.
+ */
+ static void
+ write16(uint16_t data);
+
+ /**
+ * Write up to 32 Bit to the transmit data register
+ */
+ static void
+ write32(uint32_t data);
+
+ /**
+ * Read an 8-bit value from the receive data register.
+ *
+ * @warning Reading with a size smaller than the configured data size is not allowed.
+ */
+ static uint8_t
+ read();
+
+ /**
+ * Read a 16-bit value from the receive data register.
+ *
+ * @warning Reading with a size smaller than the configured data size is not allowed.
+ */
+ static uint16_t
+ read16();
+
+ /**
+ * Read a 32-bit value from the receive data register.
+ */
+ static uint32_t
+ read32();
+
+ static void
+ enableInterruptVector(bool enable, uint32_t priority);
+
+ static void
+ enableInterrupt(Interrupt_t interrupt);
+
+ static void
+ disableInterrupt(Interrupt_t interrupt);
+
+ static void
+ acknowledgeInterruptFlags(StatusFlag_t flags);
+};
+
+} // namespace modm::platform
+
+#include "spi_hal_{{ id }}_impl.hpp"
+
+#endif // MODM_STM32H7_SPI_HAL{{ id }}_HPP
diff --git a/src/modm/platform/spi/stm32h7/spi_hal_impl.hpp.in b/src/modm/platform/spi/stm32h7/spi_hal_impl.hpp.in
new file mode 100644
index 0000000000..77770cf4bd
--- /dev/null
+++ b/src/modm/platform/spi/stm32h7/spi_hal_impl.hpp.in
@@ -0,0 +1,353 @@
+/*
+* Copyright (c) 2013, Kevin Läufer
+* Copyright (c) 2013-2017, Niklas Hauser
+* Copyright (c) 2014, Daniel Krebs
+* Copyright (c) 2020, Mike Wolfram
+* 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_STM32H7_SPI_HAL{{ id }}_HPP
+# error "Don't include this file directly, use 'spi_hal{{ id }}.hpp' instead!"
+#endif
+#include
+
+namespace modm::platform
+{
+
+inline void
+SpiHal{{ id }}::enableClock()
+{
+ Rcc::enable();
+}
+
+inline void
+SpiHal{{ id }}::disableClock()
+{
+ Rcc::disable();
+}
+
+inline void
+SpiHal{{ id }}::initialize(Prescaler prescaler,
+ MasterSelection masterSelection, DataMode dataMode,
+ DataOrder dataOrder, DataSize dataSize)
+{
+ enableClock();
+ disableTransfer();
+
+ acknowledgeInterruptFlags(
+ StatusFlag::EndOfTransfer |
+ StatusFlag::TxTransferFilled |
+ StatusFlag::Underrun |
+ StatusFlag::Overrun |
+ StatusFlag::CrcError |
+ StatusFlag::TiFrameError |
+ StatusFlag::ModeFault |
+ StatusFlag::Reload |
+ StatusFlag::Suspension
+ );
+
+ // initialize with unlimited transfer size
+ setTransferSize(0);
+
+ // Pause master transfer if RX FIFO is full
+ SPI{{ id }}->CR1 = SPI_CR1_MASRX;
+
+ SPI{{ id }}->CFG2 = static_cast(dataMode)
+ | static_cast(dataOrder)
+ | static_cast(masterSelection)
+ | SPI_CFG2_SSOE; // disable multi-master support to prevent spurious mode fault
+
+ SPI{{ id }}->CFG1 = static_cast(prescaler)
+ | static_cast(dataSize);
+}
+
+inline void
+SpiHal{{ id }}::setDataMode(DataMode dataMode)
+{
+ SPI{{ id }}->CFG2 = (SPI{{ id }}->CFG2 & ~static_cast(DataMode::Mask))
+ | static_cast(dataMode);
+}
+
+inline void
+SpiHal{{ id }}::setDataOrder(DataOrder dataOrder)
+{
+ SPI{{ id }}->CFG2 = (SPI{{ id }}->CFG2 & ~static_cast(DataOrder::Mask))
+ | static_cast(dataOrder);
+}
+
+inline void
+SpiHal{{ id }}::setDataSize(DataSize dataSize)
+{
+ SPI{{ id }}->CFG1 = (SPI{{ id }}->CFG1 & ~SPI_CFG1_DSIZE_Msk)
+ | static_cast(dataSize);
+}
+
+inline void
+SpiHal{{ id }}::setMasterSelection(MasterSelection masterSelection)
+{
+ SPI{{ id }}->CFG2 = (SPI{{ id }}->CFG2 & ~static_cast(MasterSelection::Mask))
+ | static_cast(masterSelection);
+}
+
+inline void
+SpiHal{{ id }}::setDuplexMode(DuplexMode mode)
+{
+ SPI{{ id }}->CFG2 = (SPI{{ id }}->CFG2 & ~static_cast(DuplexMode::Mask))
+ | static_cast(mode);
+}
+
+inline void
+SpiHal{{ id }}::setCrcEnabled(bool enabled)
+{
+ if (enabled) {
+ SPI{{ id }}->CFG1 |= SPI_CFG1_CRCEN;
+ } else {
+ SPI{{ id }}->CFG1 &= ~SPI_CFG1_CRCEN;
+ }
+}
+
+inline void
+SpiHal{{ id }}::setCrcSize(DataSize crcSize)
+{
+ if ((crcSize == DataSize::Bit16) or (crcSize == DataSize::Bit32)) {
+ SPI{{ id }}->CR1 |= SPI_CR1_CRC33_17;
+ } else {
+ SPI{{ id }}->CR1 &= ~SPI_CR1_CRC33_17;
+ }
+ SPI{{ id }}->CFG1 = (SPI{{ id }}->CFG1 & ~SPI_CFG1_CRCSIZE_Msk)
+ | (static_cast(crcSize) << SPI_CFG1_CRCSIZE_Pos);
+}
+
+inline void
+SpiHal{{ id }}::setCrcPolynomial(uint32_t poly)
+{
+ SPI{{ id }}->CRCPOLY = poly;
+}
+
+inline void
+SpiHal{{ id }}::setCrcInitialValue(CrcInit init)
+{
+ SPI{{ id }}->CR1 = (SPI{{ id }}->CR1 & ~static_cast(CrcInit::Mask)) |
+ static_cast(init);
+}
+
+inline void
+SpiHal{{ id }}::setTransferSize(uint16_t size, uint16_t reload)
+{
+ static_assert(SPI_CR2_TSIZE_Pos == 0);
+ SPI{{ id }}->CR2 = (reload << SPI_CR2_TSER_Pos) | size;
+}
+
+inline void
+SpiHal{{ id }}::setDmaMode(DmaMode_t mode)
+{
+ SPI{{ id }}->CFG1 = (SPI{{ id }}->CFG1 & ~(DmaMode::Tx | DmaMode::Rx).value)
+ | mode.value;
+}
+
+inline void
+SpiHal{{ id }}::setSlaveSelectMode(SlaveSelectMode mode)
+{
+ SPI{{ id }}->CFG2 = (SPI{{ id }}->CFG2 & ~SPI_CFG2_SSM_Msk)
+ | uint32_t(mode);
+}
+
+inline void
+SpiHal{{ id }}::setSlaveSelectPolarity(SlaveSelectPolarity polarity)
+{
+ SPI{{ id }}->CFG2 = (SPI{{ id }}->CFG2 & ~SPI_CFG2_SSIOP_Msk)
+ | uint32_t(polarity);
+}
+
+inline void
+SpiHal{{ id }}::setSlaveSelectState(bool state)
+{
+ if (state) {
+ SPI{{ id }}->CR1 |= SPI_CR1_SSI;
+ } else {
+ SPI{{ id }}->CR1 &= ~SPI_CR1_SSI;
+ }
+}
+
+inline void
+SpiHal{{ id }}::write(uint8_t data)
+{
+ // Write with 8-bit access
+ auto* const ptr = reinterpret_cast<__IO uint8_t*>(&SPI{{ id }}->TXDR);
+ *ptr = data;
+}
+
+inline void
+SpiHal{{ id }}::write16(uint16_t data)
+{
+ // Write with 16-bit access
+ // SPI{{ id }}->TXDR is of type "volatile uint32_t".
+ // [[gnu::may_alias]] is required to avoid undefined behaviour due to strict aliasing violations.
+ auto* const [[gnu::may_alias]] ptr = reinterpret_cast<__IO uint16_t*>(&SPI{{ id }}->TXDR);
+ *ptr = data;
+}
+
+inline void
+SpiHal{{ id }}::write32(uint32_t data)
+{
+ SPI{{ id }}->TXDR = data;
+}
+
+inline uint8_t
+SpiHal{{ id }}::read()
+{
+ // Read with 8-bit access
+ return *reinterpret_cast(&SPI{{ id }}->RXDR);
+}
+
+inline uint16_t
+SpiHal{{ id }}::read16()
+{
+ // Read with 16-bit access
+ // SPI{{ id }}->RXDR is of type "const volatile uint32_t".
+ // [[gnu::may_alias]] is required to avoid undefined behaviour due to strict aliasing violations.
+ auto* const [[gnu::may_alias]] ptr = reinterpret_cast(&SPI{{ id }}->RXDR);
+ return *ptr;
+}
+
+inline uint32_t
+SpiHal{{ id }}::read32()
+{
+ return SPI{{ id }}->RXDR;
+}
+
+inline void
+SpiHal{{ id }}::enableInterruptVector(bool enable, uint32_t priority)
+{
+ if (enable) {
+ // Set priority for the interrupt vector
+ NVIC_SetPriority(SPI{{ id }}_IRQn, priority);
+ // register IRQ at the NVIC
+ NVIC_EnableIRQ(SPI{{ id }}_IRQn);
+ }
+ else {
+ NVIC_DisableIRQ(SPI{{ id }}_IRQn);
+ }
+}
+
+inline void
+SpiHal{{ id }}::enableInterrupt(Interrupt_t interrupt)
+{
+ SPI{{ id }}->IER |= interrupt.value;
+}
+
+inline void
+SpiHal{{ id }}::disableInterrupt(Interrupt_t interrupt)
+{
+ SPI{{ id }}->IER &= ~interrupt.value;
+}
+
+inline void
+SpiHal{{ id }}::acknowledgeInterruptFlags(StatusFlag_t flags)
+{
+ constexpr auto mask =
+ SPI_IFCR_EOTC |
+ SPI_IFCR_TXTFC |
+ SPI_IFCR_UDRC |
+ SPI_IFCR_OVRC |
+ SPI_IFCR_CRCEC |
+ SPI_IFCR_TIFREC |
+ SPI_IFCR_MODFC |
+ SPI_IFCR_TSERFC |
+ SPI_IFCR_SUSPC;
+
+ SPI{{ id }}->IFCR = flags.value & mask;
+}
+
+inline SpiHal{{ id }}::StatusFlag_t
+SpiHal{{ id }}::status()
+{
+ return StatusFlag_t(SPI{{ id }}->SR);
+}
+
+inline bool
+SpiHal{{ id }}::isTransferCompleted()
+{
+ return bool(status() & StatusFlag::EndOfTransfer);
+}
+
+inline bool
+SpiHal{{ id }}::isTxCompleted()
+{
+ return bool(status() & StatusFlag::TxTransferComplete);
+}
+
+inline bool
+SpiHal{{ id }}::isTxFifoFull()
+{
+ return !(status() & StatusFlag::TxPacketSpaceAvailable);
+}
+
+inline bool
+SpiHal{{ id }}::isRxDataAvailable()
+{
+ return bool(status() & StatusFlag::RxPacketAvailable);
+}
+
+inline void
+SpiHal{{ id }}::enableTransfer()
+{
+ SPI{{ id }}->CR1 |= SPI_CR1_SPE;
+}
+
+inline void
+SpiHal{{ id }}::disableTransfer()
+{
+ SPI{{ id }}->CR1 &= ~SPI_CR1_SPE;
+}
+
+inline bool
+SpiHal{{ id }}::isTransferEnabled()
+{
+ return (SPI{{ id }}->CR1 & SPI_CR1_SPE);
+}
+
+inline void
+SpiHal{{ id }}::startMasterTransfer()
+{
+ SPI{{ id }}->CR1 |= SPI_CR1_CSTART;
+}
+
+inline void
+SpiHal{{ id }}::suspendMasterTransfer()
+{
+ SPI{{ id }}->CR1 |= SPI_CR1_CSUSP;
+}
+
+inline uint32_t
+SpiHal{{ id }}::transmitCrc()
+{
+ return SPI{{ id }}->TXCRC;
+}
+
+inline uint32_t
+SpiHal{{ id }}::receiveCrc()
+{
+ return SPI{{ id }}->RXCRC;
+}
+
+inline volatile uint32_t*
+SpiHal{{ id }}::transmitRegister()
+{
+ return &SPI{{ id }}->TXDR;
+}
+
+inline const volatile uint32_t*
+SpiHal{{ id }}::receiveRegister()
+{
+ return &SPI{{ id }}->RXDR;
+}
+
+} // namespace modm::platform
diff --git a/src/modm/platform/spi/stm32h7/spi_master.cpp.in b/src/modm/platform/spi/stm32h7/spi_master.cpp.in
new file mode 100644
index 0000000000..02b498a617
--- /dev/null
+++ b/src/modm/platform/spi/stm32h7/spi_master.cpp.in
@@ -0,0 +1,159 @@
+/*
+* Copyright (c) 2009, Martin Rosekeit
+* Copyright (c) 2009-2012, Fabian Greif
+* Copyright (c) 2010, Georgi Grinshpun
+* Copyright (c) 2012-2017, Niklas Hauser
+* Copyright (c) 2013, Kevin Läufer
+* Copyright (c) 2014, Sascha Schade
+* 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 "spi_master_{{id}}.hpp"
+
+namespace modm::platform
+{
+
+modm::ResumableResult
+SpiMaster{{ id }}::transfer(uint8_t data)
+{
+%% if use_fiber
+ while (Hal::isTxFifoFull())
+ modm::fiber::yield();
+
+ Hal::write(data);
+
+ while (!Hal::isRxDataAvailable())
+ modm::fiber::yield();
+
+ return Hal::read();
+%% else
+ // this is a manually implemented "fast resumable function"
+ // there is no context or nesting protection, since we don't need it.
+ // there are only two states encoded into 1 bit (LSB of state):
+ // 1. waiting to start, and
+ // 2. waiting to finish.
+
+ if (!(state & Bit0))
+ {
+ // wait for previous transfer to finish
+ if (Hal::isTxFifoFull())
+ return {modm::rf::Running};
+
+ Hal::write(data);
+
+ // set LSB = Bit0
+ state |= Bit0;
+ }
+
+ if (!Hal::isRxDataAvailable())
+ return {modm::rf::Running};
+
+ data = Hal::read();
+
+ state &= ~Bit0;
+ return {modm::rf::Stop, data};
+%% endif
+}
+
+modm::ResumableResult
+SpiMaster{{ id }}::transfer(
+ const uint8_t* tx, uint8_t* rx, std::size_t length)
+{
+%% if use_fiber
+ std::size_t rxIndex = 0;
+ std::size_t txIndex = 0;
+
+ while (rxIndex < length) {
+ while ((txIndex < length) and !Hal::isTxFifoFull()) {
+ Hal::write(tx ? tx[txIndex] : 0);
+ ++txIndex;
+ }
+ while ((rxIndex < length) and Hal::isRxDataAvailable()) {
+ const uint8_t data = Hal::read();
+ if (rx) {
+ rx[rxIndex] = data;
+ }
+ ++rxIndex;
+ }
+ if (rxIndex < length) {
+ modm::fiber::yield();
+ }
+ }
+%% else
+ // this is a manually implemented "fast resumable function"
+ // there is no context or nesting protection, since we don't need it.
+ // there are only two states encoded into 1 bit (Bit1 of state):
+ // 1. initialize index, and
+ // 2. wait for transfer to finish.
+
+ // we need to globally remember which byte we are currently transferring
+ static std::size_t rxIndex = 0;
+ static std::size_t txIndex = 0;
+
+ // we are only interested in Bit1
+ switch(state & Bit1)
+ {
+ case 0:
+ // we will only visit this state once
+ state |= Bit1;
+
+ // initialize index and check range
+ rxIndex = 0;
+ txIndex = 0;
+ while (rxIndex < length) {
+ default:
+ {
+ while ((txIndex < length) and !Hal::isTxFifoFull()) {
+ Hal::write(tx ? tx[txIndex] : 0);
+ ++txIndex;
+ }
+ while ((rxIndex < length) and Hal::isRxDataAvailable()) {
+ if (rx) {
+ rx[rxIndex] = Hal::read();
+ } else {
+ Hal::read();
+ }
+ ++rxIndex;
+ }
+ if (rxIndex < length) {
+ return {modm::rf::Running};
+ }
+ }
+ }
+
+ // clear the state
+ state &= ~Bit1;
+ return {modm::rf::Stop};
+ }
+%% endif
+}
+
+void
+SpiMaster{{ id }}::finishTransfer()
+{
+ if (Hal::isTransferEnabled()) {
+ while (!(Hal::status() & Hal::StatusFlag::TxTransferComplete));
+ Hal::disableTransfer();
+ }
+
+ Hal::acknowledgeInterruptFlags(
+ Hal::StatusFlag::EndOfTransfer |
+ Hal::StatusFlag::TxTransferFilled |
+ Hal::StatusFlag::Underrun |
+ Hal::StatusFlag::Overrun |
+ Hal::StatusFlag::CrcError |
+ Hal::StatusFlag::TiFrameError |
+ Hal::StatusFlag::ModeFault |
+ Hal::StatusFlag::Reload |
+ Hal::StatusFlag::Suspension
+ );
+}
+
+} // namespace modm::platform
diff --git a/src/modm/platform/spi/stm32h7/spi_master.hpp.in b/src/modm/platform/spi/stm32h7/spi_master.hpp.in
new file mode 100644
index 0000000000..f44a0883a9
--- /dev/null
+++ b/src/modm/platform/spi/stm32h7/spi_master.hpp.in
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2009-2011, Fabian Greif
+ * Copyright (c) 2010, Martin Rosekeit
+ * Copyright (c) 2011-2017, Niklas Hauser
+ * Copyright (c) 2012, Georgi Grinshpun
+ * Copyright (c) 2013, Kevin Läufer
+ * Copyright (c) 2014, Sascha Schade
+ * Copyright (c) 2022-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_STM32H7_SPI_MASTER{{ id }}_HPP
+#define MODM_STM32H7_SPI_MASTER{{ id }}_HPP
+
+#include
+#include
+#include
+#include
+%% if use_fiber
+#include
+%% endif
+#include "spi_hal_{{ id }}.hpp"
+
+namespace modm::platform
+{
+
+/**
+ * Serial peripheral interface (SPI{{ id }}).
+ *
+ * @ingroup modm_platform_spi modm_platform_spi_{{id}}
+ */
+class SpiMaster{{ id }} : public modm::SpiMaster, public SpiLock
+{
+%% if not use_fiber
+ // Bit0: single transfer state
+ // Bit1: block transfer state
+ static inline uint8_t state{0};
+%% endif
+public:
+ using Hal = SpiHal{{ id }};
+
+ using DataMode = Hal::DataMode;
+ using DataOrder = Hal::DataOrder;
+ using DataSize = Hal::DataSize;
+
+ template< class... Signals >
+ static void
+ connect()
+ {
+ using Connector = GpioConnector;
+ using Sck = typename Connector::template GetSignal;
+ using Mosi = typename Connector::template GetSignal;
+ using Miso = typename Connector::template GetSignal;
+
+ Sck::setOutput(Gpio::OutputType::PushPull);
+ Mosi::setOutput(Gpio::OutputType::PushPull);
+ Miso::setInput(Gpio::InputType::Floating);
+ Connector::connect();
+ }
+
+ template< class SystemClock, baudrate_t baudrate, percent_t tolerance=pct(5) >
+ static void
+ initialize()
+ {
+ constexpr auto result = modm::Prescaler::from_power(SystemClock::Spi{{ id }}, baudrate, 2, 256);
+ assertBaudrateInTolerance< result.frequency, baudrate, tolerance >();
+
+ // translate the prescaler into the bitmapping
+ constexpr Hal::Prescaler prescaler{result.index << SPI_CFG1_MBR_Pos};
+%% if not use_fiber
+ state = 0;
+%% endif
+
+ Hal::initialize(prescaler);
+ Hal::enableTransfer();
+ Hal::startMasterTransfer();
+ }
+
+ static void
+ setDataMode(DataMode mode)
+ {
+ finishTransfer();
+ Hal::setDataMode(mode);
+ Hal::enableTransfer();
+ Hal::startMasterTransfer();
+ }
+
+ static void
+ setDataOrder(DataOrder order)
+ {
+ finishTransfer();
+ Hal::setDataOrder(order);
+ Hal::enableTransfer();
+ Hal::startMasterTransfer();
+ }
+
+ static void
+ setDataSize(DataSize size)
+ {
+ finishTransfer();
+ Hal::setDataSize(static_cast(size));
+ Hal::enableTransfer();
+ Hal::startMasterTransfer();
+ }
+
+ static uint8_t
+ transferBlocking(uint8_t data)
+ {
+ return RF_CALL_BLOCKING(transfer(data));
+ }
+
+ static void
+ transferBlocking(const uint8_t* tx, uint8_t* rx, std::size_t length)
+ {
+ RF_CALL_BLOCKING(transfer(tx, rx, length));
+ }
+
+
+ static modm::ResumableResult
+ transfer(uint8_t data);
+
+ static modm::ResumableResult
+ transfer(const uint8_t* tx, uint8_t* rx, std::size_t length);
+
+private:
+ static void
+ finishTransfer();
+};
+
+} // namespace modm::platform
+
+#endif // MODM_STM32H7_SPI_MASTER{{ id }}_HPP
diff --git a/src/modm/platform/spi/stm32h7/spi_master_dma.hpp.in b/src/modm/platform/spi/stm32h7/spi_master_dma.hpp.in
new file mode 100644
index 0000000000..009fabe238
--- /dev/null
+++ b/src/modm/platform/spi/stm32h7/spi_master_dma.hpp.in
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2020, Mike Wolfram
+ * 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_STM32H7_SPI_MASTER{{ id }}_DMA_HPP
+#define MODM_STM32H7_SPI_MASTER{{ id }}_DMA_HPP
+
+#include
+#include "spi_master_{{ id }}.hpp"
+
+namespace modm::platform
+{
+
+/**
+ * Serial peripheral interface (SPI{{ id }}) with DMA support.
+ *
+ * This class uses the DMA controller for sending and receiving data, which
+ * greatly reduces the CPU load. Beside passing the DMA channels as template
+ * parameters the class can be used in the same way like the SpiMaster{{ id }}.
+ *
+ * @tparam DmaChannelRX DMA channel for receiving
+ * @tparam DmaChannelTX DMA channel for sending
+ *
+ * @ingroup modm_platform_spi modm_platform_spi_{{id}}
+ */
+template
+class SpiMaster{{ id }}_Dma : public modm::SpiMaster,
+ public SpiLock>
+{
+protected:
+ struct Dma {
+ using RxChannel = typename DmaChannelRx::template RequestMapping<
+ Peripheral::Spi{{ id }}, DmaBase::Signal::Rx>::Channel;
+ using TxChannel = typename DmaChannelTx::template RequestMapping<
+ Peripheral::Spi{{ id }}, DmaBase::Signal::Tx>::Channel;
+ static constexpr DmaBase::Request RxRequest = DmaChannelRx::template RequestMapping<
+ Peripheral::Spi{{ id }}, DmaBase::Signal::Rx>::Request;
+ static constexpr DmaBase::Request TxRequest = DmaChannelTx::template RequestMapping<
+ Peripheral::Spi{{ id }}, DmaBase::Signal::Tx>::Request;
+ };
+
+%% if not use_fiber
+ // Bit0: single transfer state
+ // Bit1: block transfer state
+ // Bit2: block transfer rx dma transfer completed
+ static inline uint8_t state{0};
+%% endif
+public:
+ using Hal = SpiHal{{ id }};
+
+ using DataMode = Hal::DataMode;
+ using DataOrder = Hal::DataOrder;
+ using DataSize = Hal::DataSize;
+
+ template< class... Signals >
+ static void
+ connect()
+ {
+ using Connector = GpioConnector;
+ using Sck = typename Connector::template GetSignal;
+ using Mosi = typename Connector::template GetSignal;
+ using Miso = typename Connector::template GetSignal;
+
+ Sck::setOutput(Gpio::OutputType::PushPull);
+ Mosi::setOutput(Gpio::OutputType::PushPull);
+ Miso::setInput(Gpio::InputType::Floating);
+ Connector::connect();
+ }
+
+ template< class SystemClock, baudrate_t baudrate, percent_t tolerance=pct(5) >
+ static void
+ initialize();
+
+ static void
+ setDataMode(DataMode mode)
+ {
+ Hal::setDataMode(mode);
+ }
+
+ static void
+ setDataOrder(DataOrder order)
+ {
+ Hal::setDataOrder(order);
+ }
+
+ static void
+ setDataSize(DataSize size)
+ {
+ Hal::setDataSize(static_cast(size));
+ }
+
+ static uint8_t
+ transferBlocking(uint8_t data)
+ {
+ return RF_CALL_BLOCKING(transfer(data));
+ }
+
+ /// @pre At least one of tx or rx must not be nullptr
+ static void
+ transferBlocking(const uint8_t* tx, uint8_t* rx, std::size_t length)
+ {
+ RF_CALL_BLOCKING(transfer(tx, rx, length));
+ }
+
+ static modm::ResumableResult
+ transfer(uint8_t data);
+
+ /// @pre At least one of tx or rx must not be nullptr
+ static modm::ResumableResult
+ transfer(const uint8_t* tx, uint8_t* rx, std::size_t length);
+
+protected:
+ static void
+ finishTransfer();
+
+ static void
+ startDmaTransfer(const uint8_t* tx, uint8_t* rx, std::size_t length);
+};
+
+} // namespace modm::platform
+
+#include "spi_master_{{ id }}_dma_impl.hpp"
+
+#endif // MODM_STM32_SPI_MASTER{{ id }}_DMA_HPP
diff --git a/src/modm/platform/spi/stm32h7/spi_master_dma_impl.hpp.in b/src/modm/platform/spi/stm32h7/spi_master_dma_impl.hpp.in
new file mode 100644
index 0000000000..10aa8d2ab2
--- /dev/null
+++ b/src/modm/platform/spi/stm32h7/spi_master_dma_impl.hpp.in
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2020, Mike Wolfram
+ * 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_STM32H7_SPI_MASTER{{ id }}_DMA_HPP
+# error "Don't include this file directly, use 'spi_master_{{ id }}_dma.hpp' instead!"
+#endif
+
+namespace modm::platform
+{
+
+template
+template
+void
+SpiMaster{{ id }}_Dma::initialize()
+{
+ Dma::RxChannel::configure(DmaBase::DataTransferDirection::PeripheralToMemory,
+ DmaBase::MemoryDataSize::Byte, DmaBase::PeripheralDataSize::Byte,
+ DmaBase::MemoryIncrementMode::Increment, DmaBase::PeripheralIncrementMode::Fixed,
+ DmaBase::Priority::Medium);
+ Dma::RxChannel::disableInterruptVector();
+ Dma::RxChannel::setPeripheralAddress(reinterpret_cast(Hal::receiveRegister()));
+ Dma::RxChannel::template setPeripheralRequest();
+
+ Dma::TxChannel::configure(DmaBase::DataTransferDirection::MemoryToPeripheral,
+ DmaBase::MemoryDataSize::Byte, DmaBase::PeripheralDataSize::Byte,
+ DmaBase::MemoryIncrementMode::Increment, DmaBase::PeripheralIncrementMode::Fixed,
+ DmaBase::Priority::Medium);
+ Dma::TxChannel::disableInterruptVector();
+ Dma::TxChannel::setPeripheralAddress(reinterpret_cast(Hal::transmitRegister()));
+ Dma::TxChannel::template setPeripheralRequest();
+
+%% if not use_fiber
+ state = 0;
+%% endif
+
+ constexpr auto result = modm::Prescaler::from_power(SystemClock::Spi{{ id }}, baudrate, 2, 256);
+ assertBaudrateInTolerance< result.frequency, baudrate, tolerance >();
+
+ constexpr Hal::Prescaler prescaler{result.index << SPI_CFG1_MBR_Pos};
+ Hal::initialize(prescaler);
+}
+
+template
+modm::ResumableResult
+SpiMaster{{ id }}_Dma::transfer(uint8_t data)
+{
+ // DMA is not used for single byte transfers
+%% if use_fiber
+ Hal::setDuplexMode(Hal::DuplexMode::FullDuplex);
+ Hal::setTransferSize(1);
+ Hal::enableTransfer();
+ Hal::write(data);
+ Hal::startMasterTransfer();
+
+ // wait for transfer to complete
+ while (!Hal::isTransferCompleted())
+ modm::fiber::yield();
+
+ data = SpiHal{{ id }}::read();
+ finishTransfer();
+
+ return data;
+%% else
+ // this is a manually implemented "fast resumable function"
+ // there is no context or nesting protection, since we don't need it.
+ // there are only two states encoded into 1 bit (LSB of state):
+ // 1. waiting to start, and
+ // 2. waiting to finish.
+ // LSB != Bit0 ?
+ if ( !(state & Bit0) )
+ {
+ Hal::setDuplexMode(Hal::DuplexMode::FullDuplex);
+ Hal::setTransferSize(1);
+ Hal::enableTransfer();
+ Hal::write(data);
+ Hal::startMasterTransfer();
+
+ // set LSB = Bit0
+ state |= Bit0;
+ }
+
+ if (!Hal::isTransferCompleted())
+ return {modm::rf::Running};
+
+ data = SpiHal{{ id }}::read();
+ finishTransfer();
+
+ // transfer finished
+ state &= ~Bit0;
+ return {modm::rf::Stop, data};
+%% endif
+}
+
+template
+void
+SpiMaster{{ id }}_Dma::startDmaTransfer(
+ const uint8_t* tx, uint8_t* rx, std::size_t length)
+{
+ Hal::setTransferSize(length);
+
+ if (tx and rx) {
+ Hal::setDuplexMode(Hal::DuplexMode::FullDuplex);
+ } else if (rx) {
+ Hal::setDuplexMode(Hal::DuplexMode::ReceiveOnly);
+ } else { // tx only
+ Hal::setDuplexMode(Hal::DuplexMode::TransmitOnly);
+ }
+
+ /*
+ * Required order of operations according to the reference manual:
+ * 1. Enable SPI RX DMA
+ * 2. Enable DMA channels
+ * 3. Enable SPI TX DMA
+ * 4. Start transfer
+ */
+ if (rx) {
+ Dma::RxChannel::setMemoryAddress(reinterpret_cast(rx));
+ Dma::RxChannel::setDataLength(length);
+ Hal::setDmaMode(Hal::DmaMode::Rx);
+ Dma::RxChannel::start();
+ }
+
+ if (tx) {
+ Dma::TxChannel::setMemoryAddress(reinterpret_cast(tx));
+ Dma::TxChannel::setDataLength(length);
+ Dma::TxChannel::start();
+ if (rx) {
+ Hal::setDmaMode(Hal::DmaMode::Tx | Hal::DmaMode::Rx);
+ } else {
+ Hal::setDmaMode(Hal::DmaMode::Tx);
+ }
+ }
+
+ Hal::enableTransfer();
+ Hal::startMasterTransfer();
+}
+
+
+template
+modm::ResumableResult
+SpiMaster{{ id }}_Dma::transfer(
+ const uint8_t* tx, uint8_t* rx, std::size_t length)
+{
+ using Flags = DmaBase::InterruptFlags;
+%% if use_fiber
+ startDmaTransfer(tx, rx, length);
+
+ bool dmaRxFinished = (rx == nullptr);
+ while (!Hal::isTransferCompleted() or !dmaRxFinished) {
+ if(rx) {
+ const auto flags = DmaChannelRx::getInterruptFlags();
+ if (flags & Flags::Error) {
+ break;
+ }
+ if (flags & Flags::TransferComplete) {
+ dmaRxFinished = true;
+ }
+ }
+ if(tx) {
+ const auto flags = DmaChannelTx::getInterruptFlags();
+ if (flags & Flags::Error) {
+ break;
+ }
+ }
+ modm::fiber::yield();
+ }
+ finishTransfer();
+%% else
+ // this is a manually implemented "fast resumable function"
+ // there is no context or nesting protection, since we don't need it.
+ // there are only two states encoded into 1 bit (Bit1 of state):
+ // 1. initialize index, and
+ // 2. wait for 1-byte transfer to finish.
+
+ // we are only interested in Bit1
+ switch(state & Bit1)
+ {
+ case 0:
+ // we will only visit this state once
+ state |= Bit1;
+ startDmaTransfer(tx, rx, length);
+ if (!rx) {
+ state |= Bit2;
+ }
+ [[fallthrough]];
+
+ default:
+ if (!Hal::isTransferCompleted() or !(state & Bit2)) {
+ if(rx) {
+ static DmaBase::InterruptFlags_t flags;
+ flags = DmaChannelRx::getInterruptFlags();
+ if (flags & Flags::Error) {
+ // abort on DMA error
+ finishTransfer();
+ state &= ~(Bit2 | Bit1);
+ return {modm::rf::Stop};
+ }
+ if (flags & Flags::TransferComplete) {
+ state |= Bit2;
+ }
+ }
+ if(tx) {
+ if (DmaChannelTx::getInterruptFlags() & Flags::Error) {
+ // abort on DMA error
+ finishTransfer();
+ state &= ~(Bit2 | Bit1);
+ return {modm::rf::Stop};
+ }
+ }
+ return { modm::rf::Running };
+ }
+
+ finishTransfer();
+
+ // clear the state
+ state &= ~(Bit2 | Bit1);
+ return {modm::rf::Stop};
+ }
+%% endif
+}
+
+template
+void
+SpiMaster{{ id }}_Dma::finishTransfer()
+{
+ DmaChannelTx::stop();
+ DmaChannelRx::stop();
+
+ Hal::setDmaMode(Hal::DmaMode::None);
+ Hal::disableTransfer();
+
+ Hal::acknowledgeInterruptFlags(
+ Hal::StatusFlag::EndOfTransfer |
+ Hal::StatusFlag::TxTransferFilled |
+ Hal::StatusFlag::Underrun |
+ Hal::StatusFlag::Overrun |
+ Hal::StatusFlag::CrcError |
+ Hal::StatusFlag::TiFrameError |
+ Hal::StatusFlag::ModeFault |
+ Hal::StatusFlag::Reload |
+ Hal::StatusFlag::Suspension
+ );
+}
+
+} // namespace modm::platform