diff --git a/src/shared/drivers/spi/SPIBusInterface.h b/src/shared/drivers/spi/SPIBusInterface.h
index ff501c21903e5f27f5e54598c6f10366c667f608..7085876133cb74435901dc9eefc0215e88b8ed83 100644
--- a/src/shared/drivers/spi/SPIBusInterface.h
+++ b/src/shared/drivers/spi/SPIBusInterface.h
@@ -24,6 +24,7 @@
 
 #include <interfaces-impl/gpio_impl.h>
 #include <stddef.h>
+#include <utils/ClockUtils.h>
 
 #include "SPIDefs.h"
 
@@ -45,13 +46,13 @@ namespace Boardcore
 struct SPIBusConfig
 {
     ///< Peripheral clock division
-    SPI::ClockDivider clockDivider;
+    SPI::ClockDivider clockDivider = SPI::ClockDivider::DIV_256;
 
     ///< Clock polarity and phase configuration
-    SPI::Mode mode;
+    SPI::Mode mode = SPI::Mode::MODE_0;
 
     ///< MSBit or LSBit first
-    SPI::Order bitOrder;
+    SPI::Order bitOrder = SPI::Order::MSB_FIRST;
 
     /**
      * @brief MSByte or LSByte first
@@ -74,28 +75,16 @@ struct SPIBusConfig
      * @warning This driver does not support devices which decrements registers
      * address during multiple registers accesses.
      */
-    SPI::Order byteOrder;
+    SPI::Order byteOrder = SPI::Order::MSB_FIRST;
 
     ///< Write bit behaviour, default high when reading
-    SPI::WriteBit writeBit;
+    SPI::WriteBit writeBit = SPI::WriteBit::NORMAL;
 
     ///< How long to wait before starting a tranmission after CS is set (us)
-    unsigned int csSetupTimeUs;
+    unsigned int csSetupTimeUs = 0;
 
     ///< How long to hold cs after the end of a tranmission (us)
-    unsigned int csHoldTimeUs;
-
-    SPIBusConfig(SPI::ClockDivider clockDivider = SPI::ClockDivider::DIV_256,
-                 SPI::Mode mode                 = SPI::Mode::MODE_0,
-                 SPI::Order bitOrder            = SPI::Order::MSB_FIRST,
-                 SPI::Order byteOrder           = SPI::Order::MSB_FIRST,
-                 SPI::WriteBit writeBit         = SPI::WriteBit::NORMAL,
-                 unsigned int csSetupTimeUs = 0, unsigned int csHoldTimeUs = 0)
-        : clockDivider(clockDivider), mode(mode), bitOrder(bitOrder),
-          byteOrder(byteOrder), writeBit(writeBit),
-          csSetupTimeUs(csSetupTimeUs), csHoldTimeUs(csHoldTimeUs)
-    {
-    }
+    unsigned int csHoldTimeUs = 0;
 
     bool operator==(const SPIBusConfig& other) const
     {
@@ -109,6 +98,40 @@ struct SPIBusConfig
     {
         return !(*this == other);
     }
+
+    /**
+     * @brief Computes the clock divider for the provided maximum frequency.
+     *
+     * The computed divider has the lowest possible value which generates a
+     * frequency not higher than the provided one.
+     * The spi peripheral is needed to get the source clock frequency, which
+     * depends on which APB bus the spi is connected to.
+     */
+    static SPI::ClockDivider computeDivider(SPI_TypeDef* spi, uint32_t maxFreq)
+    {
+        ClockUtils::APB apb;
+        ClockUtils::getPeripheralBus(spi, apb);
+
+        uint32_t sourceFreq   = ClockUtils::getAPBPeripheralsClock(apb);
+        uint32_t idealDivider = sourceFreq / maxFreq;
+
+        if (idealDivider <= 2)
+            return SPI::ClockDivider::DIV_2;
+        else if (idealDivider <= 4)
+            return SPI::ClockDivider::DIV_4;
+        else if (idealDivider <= 8)
+            return SPI::ClockDivider::DIV_8;
+        else if (idealDivider <= 16)
+            return SPI::ClockDivider::DIV_16;
+        else if (idealDivider <= 32)
+            return SPI::ClockDivider::DIV_32;
+        else if (idealDivider <= 64)
+            return SPI::ClockDivider::DIV_64;
+        else if (idealDivider <= 128)
+            return SPI::ClockDivider::DIV_128;
+        else
+            return SPI::ClockDivider::DIV_256;
+    }
 };
 
 /**
diff --git a/src/shared/utils/ClockUtils.h b/src/shared/utils/ClockUtils.h
index 0527a5b11e2432951f6abe85a462e05b235556f2..7cfec14d3d367dd5042cf0ba1206040452a39414 100644
--- a/src/shared/utils/ClockUtils.h
+++ b/src/shared/utils/ClockUtils.h
@@ -68,6 +68,11 @@ bool enablePeripheralClock(void* peripheral);
  */
 bool disablePeripheralClock(void* peripheral);
 
+/**
+ * @brief Returns the APB bus to which the peripheral is connected.
+ */
+bool getPeripheralBus(void* peripheral, APB apb);
+
 }  // namespace ClockUtils
 
 inline uint32_t ClockUtils::getAPBPeripheralsClock(APB bus)
