Rtic v0.5 complains about static mut variable

hi! Similarly to the example here: Access control - Real-Time Interrupt-driven Concurrency I was trying to define an app level static mut variable, but I'm getting an error This item must live outside the #[app] module. Here's a stipped down version of my code:

#[rtic::app(device = stm32f3xx_hal::pac, peripherals = true)]
const APP: () = {
    static mut RX_BUF: &'static mut [u8; 1] = singleton!(: [u8; 1] = [0; 1]).unwrap();

    struct Resources {
        dummy: bool,
    }

    #[init]
    fn init(cx: init::Context) -> init::LateResources {
        init::LateResources { dummy: false }
    }

};

From Cargo.toml:

cortex-m-rtic = { version = "0.5.9", default-features = false, features = ["cortex-m-7"] }

I'm trying to create a singleton in #[init] and use it in other tasks, please let me know if there is a better way to do it in rtic

That static code is what the framework does under the hook, you need the RX_BUF inside the Resources struct. Also i recomend you switch to the next version of this amazing crate:

https://rtic.rs/dev/book/en/

1 Like

It's been a while since I used RTIC v0.5, but I don't think you need the singleton macro. RTIC already performs a transform of the static mut variable to make it safe. I think the singleton macro might even have heritage from RTIC.

Separately, if you're open to it, I would recommend switching to RTIC v0.6. The release candidate has been out for a while, so it's nearly stable. Handling situations like this is a bit easier in the new version.

2 Likes

though in the link I've sent the static mut is defined outside of Resources before the the #[app] macros is expanded.

Also i recomend you switch to the next version of this amazing crate

Yeah, I've been thinking about it, but was a bit hesitant since it's RC, but I'll probably do it, since it seems like 0.5 is too far behind. Thanks for you advice!

but I don't think you need the singleton macro. RTIC already performs a transform of the static mut variable to make it safe. I think the singleton macro might even have heritage from RTIC.

ya, however, no matter what static mut I define in the #[app] it produces the same error, ie

#[rtic::app(device = stm32f3xx_hal::pac, peripherals = true)]
const APP: () = {
    static mut RX_BUF: &'static mut bool = false;
}

this would fail with the same error.

Separately, if you're open to it, I would recommend switching to RTIC v0.6. The release candidate has been out for a while, so it's nearly stable. Handling situations like this is a bit easier in the new version.

Thank you, was definitely considering it.

@snaumov, as I said, I don't remember v0.5 very well and I would have to set up a repo to test, so I can only guess right now. But I can offer a few things to try.

Also, you might have better luck asking for help on the RTIC Matrix channel.

  • Why are you trying to define a static reference? Why not define the bool (or other resource) directly?
  • The only times I remember using the static mut transform was inside tasks, to create a static local resource. You could do that in init, and then transfer the &'static mut reference into the LateResources struct. That's how I used to do it.
1 Like
  • Why are you trying to define a static reference? Why not define the bool (or other resource) directly?

I was trying to use this method inside a task Serial in stm32f3xx_hal::serial - Rust, which requires

B: WriteBuffer<Word = u8> + 'static,

so my attempt was to create a 'static in init and then pass it to task via Resources or in any other way.

I just realized that the link you gave points to a section of the RTIC "Under the hood" documentation. I don't think those examples are intended to be used directly. They show the kind of transform done by RTIC.

The LateResources documentation shows an example of what you're trying to do. You define a static mut inside init, then pass a static reference as a late resource. Have you tried that yet?

The LateResources documentation shows an example of what you're trying to do. You define a static mut inside init , then pass a static reference as a late resource. Have you tried that yet?

I couldn't make a static in init and transfer it to task, couldn't appease the borrow checker, so I ended up passing the Transfer itself. Leaving my code here in case it'll be helpful to someone:

    #[rtic::app(device = stm32f3xx_hal::pac, peripherals = true, dispatchers = [TIM20_BRK, TIM20_UP, TIM20_TRG_COM])]
mod app {
    use panic_itm as _;

    use stm32f3xx_hal::{
        dma::{dma1::C5, Channel, Event, Transfer},
        gpio,
        pac,
        prelude::*,
        serial::{self, Serial},
    };
    const BUF_SIZE: usize = 1;

    type RxType = serial::Rx<
        pac::USART1,
        gpio::Pin<gpio::Gpioa, gpio::U<10_u8>, gpio::Alternate<gpio::PushPull, 7_u8>>,
    >;
    type DirRedType = gpio::PE13<gpio::Output<gpio::PushPull>>;
    type DirGreenType = gpio::PE15<gpio::Output<gpio::PushPull>>;

