#embedded #resources #task #usb #resource-group

macro embedded-resources

Define peripheral usage in a single place for use everywhere

5 releases

new 0.2.1 Apr 20, 2025
0.2.0 Apr 19, 2025
0.1.5 May 11, 2024
0.1.2 Feb 19, 2024

#1271 in Hardware support

Download history 10/week @ 2025-02-09 4/week @ 2025-02-16 2/week @ 2025-02-23 78/week @ 2025-04-13

84 downloads per month

Custom license

18KB
158 lines

embedded-resources

This crate originated as a PR to assign-resources by Adam Greig.

This crate contains a macro to help assign and split up resources from a struct, such as the Peripherals struct provided by embedded PACs and HALs, into many smaller structs which can be passed to other tasks or functions.

It's best explained with the example below. Here we define new structs named UsbResources and LedResources, each containing some IO pins and a peripheral, and generate new usb_resources! and led_resources! macros. These macros will construct the respective types from the Peripherals instance. We can then move these new structs into our tasks.

Resources type aliases may be generated, so function signatures can refer to that type as well and any changes are propagated.

use embassy_stm32::Peri;
use embassy_stm32::peripherals::*;
use embedded_resources::resource_group;

#[resource_group]
struct UsbResources {
    dp: PA12,
    dm: PA11,
    usb: USB,
}

#[resource_group]
struct LedResources {
    r: PA2,
    g: PA3,
    b: PA4,
    #[alias = PWMTimer] // make an alias for this resource
    tim2: TIM2,
}

#[embassy_executor::task]
async fn usb_task(r: UsbResources) {
    // use r.dp, r.dm, r.usb
}

async fn setup_leds<'a>(r: LedResources) -> SimplePWM<'a, PWMTimer> {
    // setup three channel PWM (one for each color)       ^ alias used here
}

#[embassy_executor::task]
async fn led_task(rgb_pwm: SimplePWM<'static, PWMTimer>) {
    // use rgb_pwm                       ^ alias used here
}

#[embassy_executor::main]
async fn main(spawner: embassy_executor::Spawner) {
    let p = embassy_stm32::init(Default::default());

    let rgb_pwm = setup_leds(led_resources!(p));

    spawner.spawn(usb_task(usb_resources!(p))).unwrap();
    spawner.spawn(led_task(rgb_pwm)).unwrap();

    // can still use p.PA0, p.PA1, etc
}

This has a few advantages: you only need to write the specific pin names like PA12 in one place and can refer to them by name thereafter, you only have one argument for each task instead of potentially very many, and you don't need to write out lots of code to split the resources up. If you're targetting multiple different hardware versions, you can use #[cfg] to change pin allocations in just one place.

When adding embedded-resources as a dependency, make sure to enable the appropriate ecosystem feature for your project:

embedded-resources = { version = "0.2.0", features = ["stm32"] }

Dependencies

~0.4–0.8MB
~18K SLoC