Skip to content

Commit 2fc1fdc

Browse files
committed
cpu/stm32/periph_spi: improve clock divider calculation
With only 8 possible clock dividers, we can just loop over the values and shift the clock. In addition to being much easier to read, using shifts over divisions can be a lot faster on CPUs without hardware division. In addition an `assert()` is added that checks if the API contract regarding the SPI frequency is honored. If the requested clock is too low to be generated, we should rather have a blown assertion than hard to trace communication errors.
1 parent 1d00706 commit 2fc1fdc

File tree

1 file changed

+19
-29
lines changed

1 file changed

+19
-29
lines changed

cpu/stm32/periph/spi.c

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828

2929
#include <assert.h>
3030

31-
#include "bitarithm.h"
3231
#include "mutex.h"
3332
#include "periph/gpio.h"
3433
#include "periph/spi.h"
@@ -80,33 +79,24 @@ static inline bool _use_dma(const spi_conf_t *conf)
8079
}
8180
#endif
8281

83-
/**
84-
* @brief Multiplier for clock divider calculations
85-
*
86-
* Makes the divider calculation fixed point
87-
*/
88-
#define SPI_APB_CLOCK_SHIFT (4U)
89-
#define SPI_APB_CLOCK_MULT (1U << SPI_APB_CLOCK_SHIFT)
90-
9182
static uint8_t _get_clkdiv(const spi_conf_t *conf, uint32_t clock)
9283
{
9384
uint32_t bus_clock = periph_apb_clk(conf->apbbus);
94-
/* Shift bus_clock with SPI_APB_CLOCK_SHIFT to create a fixed point int */
95-
uint32_t div = (bus_clock << SPI_APB_CLOCK_SHIFT) / (2 * clock);
96-
DEBUG("[spi] clock: divider: %"PRIu32"\n", div);
97-
/* Test if the divider is 2 or smaller, keeping the fixed point in mind */
98-
if (div <= SPI_APB_CLOCK_MULT) {
99-
return 0;
100-
}
101-
/* determine MSB and compensate back for the fixed point int shift */
102-
uint8_t rounded_div = bitarithm_msb(div) - SPI_APB_CLOCK_SHIFT;
103-
/* Determine if rounded_div is not a power of 2 */
104-
if ((div & (div - 1)) != 0) {
105-
/* increment by 1 to ensure that the clock speed at most the
106-
* requested clock speed */
107-
rounded_div++;
108-
}
109-
return rounded_div > BR_MAX ? BR_MAX : rounded_div;
85+
86+
uint8_t div = 0;
87+
uint32_t divided_clock = bus_clock >> 1;
88+
const uint8_t div_max = SPI_CR1_BR_Msk >> SPI_CR1_BR_Pos;
89+
for (; (divided_clock > clock) && (div < div_max); div++) {
90+
divided_clock >>= 1;
91+
}
92+
93+
/* If the callers asks for an SPI frequency of at most x, bad things will
94+
* happen if this cannot be met. So let's have a blown assertion
95+
* rather than runtime failures that require a logic analyzer to
96+
* debug. */
97+
assert(divided_clock <= clock);
98+
99+
return div;
110100
}
111101

112102
void spi_init(spi_t bus)
@@ -239,11 +229,11 @@ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk)
239229
}
240230
uint8_t br = dividers[bus];
241231

242-
DEBUG("[spi] acquire: requested clock: %"PRIu32", resulting clock: %"PRIu32
243-
" BR divider: %u\n",
232+
DEBUG("[spi] acquire: requested clock: %" PRIu32
233+
" Hz, resulting clock: %" PRIu32 " Hz, BR divider: %u\n",
244234
clk,
245-
periph_apb_clk(spi_config[bus].apbbus)/(1 << (br + 1)),
246-
br);
235+
periph_apb_clk(spi_config[bus].apbbus) >> (br + 1),
236+
(unsigned)br);
247237

248238
uint16_t cr1 = ((br << BR_SHIFT) | mode | SPI_CR1_MSTR | SPI_CR1_SPE);
249239
/* Settings to add to CR2 in addition to SPI_CR2_SETTINGS */

0 commit comments

Comments
 (0)