    pub struct Leds {
        red: DirRedType,
        green: DirGreenType,
    }

    #[shared]
    struct Shared {}

    #[local]
    struct Local {
        recv: Option<Transfer<&'static mut [u8; BUF_SIZE], C5, RxType>>,
        leds: Leds,
    }

    #[init(local = [tx_buf: [u8; 2] = [0; 2], rx_buf: [u8; BUF_SIZE] = [0; BUF_SIZE]])]
    fn init(ctx: init::Context) -> (Shared, Local, init::Monotonics) {
        rtic::pend(stm32f3xx_hal::interrupt::USART1_EXTI25);

        ctx.device.RCC.ahbenr.modify(|_, w| w.dma1en().enabled());
        let mut rcc = ctx.device.RCC.constrain();
        let mut flash = ctx.device.FLASH.constrain();
        let clocks = rcc.cfgr.freeze(&mut flash.acr);
        let mut gpioa = ctx.device.GPIOA.split(&mut rcc.ahb);
        let mut pins = (
            gpioa
                .pa9
                .into_af7_push_pull(&mut gpioa.moder, &mut gpioa.otyper, &mut gpioa.afrh),
            gpioa
                .pa10
                .into_af7_push_pull(&mut gpioa.moder, &mut gpioa.otyper, &mut gpioa.afrh),
        );
        let mut serial = Serial::new(ctx.device.USART1, pins, 9600.Bd(), clocks, &mut rcc.apb2);
        let (tx, rx) = serial.split();
        let mut dma1 = ctx.device.DMA1.split(&mut rcc.ahb);
        dma1.ch5.enable_interrupt(Event::TransferComplete);

        // DIR (the LED that lights up during serial rx)
        let mut gpioe = ctx.device.GPIOE.split(&mut rcc.ahb);
        let mut red_dir: DirRedType = gpioe
            .pe13
            .into_push_pull_output(&mut gpioe.moder, &mut gpioe.otyper);
        let mut green_dir: DirGreenType = gpioe
            .pe15
            .into_push_pull_output(&mut gpioe.moder, &mut gpioe.otyper);

        // Initialize speech module
        // Import group 1
        let tx_buf = ctx.local.tx_buf;
        match hex::decode_to_slice("aa21", tx_buf) {
            Ok(res) => {}
            Err(err) => {
                let a = 1;
            }
        };
        let sending = tx.write_all(tx_buf, dma1.ch4);
        let (tx_buf, tx_channel, tx) = sending.wait();
        hex::decode_to_slice("aa37", tx_buf).unwrap();
        let sending = tx.write_all(tx_buf, tx_channel);
        let (tx_buf, tx_channel, tx) = sending.wait();

        (
            Shared {},
            Local {
                recv: Some(rx.read_exact(ctx.local.rx_buf, dma1.ch5)),
                leds: Leds {
                    red: red_dir,
                    green: green_dir,
                },
            },
            init::Monotonics(),
        )
    }

    #[idle]
    fn idle(_: idle::Context) -> ! {
        loop {}
    }

    // Triggers on RX transfer completed
    #[task(binds = DMA1_CH5, local = [recv], priority = 2)]
    fn on_rx(ctx: on_rx::Context) {
        let (rx_buf, rx_channel, rx) = ctx.local.recv.take().unwrap().wait();
        led::spawn(*rx_buf).ok();
        ctx.local.recv.replace(rx.read_exact(rx_buf, rx_channel));
    }

    #[task(local=[leds])]
    fn led(ctx: led::Context, data: [u8; BUF_SIZE]) {
        let leds = ctx.local.leds;
        match &data {
            b"\x11" => {
                // Red
                assert_eq!(1, 1);
                leds.red.toggle().expect("cannot toggle led");
            }
            b"\x12" => {
                // Green
                assert_eq!(1, 1);
                leds.green.toggle().expect("cannot toggle led");
            }
            _ => {}
        }
    }
}

1 Like

@snaumov, I thought you were using RTIC v0.5? That's v0.6 syntax. Did you decide to make the switch? In v0.6, you don't need to declare statics on your own anymore. You can use local resources of init for that. It looks like you figured that out, though.

yes, I switched to 0.6 as you and @elsuizo suggested.

1 Like