Using the flash and GPIO at the same time on a RPi Pico?

I've been struggling with code stability in my program for .. as long as I can remember.

I've been working with the assumption that it's a hardware issue from day one. I'm moving an actuator between distinct positions, and I've been working with the assumption that it sends back a feedback, or it triggers some spike - everything is powered, in the end, by the same power (although the Pico via a 5V DC-DC buck converter, the actuator directly from the main power supply).

However, writing some testing programs, that only act on the actuator, I did manage to get that code stable as a rock.

But as soon as I try that in my "main" program (dubbed "The Big Kahuna" :slight_smile:) where I also read buttons, turn on/off LEDs, manage a Neopixel etc etc, the instability remains.

It just resets (not crashes!) quite semi-randomly. Semi-randomly, because it's in roughly three different places, but no of those should cause this, so it's probably one of the tasks being delayed and not as quick every time.

But some pointers here and there, I'm now working on the assumption that it's something to do with the flash - I need to store some state values between runs.

Disabling all that, "The Big Kahuna" works just fine!

So.. Reading up on the flash and how to use it (this was among the first code I wrote, when I was extremely green in Rust and microcontrollers, so I didn't pay attention on how to use it, just found code "out there" that worked), I'm now seeing that you can't use the flash and some of the GPIO pins at the same time... ??!

Because "I" wrote the flash part first, which worked perfectly, then wrote the actuator part, then the trouble began, so "obviously" I thought it was the actuator or the code that was at fault :smiley:.

But the information "out there" is .. not just inconclusive, but also contradictory.. Some say that it's GPIO20-25, GPIO23-29 and some GPIO23-25. Not sure which it is, but either way, I'm using some of those (GPIO 20, 21, 22, and 28 (as ADC)).

Now, only GPIO28 is actively used, the others are only allocated, but not actually read/written to. GPIO28 (as an ADC) I use to read the actuator brush (to know "where" it is).

I'm trying to read the RP2040 datasheet, but this is extremely low-level for me, so I'd appreciate some "interpretation" on this.

What ARE the pins for the QSPI (where the flash sits), and which pins can I not use at the same time?

The datasheet say:

The six QSPI Bank GPIO pins are typically used by the XIP peripheral to communicate with an external flash device. However, there are two scenarios where the pins can be used as software-controlled GPIOs:
• If a SPI or Dual-SPI flash device is used for execute-in-place, then the SD2 and SD3 pins are not used for flash access, and can be used for other GPIO functions on the circuit board.
• If RP2040 is used in a flashless configuration (USB boot only), then all six pins can be used for software-controlled GPIO functions

The flash in the Pico, is that considered an external flash (from the point of the RP2040, which is the subject of the datasheet :slight_smile:), and what is "flashless configuration"?

I'm using openocd to write my program(s) to the Pico, but I haven't seen anything about this in its documentation.

The RP2040 datasheet is 642 pages (!!) long, with extremely low-level info, that I understand only a part of, so if it's in there, feel free to point me to the section/page and I'll try to understand that better.

are you using a custom rp2040 board? as far as I know, the official pico board has the QSPI pins wired directly to the flash chip and there's no breakout pins for them.

the QSPI pins are physically in a distinct IO bank than the user GPIO bank. each bank is controlled using different registers. in the datasheet, they are referred to as IO_BANK0 and IO_QSPI, respectively.

in other words, if your sensors/actuators are connected to the "regular" gpio pins, i.e. GPIO 0 through 29, then they are definitely NOT the QSPI pins.

I think your problem is more likely caused by incorrect access to the flash, even potential data corruption. because the rp2040 uses external flash in XIP mode by default, it's non trivial to use the flash for data storage. I know the theoretical process, but I have never done it myself.

the C sdk has a helper function flash_safe_execute(), which is used by the flash_program example, but I don't think a similar function is available in the rust hal crate, you'll have to re-implement it, or port it from the C sdk:

This is the original Pico from RaspberryPi, nothing out of the ordinary..

Because it doesn't have any breakout pins for the QSPI (that I knew of anyway!), that's why I'm so confused now! :slight_smile:.

It originally didn't even crossed my mind that there should be any problems using the flash and all the GPIOs on the board.

But because there's a lot of documentation out there that say you can't use the flash with "certain" GPIOs (which ones differ from site to site!), I'm now even more confused.

I used this code as base: embassy/examples/rp/src/bin/flash.rs at main · embassy-rs/embassy · GitHub, then I "just" wrapped that in a Mutex (not the whole program, but hopefully that's enough to show what I'm doing):

use embassy_rp::{
    flash::{Async as FlashAsync, Flash},
    peripherals::FLASH,
};
use static_cell::StaticCell;

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

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

    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));
}

In every case, I use flash.lock().await to create a lock on the Mutex. As far as I understand it (which is not very much to be fair :smiley:), that should make it safe to use across threads and tasks.. ?

I don't think that's the case.

in order to access the flash in non-XIP mode, you should disable interrupt on the running core and coordinate with the other core to not access the flash in any manner. the async mutex is simply NOT designed for this use case at all.

I'm not familiar with the flash driver of embassy, but looking at the source code, it seems to already have taken care of the low level details, as all the flash operations are wrapped with a helper function named in_ram(), which is probably embassy's port of the flash_safe_execute() function from the C sdk:

so, although the mutex does nothing when it comes to protect the access to the flash, the flash driver should be safe call. your problem might have other cause.

is it possible that the random reset you see was caused by a hardfault, or maybe power instability (a.k.a. brown-out)?

2 Likes

Perfect, thanx!!

Not sure exactly what else I've done, but I know it didn't work when I ran all that on CORE0!

But I made sure that all tasks that accesses the flash now runs on CORE0 only. I've only run it for a few minutes, but it seems stable enough. Thanx again for the pointer!

It's kind'a weird that it resets like that, instead of .. failing with the Err the code is supposed to throw..