Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request for Feedback: embedded-platform #412

Open
dflemstr opened this issue Jan 6, 2020 · 2 comments
Open

Request for Feedback: embedded-platform #412

dflemstr opened this issue Jan 6, 2020 · 2 comments

Comments

@dflemstr
Copy link

dflemstr commented Jan 6, 2020

I recently started down a rabbit hole of creating my own abstraction over embedded-hal that makes it easy to mix and match various dev boards. My initial use-case has been to do experiments with Adafruit Feather boards, where I want to plug two devices together and expect the code to "just work" without having to figure out the specific pin/peripheral mappings between them. On the other hand, I didn't want to go all in and rewrite all crates in existence; instead, I wanted to rely on existing -hal and driver crates as much as possible.

As a result, I created https://github.com/dflemstr/embedded-platform (see docs at https://docs.rs/embedded-platform, in particular the example Feather spec). I realize that the scope of that library is huge and hence I would like to get feedback on the approach. I'm happy to share control of the repository in case others think this is a good idea, or yank the crate if not :)

Since I'm not super familiar with this WG, I don't know which exact process to follow here. Please give me input on what to do!

A teaser example:

async fn run() -> Result<(), nrf52840_platform::error::Error> {
    use embedded_platform::platform::PlatformExt;

    let platform = nrf52840_platform::ParticleArgon::initialize().await?;

    feather_blink(platform).await?;

    Ok(())
}

async fn feather_blink<P>(mut feather: P) -> Result<(), P::Error>
where
    P: embedded_platform::specs::feather::Feather,
{
    use embedded_platform::gpio::IntoPushPullOutputPin;
    use embedded_platform::gpio::OutputPinExt;

    let mut main_led = feather.take_main_led().into_push_pull_output_pin(false)?;
    let mut on = false;
    let timer = todo!(); // TODO: didn't have time to implement timer API yet

    while let _ = timer.next().await? {
        on = !on;
        main_led.set(on).await?;
    }
}

Quoting the README:

This is defines the embedded-platform set of crates. The idea is to add device and peripheral support to complement embedded-hal-based crates. This makes it possible to plug-and-play and mix-and-match different crates that adhere to common specs. For example, if you have a nrf52840-based MCU as well as a ili9341-based device, and both adhere to the Adafruit Feather spec (pin layout, voltage levels, ...), you can connect them up and all the wiring will be done for you.

The ambition is that embedded-platform should be to embedded-hal what tokio is to mio.

Some design trade-offs that have been made:

  • #![forbid(unsafe_code)]; that belongs in -pac or -hal crates.
  • Don't require alloc.
  • Do some compatibility checks at runtime during startup instead of at compile time, for example to check that a pin is used only once. It turns out to be super tricky to do granular ownership mapping of device registers at compile time (this has been done in drone-os), and instead we opt to do some checks at runtime (e.g. Option::take). This wastes a dozen or so instructions at startup, which is a one-time cost.
  • All APIs are async-first, so that code won't have to block and we can be power efficient. This does require an executor, and one can be made that doesn't require alloc, yet to be written.
  • The crate uses its own HAL-like traits for e.g. OutputPin or I2cRead to enable async APIs as well as smooth over any incompatibilities between embedded_hal::gpio::v1 and embedded_hal::gpio::v2 etc.
  • All platform crates should be maintained in this repository so that changes like the last bullet point can be made in lock-step.
  • Don't expose interrupts to the user. mypin.changes() should return an async futures::Stream when the pin changes. In the background, we stash away a Waker that gets called from the interrupt handler.

You can think about the intended stack like this:

┌─────────────────────────────────────────┐
│         Peripheral Access Crate         │
│            e.g. nrf52840-pac            │
├─────────────────────────────────────────┤
│        Hardware Abstraction Layer       │
│            e.g. nrf52840-hal            │
├─────────────────────────────────────────┤
│         Platform Implementation         │
│          e.g. nrf52840-platform         │
│ ┌─────────────────────────────────────┐ │
│ │          Specific Product           │ │
│ │         e.g. Particle Argon         │ │
│ ├─────────────────────────────────────┤ │
│ │            Common Spec              │ │
│ │        e.g. Adafruit Feather        │ │
│ │          or Arduino Shield          │ │
│ ├─────────────────────────────────────┤ │
│ │              Adapter                │ │
│ │        e.g. "Main SPI bus" on       │ │
│ │        specific Feather pins        │ │
│ └─────────────────────────────────────┘ │
├─────────────────────────────────────────┤
│              Device Driver              │
│              e.g. ili9341               │
└─────────────────────────────────────────┘
@thejpster
Copy link
Contributor

I like the idea. It is to a BSP, what embedded-hal is to an SPI peripheral driver.

@dflemstr
Copy link
Author

dflemstr commented Jan 9, 2020

@thejpster okay, I will continue working on it for a while longer then and see where it ends up! (possibly adding support for more boards etc)

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

No branches or pull requests

2 participants