-
Notifications
You must be signed in to change notification settings - Fork 790
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
introduce OptionalPeripherals
#2213
Conversation
Could you show an example of why this is useful? |
Sure! Basically I'm trying to provide high-level features ("net", "usb", "usb_ethernet", ...) that an application can select, together with target board features (e.g., "nrf52840dk", "rpi-pico"), and then minimize code duplication, having shared initialization functions. #[cfg(feature = "nrf52840dk")]
mod nrf52840dk {
pub type UsbDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>;
pub fn init_usb(p: Peripherals) -> UsbDriver {
use embassy_nrf::peripherals;
use embassy_nrf::usb::{vbus_detect::HardwareVbusDetect, Driver};
pub fn usb_driver(p: &mut Peripherals) -> UsbDriver {
let usbd = p.USBD; // cannot move out
use super::Irqs;
Driver::new(usbd, Irqs, HardwareVbusDetect::new(Irqs))
}
}
}
#[cfg(feature = "rpi-pico")]
pub mod rpi_pico {
use embassy_rp::peripherals;
use embassy_rp::usb::Driver;
pub type UsbDriver = Driver<'static, peripherals::USB>;
pub fn usb_driver(p: &mut Peripherals) -> UsbDriver {
let usb = p.USB; // cannot move out
Driver::new(usb, super::Irqs)
}
}
#[cfg(feature = "nrf52840dk")]
use nrf52840dk as board;
#[cfg(feature = "rpi-pico")]
use rpi_pico as board;
fn init(p: Peripherals) {
let usb_driver = board::usb_driver(&mut p);
} (pseudo code and mostly conceived example) In this example, I'd like to move the initialization of For this example, there's ways to work around this (e.g., by passing Down the line there'll be more variants, more peripheral types, more places to pull |
I recently published For example: #[cfg(feature = "nrf52840dk")]
assign_resources! {
usb_driver: UsbResources {
usb: USBD,
}
}
#[cfg(feature = "rpi-pico")]
assign_resources! {
usb_driver: UsbResources {
usb: USB,
}
}
pub fn make_usb_driver(r: UsbResources) -> UsbDriver {
// it's always called `r.usb` in `UsbResources` and is an owned peripheral, not a reference
Driver::new(r.usb, Irqs, #[cfg(feature = "nrf52840dk")] HardwareVbusDetect::new(Irqs))
}
fn init(p: Peripherals) {
let r = split_resources!(p);
// pass `make_usb_driver()` all the USB related resources (pins, peripherals, DMA channels, etc)
let usb_driver = make_usb_driver(r.usb_driver);
} |
This is very useful, and it solves the example I gave pretty elegantly. But I think I aimed too low. :) Even with assign-resources, there's a single place in the sources that owns Peripherals and needs to be able to do IMO using the type system & borrow checker to provide compile time "allocation" for the resources doesn't scale. I wish I could create this OptionalPeripherals outside of embassy (in our code), but I'm not sure Rust's macros have enough information. And, doing it in |
Just so this doesn't get cleaned: we might still want to use this, and are still searching for alternatives or better justification. :) |
3839c7c
to
bb38852
Compare
Some update: We've been using In combination, it allows writing applications that don't fully control I'm realizing that we don't really have a succinct example showcasing this. Probably our hid test application comes closest. The general idea is that an (application or library) crate can do sth like this: use riot_rs::...;
riot_rs::define_peripherals!(Buttons {
btn1: P0_11,
btn2: P0_12,
btn3: P0_24,
btn4: P0_25,
});
#[riot_rs::task(autostart, peripherals)]
async fn button_user(buttons: Buttons) {
// this task now owns the peripherals in `Buttons`
...
} This should look quite familiar to a "regular" embassy application. Here, the ariel-os crates drive MCU setup & startup (controlling We'd love to upstream this so we might eventually not use a fork of embassy. And we don't see a way to implement this in our code, the Personally I think the name needs bike shedding. Apart from that, I hope embassy finds this useful enough to merge, or unintrusive enough to merge because we want to use it. edit updated links to the new project name and location (Ariel OS). |
I'm sorry, but I don't think this is something we should merge.
so I'm going to close this. Thanks anyway for the PR! |
Hm, this is very unfortunate, we depend on this quit a bit in Ariel OS, and we'd love to not have to use a fork at some point. What the runtime tracking allows us to do, and what no other solution that we know of does, is that it solves what we've dubbed the "partial move problem" in code that is independent at the type level.
I think (In Ariel, we're trying to mitigate the "unwrap of None" panics by having some defined "initialization phase", but we're not too happy about that either. ) We'd love to handle this in our code base, but don't see how. Suggestions welcome. :) @Dirbaio, is this sufficiently different to make you reconsider? Or, can we maybe ease taking this change in somehow? Some options I see:
|
I needed a way to spread peripheral initialization over multiple
init()
functions.Passing
&mut Peripherals
around won't work as it's field cannot be moved out.But
steal()
ingPeripherals
in multiple places doesn't seem right.So this PR augments
peripherals_struct!()
to also create astruct OptionalPeripherals {}
that contains all fields ofPeripherals
, but wrapped inOption
.This is quite wasteful (1b per Peripheral, e.g., ~119b on nrf52840). I hope that this will be on stack only, or edit we'll optimize this into some kind of bit-field but with a similar interface, or even just compile it out.
edit
this only exposesnow supports nrf, stm32 and rp. We have a similar PR to esp-rs.OptionalPeripherals
for nrf52840 now. If I get positive feedback on the idea, I'll add re-exporting for the other crates. Marking as draft for now.