Mutex and multicore

⚓ Rust    📅 2026-01-22    👤 surdeus    👁️ 1      

surdeus

Info

This post is auto-generated from RSS feed The Rust Programming Language Forum - Latest topics. Source: Mutex and multicore

I'm writing an embedded program on my Pico (the original one) to turn on/off LEDs, read buttons, move an actuator to distinct positions, read fingerprints and write to the flash.

I'm using EVERY pin on the Pico (except two, which I'll get to eventually :slight_smile:), most of them as GPIO, but also UARTs (one for the debug probe and two - TX/RX - for the fingerprint scanner) and I2C.

The test program I wrote to test the control of the actuator works flawlessly. BUT, that program only uses the three ADC pins (ADC2, ADC_VREF and AGND), nothing else.

Moving over to the main program, it resets (as in, not a crash or hang which have to be dealt with by the Watchdog, and that I can see in the logs - just "boom" starts up again) as soon as I try to do anything (press a button for example). My guess is, that "something" is interfering with "something" else.

The worst thing, it's keep reseting a few times in a row, before settling down. Before it does it all again when I press (any) button.

These all run as individual tasks. One for the actuator controller, four button readers, four LED controllers, two for the hopefully-one-day CAN reader/writer - isn't actually doing anything yet other than out some info on the serial - then one for the Watchdog. All in, 12 task, that communicate through embassy_sync::channel::{Channel, Receiver}. Seems to work just fine.

However, I figured I should use the second core, it's getting a bit .. "sluggish" running all that on only one core.

That's when all hell broke loose, so to speak. And the error might indicate my theory that there's some interference somewhere:

error[E0382]: borrow of moved value: `flash`
   --> src/drive-by-wire.rs:235:9
    |
122 |     let flash = FLASH.init(Mutex::new(flash));
    |         ----- move occurs because `flash` has type `&mut Mutex<NoopRawMutex, Flash<'_, FLASH, Async, 2097152>>`, which does not implement the `Copy` trait
...
159 |         move || {
    |         ------- value moved into closure here
...
164 |                     flash,
    |                     ----- variable moved due to use in closure
...
235 |         flash,
    |         ^^^^^ value borrowed here after move
    |
note: consider changing this parameter type in function `actuator_control` to borrow instead if owning the value isn't necessary
   --> src/lib_actuator.rs:21:12
    |
 19 | pub async fn actuator_control(
    |              ---------------- in this function
 20 |     receiver: Receiver<'static, CriticalSectionRawMutex, Button, 64>,
 21 |     flash: &'static FlashMutex,
    |            ^^^^^^^^^^^^^^^^^^^ this parameter takes ownership of the value
    = note: the full name for the type has been written to '/Users/turbo/src/Mercedes SLK Drive Selector/drive-by-wire/code/target/thumbv6m-none-eabi/debug/deps/drive_by_wire-0a9890b4c527948e.long-type-11434618967207936082.txt'
    = note: consider using `--verbose` to print the full type name to the console

For more information about this error, try `rustc --explain E0382`.
error: could not compile `drive-by-wire` (bin "drive-by-wire") due to 1 previous error

I do wonder if the compiler didn't see some referencing/borrowing/owning/whatever (a concept I don't fully understand how it works in practice). I (think!) I understand it on a very theoretical and .. ideological POV, but not how it works in practice in Rust (with statics, mut's etc etc).

The main() code - src/drive-by-wire.rs:

use embassy_rp::{
    [...]
    flash::{Async as FlashAsync, Flash}
    [...]
};

#[embassy_executor::main]
async fn main(spawner: Spawner) {
    [...]
    let flash = Flash::<_, FlashAsync, FLASH_SIZE>::new(p.FLASH, p.DMA_CH3);
    static FLASH: StaticCell<FlashMutex> = StaticCell::new();
    let flash = FLASH.init(Mutex::new(flash));

Few lines later, before trying multicore:

    spawner.spawn(unwrap!(actuator_control(CHANNEL_ACTUATOR.receiver(), flash, actuator)));

Replaced with:

    spawn_core1(
        p.CORE1,
        unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) },
        move || {
            let executor0 = EXECUTOR0.init(Executor::new());
            executor0.run(|spawner| {
                spawner.spawn(unwrap!(actuator_control(
                    CHANNEL_ACTUATOR.receiver(),
                    flash,
                    actuator
                )))
            });
        },
    );

Later, I need to spawn the button readers:

    spawner.spawn(unwrap!(read_button(
        spawner,
        flash,
        fp_scanner,
        Button::P,
        p.PIN_2.into(),
        p.PIN_14.into(),
    ))); // button/P

Four of those, all called the same way, but with different pins etc. Same flash variable though!

The actuator_control() function - src/lib_actuator.rs:

pub async fn actuator_control(
    receiver: Receiver<'static, CriticalSectionRawMutex, Button, 64>,
    flash: &'static FlashMutex,
    mut actuator: Actuator<'static>,
) {
    [...]
}

The FlashMutex and FLASH_SIZE - src/lib_config.rs:

pub const FLASH_SIZE: usize = 2 * 1024 * 1024;
pub type FlashMutex = Mutex<NoopRawMutex, Flash<'static, FLASH, Async, FLASH_SIZE>>;

Sorry if I have included to much information (or, more likely, not enough! :slight_smile: ), or haven't explained the problem thorough, but that's probably because I have absolutely no idea where even begin to look for a solution :slight_smile:.

I have no idea what I'm doing, most of the code was gathered from bits and pieces on the 'Net, cobbled together and I somehow (very mysteriously!! :slight_smile: ) got it working.

Or rather, they all work independently, in separate programs! I have one for testing the actuator, for moving it forward or backward, writing to the flash, etc etc.

I can't post more than five links, so have a look at my code repo.

They all work just fine. So I'm not completely useless, and I do understand what I have done in those (mostly :slight_smile:). However, when I combine the code into "The Big Kahuna", that's when it all fails, and I'm not experienced enough with Rust, or embedded programming, to figure it out on my own.

Because the code works independently, I'm fairly certain that the code is (mostly) sound. But there must be an interference somewhere. Maybe I'm trying to do two (or more) things on the same "thing" (whatever that is).

It doesn't resets at the exact same place every time, but it's either when the actuator is moving, or when it's writing to the flash. It's not impossible that it's crashing when the actuator is moving and reading from the flash (it just resets before defmt have time to receive it - I do often get a (HOST) malformed frame skipped just before the "Starting" (which happens extremely early, one of the very first lines in main()). Or any combination of those.

Which is why I think it might have something to do with the flash, and how I'm using it.

I would very much appreciate some assist on this, if nothing else so recommendation and points to documentation.

3 posts - 2 participants

Read full topic

🏷️ Rust_feed