Skip to content

Conversation

fhessel
Copy link

@fhessel fhessel commented Oct 24, 2019

Contribution description

This PR extends the introduction of GPIO hardware access on the native board, and adapts the hardware SPI module for the new feature.

This is required to avoid colliding number spaces for the SPI_HWCS and GPIO_PIN macros, which can both be used to create values for the cs parameters of the spi_... functions. Furthermore, this PR allows using arbitrary GPIOs as CS lines, which gives more flexibility and support for a greater number of parallel SPI devices.

Known issues

When the SPI module is used on a Linux spidev, and one or more of its lines is shared with a Linux gpiochip, then accessing the lines through functions like gpio_set will cause a segfault.

Example: Use the tests/periph_spi application on a Raspberry Pi 3, with TERMFLAGS = -g/dev/gpiochip0 and -p0:0:/dev/spidev0.0.

In this case GPIO_PIN(0, 8) and SPI_HWCS(0) point to the same line, but only using init 0 0 0 -1 0 works in the test application, init 0 0 0 0 8 (which will use the gpio module) causes the aforementioned segfault.

I think, this is hard to circumvent, but should be documented somewhere. Identifying this situation would require knowledge about the mapping of the physical lines in both, the spidev and gpiochip devices. This may be strongly hardware-specific.

Testing procedure

The easiest way is the loopback test, which can be run like explained in RIOT-OS#11352. However, this doesn't prove the function of the cs lines. This could be achieved, e.g., by connecting them to an oscilloscope.

In the tests/periph_spi application, the bus is initialized by x y z <cs-port> <cs-pin>. If cs-port is -1, HWCS(cs-pin) will be called, otherwise, GPIO_PIN(cs-port, cs-pin) is used. This allows quick switching between different cs lines.

Issues/PRs references

Extension to RIOT-OS#12451

@benpicco
Copy link
Owner

Thank you very much - I'll look into it!
But does that mean that this would still make connecting an SPI module that requires additional GPIOs impossible?

If this is a limitation of the new gpiochip API I'd have to go back to the old sysfs API - but first I would suspect a bug in my code.

@fhessel
Copy link
Author

fhessel commented Oct 28, 2019

Maybe I'll also need to investigate that further to see where that segfault comes from. What I observed until now: If you use the SPI and GPIO module in parallel, the only limitation is that you cannot access the hard-wired CE-pins of the SPI interface (and probably also not MISO/MOSI/SCK, I haven't tried that yet) through your GPIO implementation.

So if you start your application on a Pi with --spi=0:0:/dev/spidev0.0, GPIO 8 is bound to its role of being CE0, so using -g/dev/gpiochip0 and accessing GPIO_PIN(0, 8) does not work. That may just be a problem with tests/periph_spi not checking some return values. So in that specific test application, you have to use -1 0 instead of 0 8 for the bus initialization. Other GPIOs which are not part of the SPI bus are fine, using an arbitrary pin, e.g. GPIO27, by initializing with 0 27 works, and that line is even pulled low when communicating on the bus (this is part of what this PR does).

To me, such a limitation on the Linux side would make perfectly sense. It would only be nice if both modules coordinate their use of the pins somehow. Maybe it's possible to use the gpiochip API (haven't used it before) to see if some lines are already in use. Otherwise, I don't know how to learn from /dev/spidev0.0 that it points to lines 8-11 on /dev/gpiochip0, or vice-versa.

@fhessel
Copy link
Author

fhessel commented Nov 11, 2019

I did some more testing.

