diff --git a/Makefile.include b/Makefile.include index 9cf0618a3dc9..eeaf923633e1 100644 --- a/Makefile.include +++ b/Makefile.include @@ -447,6 +447,9 @@ else # always select CPU core features FEATURES_REQUIRED += $(filter cpu_core_%,$(FEATURES_PROVIDED)) + # add features provided by modules + include $(RIOTMAKE)/features_provided_by_modules.inc.mk + # check if required features are provided and update $(FEATURES_USED) include $(RIOTMAKE)/features_check.inc.mk diff --git a/cpu/lm4f120/include/periph_cpu.h b/cpu/lm4f120/include/periph_cpu.h index 9d39286d8ba0..902ce052defa 100644 --- a/cpu/lm4f120/include/periph_cpu.h +++ b/cpu/lm4f120/include/periph_cpu.h @@ -178,7 +178,7 @@ typedef enum { SPI_MODE_0 = SSI_FRF_MOTO_MODE_0, /**< CPOL=0, CPHA=0 */ SPI_MODE_1 = SSI_FRF_MOTO_MODE_1, /**< CPOL=0, CPHA=1 */ SPI_MODE_2 = SSI_FRF_MOTO_MODE_2, /**< CPOL=1, CPHA=0 */ - SPI_MODE_3 = SSI_FRF_MOTO_MODE_0, /**< CPOL=1, CPHA=1 */ + SPI_MODE_3 = SSI_FRF_MOTO_MODE_3, /**< CPOL=1, CPHA=1 */ } spi_mode_t; /** @} */ #endif /* ndef DOXYGEN */ diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 422f974a7653..1fa914854083 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -175,6 +175,11 @@ ifneq (,$(filter stmpe811_%,$(USEMODULE))) USEMODULE += stmpe811 endif +ifneq (,$(filter soft_spi_as_periph_spi,$(USEMODULE))) + USEMODULE += soft_spi + FEAUTRES_PROVIDED += periph_spi +endif + ifneq (,$(filter sx126%,$(USEMODULE))) USEMODULE += sx126x endif diff --git a/drivers/include/soft_spi.h b/drivers/include/soft_spi.h index 69860561b7f2..8759750d6360 100644 --- a/drivers/include/soft_spi.h +++ b/drivers/include/soft_spi.h @@ -13,10 +13,26 @@ * * This module provides a software implemented Serial Peripheral Interface bus. * It is intended to be used in situation where hardware spi is not available. - * The signatures of the functions are similar to the functions declared in spi.h - * The clock speed is approximated by using xtimer_usleep. - * Currently only the use of MOSI in master mode is implemented. Therefore receiving - * data from a slave is currently not possible. + * The signatures of the functions are identical to the functions declared in + * `spi.h`. The clock speed is approximated by using `ztimer_sleep()` with + * `ZTIMER_USEC` as clock. + * + * To use the software SPI as drop-in replacement for `periph_spi`, add the + * following to your application's Makefile: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * USEMODULE += soft_spi_as_periph_spi + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This will hook up the functions defined here into the `periph_spi` interface, + * so that a call to e.g. `spi_acquire()` becomes an alias for + * `soft_spi_acquire()`. Hence, it is not possible to real `periph_spi` and + * `soft_spi_as_periph_spi` at the same time. Using `soft_spi_as_periph_spi` + * will therefore disable any other `periph_spi` implementation, when used. + * + * Without `soft_spi_as_periph_spi`, you can use both `soft_spi` and + * `periph_spi` at the same time. + * * @{ * * @file @@ -28,14 +44,19 @@ #ifndef SOFT_SPI_H #define SOFT_SPI_H +#include "board.h" +#include "mutex.h" #include "periph/gpio.h" #include "periph/spi.h" -#include "mutex.h" #ifdef __cplusplus extern "C" { #endif +#ifndef SOFT_SPI_NUMOF +#define SOFT_SPI_NUMOF 1 /**< Number of soft SPI interfaces */ +#endif + /** * @defgroup drivers_softspi_config Software SPI driver compile configuration * @ingroup config_drivers_soft_periph @@ -78,35 +99,34 @@ typedef gpio_t soft_spi_cs_t; * @brief Status codes used by the SPI driver interface */ enum { - SOFT_SPI_OK = 0, /**< everything went as planned */ - SOFT_SPI_NODEV = -1, /**< invalid SPI bus specified */ - SOFT_SPI_NOCS = -2, /**< invalid chip select line specified */ - SOFT_SPI_NOMODE = -3, /**< selected mode is not supported */ - SOFT_SPI_NOCLK = -4 /**< selected clock value is not supported */ + SOFT_SPI_OK = SPI_OK, /**< everything went as planned */ + SOFT_SPI_NODEV = SPI_NODEV, /**< invalid SPI bus specified */ + SOFT_SPI_NOCS = SPI_NOCS, /**< invalid chip select line specified */ + SOFT_SPI_NOMODE = SPI_NOMODE, /**< selected mode is not supported */ + SOFT_SPI_NOCLK = SPI_NOCLK /**< selected clock value is not supported */ }; /** * @brief Available SPI modes, defining the configuration of clock polarity * and clock phase * - * RIOT is using the mode numbers as commonly defined by most vendors - * (https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Mode_numbers): - * - * - MODE_0: CPOL=0, CPHA=0 - The first data bit is sampled by the receiver on - * the first SCK rising SCK edge (this mode is used most often). - * - MODE_1: CPOL=0, CPHA=1 - The first data bit is sampled by the receiver on - * the second rising SCK edge. - * - MODE_2: CPOL=1, CPHA=0 - The first data bit is sampled by the receiver on - * the first falling SCK edge. - * - MODE_3: CPOL=1, CPHA=1 - The first data bit is sampled by the receiver on - * the second falling SCK edge. + * @deprecated Directly use spi_mode_t instead */ -typedef enum { - SOFT_SPI_MODE_0 = 0, /**< CPOL=0, CPHA=0 */ - SOFT_SPI_MODE_1, /**< CPOL=0, CPHA=1 */ - SOFT_SPI_MODE_2, /**< CPOL=1, CPHA=0 */ - SOFT_SPI_MODE_3 /**< CPOL=1, CPHA=1 */ -} soft_spi_mode_t; +typedef spi_mode_t soft_spi_mode_t; + +/** + * @name Deprecated aliases + * @{ + */ +#define SOFT_SPI_MODE_0 SPI_MODE_0 /**< Deprecated alias for SPI_MODE_0 */ +#define SOFT_SPI_MODE_1 SPI_MODE_1 /**< Deprecated alias for SPI_MODE_1 */ +#define SOFT_SPI_MODE_2 SPI_MODE_2 /**< Deprecated alias for SPI_MODE_2 */ +#define SOFT_SPI_MODE_3 SPI_MODE_3 /**< Deprecated alias for SPI_MODE_3 */ + +#define SOFT_SPI_CLK_100KHZ SPI_CLK_100KHZ /**< Deprecated alias for SPI_CLK_100KHZ */ +#define SOFT_SPI_CLK_400KHZ SPI_CLK_400KHZ /**< Deprecated alias for SPI_CLK_400KHZ */ +#define SOFT_SPI_CLK_DEFAULT SPI_CLK_10MHZ /**< Deprecated alias for SPI_CLK_10MHZ */ +/** @} */ /** * @brief Available SPI clock speeds @@ -114,12 +134,10 @@ typedef enum { * The actual speed of the bus varies between CPUs and depends on the speed * of the processing. The values of the enum entries represent the approximate * delay between two clock edges. + * + * @deprecated Directly use spi_clk_t instead */ -typedef enum { - SOFT_SPI_CLK_100KHZ = 5, /**< drive the SPI bus with less than 100kHz */ - SOFT_SPI_CLK_1MHZ = 1, /**< drive the SPI bus with less than 1MHz */ - SOFT_SPI_CLK_DEFAULT = 0, /**< drive the SPI bus with maximum speed possible */ -} soft_spi_clk_t; +typedef spi_clk_t soft_spi_clk_t; /** * @brief Software SPI port descriptor @@ -128,8 +146,9 @@ typedef struct { gpio_t miso_pin; /**< MOSI pin */ gpio_t mosi_pin; /**< MOSI pin */ gpio_t clk_pin; /**< CLK pin */ - soft_spi_mode_t soft_spi_mode; /**< data and clock polarity */ - soft_spi_clk_t soft_spi_clk; /**< clock speed */ + spi_mode_t soft_spi_mode; /**< data and clock polarity */ + uint16_t sleep_us; /**< microseconds before toggling the clock + * pin */ } soft_spi_conf_t; /** @@ -168,9 +187,9 @@ void soft_spi_init_pins(soft_spi_t bus); * @param[in] bus SPI device that is used with the given CS line * @param[in] cs chip select pin to initialize * - * @return SOFT_SPI_OK on success - * @return SOFT_SPI_NODEV on invalid device - * @return SOFT_SPI_NOCS on invalid CS pin/line + * @retval 0 success + * @retval -ENXIO invalid device + * @retval -EINVAL invalid CS pin/line */ int soft_spi_init_cs(soft_spi_t bus, soft_spi_cs_t cs); @@ -184,6 +203,9 @@ int soft_spi_init_cs(soft_spi_t bus, soft_spi_cs_t cs); * * @note This function expects the @p bus and the @p cs parameters to be * valid (they are checked in soft_spi_init and soft_spi_init_cs before) + * @warning For clock speeds higher than 400 kHz in @p clk, the SPI is driven as fast as the + * implementation can manage. Don't expect the soft SPI to be able to compete with + * peripheral SPI implementations in clock speed. * * @param[in] bus SPI device to access * @param[in] cs chip select pin/line to use @@ -227,7 +249,7 @@ uint8_t soft_spi_transfer_byte(soft_spi_t bus, soft_spi_cs_t cs, bool cont, uint * @param[in] len number of bytes to transfer */ void soft_spi_transfer_bytes(soft_spi_t bus, soft_spi_cs_t cs, bool cont, - const void *out, void *in, size_t len); + const void *out, void *in, size_t len); /** * @brief Transfer one byte to/from a given register address @@ -258,7 +280,7 @@ uint8_t soft_spi_transfer_reg(soft_spi_t bus, soft_spi_cs_t cs, uint8_t reg, uin * @param[in] len number of bytes to transfer */ void soft_spi_transfer_regs(soft_spi_t bus, soft_spi_cs_t cs, uint8_t reg, - const void *out, void *in, size_t len); + const void *out, void *in, size_t len); #ifdef __cplusplus } diff --git a/drivers/periph_common/Kconfig.spi b/drivers/periph_common/Kconfig.spi index d928196d7825..b22057fd2e0d 100644 --- a/drivers/periph_common/Kconfig.spi +++ b/drivers/periph_common/Kconfig.spi @@ -10,7 +10,7 @@ menuconfig MODULE_PERIPH_SPI depends on HAS_PERIPH_SPI select MODULE_PERIPH_COMMON -if MODULE_PERIPH_SPI +if MODULE_PERIPH_SPI && !MODULE_SOFT_SPI_AS_PERIPH_SPI config MODULE_PERIPH_INIT_SPI bool "Auto initialize SPI peripheral" diff --git a/drivers/periph_common/init.c b/drivers/periph_common/init.c index 9d88170b7261..c596c6917438 100644 --- a/drivers/periph_common/init.c +++ b/drivers/periph_common/init.c @@ -32,6 +32,9 @@ #ifdef MODULE_PERIPH_INIT_SPI #include "periph/spi.h" #endif +#ifdef MODULE_PERIPH_INIT_SOFT_SPI +#include "soft_spi.h" +#endif #ifdef MODULE_PERIPH_INIT_RTC #include "periph/rtc.h" #endif @@ -77,6 +80,12 @@ void periph_init(void) } #endif +#ifdef MODULE_PERIPH_INIT_SOFT_SPI + for (unsigned i = 0; i < SOFT_SPI_NUMOF; i++) { + soft_spi_init(SOFT_SPI_DEV(i)); + } +#endif + /* Initialize RTT before RTC to allow for RTT based RTC implementations */ #ifdef MODULE_PERIPH_INIT_RTT rtt_init(); diff --git a/drivers/periph_common/spi.c b/drivers/periph_common/spi.c index 8b5edad7e807..e0df8d86299a 100644 --- a/drivers/periph_common/spi.c +++ b/drivers/periph_common/spi.c @@ -25,6 +25,8 @@ #include "cpu.h" #include "periph/spi.h" +#if defined(SPI_NUMOF) && !defined(MODULE_SOFT_SPI_AS_PERIPH_SPI) + #ifdef PERIPH_SPI_NEEDS_INIT_CS int spi_init_cs(spi_t bus, spi_cs_t cs) { @@ -67,3 +69,5 @@ void spi_transfer_regs(spi_t bus, spi_cs_t cs, spi_transfer_bytes(bus, cs, false, out, in, len); } #endif + +#endif diff --git a/drivers/soft_spi/Kconfig b/drivers/soft_spi/Kconfig index 5ba30b749a38..629d55773305 100644 --- a/drivers/soft_spi/Kconfig +++ b/drivers/soft_spi/Kconfig @@ -10,4 +10,10 @@ config MODULE_SOFT_SPI depends on HAS_PERIPH_GPIO depends on TEST_KCONFIG select MODULE_PERIPH_GPIO - select MODULE_XTIMER + select MODULE_ZTIMER + select ZTIMER_USEC + +config MODULE_SOFT_SPI_AS_PERIPH_SPI + bool "Use the software-implemented SPI to provide the peripheral SPI API" + select MODULE_SOFT_SPI + select HAS_PERIPH_SPI diff --git a/drivers/soft_spi/Makefile.dep b/drivers/soft_spi/Makefile.dep index 8525f848a7c3..bd320694cceb 100644 --- a/drivers/soft_spi/Makefile.dep +++ b/drivers/soft_spi/Makefile.dep @@ -1,2 +1,6 @@ FEATURES_REQUIRED += periph_gpio -USEMODULE += xtimer +USEMODULE += ztimer_usec + +ifneq (,$(filter periph_init,$(USEMODULE))) + USEMODULE += periph_init_soft_spi +endif diff --git a/drivers/soft_spi/soft_spi.c b/drivers/soft_spi/soft_spi.c index 8786b4bcdbe9..3183cdc2c328 100644 --- a/drivers/soft_spi/soft_spi.c +++ b/drivers/soft_spi/soft_spi.c @@ -22,7 +22,7 @@ #include "mutex.h" #include "periph/gpio.h" -#include "xtimer.h" +#include "ztimer.h" #include "soft_spi.h" #include "soft_spi_params.h" @@ -89,7 +89,7 @@ int soft_spi_init_cs(soft_spi_t bus, soft_spi_cs_t cs) DEBUG("Soft SPI init CS\n"); if (!soft_spi_bus_is_valid(bus)) { DEBUG("Soft SPI bus not valid\n"); - return SOFT_SPI_NODEV; + return -ENXIO; } if (gpio_is_valid(cs) && !gpio_is_equal(cs, SOFT_SPI_CS_UNDEF)) { @@ -98,7 +98,7 @@ int soft_spi_init_cs(soft_spi_t bus, soft_spi_cs_t cs) gpio_set(cs); } - return SOFT_SPI_OK; + return 0; } static inline int soft_spi_mode_is_valid(soft_spi_mode_t mode) @@ -121,18 +121,29 @@ void soft_spi_acquire(soft_spi_t bus, soft_spi_cs_t cs, soft_spi_mode_t mode, so soft_spi_config[bus].soft_spi_mode = mode; switch (mode) { - case SOFT_SPI_MODE_0: - case SOFT_SPI_MODE_1: - /* CPOL=0 */ - gpio_clear(soft_spi_config[bus].clk_pin); - break; - case SOFT_SPI_MODE_2: - case SOFT_SPI_MODE_3: - /* CPOL=1 */ - gpio_set(soft_spi_config[bus].clk_pin); - break; - } - soft_spi_config[bus].soft_spi_clk = clk; + case SPI_MODE_0: + case SPI_MODE_1: + /* CPOL=0 */ + gpio_clear(soft_spi_config[bus].clk_pin); + break; + case SPI_MODE_2: + case SPI_MODE_3: + /* CPOL=1 */ + gpio_set(soft_spi_config[bus].clk_pin); + break; + } + + switch (clk) { + case SPI_CLK_100KHZ: + soft_spi_config[bus].sleep_us = 5; + break; + case SPI_CLK_400KHZ: + soft_spi_config[bus].sleep_us = 1; + break; + default: + soft_spi_config[bus].sleep_us = 0; + break; + } } void soft_spi_release(soft_spi_t bus) @@ -144,8 +155,8 @@ void soft_spi_release(soft_spi_t bus) static inline uint8_t _transfer_one_byte(soft_spi_t bus, uint8_t out) { uint8_t i = 8; - if (SOFT_SPI_MODE_1 == soft_spi_config[bus].soft_spi_mode || - SOFT_SPI_MODE_3 == soft_spi_config[bus].soft_spi_mode) { + if (SPI_MODE_1 == soft_spi_config[bus].soft_spi_mode || + SPI_MODE_3 == soft_spi_config[bus].soft_spi_mode) { /* CPHA = 1*/ gpio_toggle(soft_spi_config[bus].clk_pin); } @@ -154,7 +165,7 @@ static inline uint8_t _transfer_one_byte(soft_spi_t bus, uint8_t out) uint8_t bit = out >> 7; gpio_write(soft_spi_config[bus].mosi_pin, bit); - xtimer_usleep(soft_spi_config[bus].soft_spi_clk); + ztimer_sleep(ZTIMER_USEC, soft_spi_config[bus].sleep_us); gpio_toggle(soft_spi_config[bus].clk_pin); out <<= 1; /*shift transfer register*/ @@ -162,17 +173,17 @@ static inline uint8_t _transfer_one_byte(soft_spi_t bus, uint8_t out) bit = gpio_read(soft_spi_config[bus].miso_pin); out = bit ? (out | 0x01) : (out & 0xfe); /*set or delete bit 0*/ - xtimer_usleep(soft_spi_config[bus].soft_spi_clk); + ztimer_sleep(ZTIMER_USEC, soft_spi_config[bus].sleep_us); --i; if (i > 0) { gpio_toggle(soft_spi_config[bus].clk_pin); } } while (i > 0); - if (SOFT_SPI_MODE_0 == soft_spi_config[bus].soft_spi_mode || - SOFT_SPI_MODE_2 == soft_spi_config[bus].soft_spi_mode) { + if (SPI_MODE_0 == soft_spi_config[bus].soft_spi_mode || + SPI_MODE_2 == soft_spi_config[bus].soft_spi_mode) { /* CPHA = 0 */ - xtimer_usleep(soft_spi_config[bus].soft_spi_clk); + ztimer_sleep(ZTIMER_USEC, soft_spi_config[bus].sleep_us); gpio_toggle(soft_spi_config[bus].clk_pin); } @@ -240,3 +251,33 @@ void soft_spi_transfer_bytes(soft_spi_t bus, soft_spi_cs_t cs, bool cont, } } } + +uint8_t soft_spi_transfer_reg(soft_spi_t bus, soft_spi_cs_t cs, uint8_t reg, uint8_t out) +{ + soft_spi_transfer_bytes(bus, cs, true, ®, NULL, 1); + return soft_spi_transfer_byte(bus, cs, false, out); +} + +void soft_spi_transfer_regs(soft_spi_t bus, soft_spi_cs_t cs, uint8_t reg, + const void *out, void *in, size_t len) +{ + soft_spi_transfer_bytes(bus, cs, true, ®, NULL, 1); + soft_spi_transfer_bytes(bus, cs, false, out, in, len); +} + +#if MODULE_SOFT_SPI_AS_PERIPH_SPI +void spi_init(spi_t bus) __attribute__((alias("soft_spi_init"))); +void spi_init_pins(spi_t bus) __attribute__((alias("soft_spi_init_pins"))); +int spi_init_cs(spi_t bus, spi_cs_t cs) __attribute__((alias("soft_spi_init_cs"))); +void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) + __attribute__((alias("soft_spi_acquire"))); +void spi_release(spi_t bus) __attribute__((alias("soft_spi_release"))); +uint8_t spi_transfer_byte(spi_t bus, spi_cs_t cs, bool cont, uint8_t out) + __attribute__((alias("soft_spi_transfer_byte"))); +void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont,const void *out, void *in, size_t len) + __attribute__((alias("soft_spi_transfer_bytes"))); +uint8_t spi_transfer_reg(spi_t bus, spi_cs_t cs, uint8_t reg, uint8_t out) + __attribute__((alias("soft_spi_transfer_reg"))); +void spi_transfer_regs(spi_t bus, spi_cs_t cs, uint8_t reg, const void *out, void *in, size_t len) + __attribute__((alias("soft_spi_transfer_regs"))); +#endif /* MODULE_SOFT_SPI_AS_PERIPH_SPI */ diff --git a/makefiles/features_modules.inc.mk b/makefiles/features_modules.inc.mk index d6afe8b38449..326b2d48b76f 100644 --- a/makefiles/features_modules.inc.mk +++ b/makefiles/features_modules.inc.mk @@ -7,6 +7,10 @@ PERIPH_FEATURES := $(filter periph_%,$(FEATURES_USED)) # all periph features correspond to a periph submodule # FEATURES_USED is defined in Makefile.features +ifneq (,$(filter soft_spi_as_periph_spi,$(USEMODULE))) + # don't use periph_spi module if a soft_spi is used to provide it + PERIPH_FEATURES := $(filter-out periph_spi,$(PERIPH_FEATURES)) +endif USEMODULE += $(PERIPH_FEATURES) # Add all USED periph_% init modules unless they are blacklisted diff --git a/makefiles/features_provided_by_modules.inc.mk b/makefiles/features_provided_by_modules.inc.mk new file mode 100644 index 000000000000..7d1778d78b0e --- /dev/null +++ b/makefiles/features_provided_by_modules.inc.mk @@ -0,0 +1,9 @@ +# Add features provided by modules +# +# This is done *before* the regular dependency resolution in Makefile.dep. +# Hence, an application requiring to provide a feature via a module must do +# so early on + +ifneq (,$(filter soft_spi_as_periph_spi,$(USEMODULE))) + FEATURES_PROVIDED += periph_spi +endif diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 2531e39681b3..157c2c304552 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -203,6 +203,7 @@ PSEUDOMODULES += sock_ip PSEUDOMODULES += sock_tcp PSEUDOMODULES += sock_udp PSEUDOMODULES += socket_zep_hello +PSEUDOMODULES += soft_spi_as_periph_spi PSEUDOMODULES += soft_uart_modecfg PSEUDOMODULES += stdin PSEUDOMODULES += stdio_available