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

Simple Pin type aliases? #9

Closed
tnahs opened this issue Jul 3, 2023 · 1 comment
Closed

Simple Pin type aliases? #9

tnahs opened this issue Jul 3, 2023 · 1 comment

Comments

@tnahs
Copy link

tnahs commented Jul 3, 2023

Hey! Thanks so much for this crate! I've run into an issue and I'm looking for some guidance.

I'm somewhat new to Rust and very new to embedded so hopefully my question will make sense. Is there a way to type alias expander pins as simply as an MCU pin? For example, the stm32f1xx-hal has simple type aliases for each pin, something like PA0 or PB9.

What I'm trying to solve:

In my project I've got a few basic components: single-pin LED, dual-pin LED and a switch. I've then created assemblies that combine these building blocks to create something like a "button that has a multi-color LED". I wrote them all with the embedded-hal OuputPin or InputPin traits in mind. Here's a simplified version of the Led struct:

pub struct Led<Pin>
where
    Pin: OutputPin,
{
    pin: Pin,
}

impl<Pin> Led<Pin>
where
    Pin: OutputPin,
{
    #[must_use]
    pub fn new(pin: Pin) -> Self {
        Led { pin }
    }

    pub fn on(&mut self) {
        self.pin.set_high().ok();
    }

    pub fn off(&mut self) {
        self.pin.set_low().ok();
    }
}

In order to control these components/assemblies I need to define them in rtic as resources in the Shared resource struct. The catch is, as far as I understand, that the Shared struct doesn't allow lifetimes or generic parameters. Before I added the expander I was just defining Led with the MCU pin within the struct like so:

#[shared]
struct Shared {
    led: Led<stm32f1xx_hal::gpio::PA0>,
}

I would do the same thing with GPIO expander pins but the type for a pin is incredibly complicated and has lifetime and generic parameters. I checked other expander crates and they all seems to have the same sort of complex type signatures. So I'm under the impression that the answer to my question is probably "no".

In any case, I tried using the full GPIO expander pin type definition and just giving it a static lifetime. But when I did that, rtic complained that the type (RefCell) was not thread safe. I know I'm running this single-threaded so I defined a Newtype around the GPIO expander pin type and implemented an unsafe Sync for it. Then I got lifetime issues which I wasn't surprised to see.

Thoughts? Is there something I'm missing? I feel like I'm doing something I shouldn't be doing as this is feeling way difficult. I'm currently stumped and have been considering just using a larger pin variant of the stm32. However I'd really love to get the expander working!

Thanks for taking the time! :D

For the sake of completion here's a fully "working" example that does not compile.

#![no_main]
#![no_std]

use bluepill_rtic_tca9555 as _; // global logger + panicking-behavior + memory layout

use embedded_hal::digital::v2::OutputPin;
use port_expander::Pca9555;
use rtic::app;
use shared_bus::BusManagerSimple;
use stm32f1xx_hal::afio::AfioExt;
use stm32f1xx_hal::flash::FlashExt;
use stm32f1xx_hal::gpio::GpioExt;
use stm32f1xx_hal::i2c::{BlockingI2c, DutyCycle, Mode};
use stm32f1xx_hal::rcc::RccExt;
use systick_monotonic::fugit::{ExtU64, RateExtU32};
use systick_monotonic::Systick;

const CLOCK_HZ: u32 = 36_000_000;
const CRYSTAL_HZ: u32 = 8_000_000;
const TASK_TIMER_HZ: u32 = 1000;

pub struct Led<Pin>
where
    Pin: OutputPin,
{
    pin: Pin,
}

impl<Pin> Led<Pin>
where
    Pin: OutputPin,
{
    #[must_use]
    pub fn new(pin: Pin) -> Self {
        Led { pin }
    }

    pub fn on(&mut self) {
        self.pin.set_high().ok();
    }

    pub fn off(&mut self) {
        self.pin.set_low().ok();
    }
}

#[app(device = stm32f1xx_hal::pac, peripherals = true, dispatchers = [SPI1])]
mod app {

    use super::*;

    #[monotonic(binds = SysTick, default = true)]
    type MonoTimer = Systick<TASK_TIMER_HZ>;

    #[local]
    struct Local {}

    #[shared]
    struct Shared {
        // LED using pin from the MCU.
        led_mcu: Led<Pin>, // ERROR

        // LEDs usin pins from GPIO expander.
        led_ex1: Led<Pin>, // ERROR
        led_ex2: Led<Pin>, // ERROR
    }

