embassy-rs / embassy

Modern embedded framework, using Rust and async.
https://embassy.dev
Apache License 2.0
5.66k stars 790 forks source link

stm32: add blocking DMA support #621

Open Dirbaio opened 2 years ago

Dirbaio commented 2 years ago

Currently blocking impls are only enabled when creating drivers with NoDma. Or they are always enabled, but they don't use DMA, which is confusing.

Instead, we should make it so that drivers can work in blocking mode with DMA too. Blocking operations would start DMA then spin-wait for it to finish.

Benefits:

  1. data keeps being copied even while the current thread is preempted.
  2. allows mixing blocking and async calls, can improve code size in some cases.
jrmoulton commented 11 months ago

I'm semi-blocked on this being implemented. I have a device that has an ssd1306 display and an eeprom on the same i2c lines. The display only implements the blocking traits and I want the eeprom to be nonblocking.

This sounds fairly straight forward to implements and I'd be willing to take a crack at it. Would there need to be anything more fancy than just waiting in a loop for the transfer to finish?

jrmoulton commented 11 months ago

Also to make this work I think it would make sense to be able to get a blocking I2C device from an async bus correct?

let async_i2c_mutex_bus = embassy_sync::mutex::Mutex::new(i2c);
let display_i2c = embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice::new(&async_i2c_mutex_bus);
mbrieske commented 1 month ago

Has there been any progress on this? I just ran into the same problem as @jrmoulton: trying to use two devices at the same time, with one being blocking and the other non-blocking. How did you solve your issue in the end?

Dirbaio commented 1 month ago

Implementing blocking DMA won't allow sharing an i2c bus between an async driver and a blocking driver.

That's a completely separate issue, that requires a Mutex that you can either lock either blockingly or asyncly, like tokio's Mutex. You need an OS that gives you "real" threads for that, because you need to be able to schedule-out a thread that's blocking-waiting on the mutex to let async tasks make progress and unlock the mutex. Without real threads you just get a deadlock.

So, this is not possible with bare-metal async at all. A shared bus can be either all blocking or all async, but not a mix.

As a workaround you can use blocking bus sharing, then convert a blocking i2c to a "fake async" i2c for the async driver using https://docs.embassy.dev/embassy-embedded-hal/git/default/adapter/struct.BlockingAsync.html . This is not ideal since the async driver will actually behave blockingly, but at least it should give you something that compiles and works.