diff --git a/cpu/stm32/include/periph_cpu.h b/cpu/stm32/include/periph_cpu.h index ee53c80fd1..f74611c03d 100644 --- a/cpu/stm32/include/periph_cpu.h +++ b/cpu/stm32/include/periph_cpu.h @@ -22,6 +22,7 @@ #define PERIPH_CPU_H #include "cpu.h" +#include "macros/units.h" #if defined(CPU_FAM_STM32F0) #include "periph/f0/periph_cpu.h" @@ -620,6 +621,25 @@ typedef enum { /** @} */ #endif /* ndef DOXYGEN */ +/** + * @brief Override SPI clock speed values + * @{ + */ +#define HAVE_SPI_CLK_T +enum { + SPI_CLK_100KHZ = KHZ(100), /**< drive the SPI bus with 100KHz */ + SPI_CLK_400KHZ = KHZ(400), /**< drive the SPI bus with 400KHz */ + SPI_CLK_1MHZ = MHZ(1), /**< drive the SPI bus with 1MHz */ + SPI_CLK_5MHZ = MHZ(5), /**< drive the SPI bus with 5MHz */ + SPI_CLK_10MHZ = MHZ(10), /**< drive the SPI bus with 10MHz */ +}; + +/** + * @brief SPI clock type + */ +typedef uint32_t spi_clk_t; +/** @} */ + /** * @brief Structure for UART configuration data */ diff --git a/cpu/stm32/periph/spi.c b/cpu/stm32/periph/spi.c index e1869ffb27..7ac5e7d732 100644 --- a/cpu/stm32/periph/spi.c +++ b/cpu/stm32/periph/spi.c @@ -26,16 +26,21 @@ * @} */ +#include "bitarithm.h" #include "cpu.h" #include "mutex.h" #include "assert.h" #include "periph/spi.h" #include "pm_layered.h" +#define ENABLE_DEBUG (0) +#include "debug.h" + /** * @brief Number of bits to shift the BR value in the CR1 register */ #define BR_SHIFT (3U) +#define BR_MAX (7U) #ifdef SPI_CR2_FRXTH /* configure SPI for 8-bit data width */ @@ -52,6 +57,16 @@ */ static mutex_t locks[SPI_NUMOF]; +/** + * @brief Clock configuration cache + */ +static uint32_t clocks[SPI_NUMOF]; + +/** + * @brief Clock divider cache + */ +static uint8_t dividers[SPI_NUMOF]; + static inline SPI_TypeDef *dev(spi_t bus) { return spi_config[bus].dev; @@ -64,6 +79,35 @@ static inline bool _use_dma(const spi_conf_t *conf) } #endif +/** + * @brief Multiplier for clock divider calculations + * + * Makes the divider calculation fixed point + */ +#define SPI_APB_CLOCK_SHIFT (4U) +#define SPI_APB_CLOCK_MULT (1U << SPI_APB_CLOCK_SHIFT) + +static uint8_t _get_clkdiv(const spi_conf_t *conf, uint32_t clock) +{ + uint32_t bus_clock = periph_apb_clk(conf->apbbus); + /* Shift bus_clock with SPI_APB_CLOCK_SHIFT to create a fixed point int */ + uint32_t div = (bus_clock << SPI_APB_CLOCK_SHIFT) / (2 * clock); + DEBUG("[spi] clock: divider: %"PRIu32"\n", div); + /* Test if the divider is 2 or smaller, keeping the fixed point in mind */ + if (div <= SPI_APB_CLOCK_MULT) { + return 0; + } + /* determine MSB and compensate back for the fixed point int shift */ + uint8_t rounded_div = bitarithm_msb(div) - SPI_APB_CLOCK_SHIFT; + /* Determine if rounded_div is not a power of 2 */ + if ((div & (div - 1)) != 0) { + /* increment by 1 to ensure that the clock speed at most the + * requested clock speed */ + rounded_div++; + } + return rounded_div > BR_MAX ? BR_MAX : rounded_div; +} + void spi_init(spi_t bus) { assert(bus < SPI_NUMOF); @@ -162,7 +206,18 @@ int spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) /* enable SPI device clock */ periph_clk_en(spi_config[bus].apbbus, spi_config[bus].rccmask); /* enable device */ - uint8_t br = spi_divtable[spi_config[bus].apbbus][clk]; + if (clk != clocks[bus]) { + dividers[bus] = _get_clkdiv(&spi_config[bus], clk); + clocks[bus] = clk; + } + uint8_t br = dividers[bus]; + + DEBUG("[spi] acquire: requested clock: %"PRIu32", resulting clock: %"PRIu32 + " BR divider: %u\n", + clk, + periph_apb_clk(spi_config[bus].apbbus)/(1 << (br + 1)), + br); + uint16_t cr1_settings = ((br << BR_SHIFT) | mode | SPI_CR1_MSTR); /* Settings to add to CR2 in addition to SPI_CR2_SETTINGS */ uint16_t cr2_extra_settings = 0;