    #[init]
    fn init(ctx: init::Context) -> (Shared, Local, init::Monotonics) {
        // Setup Clocks

        let mut flash = ctx.device.FLASH.constrain();
        let rcc = ctx.device.RCC.constrain();

        let clocks = rcc
            .cfgr
            .use_hse(CRYSTAL_HZ.Hz())
            .sysclk(CLOCK_HZ.Hz())
            .pclk1(CLOCK_HZ.Hz())
            .freeze(&mut flash.acr);

        // Initialize Systick

        let mono = Systick::new(ctx.core.SYST, CLOCK_HZ);

        // Init Expanders

        let mut afio = ctx.device.AFIO.constrain();
        let mut gpio_a = ctx.device.GPIOA.split();
        let mut gpio_b = ctx.device.GPIOB.split();

        let scl = gpio_b.pb8.into_alternate_open_drain(&mut gpio_b.crh);
        let sda = gpio_b.pb9.into_alternate_open_drain(&mut gpio_b.crh);

        let i2c = BlockingI2c::i2c1(
            ctx.device.I2C1,
            (scl, sda),
            &mut afio.mapr,
            Mode::Fast {
                frequency: 100_000.Hz(),
                duty_cycle: DutyCycle::Ratio2to1,
            },
            clocks,
            1000,
            10,
            1000,
            1000,
        );

        let bus = BusManagerSimple::new(i2c);

        let mut ex1 = Pca9555::new(bus.acquire_i2c(), false, false, false);
        let mut ex2 = Pca9555::new(bus.acquire_i2c(), false, false, true);

        let pins_ex1 = ex1.split();
        let pin_ex1_00 = pins_ex1.io0_0.into_output().unwrap();

        let pins_ex2 = ex2.split();
        let pin_ex2_00 = pins_ex2.io0_0.into_output().unwrap();

        let pin_mcu_00 = gpio_a.pa0.into_push_pull_output(&mut gpio_a.crl);

        // Init LEDs

        let led_mcu = Led::new(pin_mcu_00);
        let led_ex1 = Led::new(pin_ex1_00);
        let led_ex2 = Led::new(pin_ex2_00);

        // The type I get for `led_ex1` and `led_ex2`.
        //
        // Unformatted:
        //
        // Led<port_expander::Pin<'_, port_expander::mode::Output, NullMutex<port_expander::dev::pca9555::Driver<I2cProxy<'_, NullMutex<stm32f1xx_hal::i2c::BlockingI2c<I2C1, (stm32f1xx_hal::gpio::Pin<'B', 8, stm32f1xx_hal::gpio::Alternate<stm32f1xx_hal::gpio::OpenDrain>>, stm32f1xx_hal::gpio::Pin<'B', 9, stm32f1xx_hal::gpio::Alternate<stm32f1xx_hal::gpio::OpenDrain>>)>>>>>>>
        //
        //
        // Formatted:
        //
        // Led<
        //     port_expander::Pin<
        //         '_,
        //         port_expander::mode::Output,
        //         NullMutex<
        //             port_expander::dev::pca9555::Driver<
        //                 I2cProxy<
        //                     '_,
        //                     NullMutex<
        //                         stm32f1xx_hal::i2c::BlockingI2c<
        //                             I2C1,
        //                             (
        //                                 stm32f1xx_hal::gpio::Pin<
        //                                     'B',
        //                                     8,
        //                                     stm32f1xx_hal::gpio::Alternate<
        //                                         stm32f1xx_hal::gpio::OpenDrain,
        //                                     >,
        //                                 >,
        //                                 stm32f1xx_hal::gpio::Pin<
        //                                     'B',
        //                                     9,
        //                                     stm32f1xx_hal::gpio::Alternate<
        //                                         stm32f1xx_hal::gpio::OpenDrain,
        //                                     >,
        //                                 >,
        //                             ),
        //                         >,
        //                     >,
        //                 >,
        //             >,
        //         >,
        //     >,
        // >;

        blink::spawn().ok();

        (
            Shared {
                led_mcu,
                led_ex1,
                led_ex2,
            },
            Local {},
            init::Monotonics(mono),
        )
    }

    #[task(shared = [led_mcu, led_ex1, led_ex2], local = [count: u8 = 0])]
    fn blink(ctx: blink::Context) {
        let led_mcu = ctx.shared.led_mcu;
        let led_ex1 = ctx.shared.led_ex1;
        let led_ex2 = ctx.shared.led_ex2;

        (led_mcu, led_ex1, led_ex2).lock(|led_mcu, led_ex1, led_ex2| {
            if *ctx.local.count % 2 == 0 {
                led_mcu.on();
                led_ex1.on();
                led_ex2.on();
            } else {
                led_mcu.off();
                led_ex1.off();
                led_ex2.off();
            }
        });

        *ctx.local.count += 1;

        blink::spawn_at(monotonics::now() + 1.secs()).ok();
    }
}
@tnahs
Copy link
Author

tnahs commented Oct 23, 2023

Closing this as I ended up implementing a simple driver for my needs! :)

@tnahs tnahs closed this as completed Oct 23, 2023
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

1 participant