1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-16 18:13:49 +01:00

stm32: Add support for arbitrary SPI clock rates

This commit is contained in:
Koen Zandberg 2020-08-09 19:58:17 +02:00
parent 99365873fd
commit b9d62e47d3
No known key found for this signature in database
GPG Key ID: 0895A893E6D2985B
2 changed files with 76 additions and 1 deletions

View File

@ -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
*/

View File

@ -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;