@@ -912,4 +917,200 @@ inline bool ClockUtils::disablePeripheralClock(void* peripheral)
     return true;
 }
 
+inline bool ClockUtils::getPeripheralBus(void* peripheral, APB apb)
+{
+    switch (reinterpret_cast<uint32_t>(peripheral))
+    {
+        // APB1 peripherals
+        {
+#ifdef TIM2_BASE
+            case TIM2_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef TIM3_BASE
+            case TIM3_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef TIM4_BASE
+            case TIM4_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef TIM5_BASE
+            case TIM5_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef TIM6_BASE
+            case TIM6_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef TIM7_BASE
+            case TIM7_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef TIM12_BASE
+            case TIM12_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef TIM13_BASE
+            case TIM13_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef TIM14_BASE
+            case TIM14_BASE:
+                apb = APB::APB1;
+#endif
+// RTC register interface gate only on stm32f7 micro controllers
+#if defined(RTC_BASE) && defined(_ARCH_CORTEXM7_STM32F7)
+            case RTC_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef WWDG_BASE
+            case WWDG_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef SPI2_BASE
+            case SPI2_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef SPI3_BASE
+            case SPI3_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef USART2_BASE
+            case USART2_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef USART3_BASE
+            case USART3_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef UART4_BASE
+            case UART4_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef UART5_BASE
+            case UART5_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef I2C1_BASE
+            case I2C1_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef I2C2_BASE
+            case I2C2_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef I2C3_BASE
+            case I2C3_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef CAN1_BASE
+            case CAN1_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef CAN2_BASE
+            case CAN2_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef PWR_BASE
+            case PWR_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef DAC_BASE
+            case DAC_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef UART7_BASE
+            case UART7_BASE:
+                apb = APB::APB1;
+#endif
+#ifdef UART8_BASE
+            case UART8_BASE:
+                apb = APB::APB1;
+#endif
+        }
+
+        // APB2 peripherals
+        {
+#ifdef TIM1_BASE
+            case TIM1_BASE:
+                apb = APB::APB2;
+#endif
+#ifdef TIM8_BASE
+            case TIM8_BASE:
+                apb = APB::APB2;
+#endif
+#ifdef USART1_BASE
+            case USART1_BASE:
+                apb = APB::APB2;
+#endif
+#ifdef USART6_BASE
+            case USART6_BASE:
+                apb = APB::APB2;
+#endif
+#ifdef ADC1_BASE
+            case ADC1_BASE:
+                apb = APB::APB2;
+#endif
+#ifdef ADC2_BASE
+            case ADC2_BASE:
+                apb = APB::APB2;
+#endif
+#ifdef ADC3_BASE
+            case ADC3_BASE:
+                apb = APB::APB2;
+#endif
+#ifdef SDIO_BASE
+            case SDIO_BASE:
+                apb = APB::APB2;
+#endif
+#ifdef SPI1_BASE
+            case SPI1_BASE:
+                apb = APB::APB2;
+#endif
+#ifdef SPI4_BASE
+            case SPI4_BASE:
+                apb = APB::APB2;
+#endif
+#ifdef SYSCFG_BASE
+            case SYSCFG_BASE:
+                apb = APB::APB2;
+#endif
+#ifdef TIM9_BASE
+            case TIM9_BASE:
+                apb = APB::APB2;
+#endif
+#ifdef TIM10_BASE
+            case TIM10_BASE:
+                apb = APB::APB2;
+#endif
+#ifdef TIM11_BASE
+            case TIM11_BASE:
+                apb = APB::APB2;
+#endif
+#ifdef SPI5_BASE
+            case SPI5_BASE:
+                apb = APB::APB2;
+#endif
+#ifdef SPI6_BASE
+            case SPI6_BASE:
+                apb = APB::APB2;
+#endif
+#ifdef SAI1_BASE
+            case SAI1_BASE:
+                apb = APB::APB2;
+#endif
+#ifdef LTDC_BASE
+            case LTDC_BASE:
+                apb = APB::APB2;
+#endif
+        }
+
+        default:
+            return false;
+    }
+
+    return true;
+}
+
 }  // namespace Boardcore