First, the good news:

  • With a freshly setup Raspberry Pi and repository, I cannot reproduce the segfault anymore (I tried it on a Raspberry Pi 3B and a 3B+)
  • Using SPI_HWCS(0) and GPIO_PIN(0, 8) makes no difference anymore. So you can use both variants to specify the chip select line, even if they are mapped to the same pin (Raspberry Pi 3B+).
    • Even switching back and forth between both seems to work (using init 0 0 0 -1 0 and init 0 0 0 0 8 in the perip_spi test application)
  • Using this SX1276 LoRa transceiver, I could verify that using arbitrary GPIOs for chip select works (that HAT has a wiring error connecting CS to GPIO25, so it didn't work before, but does now; tested on Raspberry Pi 3B).
    • During that test, I found another small bug in my code regarding the cont flag of spi_transfer_bytes, which wasn't handled correctly with GPIO-based CS lines. That is fixed in the additional commit. Now, transactions with multiple calls to spi_transfer_bytes should work as well.

The bad news:

  • If you take one of the SPI data lines (MISO, MOSI, CLK) and pass it as cs parameter to spi_init_cs using the GPIO_PIN macro, that line will no longer be usable through the /dev/spidev* interface, which in general makes communication with peripherals impossible. This state remains until you restart the Linux host (tested on Raspberry Pi 3B, 3B+).

So while the problem of mapping hardware chip select lines to their representation in the gpiochip is gone, that issue remains for the MISO, MOSI, and CLK lines. The only solution would be forbidding to gpio_initializing these pins when RIOT is started with SPI support. But that would introduce some mutual dependencies between the GPIO and the SPI implementation, and I don't know if that's desirable. And again, it is hard to find out which of the gpiochip lines are those special SPI lines. And as using MISO, MOSI, or CLK as CS line makes no sense in general, I think a note in the documentation will be sufficient here.

@benpicco benpicco merged commit 00f32a2 into benpicco:native-gpio Feb 4, 2020
@benpicco
Copy link
Owner

benpicco commented Feb 4, 2020

Thank you a lot for the patch!
Sorry it took me so long to get back to it.
Let's hope we can get this merged soon upstream too.

@fhessel
Copy link
Author

fhessel commented Feb 4, 2020

That'd be great. I'm using your GPIO implementation and this patch cherry-picked onto a 2019-10-based branch in my project for some time now and it seems to be quite stable for that scenario.

benpicco pushed a commit that referenced this pull request Feb 23, 2023
18392: drivers/servo: reimplement with high level interface r=benpicco a=maribu

### Contribution description

The previous servo driver didn't provide any benefit over using PWM directly, as users controlled the servo in terms of PWM duty cycles. This changes the interface to provide a high level interface that abstracts the gory PWM details.

In addition, a SAUL layer and auto-initialization is provided.

### Testing procedure

The test application provides access to the servo driver via the `saul` shell command.

```
> saul
2022-08-02 22:12:31,826 # saul
2022-08-02 22:12:31,827 # ID	Class		Name
2022-08-02 22:12:31,830 # #0	ACT_SWITCH	LD1(green)
2022-08-02 22:12:31,832 # #1	ACT_SWITCH	LD2(blue)
2022-08-02 22:12:31,834 # #2	ACT_SWITCH	LD3(red)
2022-08-02 22:12:31,837 # #3	SENSE_BTN	B1(User button)
2022-08-02 22:12:31,838 # #4	ACT_SERVO	servo
> saul write 4 0
2022-08-02 22:12:41,443 # saul write 4 0
2022-08-02 22:12:41,445 # Writing to device #4 - servo
2022-08-02 22:12:41,447 # Data:	             0 
2022-08-02 22:12:41,450 # [servo] setting 0 to 2949 (0 / 255)
2022-08-02 22:12:41,453 # data successfully written to device #4
> saul write 4 256
2022-08-02 22:12:45,343 # saul write 4 256
2022-08-02 22:12:45,346 # Writing to device #4 - servo
2022-08-02 22:12:45,347 # Data:	           256 
2022-08-02 22:12:45,351 # [servo] setting 0 to 6865 (255 / 255)
2022-08-02 22:12:45,354 # data successfully written to device #4
```

Each write resulted in the MG90S servo that I connected to move to the corresponding position.

### Issues/PRs references

Co-authored-by: Marian Buschsieweke <marian.buschsieweke@ovgu.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants