Description
This is related to #176 but since it also affect the non-async api I figured it deserves it's own issue.
As discussed in #176 (comment), according to several non-official sources, SD cards that share the bus with other slaves need to be sent a dummy 0xFF
byte with CS deasserted after every communication, before communicating with other slaves. Otherwise a bus conflict can occur.
If you share the SPI bus with other slaves, always send another "dummy" byte (or more) with the CS line inactive (high) after each communication to the SD card before accessing other slaves. Otherwise, the SD card will keep the DO/MISO ((master in slave out)) output active and cause two slaves to drive/short-circuit the MISO line! So: CS low, exchange data with SD card, CS high, send dummy data (e.g. one byte 0xFF), then you can communicate with other slaves.
https://electronics.stackexchange.com/a/602106/256470
In the SPI bus, each slave device is selected with separated CS signals, and plural devices can be attached to an SPI bus. Generic SPI slave device enables/disables its DO output by CS signal asynchronously to share an SPI bus. However MMC/SDC enables/disables the DO output in synchronising to the SCLK. This means there is a posibility of bus conflict with MMC/SDC and another SPI slave that shares an SPI bus. [...] Therefore to make MMC/SDC release the MISO line, the master device needs to send a byte after the CS signal is deasserted.
http://elm-chan.org/docs/mmc/mmc_e.html#spibus
I brought this up on matrix and the conclusion was that this requirement is fundamentally not compatible with the SpiDevice
trait. Workarounds like using a second SpiDevice
don't solve the problem. This applies to both the blocking and the async versions because in both cases there's no way to guarantee that other tasks or threads don't sneak in and use the bus between the main communication and sending the dummy byte.
The solution proposed in chat and in rust-embedded/embedded-hal#478 is to define a new trait SdCardDevice
and provide impls for (OutputPin, &RefCell<SpiBus>)
, (OutputPin, &Mutex<SpiBus>)
, (OutputPin, SpiBus)
(for exclusive use of the bus) that have the desired behavior. This still allows the bus to be shared with other devices by using embedded-hal-bus
types. If backwards compatibility is important there could also be an impl for SpiDevice
that keeps the current possibly incorrect behavior.
This seems like a feasible approach but it adds some boilerplate to the crate and probably an optional dependency on embassy-sync
to allow users to share the bus in embassy projects. I'm happy to give this a go but I wanted to first ask if these downsides are acceptable. embasys-sync
is not 1.0 yet so this would add a small maintenance burden.
cc @Be-ing