rust-embedded / embedded-hal

A Hardware Abstraction Layer (HAL) for embedded systems
Apache License 2.0
2.01k stars 202 forks source link

Add SPI traits that are not bound to `Word`? #637

Closed jmjoy closed 3 weeks ago

jmjoy commented 3 weeks ago

Some external devices use SPI transfers with flexible data sizes, such as certain small OLED screens.

When using Embassy, this works very well because the transfer method of its SPI struct uses the word size as a generic parameter of the method: https://docs.embassy.dev/embassy-stm32/git/stm32f103c8/spi/struct.Spi.html#method.transfer

However, in embedded-hal, the SPI generics are bound to Word, such as SpiBus<Word>. For SPI structs that own the implementation of this trait, it's not possible to flexibly change the data size: https://docs.rs/embedded-hal/1.0.0/embedded_hal/spi/trait.SpiBus.html

Therefore, is it possible to add an SPI trait that is not bound to Word?

burrbull commented 3 weeks ago

What the problem to implement SpiBus<AnyWord> for Spi?

Where:


enum AnyWord {
    U8(u8),
    U16(u16),
    AnyOther,
}
``` or just `struct AnyWord`.
jmjoy commented 3 weeks ago

What the problem to implement SpiBus<AnyWord> for Spi?

Where:

enum AnyWord {
    U8(u8),
    U16(u16),
    AnyOther,
}
``` or just `struct AnyWord`.

Using enum will increase the size of elements. For example, the element in the example will ultimately be equal to the size of a u16 + 1, which is not favorable for embedded systems.

Dirbaio commented 3 weeks ago

However, in embedded-hal, the SPI generics are bound to Word, such as SpiBus. For SPI structs that own the implementation of this trait, it's not possible to flexibly change the data size

You can do multiple where bounds for different word sizes at the same time, which makes it behave similarly to a generic method.

struct MyDriver<T> {
    spi: T,
}

impl<T> MyDriver<T>
where
    T: SpiBus<u8> + SpiBus<u16>, // two bounds!
{
    fn do_something(&mut self) {
        self.spi.write(&[0u8, 1, 2, 3]).unwrap(); // will use SpiBus<u8>>::write
        self.spi.write(&[0u16, 1, 2, 3]).unwrap(); // will use SpiBus<u16>>::write
    }
}

What the problem to implement SpiBus<AnyWord> for Spi?

this wouldn't work with DMA. To be able to DMA you need an array of "bare" u8 or u16 etc.

jmjoy commented 3 weeks ago

However, in embedded-hal, the SPI generics are bound to Word, such as SpiBus. For SPI structs that own the implementation of this trait, it's not possible to flexibly change the data size

You can do multiple where bounds for different word sizes at the same time, which makes it behave similarly to a generic method.

struct MyDriver<T> {
    spi: T,
}

impl<T> MyDriver<T>
where
    T: SpiBus<u8> + SpiBus<u16>, // two bounds!
{
    fn do_something(&mut self) {
        self.spi.write(&[0u8, 1, 2, 3]).unwrap(); // will use SpiBus<u8>>::write
        self.spi.write(&[0u16, 1, 2, 3]).unwrap(); // will use SpiBus<u16>>::write
    }
}

What the problem to implement SpiBus<AnyWord> for Spi?

this wouldn't work with DMA. To be able to DMA you need an array of "bare" u8 or u16 etc.

Thank you for your great use of generics!