diff --git a/CMakeLists.txt b/CMakeLists.txt
index c5d677191b4f9b6bcdb43eb84697bddab5116297..7aed00d9d11cfa75c05b821be18c5e53a69b2f5d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -187,9 +187,6 @@ sbs_target(test-can-loopback stm32f429zi_skyward_death_stack_x)
 add_executable(test-can-protocol src/tests/drivers/canbus/CanProtocol/test-can-protocol.cpp)
 sbs_target(test-can-protocol stm32f429zi_skyward_death_stack_x)
 
-add_executable(test-dma-spi src/tests/drivers/dma/test-dma-spi.cpp)
-sbs_target(test-dma-spi stm32f429zi_stm32f4discovery)
-
 add_executable(test-dsgamma src/tests/drivers/test-dsgamma.cpp)
 sbs_target(test-dsgamma stm32f429zi_stm32f4discovery)
 
diff --git a/src/shared/drivers/spi/SPI.h b/src/shared/drivers/spi/SPI.h
deleted file mode 100644
index e146c323c5db96c5bb5915202e848a88ad8be890..0000000000000000000000000000000000000000
--- a/src/shared/drivers/spi/SPI.h
+++ /dev/null
@@ -1,481 +0,0 @@
-/* Copyright (c) 2021 Skyward Experimental Rocketry
- * Author: Alberto Nidasio
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#pragma once
-
-#include <interfaces/arch_registers.h>
-#include <stddef.h>
-#include <utils/ClockUtils.h>
-
-#ifndef USE_MOCK_PERIPHERALS
-using SPIType = SPI_TypeDef;
-#else
-#include <utils/TestUtils/FakeSpiTypedef.h>
-using SPIType = Boardcore::FakeSpiTypedef;
-#endif
-
-namespace Boardcore
-{
-
-/**
- * @brief Driver for STM32 low level SPI peripheral.
- *
- * This driver applies to the whole STM32F4xx family.
- *
- * The serial peripheral interface (SPI) allows half/full-duplex, synchronous,
- * serial communication with external devices. The interface can be configured
- * as the master and in this case it provides the communication clock (SCK) to
- * the external slave device. The peripheral is also capable of reliable
- * communication using CRC checking.
- *
- * SPI main features:
- * - Full-duplex synchronous transfers on three lines
- * - 8- or 16-bit transfer frame format selection
- * - Master or slave operation
- * - 8 master mode baud rate prescaler (f_PCLK/2 max.)
- * - Programmable clock polarity and phase
- * - Programmable data order with MSB-first or LSB-first shifting
- * - Hardware CRC feature for reliable communication
- * - DMA capability
- */
-class SPI
-{
-public:
-    enum class BitOrder : uint16_t
-    {
-        MSB_FIRST = 0,
-        LSB_FIRST = 0x80
-    };
-
-    /**
-     * @brief SPI Clock divider.
-     *
-     * SPI clock frequency will be equal to the SPI peripheral bus clock speed
-     * divided by the specified value.
-     *
-     * Eg: DIV_2 --> spi clock freq = f_PCLK / 2
-     */
-    enum class ClockDivider : uint8_t
-    {
-        DIV_2   = 0x00,
-        DIV_4   = 0x08,
-        DIV_8   = 0x10,
-        DIV_16  = 0x18,
-        DIV_32  = 0x20,
-        DIV_64  = 0x28,
-        DIV_128 = 0x30,
-        DIV_256 = 0x38,
-    };
-
-    enum class Mode : uint8_t
-    {
-        MODE_0 = 0,  ///< CPOL = 0, CPHA = 0
-        MODE_1 = 1,  ///< CPOL = 0, CPHA = 1
-        MODE_2 = 2,  ///< CPOL = 1, CPHA = 0
-        MODE_3 = 3   ///< CPOL = 1, CPHA = 1
-    };
-
-    /**
-     * @brief Automatically enables the timer peripheral clock.
-     */
-    explicit SPI(SPIType *spi);
-
-    /**
-     * @brief Resets the registers and disables the peripheral clock.
-     */
-    ~SPI();
-
-    SPIType *getSpi();
-
-    /**
-     * @brief Resets the peripheral configuration.
-     */
-    void reset();
-
-    /**
-     * @brief Enables the peripheral.
-     */
-    void enable();
-
-    /**
-     * @brief Disables the peripheral.
-     */
-    void disable();
-
-    void set8BitFrameFormat();
-
-    void set16BitFrameFormat();
-
-    void enableSoftwareSlaveManagement();
-
-    void disableSoftwareSlaveManagement();
-
-    void enableInternalSlaveSelection();
-
-    void disableInternalSlaveSelection();
-
-    void setBitOrder(BitOrder bitOrder);
-
-    void setClockDiver(ClockDivider divider);
-
-    void setSlaveConfiguration();
-
-    void setMasterConfiguration();
-
-    void setMode(Mode mode);
-
-    void enableTxDMARequest();
-
-    void disableTxDMARequest();
-
-    void enableRxDMARequest();
-
-    void disableRxDMARequest();
-
-    void waitPeripheral();
-
-    // Read, write and transfer operations in master mode
-
-    /**
-     * @brief Reads a single byte from the bus.
-     *
-     * @return Byte read from the bus.
-     */
-    uint8_t read();
-
-    /**
-     * @brief Reads a single half word from the bus.
-     *
-     * @return Half word read from the bus.
-     */
-    uint16_t read16();
-
-    /**
-     * @brief Reads multiple bytes from the bus
-     *
-     * @param data Buffer to be filled with received data.
-     * @param size Size of the buffer in bytes.
-     */
-    void read(uint8_t *data, size_t nBytes);
-
-    /**
-     * @brief Reads multiple half words from the bus
-     *
-     * @param data Buffer to be filled with received data.
-     * @param size Size of the buffer in bytes.
-     */
-    void read16(uint16_t *data, size_t nBytes);
-
-    /**
-     * @brief Writes a single byte to the bus.
-     *
-     * @param data Byte to write.
-     */
-    void write(uint8_t data);
-
-    /**
-     * @brief Writes a single half word to the bus.
-     *
-     * @param data Half word to write.
-     */
-    void write16(uint16_t data);
-
-    /**
-     * @brief Writes multiple bytes to the bus.
-     *
-     * @param data Buffer containing data to write.
-     * @param size Size of the buffer in bytes.
-     */
-    void write(const uint8_t *data, size_t nBytes);
-
-    /**
-     * @brief Writes multiple half words to the bus.
-     *
-     * @param data Buffer containing data to write.
-     * @param size Size of the buffer in bytes.
-     */
-    void write16(const uint16_t *data, size_t nBytes);
-
-    /**
-     * @brief Full duplex transmission of one byte on the bus.
-     *
-     * @param data Byte to write.
-     * @return Byte read from the bus.
-     */
-    uint8_t transfer(uint8_t data);
-
-    /**
-     * @brief Full duplex transmission of one half word on the bus.
-     *
-     * @param data Half word to write.
-     * @return Half word read from the bus.
-     */
-    uint16_t transfer16(uint16_t data);
-
-    /**
-     * @brief Full duplex transmission of multiple bytes on the bus.
-     *
-     * @param data Buffer containing data to trasfer.
-     * @param size Size of the buffer in bytes.
-     */
-    void transfer(uint8_t *data, size_t nBytes);
-
-    /**
-     * @brief Full duplex transmission of multiple half words on the bus.
-     *
-     * @param data Buffer containing data to trasfer.
-     * @param size Size of the buffer in bytes.
-     */
-    void transfer16(uint16_t *data, size_t nBytes);
-
-private:
-    SPIType *spi;
-};
-
-inline SPI::SPI(SPIType *spi) : spi(spi)
-{
-    ClockUtils::enablePeripheralClock(spi);
-}
-
-inline SPI::~SPI() { ClockUtils::disablePeripheralClock(spi); }
-
-inline SPIType *SPI::getSpi() { return spi; }
-
-inline void SPI::reset()
-{
-    spi->CR1    = 0;
-    spi->CR2    = 0;
-    spi->DR     = 0;
-    spi->RXCRCR = 0;
-    spi->TXCRCR = 0;
-}
-
-inline void SPI::enable() { spi->CR1 |= SPI_CR1_SPE; }
-
-inline void SPI::disable() { spi->CR1 &= ~SPI_CR1_SPE; }
-
-inline void SPI::set8BitFrameFormat() { spi->CR1 &= ~SPI_CR1_DFF; }
-
-inline void SPI::set16BitFrameFormat() { spi->CR1 |= SPI_CR1_DFF; }
-
-inline void SPI::enableSoftwareSlaveManagement() { spi->CR1 |= SPI_CR1_SSM; }
-
-inline void SPI::disableSoftwareSlaveManagement() { spi->CR1 &= ~SPI_CR1_SSM; }
-
-inline void SPI::enableInternalSlaveSelection() { spi->CR1 |= SPI_CR1_SSI; }
-
-inline void SPI::disableInternalSlaveSelection() { spi->CR1 &= ~SPI_CR1_SSI; }
-
-inline void SPI::setBitOrder(BitOrder bitOrder)
-{
-    // First clear the configuration
-    spi->CR1 &= ~SPI_CR1_LSBFIRST;
-
-    // Set the new value
-    spi->CR1 |= static_cast<uint32_t>(bitOrder);
-}
-
-inline void SPI::setClockDiver(ClockDivider divider)
-{
-    // First clear the configuration
-    spi->CR1 &= ~SPI_CR1_BR;
-
-    // Set the new value
-    spi->CR1 |= static_cast<uint32_t>(divider);
-}
-
-inline void SPI::setSlaveConfiguration() { spi->CR1 &= ~SPI_CR1_MSTR; }
-
-inline void SPI::setMasterConfiguration() { spi->CR1 |= SPI_CR1_MSTR; }
-
-inline void SPI::setMode(Mode mode)
-{
-    // First clear the configuration
-    spi->CR1 &= ~(SPI_CR1_CPOL | SPI_CR1_CPHA);
-
-    // Set the new value
-    spi->CR1 |= static_cast<uint32_t>(mode);
-}
-
-inline void SPI::enableTxDMARequest() { spi->CR2 |= SPI_CR2_TXDMAEN; }
-
-inline void SPI::disableTxDMARequest() { spi->CR2 &= ~SPI_CR2_TXDMAEN; }
-
-inline void SPI::enableRxDMARequest() { spi->CR2 |= SPI_CR2_RXDMAEN; }
-
-inline void SPI::disableRxDMARequest() { spi->CR2 &= ~SPI_CR2_RXDMAEN; }
-
-inline void SPI::waitPeripheral()
-{
-    while ((spi->SR & SPI_SR_TXE) == 0)
-        ;
-    while ((spi->SR & SPI_SR_BSY) > 0)
-        ;
-}
-
-// Read, write and transfer operations in master mode
-
-inline uint8_t SPI::read() { return transfer(static_cast<uint8_t>(0)); }
-
-inline uint16_t SPI::read16() { return transfer(static_cast<uint16_t>(0)); }
-
-inline void SPI::read(uint8_t *data, size_t nBytes)
-{
-    for (size_t i = 0; i < nBytes; i++)
-        data[i] = read();
-}
-
-inline void SPI::read16(uint16_t *data, size_t nBytes)
-{
-    // Set 16 bit frame format
-    set16BitFrameFormat();
-
-    for (size_t i = 0; i < nBytes / 2; i++)
-    {
-        // Wait until the peripheral is ready to transmit
-        while ((spi->SR & SPI_SR_TXE) == 0)
-            ;
-
-        // Write the data item to transmit
-        spi->DR = 0;
-
-        // Wait until data is received
-        while ((spi->SR & SPI_SR_RXNE) == 0)
-            ;
-
-        // Read the received data item
-        data[i] = static_cast<uint16_t>(spi->DR);
-    }
-
-    // Go back to 8 bit frame format
-    set8BitFrameFormat();
-}
-
-inline void SPI::write(uint8_t data) { transfer(data); }
-
-inline void SPI::write16(uint16_t data) { transfer(data); }
-
-inline void SPI::write(const uint8_t *data, size_t nBytes)
-{
-    for (size_t i = 0; i < nBytes; i++)
-        transfer(data[i]);
-}
-
-inline void SPI::write16(const uint16_t *data, size_t nBytes)
-{
-    // Set 16 bit frame format
-    set16BitFrameFormat();
-
-    for (size_t i = 0; i < nBytes / 2; i++)
-    {
-        // Wait until the peripheral is ready to transmit
-        while ((spi->SR & SPI_SR_TXE) == 0)
-            ;
-
-        // Write the data item to transmit
-        spi->DR = static_cast<uint16_t>(data[i]);
-
-        // Wait until data is received
-        while ((spi->SR & SPI_SR_RXNE) == 0)
-            ;
-
-        // Read the received data item
-        (void)spi->DR;
-    }
-
-    // Go back to 8 bit frame format
-    set8BitFrameFormat();
-}
-
-inline uint8_t SPI::transfer(uint8_t data)
-{
-    // Wait until the peripheral is ready to transmit
-    while ((spi->SR & SPI_SR_TXE) == 0)
-        ;
-
-    // Write the data item to transmit
-    spi->DR = static_cast<uint8_t>(data);
-
-    // Wait until data is received
-    while ((spi->SR & SPI_SR_RXNE) == 0)
-        ;
-
-    // Read the received data item
-    return static_cast<uint8_t>(spi->DR);
-}
-
-inline uint16_t SPI::transfer16(uint16_t data)
-{
-    // Set 16 bit frame format
-    set16BitFrameFormat();
-
-    // Wait until the peripheral is ready to transmit
-    while ((spi->SR & SPI_SR_TXE) == 0)
-        ;
-
-    // Write the data item to transmit
-    spi->DR = static_cast<uint16_t>(data);
-
-    // Wait until data is received
-    while ((spi->SR & SPI_SR_RXNE) == 0)
-        ;
-
-    // Go back to 8 bit frame format
-    set8BitFrameFormat();
-
-    // Read the received data item
-    return static_cast<uint16_t>(spi->DR);
-}
-
-inline void SPI::transfer(uint8_t *data, size_t nBytes)
-{
-    for (size_t i = 0; i < nBytes; i++)
-        data[i] = transfer(data[i]);
-}
-
-inline void SPI::transfer16(uint16_t *data, size_t nBytes)
-{
-    // Set 16 bit frame format
-    set16BitFrameFormat();
-
-    for (size_t i = 0; i < nBytes / 2; i++)
-    {
-        // Wait until the peripheral is ready to transmit
-        while ((spi->SR & SPI_SR_TXE) == 0)
-            ;
-
-        // Write the data item to transmit
-        spi->DR = static_cast<uint16_t>(data[i]);
-
-        // Wait until data is received
-        while ((spi->SR & SPI_SR_RXNE) == 0)
-            ;
-
-        // Read the received data item
-        data[i] = static_cast<uint16_t>(spi->DR);
-    }
-
-    // Go back to 8 bit frame format
-    set8BitFrameFormat();
-}
-
-}  // namespace Boardcore
diff --git a/src/shared/drivers/spi/SPIBus.h b/src/shared/drivers/spi/SPIBus.h
index a10af7e296aa35d8aaed5f3c14eac9119b7df802..306af46719c2d38fbeeaee1fd83fa91948d73a73 100644
--- a/src/shared/drivers/spi/SPIBus.h
+++ b/src/shared/drivers/spi/SPIBus.h
@@ -23,6 +23,7 @@
 #pragma once
 
 #include <interfaces/delays.h>
