Skip to content

drivers/soft_spi_as_periph_spi: new module#16196

Open
maribu wants to merge 6 commits intoRIOT-OS:masterfrom
maribu:soft_spi
Open

drivers/soft_spi_as_periph_spi: new module#16196
maribu wants to merge 6 commits intoRIOT-OS:masterfrom
maribu:soft_spi

Conversation

@maribu
Copy link
Member

@maribu maribu commented Mar 15, 2021

Contribution description

Allow using soft_spi in place if periph_api by adding

USEMODULE += soft_spi_as_periph_spi

to an applications Makefile.

Testing procedure

I tested this using tests/driver_bmx280 using an BME280 sensor conntected to a Nucleo-F767 with the following patch applied:

diff --git a/drivers/bmx280/include/bmx280_params.h b/drivers/bmx280/include/bmx280_params.h
index 09296398d0..c9ae1fcdcd 100644
--- a/drivers/bmx280/include/bmx280_params.h
+++ b/drivers/bmx280/include/bmx280_params.h
@@ -44,7 +44,7 @@ extern "C" {
 #define BMX280_PARAM_CLK            SPI_CLK_5MHZ
 #endif
 #ifndef BMX280_PARAM_CS
-#define BMX280_PARAM_CS             GPIO_PIN(0, 0)
+#define BMX280_PARAM_CS             GPIO_PIN(PORT_D, 7)
 #endif
 #else
 /* I2C configuration */
diff --git a/drivers/soft_spi/include/soft_spi_params.h b/drivers/soft_spi/include/soft_spi_params.h
index fd3d7bb55e..4ca71eae6b 100644
--- a/drivers/soft_spi/include/soft_spi_params.h
+++ b/drivers/soft_spi/include/soft_spi_params.h
@@ -26,13 +26,13 @@ extern "C" {
 #endif
 
 #ifndef SOFT_SPI_PARAM_MISO
-#define SOFT_SPI_PARAM_MISO         (GPIO_UNDEF)
+#define SOFT_SPI_PARAM_MISO         (GPIO_PIN(PORT_E, 5))
 #endif
 #ifndef SOFT_SPI_PARAM_MOSI
-#define SOFT_SPI_PARAM_MOSI         (GPIO_PIN(0, 0))
+#define SOFT_SPI_PARAM_MOSI         (GPIO_PIN(PORT_E, 6))
 #endif
 #ifndef SOFT_SPI_PARAM_CLK
-#define SOFT_SPI_PARAM_CLK          (GPIO_PIN(0, 1))
+#define SOFT_SPI_PARAM_CLK          (GPIO_PIN(PORT_E, 2))
 #endif
 
 #ifndef SOFT_SPI_PARAMS
diff --git a/tests/driver_bmx280/Makefile b/tests/driver_bmx280/Makefile
index 6bacf6e181..b4ecea4259 100644
--- a/tests/driver_bmx280/Makefile
+++ b/tests/driver_bmx280/Makefile
@@ -1,8 +1,9 @@
 include ../Makefile.tests_common
 
-DRIVER ?= bme280_i2c
+DRIVER ?= bme280_spi
 
 USEMODULE += fmt
+USEMODULE += soft_spi_as_periph_spi
 USEMODULE += $(DRIVER)
 
 include $(RIOTBASE)/Makefile.include

The test output was:

2021-03-16 00:02:52,606 # main(): This is RIOT! (Version: 2021.04-devel-1046-g3e57c-soft_spi)
2021-03-16 00:02:52,608 # BMX280 test application
2021-03-16 00:02:52,608 # 
2021-03-16 00:02:52,611 # +------------Initializing------------+
2021-03-16 00:02:52,618 # Initialization successful
2021-03-16 00:02:52,619 # 
2021-03-16 00:02:52,622 # +------------Calibration Data------------+
2021-03-16 00:02:52,623 # dig_T1: 27901
2021-03-16 00:02:52,625 # dig_T2: 26171
2021-03-16 00:02:52,626 # dig_T3: 50
2021-03-16 00:02:52,627 # dig_P1: 38993
2021-03-16 00:02:52,628 # dig_P2: -10639
2021-03-16 00:02:52,630 # dig_P3: 3024
2021-03-16 00:02:52,631 # dig_P4: 10124
2021-03-16 00:02:52,632 # dig_P5: -261
2021-03-16 00:02:52,633 # dig_P6: -7
2021-03-16 00:02:52,634 # dig_P7: 12300
2021-03-16 00:02:52,635 # dig_P8: -12000
2021-03-16 00:02:52,636 # dig_P9: 5000
2021-03-16 00:02:52,641 # dig_H1: 0
2021-03-16 00:02:52,641 # dig_H2: 373
2021-03-16 00:02:52,641 # dig_H3: 0
2021-03-16 00:02:52,642 # dig_H4: 292
2021-03-16 00:02:52,642 # dig_H5: 50
2021-03-16 00:02:52,642 # dig_H6: 30
2021-03-16 00:02:52,642 # 
2021-03-16 00:02:52,646 # +--------Starting Measurements--------+
2021-03-16 00:02:52,665 # Temperature [°C]: 20.17
2021-03-16 00:02:52,667 #    Pressure [Pa]: 100600
2021-03-16 00:02:52,670 #   Humidity [%rH]: 44.36
2021-03-16 00:02:52,670 # 
2021-03-16 00:02:52,673 # +-------------------------------------+
2021-03-16 00:02:52,673 # 
2021-03-16 00:02:54,673 # Temperature [°C]: 20.18
2021-03-16 00:02:54,675 #    Pressure [Pa]: 100598
2021-03-16 00:02:54,677 #   Humidity [%rH]: 44.50
2021-03-16 00:02:54,678 # 
2021-03-16 00:02:54,681 # +-------------------------------------+
2021-03-16 00:02:54,681 # 
2021-03-16 00:02:56,680 # Temperature [°C]: 20.17
2021-03-16 00:02:56,682 #    Pressure [Pa]: 100601
2021-03-16 00:02:56,684 #   Humidity [%rH]: 43.84
2021-03-16 00:02:56,685 # 
2021-03-16 00:02:56,687 # +-------------------------------------+
2021-03-16 00:02:56,688 # 
2021-03-16 00:02:58,687 # Temperature [°C]: 20.17
2021-03-16 00:02:58,689 #    Pressure [Pa]: 100607
2021-03-16 00:02:58,691 #   Humidity [%rH]: 43.03
2021-03-16 00:02:58,692 # 
2021-03-16 00:02:58,695 # +-------------------------------------+
2021-03-16 00:02:58,695 # 

Issues/PRs references

Depends on and includes

@maribu maribu added Type: enhancement The issue suggests enhanceable parts / The PR enhances parts of the codebase / documentation State: waiting for other PR State: The PR requires another PR to be merged first Area: drivers Area: Device drivers labels Mar 15, 2021
@maribu maribu requested a review from benpicco March 15, 2021 23:03
@maribu maribu requested a review from MrKevinWeiss as a code owner March 15, 2021 23:03
@maribu maribu changed the title Soft spi drivers/soft_spi_as_periph_spi: new module Mar 15, 2021
@maribu
Copy link
Member Author

maribu commented Mar 16, 2021

This PR also needs to add some documentation and it wouldn't hurt to split out the addition of the register access and the auto into their own commit, each. I will do so when #16164 is merged.

@maribu maribu removed the State: waiting for other PR State: The PR requires another PR to be merged first label Mar 17, 2021
@maribu
Copy link
Member Author

maribu commented Mar 17, 2021

Rebased, split into multiple commits, and added missing doc.

The test as described above still works:

tests/driver_bmx280 using soft_spi on a Nucleo-F767ZI
2021-03-17 09:49:15,405 # main(): This is RIOT! (Version: 2021.04-devel-1060-g43661-soft_spi)
2021-03-17 09:49:15,407 # BMX280 test application
2021-03-17 09:49:15,408 # 
2021-03-17 09:49:15,411 # +------------Initializing------------+
2021-03-17 09:49:15,417 # Initialization successful
2021-03-17 09:49:15,418 # 
2021-03-17 09:49:15,421 # +------------Calibration Data------------+
2021-03-17 09:49:15,422 # dig_T1: 27901
2021-03-17 09:49:15,424 # dig_T2: 26171
2021-03-17 09:49:15,425 # dig_T3: 50
2021-03-17 09:49:15,426 # dig_P1: 38993
2021-03-17 09:49:15,427 # dig_P2: -10639
2021-03-17 09:49:15,429 # dig_P3: 3024
2021-03-17 09:49:15,430 # dig_P4: 10124
2021-03-17 09:49:15,431 # dig_P5: -261
2021-03-17 09:49:15,432 # dig_P6: -7
2021-03-17 09:49:15,433 # dig_P7: 12300
2021-03-17 09:49:15,434 # dig_P8: -12000
2021-03-17 09:49:15,435 # dig_P9: 5000
2021-03-17 09:49:15,436 # dig_H1: 0
2021-03-17 09:49:15,437 # dig_H2: 373
2021-03-17 09:49:15,438 # dig_H3: 0
2021-03-17 09:49:15,439 # dig_H4: 292
2021-03-17 09:49:15,440 # dig_H5: 50
2021-03-17 09:49:15,441 # dig_H6: 30
2021-03-17 09:49:15,441 # 
2021-03-17 09:49:15,445 # +--------Starting Measurements--------+
2021-03-17 09:49:15,464 # Temperature [°C]: 18.70
2021-03-17 09:49:15,466 #    Pressure [Pa]: 101607
2021-03-17 09:49:15,469 #   Humidity [%rH]: 46.68
2021-03-17 09:49:15,470 # 
2021-03-17 09:49:15,472 # +-------------------------------------+
2021-03-17 09:49:15,472 # 
2021-03-17 09:49:17,472 # Temperature [°C]: 18.71
2021-03-17 09:49:17,474 #    Pressure [Pa]: 101605
2021-03-17 09:49:17,477 #   Humidity [%rH]: 45.16
2021-03-17 09:49:17,477 # 
2021-03-17 09:49:17,480 # +-------------------------------------+
2021-03-17 09:49:17,481 # 
2021-03-17 09:49:19,480 # Temperature [°C]: 18.70
2021-03-17 09:49:19,482 #    Pressure [Pa]: 101601
2021-03-17 09:49:19,484 #   Humidity [%rH]: 45.16
2021-03-17 09:49:19,485 # 
2021-03-17 09:49:19,487 # +-------------------------------------+
2021-03-17 09:49:19,488 # 
2021-03-17 09:49:21,488 # Temperature [°C]: 18.69
2021-03-17 09:49:21,490 #    Pressure [Pa]: 101607
2021-03-17 09:49:21,492 #   Humidity [%rH]: 46.98
2021-03-17 09:49:21,493 # 
2021-03-17 09:49:21,496 # +-------------------------------------+

@maribu maribu added the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label Mar 17, 2021
@leandrolanzieri
Copy link
Contributor

I guess then, something like what I tried here (but for SPI) can be added now? #15717 (comment)

@maribu
Copy link
Member Author

maribu commented Mar 19, 2021

I guess then, something like what I tried here (but for SPI) can be added now? #15717 (comment)

We could also adopt the approach here for UART. As long as the signature is identical, it is no problem to provide symbol aliases for the "real" API as drop in replacement.

@MrKevinWeiss MrKevinWeiss added this to the Release 2021.07 milestone Jun 22, 2021
@MrKevinWeiss MrKevinWeiss removed this from the Release 2021.07 milestone Jul 15, 2021
* 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 `xtimer_nanosleep()`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maribu is it worth using nanosleep here? AFAIK most xtimer backends are using a < 1MHz timer anyway, so best result achievable would be 1us no? ztimer could use ptp but that is another story.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I very much agree.

Note that I only changed the wording of the doc in this PR, the use of xtimer_nanosleep() is a preexisting design decision. Maybe it is best to tackle this in a separate PR?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, can you rebase this one?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rebased it on master. I will later retest again, the rebase was a bit involved and I hope I didn't mess anything up.

@github-actions github-actions bot added the Area: build system Area: Build system label Nov 18, 2021
@fjmolinas fjmolinas added this to the Release 2022.01 milestone Nov 25, 2021
@fjmolinas
Copy link
Contributor

@maribu seems like Kconfig needs an update, otherwise there are some static check issues and this PR unconvered a bug in lm4f120, would you mind adding it to the PR? Not worth its own PR and CI run IMO:

diff --git a/cpu/lm4f120/include/periph_cpu.h b/cpu/lm4f120/include/periph_cpu.h
index 9d39286d8b..902ce052de 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 */

@github-actions github-actions bot added Area: cpu Area: CPU/MCU ports Platform: ARM Platform: This PR/issue effects ARM-based platforms labels Nov 25, 2021
@maribu maribu removed the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label Nov 25, 2021
@fjmolinas
Copy link
Contributor

Did something pop up in the ci run @maribu ?

@maribu
Copy link
Member Author

maribu commented Nov 29, 2021

The KConfig TODO is still open. I wanted to tackle this prior to letting the CI burn throw 3 days of CPU time :-)

@OlegHahm OlegHahm added the State: waiting for author State: Action by the author of the PR is required label Mar 9, 2022
@OlegHahm
Copy link
Member

OlegHahm commented Mar 9, 2022

@maribu, ping!

maribu added 6 commits March 10, 2022 14:12
This should result in SPI_MODE_3 properly working on the lm4f120.
soft_spi_transfer_reg() and soft_spi_transfer_regs() never have been
implemented. This fixes the issue.
- Use spi_clk_t and spi_mode_t instead of soft_spi_clk_t and
  soft_spi_mode_t
    - For backward compatibility, the old types are defined as aliases
    - Using identical function signatures will allow using soft_spi as
      drop in replacement for periph_spi
- Use negative errno codes for error reporting
    - Again, backward compatibility with the enum is provided
    - This brings the API in alignment with the current style
      requirements
- No custom clock speeds
    - Instead reuse the clock speeds of SPI and do best effort for
      frequencies higher than 400 kHz
Allow using soft_spi in place if periph_api by adding

    USEMODULE += soft_spi_as_periph_spi

to an application's Makefile.
@github-actions github-actions bot added the Area: Kconfig Area: Kconfig integration label Mar 10, 2022
@maribu
Copy link
Member Author

maribu commented Mar 10, 2022

Testing can be done with

make BOARD=rpi-pico DRIVER=bme280_spi -C tests/driver_bmx280

which should yield:

There are unsatisfied feature requirements: periph_spi

while

USEMODULE=soft_spi_as_periph_spi make BOARD=rpi-pico DRIVER=bme280_spi -C tests/driver_bmx280

Should build and work (provided decent values for SOFT_SPI_PARAM_MISO, SOFT_SPI_PARAM_MOSI, and SOFT_SPI_PARAM_CLK are given and a BME280 sensor is connected to those).

Comment on lines +1 to +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
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is new and will need some discussion. I'd like to summon @fjmolinas regarding this.

As a data point: KConfig modelling was just find without crude hacks like this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far all FEATURES are declarative, and this involves having them not be declarative (which is for me the major change), MODULE having FEATURES I don't see a problem with at a first glance.

What I wonder is if this couldn't be modeled differently to avoid this change. Technically all you need to provide a soft_spi is periph_timer, so this could be kept declarative by changing the model to:

FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_spi_sw

All platforms providing a periph_timer would provide the softspi feature. And then all modules requiring it would instead do:

FEATURES_REQUIRED_ANY += periph_spi|periph_spi_sw
FEATURES_CONFLICT += periph_spi|periph_spi_sw

Selecting the right modules to implement that feature becomes a dependency resolution issue.

You would still be able to have FEATURES_REQUIRED += periph_spi and USEMODULE += soft_spi. Your soft_spi_as_periph_spi is selected by the periph_spi_sw feature.

What I like of this is that it keeps it declarative. But I'm not sure how to do it in Kconfig. The FEATURES_REQUIRED_ANY and FEATURES_CONFLICT are not really modelled. But it seems that a choice with some indirection would work (summon @leandrolanzieri and @MrKevinWeiss as well)

choice PERIPH_SPI_API
  bool "periph_spi api backend"
  config MODULE_PERIPH_SPI
      depends on HAS_PERIPH_SPI
  config MODULE_SOFT_SPI_AS_PERIPH_SPI
      depends on HAS_PERIPH_SPI_SW
      select MODULE_SOFT_SPI
endchoice

config MODULE_SOFT_SPI
    bool "Software-implemented SPI"
    depends on HAS_PERIPH_GPIO
    depends on TEST_KCONFIG
    select MODULE_PERIPH_GPIO
    select MODULE_ZTIMER
    select ZTIMER_USEC

Note: all used names are place holders.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far all FEATURES are declarative, and this involves having them not be declarative (which is for me the major change), MODULE having FEATURES I don't see a problem with at a first glance.

What I wonder is if this couldn't be modeled differently to avoid this change. Technically all you need to provide a soft_spi is periph_timer, so this could be kept declarative by changing the model to:

FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_spi_sw

All platforms providing a periph_timer would provide the softspi feature. And then all modules requiring it would instead do:

FEATURES_REQUIRED_ANY += periph_spi|periph_spi_sw
FEATURES_CONFLICT += periph_spi|periph_spi_sw

Selecting the right modules to implement that feature becomes a dependency resolution issue.

You would still be able to have FEATURES_REQUIRED += periph_spi and USEMODULE += soft_spi. Your soft_spi_as_periph_spi is selected by the periph_spi_sw feature.

What I like of this is that it keeps it declarative. But I'm not sure how to do it in Kconfig. The FEATURES_REQUIRED_ANY and FEATURES_CONFLICT are not really modelled. But it seems that a choice with some indirection would work (summon @leandrolanzieri and @MrKevinWeiss as well)

choice PERIPH_SPI_API
  bool "periph_spi api backend"
  config MODULE_PERIPH_SPI
      depends on HAS_PERIPH_SPI
  config MODULE_SOFT_SPI_AS_PERIPH_SPI
      depends on HAS_PERIPH_SPI_SW
      select MODULE_SOFT_SPI
endchoice

config MODULE_SOFT_SPI
    bool "Software-implemented SPI"
    depends on HAS_PERIPH_GPIO
    depends on TEST_KCONFIG
    select MODULE_PERIPH_GPIO
    select MODULE_ZTIMER
    select ZTIMER_USEC

Note: all used names are place holders.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having different providers would not be an issue if we used strictly depends on instead of selecting some modules (although it comes with the known cumbersome configuration). Based on the @fjmolinas proposal, I think a choice is the best option. I'd add a switch symbol and a feature to be replaced across all the current users of HAS_PERIPH_SPI and MODULE_PERIPH_SPI.

# Kconfig.spi

menuconfig SPI_SUPORT
    bool "SPI support"
    depends on TEST_KCONFIG
    depends on HAS_SPI

choice SPI_IMPLEMENTATION
  bool "SPI implementation"
  depends on SPI_SUPORT

config MODULE_PERIPH_SPI
    bool "Peripheral"
    depends on HAS_SPI

endchoice

# related features
config HAS_SPI
    bool

config HAS_PERIPH_SPI
    bool
    select HAS_SPI
# drivers/soft_spi/Kconfig

# extend the choice options
choice SPI_IMPLEMENTATION
    depends on SPI_SUPPORT

config MODULE_SOFT_SPI
    bool "Software-implemented SPI"
    depends on HAS_SPI_SW
    select MODULE_PERIPH_GPIO
    select MODULE_ZTIMER
    select ZTIMER_USEC

endchoice

config HAS_SPI_SW
    bool
    depends on HAS_PERIPH_TIMER
    depends on HAS_PERIPH_GPIO
    select HAS_SPI
    default y

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MODULE having FEATURES I don't see a problem with at a first glance.

I'm guessing this at least would change the way we produce info-features-provided, right? As a dependency resolution would be required now to actually get all the features. Moreover, order of inclusion comes into play now, as we always consider that features are set in stone before requiring/checking them in Makefile.dep.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the right approach would be to let the drivers depend on an spi module, rather than on a periph_spi feature. The driver likely cares little how the API is provided, as long as the API is there and actually drives an SPI bus. And will also be easier to maintain the list of optional feature dependencies in the single place of the spi module, rather than for each driver.

In fact, making this spi module a dedicated layer would allow mixing implementations. If an application can keep all hw buses busy via DMA, there could be value in connecting another device via bit-banged SPI. Or, maybe more realistically, some MCUs like the RP2040 have hardware that can be turned into an additional SPI bus (such as the PIO state machines) but doesn't naturally map to the same driver. A high level spi module could multiplex all used SPI providers. @benpicco also on this band wagon, as far as I know.

This PR is IMO just a poor waiting to be replaced by a proper solution (: But it might still be useful when porting RIOT to a new MCU until a a hardware SPI driver materialises.

@stale
Copy link

stale bot commented Sep 21, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you want me to ignore this issue, please mark it with the "State: don't stale" label. Thank you for your contributions.

@stale stale bot added the State: stale State: The issue / PR has no activity for >185 days label Sep 21, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: build system Area: Build system Area: cpu Area: CPU/MCU ports Area: drivers Area: Device drivers Area: Kconfig Area: Kconfig integration Platform: ARM Platform: This PR/issue effects ARM-based platforms State: stale State: The issue / PR has no activity for >185 days State: waiting for author State: Action by the author of the PR is required Type: enhancement The issue suggests enhanceable parts / The PR enhances parts of the codebase / documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants