stm32-rs / stm32l0xx-hal

A hardware abstraction layer (HAL) for the STM32L0 series microcontrollers written in Rust
BSD Zero Clause License
96 stars 60 forks source link

Half duplex SPI #75

Open dzarda opened 4 years ago

dzarda commented 4 years ago

SPI would benefit from half-duplex comms (SPI control register 1: BIDIMODE).

I realize this trait is missing in embedded-hal. I could potentially try to figure this out and bodge a prototype. But that ain't gonna be easy since I'm rather new to Rust.

Any pointers appreciated.

dzarda commented 4 years ago

I'm trying to enable SPI1 in RCC.APB2ENR from the consuming crate. I'm unable to do that since the RCC rb has been moved in freeze() and is hiding in hal::Rcc under pub(crate). Is implementing peripheral drivers outside of -hal packages frowned upon?

hannobraun commented 4 years ago

In general, HALs attempt to provide an API that protects the user from making mistakes (although in practice, all that I know fall short in various ways). One such mistake would be to, for example, to initialize a UART at 115200 baud, then invalidating that configuration by changing the clocks in RCC, which this HAL prevents by freezing the clock configuration.

Is implementing peripheral drivers outside of -hal packages frowned upon?

I don't know if it's frowned upon, but I think HALs generally assume that they are in control. You are free to work around that by accessing the PAC API directly (see RCC::ptr). This requires unsafe, and you'd have to take care not to get in the way of anything the HAL is doing, if you're using both APIs in parallel.

dzarda commented 4 years ago

Thanks. I then got stuck on figuring out the receiving part. Let's say we'd have some blocking:: API with roughly:

fn read(&mut self, words: &mut [u8]) -> Result<(), Error>;
fn write(&mut self, words: &[u8]) -> Result<(), Error>;

The write impl is pretty straightforward, but with read one has to actively manage the half duplex: BIDIMODE=1 & BIDIOE=0 causes infinite SCK train to be produced. We don't want that, SCK should be disabled after receiving N words.

  1. First solution would be busy polling and setting the control bits afterwards. This is fine as we're talking blocking API anyway. Unusable otherwise.
  2. Use the interrupt to shut down the SCK train afterwards. Requires dependency on enabled interrupts - probably bad?

Ref https://github.com/rust-embedded/embedded-hal/issues/144