Skip to content

Commit f4729c2

Browse files
committed
cpu/stm32/periph_spi: improve prescaler calculation
With only 8 possible prescalers, 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. Finally, the term prescaler is used instead of divider, as divider may imply that the frequency is divided by the given value n, but in fact is divided by 2^(n+1).
1 parent 63a2a50 commit f4729c2

File tree

1 file changed

+24
-34
lines changed

1 file changed

+24
-34
lines changed

cpu/stm32/periph/spi.c

Lines changed: 24 additions & 34 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"
@@ -64,9 +63,9 @@ static mutex_t locks[SPI_NUMOF];
6463
static uint32_t clocks[SPI_NUMOF];
6564

6665
/**
67-
* @brief Clock divider cache
66+
* @brief Clock prescaler cache
6867
*/
69-
static uint8_t dividers[SPI_NUMOF];
68+
static uint8_t prescalers[SPI_NUMOF];
7069

7170
static inline SPI_TypeDef *dev(spi_t bus)
7271
{
@@ -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-
91-
static uint8_t _get_clkdiv(const spi_conf_t *conf, uint32_t clock)
82+
static uint8_t _get_prescaler(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 prescaler = 0;
87+
uint32_t prescaled_clock = bus_clock >> 1;
88+
const uint8_t prescaler_max = SPI_CR1_BR_Msk >> SPI_CR1_BR_Pos;
89+
for (; (prescaled_clock > clock) && (prescaler < prescaler_max); prescaler++) {
90+
prescaled_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(prescaled_clock <= clock);
98+
99+
return prescaler;
110100
}
111101

112102
void spi_init(spi_t bus)
@@ -234,16 +224,16 @@ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk)
234224
periph_clk_en(spi_config[bus].apbbus, spi_config[bus].rccmask);
235225
/* enable device */
236226
if (clk != clocks[bus]) {
237-
dividers[bus] = _get_clkdiv(&spi_config[bus], clk);
227+
prescalers[bus] = _get_prescaler(&spi_config[bus], clk);
238228
clocks[bus] = clk;
239229
}
240-
uint8_t br = dividers[bus];
230+
uint8_t br = prescalers[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 prescaler: %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)