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