+#include <utils/ClockUtils.h>
 
 #include "SPIBusInterface.h"
 
@@ -37,8 +38,23 @@ namespace Boardcore
 {
 
 /**
- * @brief Main implementation of SPIBusInterface used for accessing the SPI
- * peripheral in master mode
+ * @brief Driver for STM32 low level SPI peripheral.
+ *
+ * This driver applies to the whole STM32F4xx family.
+ *
+ * The serial peripheral interface (SPI) allows half/full-duplex, synchronous,
+ * serial communication with external devices. The interface can be configured
+ * as the master and in this case it provides the communication clock (SCK) to
+ * the external slave device. The peripheral is also capable of reliable
+ * communication using CRC checking.
+ *
+ * Supported SPI main features:
+ * - Full-duplex synchronous transfers on three lines
+ * - 8- or 16-bit transfer frame format selection
+ * - Master operation
+ * - 8 master mode baud rate prescaler (f_PCLK/2 max.)
+ * - Programmable clock polarity and phase
+ * - Programmable data order with MSB-first or LSB-first shifting
  */
 class SPIBus : public SPIBusInterface
 {
@@ -51,6 +67,58 @@ public:
     SPIBus(SPIBus&&)                 = delete;
     SPIBus& operator=(SPIBus&&)      = delete;
 
+    /**
+     * @brief Retrieve the pointer to the peripheral currently used.
+     */
+    SPIType* getSpi();
+
+    /**
+     * @brief Resets the peripheral configuration.
+     */
+    void reset();
+
+    /**
+     * @brief Enables the peripheral.
+     */
+    void enable();
+
+    /**
+     * @brief Disables the peripheral.
+     */
+    void disable();
+
+    void set8BitFrameFormat();
+
+    void set16BitFrameFormat();
+
+    void enableSoftwareSlaveManagement();
+
+    void disableSoftwareSlaveManagement();
+
+    void enableInternalSlaveSelection();
+
+    void disableInternalSlaveSelection();
+
+    void setBitOrder(SPI::BitOrder bitOrder);
+
+    void setClockDiver(SPI::ClockDivider divider);
+
+    void setSlaveConfiguration();
+
+    void setMasterConfiguration();
+
+    void setMode(SPI::Mode mode);
+
+    void enableTxDMARequest();
+
+    void disableTxDMARequest();
+
+    void enableRxDMARequest();
+
+    void disableRxDMARequest();
+
+    void waitPeripheral();
+
     /**
      * @brief Configures and enables the bus with the provided configuration.
      *
@@ -123,7 +191,7 @@ public:
      * @param data Buffer containing data to write.
      * @param size Size of the buffer.
      */
-    void write(uint8_t* data, size_t size) override;
+    void write(const uint8_t* data, size_t size) override;
 
     /**
      * @brief Writes multiple half words to the bus.
@@ -131,7 +199,7 @@ public:
      * @param data Buffer containing data to write.
      * @param size Size of the buffer.
      */
-    void write16(uint16_t* data, size_t size) override;
+    void write16(const uint16_t* data, size_t size) override;
 
     /**
      * @brief Full duplex transmission of one byte on the bus.
@@ -166,12 +234,95 @@ public:
     void transfer16(uint16_t* data, size_t size) override;
 
 private:
-    SPI spi;
+    SPIType* spi;
     SPIBusConfig config{};
     bool firstConfigApplied = false;
 };
 
-inline SPIBus::SPIBus(SPIType* spi) : spi(spi) {}
+inline SPIBus::SPIBus(SPIType* spi) : spi(spi)
+{
+    ClockUtils::enablePeripheralClock(spi);
+}
+
+inline SPIType* SPIBus::getSpi() { return spi; }
+
+inline void SPIBus::reset()
+{
+    spi->CR1    = 0;
+    spi->CR2    = 0;
+    spi->DR     = 0;
+    spi->RXCRCR = 0;
+    spi->TXCRCR = 0;
+}
+
+inline void SPIBus::enable() { spi->CR1 |= SPI_CR1_SPE; }
+
+inline void SPIBus::disable() { spi->CR1 &= ~SPI_CR1_SPE; }
+
+inline void SPIBus::set8BitFrameFormat() { spi->CR1 &= ~SPI_CR1_DFF; }
+
+inline void SPIBus::set16BitFrameFormat() { spi->CR1 |= SPI_CR1_DFF; }
+
+inline void SPIBus::enableSoftwareSlaveManagement() { spi->CR1 |= SPI_CR1_SSM; }
+
+inline void SPIBus::disableSoftwareSlaveManagement()
+{
+    spi->CR1 &= ~SPI_CR1_SSM;
+}
+
+inline void SPIBus::enableInternalSlaveSelection() { spi->CR1 |= SPI_CR1_SSI; }
+
+inline void SPIBus::disableInternalSlaveSelection()
+{
+    spi->CR1 &= ~SPI_CR1_SSI;
+}
+
+inline void SPIBus::setBitOrder(SPI::BitOrder bitOrder)
+{
+    // First clear the configuration
+    spi->CR1 &= ~SPI_CR1_LSBFIRST;
+
+    // Set the new value
+    spi->CR1 |= static_cast<uint32_t>(bitOrder);
+}
+
+inline void SPIBus::setClockDiver(SPI::ClockDivider divider)
+{
+    // First clear the configuration
+    spi->CR1 &= ~SPI_CR1_BR;
+
+    // Set the new value
+    spi->CR1 |= static_cast<uint32_t>(divider);
+}
+
+inline void SPIBus::setSlaveConfiguration() { spi->CR1 &= ~SPI_CR1_MSTR; }
+
+inline void SPIBus::setMasterConfiguration() { spi->CR1 |= SPI_CR1_MSTR; }
+
+inline void SPIBus::setMode(SPI::Mode mode)
+{
+    // First clear the configuration
+    spi->CR1 &= ~(SPI_CR1_CPOL | SPI_CR1_CPHA);
+
+    // Set the new value
+    spi->CR1 |= static_cast<uint32_t>(mode);
+}
+
+inline void SPIBus::enableTxDMARequest() { spi->CR2 |= SPI_CR2_TXDMAEN; }
+
+inline void SPIBus::disableTxDMARequest() { spi->CR2 &= ~SPI_CR2_TXDMAEN; }
+
+inline void SPIBus::enableRxDMARequest() { spi->CR2 |= SPI_CR2_RXDMAEN; }
+
+inline void SPIBus::disableRxDMARequest() { spi->CR2 &= ~SPI_CR2_RXDMAEN; }
+
+inline void SPIBus::waitPeripheral()
+{
+    while ((spi->SR & SPI_SR_TXE) == 0)
+        ;
+    while ((spi->SR & SPI_SR_BSY) > 0)
+        ;
+}
 
 inline void SPIBus::configure(SPIBusConfig newConfig)
 {
@@ -183,33 +334,34 @@ inline void SPIBus::configure(SPIBusConfig newConfig)
         firstConfigApplied = true;
 
         // Wait until the peripheral is done before changing configuration
-        spi.waitPeripheral();
+        waitPeripheral();
 
         // Disable the peripheral
-        spi.disable();
+        disable();
 
         // Configure clock polarity and phase
-        spi.setMode(config.mode);
+        setMode(config.mode);
 
         // Configure clock frequency
-        spi.setClockDiver(config.clockDivider);
+        setClockDiver(config.clockDivider);
 
         // Configure bit order
-        spi.setBitOrder(config.bitOrder);
+        setBitOrder(config.bitOrder);
 
         // Configure chip select and master mode
-        spi.enableSoftwareSlaveManagement();
-        spi.enableInternalSlaveSelection();
-        spi.setMasterConfiguration();
+        enableSoftwareSlaveManagement();
+        enableInternalSlaveSelection();
+        setMasterConfiguration();
 
         // Enable the peripheral
-        spi.enable();
+        enable();
     }
 }
 
 inline void SPIBus::select(GpioType& cs)
 {
     cs.low();
+
     if (config.csSetupTimeUs > 0)
     {
         miosix::delayUs(config.csSetupTimeUs);
@@ -222,45 +374,152 @@ inline void SPIBus::deselect(GpioType& cs)
     {
         miosix::delayUs(config.csHoldTimeUs);
     }
+
     cs.high();
 }
 
-// Read, write and transfer operations
+inline uint8_t SPIBus::read() { return transfer(static_cast<uint8_t>(0)); }
+
+inline uint16_t SPIBus::read16() { return transfer(static_cast<uint16_t>(0)); }
+
+inline void SPIBus::read(uint8_t* data, size_t nBytes)
+{
+    for (size_t i = 0; i < nBytes; i++)
+        data[i] = read();
+}
+
+inline void SPIBus::read16(uint16_t* data, size_t nBytes)
+{
+    // Set 16 bit frame format
+    set16BitFrameFormat();
+
+    for (size_t i = 0; i < nBytes / 2; i++)
+    {
+        // Wait until the peripheral is ready to transmit
+        while ((spi->SR & SPI_SR_TXE) == 0)
+            ;
+
+        // Write the data item to transmit
+        spi->DR = 0;
 
-inline uint8_t SPIBus::read() { return spi.read(); }
+        // Wait until data is received
+        while ((spi->SR & SPI_SR_RXNE) == 0)
+            ;
 
-inline uint16_t SPIBus::read16() { return spi.read16(); }
+        // Read the received data item
+        data[i] = static_cast<uint16_t>(spi->DR);
+    }
+
+    // Go back to 8 bit frame format
+    set8BitFrameFormat();
+}
+
+inline void SPIBus::write(uint8_t data) { transfer(data); }
 
-inline void SPIBus::read(uint8_t* data, size_t size) { spi.read(data, size); }
+inline void SPIBus::write16(uint16_t data) { transfer(data); }
 
-inline void SPIBus::read16(uint16_t* data, size_t size)
+inline void SPIBus::write(const uint8_t* data, size_t nBytes)
 {
-    spi.read16(data, size);
+    for (size_t i = 0; i < nBytes; i++)
+        transfer(data[i]);
 }
 
-inline void SPIBus::write(uint8_t data) { spi.write(data); }
+inline void SPIBus::write16(const uint16_t* data, size_t nBytes)
+{
+    // Set 16 bit frame format
+    set16BitFrameFormat();
+
+    for (size_t i = 0; i < nBytes / 2; i++)
+    {
+        // Wait until the peripheral is ready to transmit
+        while ((spi->SR & SPI_SR_TXE) == 0)
+            ;
+
+        // Write the data item to transmit
+        spi->DR = static_cast<uint16_t>(data[i]);
+
+        // Wait until data is received
+        while ((spi->SR & SPI_SR_RXNE) == 0)
+            ;
 
-inline void SPIBus::write16(uint16_t data) { spi.write(data); }
+        // Read the received data item
+        (void)spi->DR;
+    }
 
-inline void SPIBus::write(uint8_t* data, size_t size) { spi.write(data, size); }
+    // Go back to 8 bit frame format
+    set8BitFrameFormat();
+}
 
-inline void SPIBus::write16(uint16_t* data, size_t size)
+inline uint8_t SPIBus::transfer(uint8_t data)
 {
-    spi.write16(data, size);
+    // Wait until the peripheral is ready to transmit
+    while ((spi->SR & SPI_SR_TXE) == 0)
+        ;
+
+    // Write the data item to transmit
+    spi->DR = static_cast<uint8_t>(data);
+
+    // Wait until data is received
+    while ((spi->SR & SPI_SR_RXNE) == 0)
+        ;
+
+    // Read the received data item
+    return static_cast<uint8_t>(spi->DR);
 }
 
-inline uint8_t SPIBus::transfer(uint8_t data) { return spi.transfer(data); }
+inline uint16_t SPIBus::transfer16(uint16_t data)
+{
+    // Set 16 bit frame format
+    set16BitFrameFormat();
+
+    // Wait until the peripheral is ready to transmit
+    while ((spi->SR & SPI_SR_TXE) == 0)
+        ;
+
+    // Write the data item to transmit
+    spi->DR = static_cast<uint16_t>(data);
+
+    // Wait until data is received
+    while ((spi->SR & SPI_SR_RXNE) == 0)
+        ;
 
-inline uint16_t SPIBus::transfer16(uint16_t data) { return spi.transfer(data); }
+    // Go back to 8 bit frame format
+    set8BitFrameFormat();
 
-inline void SPIBus::transfer(uint8_t* data, size_t size)
+    // Read the received data item
+    return static_cast<uint16_t>(spi->DR);
+}
+
+inline void SPIBus::transfer(uint8_t* data, size_t nBytes)
 {
-    spi.transfer(data, size);
+    for (size_t i = 0; i < nBytes; i++)
+        data[i] = transfer(data[i]);
 }
 
-inline void SPIBus::transfer16(uint16_t* data, size_t size)
+inline void SPIBus::transfer16(uint16_t* data, size_t nBytes)
 {
-    spi.transfer16(data, size);
+    // Set 16 bit frame format
+    set16BitFrameFormat();
+
+    for (size_t i = 0; i < nBytes / 2; i++)
+    {
+        // Wait until the peripheral is ready to transmit
+        while ((spi->SR & SPI_SR_TXE) == 0)
+            ;
+
+        // Write the data item to transmit
+        spi->DR = static_cast<uint16_t>(data[i]);
+
+        // Wait until data is received
+        while ((spi->SR & SPI_SR_RXNE) == 0)
+            ;
+
+        // Read the received data item
+        data[i] = static_cast<uint16_t>(spi->DR);
+    }
+
+    // Go back to 8 bit frame format
+    set8BitFrameFormat();
 }
 
 }  // namespace Boardcore
diff --git a/src/shared/drivers/spi/SPIBusInterface.h b/src/shared/drivers/spi/SPIBusInterface.h
index 12fde48f221732752690b0b109f40ef55c638b01..db8158e49cc8fe950e9e2c1600cf896aa91c0f47 100644
--- a/src/shared/drivers/spi/SPIBusInterface.h
+++ b/src/shared/drivers/spi/SPIBusInterface.h
@@ -23,8 +23,9 @@
 #pragma once
 
 #include <interfaces-impl/gpio_impl.h>
+#include <stddef.h>
 
-#include "SPI.h"
+#include "SPIDefs.h"
 
 #ifndef USE_MOCK_PERIPHERALS
 using GpioType = miosix::GpioPin;
@@ -170,7 +171,7 @@ public:
      * @param data Buffer containing data to write.
      * @param size Size of the buffer.
      */
-    virtual void write(uint8_t* data, size_t size) = 0;
+    virtual void write(const uint8_t* data, size_t size) = 0;
 
     /**
      * @brief Writes multiple half words to the bus.
@@ -178,7 +179,7 @@ public:
      * @param data Buffer containing data to write.
      * @param size Size of the buffer.
      */
-    virtual void write16(uint16_t* data, size_t size) = 0;
+    virtual void write16(const uint16_t* data, size_t size) = 0;
 
     /**
      * @brief Full duplex transmission of one byte on the bus.
diff --git a/src/shared/drivers/spi/SPIDefs.h b/src/shared/drivers/spi/SPIDefs.h
new file mode 100644
index 0000000000000000000000000000000000000000..ce7a7682d56717b1e665dea540ac4b3278fd1c26
--- /dev/null
+++ b/src/shared/drivers/spi/SPIDefs.h
@@ -0,0 +1,91 @@
+/* Copyright (c) 2021 Skyward Experimental Rocketry
+ * Author: Alberto Nidasio
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#pragma once
+
+#include <interfaces/arch_registers.h>
+#include <stdint.h>
+
+namespace Boardcore
+{
+
+/**
+ * @brief Driver for STM32 low level SPI peripheral.
+ *
+ * This driver applies to the whole STM32F4xx family.
+ *
+ * The serial peripheral interface (SPI) allows half/full-duplex, synchronous,
+ * serial communication with external devices. The interface can be configured
+ * as the master and in this case it provides the communication clock (SCK) to
+ * the external slave device. The peripheral is also capable of reliable
+ * communication using CRC checking.
+ *
+ * SPI main features:
+ * - Full-duplex synchronous transfers on three lines
+ * - 8- or 16-bit transfer frame format selection
+ * - Master or slave operation
+ * - 8 master mode baud rate prescaler (f_PCLK/2 max.)
+ * - Programmable clock polarity and phase
+ * - Programmable data order with MSB-first or LSB-first shifting
+ * - Hardware CRC feature for reliable communication
+ * - DMA capability
+ */
+namespace SPI
+{
+
+enum class BitOrder : uint16_t
+{
+    MSB_FIRST = 0,
+    LSB_FIRST = 0x80
+};
+
+/**
+ * @brief SPI Clock divider.
+ *
+ * SPI clock frequency will be equal to the SPI peripheral bus clock speed
+ * divided by the specified value.
+ *
+ * Eg: DIV_2 --> spi clock freq = f_PCLK / 2
+ */
+enum class ClockDivider : uint8_t
+{
+    DIV_2   = 0x00,
+    DIV_4   = 0x08,
+    DIV_8   = 0x10,
+    DIV_16  = 0x18,
+    DIV_32  = 0x20,
+    DIV_64  = 0x28,
+    DIV_128 = 0x30,
+    DIV_256 = 0x38,
+};
+
+enum class Mode : uint8_t
+{
+    MODE_0 = 0,  ///< CPOL = 0, CPHA = 0
+    MODE_1 = 1,  ///< CPOL = 0, CPHA = 1
+    MODE_2 = 2,  ///< CPOL = 1, CPHA = 0
+    MODE_3 = 3   ///< CPOL = 1, CPHA = 1
+};
+
+}  // namespace SPI
+
+}  // namespace Boardcore
diff --git a/src/tests/drivers/dma/test-dma-spi-raw.cpp b/src/tests/drivers/dma/test-dma-spi-raw.cpp
deleted file mode 100644
index e03af9877ffb98df2ff86f7e62433c544f58de5c..0000000000000000000000000000000000000000
--- a/src/tests/drivers/dma/test-dma-spi-raw.cpp
+++ /dev/null
@@ -1,234 +0,0 @@
-/* Copyright (c) 2021 Skyward Experimental Rocketry
- * Author: Alberto Nidasio
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include <miosix.h>
-
-using namespace miosix;
-
-static bool dmaComplete = false;
-static bool dmaError    = false;
-
-static uint8_t txData[]  = {0xA8, 0x00};
-static uint8_t rxData[2] = {0};
-
-GpioPin csPin   = GpioPin(GPIOC_BASE, 1);
-GpioPin sckPin  = GpioPin(GPIOF_BASE, 7);
-GpioPin misoPin = GpioPin(GPIOF_BASE, 8);
-GpioPin mosiPin = GpioPin(GPIOF_BASE, 9);
-
-int main()
-{
-    // SPI INIT
-    {
-        csPin.mode(Mode::OUTPUT);
-        csPin.high();
-        sckPin.mode(Mode::ALTERNATE);
-        sckPin.alternateFunction(5);
-        mosiPin.mode(Mode::ALTERNATE);
-        mosiPin.alternateFunction(5);
-        misoPin.mode(Mode::ALTERNATE);
-        misoPin.alternateFunction(5);
-
-        // Enable SPI clock for SPI5 interface
-        RCC->APB2ENR |= RCC_APB2ENR_SPI5EN;
-
-        // Clear configuration for SPI interface
-        SPI5->CR1 = 0;
-        SPI5->CR2 = 0;
-
-        // 1: Set BR[2:0] bits to define the baud rate
-        SPI5->CR1 |= SPI_CR1_BR;
-
-        // 2: Setup clock phase and polarity (MODE1: CPOL = 1, CPHA = 1)
-        SPI5->CR1 |= SPI_CR1_CPOL | SPI_CR1_CPHA;
-
-        // 7: Set MSTR to set master mode
-        SPI5->CR1 |= SPI_CR1_SSI | SPI_CR1_SSM | SPI_CR1_MSTR;
-
-        // 8: Enable DMA for tx
-        SPI5->CR2 |= SPI_CR2_TXDMAEN;
-        SPI5->CR2 |= SPI_CR2_RXDMAEN;
-
-        // Enable SPI
-        SPI5->CR1 |= SPI_CR1_SPE;
-
-        printf("SPI interface setup completed...\n");
-    }
-
-    // 0: Enable DMA2 clock
-    RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
-
-    // DMA2 Stream4 channel 3 for SPI5 TX
-    {
-        // 1: Disable the stream
-
-        // Disable stream
-        DMA2_Stream4->CR &= ~DMA_SxCR_EN;
-
-        // Wait for the stream to be disabled
-        while (DMA2_Stream4->CR & DMA_SxCR_EN)
-            ;
-
-        // Reset DMA configuration
-        DMA2_Stream4->CR = 0;
-
-        // Ensures all the status bits are cleared by explicitly clearing them
-        DMA2->LIFCR = 0x0F7D0F7D;  // Mask excluding reserved bits
-        DMA2->HIFCR = 0x0F7D0F7D;
-
-        // 2: Set the peripheral address
-        DMA2_Stream4->PAR = (uint32_t) & (SPI5->DR);
-
-        // 3: Set the memory address
-        DMA2_Stream4->M0AR = (uint32_t)txData;
-
-        // 4: Configure the total number of data items
-        DMA2_Stream4->NDTR = 2;
-
-        // 5: Select the DMA channel
-        DMA2_Stream4->CR |= DMA_SxCR_CHSEL_1;  // Channel 2 for SPI5 Tx
-
-        // 7: Configure the stream priority to very high
-        DMA2_Stream4->CR |= DMA_SxCR_PL;
-
-        // 9: Other configuration
-
-        // Data transfer memory-to-peripheral
-        DMA2_Stream4->CR |= DMA_SxCR_DIR_0;
-
-        // Address increment mode
-        DMA2_Stream4->CR |= DMA_SxCR_MINC;
-
-        // Interrupts (transfer complete)
-        DMA2_Stream4->CR |= DMA_SxCR_TCIE;
-
-        // Register interrupt
-        NVIC_EnableIRQ(DMA2_Stream4_IRQn);
-        NVIC_SetPriority(DMA2_Stream4_IRQn, 15);
-    }
-
-    // DMA2 Stream3 channel 3 for SPI5 RX
-    {
-        // 1: Disable the stream
-
-        // Disable stream
-        DMA2_Stream3->CR &= ~DMA_SxCR_EN;
-
-        // Wait for the stream to be disabled
-        while (DMA2_Stream3->CR & DMA_SxCR_EN)
-            ;
-
-        // Reset DMA configuration
-        DMA2_Stream3->CR = 0;
-
-        // Ensures all the status bits are cleared by explicitly clearing them
-        DMA1->LIFCR = 0x0F7D0F7D;  // Mask excluding reserved bits
-        DMA1->HIFCR = 0x0F7D0F7D;
-
-        // 2: Set the peripheral address
-        DMA2_Stream3->PAR = (uint32_t) & (SPI5->DR);
-
-        // 3: Set the memory address
-        DMA2_Stream3->M0AR = (uint32_t)rxData;
-
-        // 4: Configure the total number of data items
-        DMA2_Stream3->NDTR = 2;
-
-        // 5: Select the DMA channel
-        DMA2_Stream3->CR |= DMA_SxCR_CHSEL_1;  // Channel 2 for SPI5 Rx
-
-        // 7: Configure the stream priority to very high
-        DMA2_Stream3->CR |= DMA_SxCR_PL;
-
-        // 9: Other configuration
-
-        // Address increment mode
-        DMA2_Stream3->CR |= DMA_SxCR_MINC;
-    }
-
-    // Transaction 1
-    while (true)
-    {
-        DMA2->LIFCR = 0x0F7D0F7D;
-        DMA2->HIFCR = 0x0F7D0F7D;
-
-        csPin.low();
-
-        // Enable DMA to start serving requests from SPI interface
-        DMA2_Stream3->CR |= DMA_SxCR_EN;
-        DMA2_Stream4->CR |= DMA_SxCR_EN;
-
-        delayUs(1);
-
-        // Wait for completion
-        while ((SPI5->SR & SPI_SR_TXE) == 0)
-            ;
-        while (SPI5->SR & SPI_SR_BSY)
-            ;
-
-        csPin.high();
-
-        if (dmaError)
-        {
-            dmaError = false;
-            printf("DMA transfer error!\n");
-        }
-
-        if (dmaComplete)
-        {
-            dmaError = false;
-            printf("DMA transfer completed!\n");
-        }
-
-        printf("Rx data: 0x%02X%02X\n", rxData[0], rxData[1]);
-        rxData[0] = 0;
-        rxData[1] = 0;
-
-        delayMs(1000);
-    }
-}
-
-void __attribute__((naked)) DMA2_Stream4_IRQHandler()
-{
-    saveContext();
-    asm volatile("bl _Z27DMA2_Stream4_IRQHandlerImplv");
-    restoreContext();
-}
-
-void __attribute__((used)) DMA2_Stream4_IRQHandlerImpl()
-{
-    if (DMA2->HISR & DMA_HISR_TCIF4)
-    {
-        dmaComplete = true;
-
-        // Clear the interrupt
-        SET_BIT(DMA2->HIFCR, DMA_HIFCR_CTCIF4);
-    }
-
-    if (DMA2->HISR & DMA_HISR_TEIF4)
-    {
-        dmaError = true;
-
-        // Clear the interrupt
-        SET_BIT(DMA2->HIFCR, DMA_HIFCR_CTEIF4);
-    }
-}
diff --git a/src/tests/drivers/dma/test-dma-spi.cpp b/src/tests/drivers/dma/test-dma-spi.cpp
deleted file mode 100644
index 647d2e52124e556c7953e44912fd599b47a6fbed..0000000000000000000000000000000000000000
--- a/src/tests/drivers/dma/test-dma-spi.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-/* Copyright (c) 2021 Skyward Experimental Rocketry
- * Author: Alberto Nidasio
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include <drivers/dma/DMAStream.h>
-#include <drivers/spi/SPI.h>
-#include <miosix.h>
-
-/**
- * This test reads the gyroscope on the Discovery 429 using SPI with DMA.
- */
-
-using namespace miosix;
-using namespace Boardcore;
-
-GpioPin csPin   = GpioPin(GPIOC_BASE, 1);
-GpioPin sckPin  = GpioPin(GPIOF_BASE, 7);
-GpioPin misoPin = GpioPin(GPIOF_BASE, 8);
-GpioPin mosiPin = GpioPin(GPIOF_BASE, 9);
-
-SPI spi5 = SPI(SPI5);
-
-DMAStream txStream = DMAStream(DMA2_Stream4);
-DMAStream rxStream = DMAStream(DMA2_Stream3);
-
-static uint8_t txData[]  = {0xA8, 0x00};
-static uint8_t rxData[2] = {0};
-
-void initGPIOs();
-void initSPI();
-void initDMA();
-
-int main()
-{
-    initGPIOs();
-    initSPI();
-    initDMA();
-
-    while (true)
-    {
-        csPin.low();
-
-        // Enable DMA to start serving requests from SPI interface
-        rxStream.enable();
-        txStream.enable();
-
-        delayUs(1);
-
-        // Wait for completion
-        spi5.waitPeripheral();
-
-        csPin.high();
-
-        printf("Rx data: 0x%02X%02X\n", rxData[0], rxData[1]);
-        rxData[0] = 0;
-        rxData[1] = 0;
-
-        delayMs(1000);
-    }
-}
-
-void initGPIOs()
-{
-    csPin.mode(Mode::OUTPUT);
-    csPin.high();
-    sckPin.mode(Mode::ALTERNATE);
-    sckPin.alternateFunction(5);
-    mosiPin.mode(Mode::ALTERNATE);
-    mosiPin.alternateFunction(5);
-    misoPin.mode(Mode::ALTERNATE);
-    misoPin.alternateFunction(5);
-}
-
-void initSPI()
-{
-    spi5.reset();
-    spi5.setClockDiver(SPI::ClockDivider::DIV_256);
-    spi5.setMode(SPI::Mode::MODE_3);
-    spi5.enableInternalSlaveSelection();
-    spi5.enableSoftwareSlaveManagement();
-    spi5.setMasterConfiguration();
-    spi5.enableTxDMARequest();
-    spi5.enableRxDMARequest();
-    spi5.enable();
-}
-
-void initDMA()
-{
-    // 0: Enable DMA2 clock
-    RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
-
-    // DMA2 Stream4 channel 2 for SPI5 Tx
-    {
-        txStream.reset();
-        txStream.setPeripheralAddress(const_cast<uint32_t*>(&(SPI5->DR)));
-        txStream.setMemory0Address(reinterpret_cast<uint32_t*>(txData));
-        txStream.setNumberOfDataItems(2);
-        txStream.setStreamChannel(DMAStream::Channel::CHANNEL2);
-        txStream.setStreamPriorityLevel(DMAStream::PriorityLevel::VERY_HIGH);
-        txStream.setDataTransferDirection(
-            DMAStream::DataTransferDirection::MEM_TO_PERIPH);
-        txStream.enableMemoryIncrement();
-    }
-
-    // DMA2 Stream3 channel 2 for SPI5 Rx
-    {
-        rxStream.reset();
-        rxStream.setPeripheralAddress(const_cast<uint32_t*>(&(SPI5->DR)));
-        rxStream.setMemory0Address(reinterpret_cast<uint32_t*>(rxData));
-        rxStream.setNumberOfDataItems(2);
-        rxStream.setStreamChannel(DMAStream::Channel::CHANNEL2);
-        rxStream.setStreamPriorityLevel(DMAStream::PriorityLevel::VERY_HIGH);
-        rxStream.enableMemoryIncrement();
-    }
-}