Rust starter for Daisy Seed

I feel like I’m missing something pretty simple, but I’ve got my first Daisy program written in rust, and i can run it just fine through openocd with a cargo run --release. How can i get it so that if I disconnect the debugger and only plug the daisy into a usb power supply my program boots up at the start? I’ve tried using

cargo build --release
cargo objcopy --release -- -O binary myapp.bin
dfu-util -a 0 -s 0x08000000 -D myapp.bin

And it appears to load just fine from my computer to the Daisy over USB, but the program won’t run on its own. Any suggestions would be appreciated!

1 Like

Anyone able to point me in the right direction for connecting a SSD1306 display via SPI using the rust_bsp crate? I’m pretty new to embedded, been learning rust for a couple months now so I’m just not sure if it’s blatant or not if i’m doing something wrong.

I know my connections are right, because I got some c++ code that does some basic display stuff. But i cannot get any output on the display using rust_bsp. It compiles and flashes to the daisy successfully, just not getting any output on the display. Here’s my code:

#![no_main]
#![no_std]

use panic_semihosting as _;
use cortex_m_rt::{entry, ExceptionFrame, exception};
use daisy_bsp::{
    hal::{self, delay::Delay, spi::{NoMiso, MODE_0}, prelude::*, rcc::PllConfigStrategy,},
    pac, 
};

use ssd1306::{
    Ssd1306,
    mode::DisplayConfig,
    rotation::DisplayRotation,
    size::DisplaySize128x64,
};

#[entry]
fn main() -> ! {
    let cp = cortex_m::Peripherals::take().unwrap();
    let dp = pac::Peripherals::take().unwrap();
    let board = daisy_bsp::Board::take().unwrap();

    // - power & clocks -------------------------------------------------------
    let pwr = dp.PWR.constrain();
    let pwrcfg = pwr.vos0(&dp.SYSCFG).freeze();
    let ccdr = dp.RCC.constrain()
        .use_hse(16.mhz())                           // external crystal @ 16 MHz
        .pll1_strategy(PllConfigStrategy::Iterative) // pll1 drives system clock
        .sys_ck(480.mhz())                           // system clock @ 480 MHz
        .freeze(pwrcfg, &dp.SYSCFG);


    // - pins -----------------------------------------------------------------
    let pins = board.split_gpios(
        dp.GPIOA.split(ccdr.peripheral.GPIOA),
        dp.GPIOB.split(ccdr.peripheral.GPIOB),
        dp.GPIOC.split(ccdr.peripheral.GPIOC),
        dp.GPIOD.split(ccdr.peripheral.GPIOD),
        dp.GPIOE.split(ccdr.peripheral.GPIOE),
        dp.GPIOF.split(ccdr.peripheral.GPIOF),
        dp.GPIOG.split(ccdr.peripheral.GPIOG)
    );


    // - SPI1 -----------------------------------------------------------------
    let nss = pins.SEED_PIN_7.into_push_pull_output();
    let sck = pins.SEED_PIN_8.into_alternate_af5();
    let mosi = pins.SEED_PIN_10.into_alternate_af5();

    let dc = pins.SEED_PIN_9.into_push_pull_output();

    let mut rst = pins.SEED_PIN_30.into_push_pull_output();
    let mut delay = Delay::new(cp.SYST, ccdr.clocks);


    let spi: hal::spi::Spi<pac::SPI1, hal::spi::Enabled, u8> = dp.SPI1.spi(
        (
            sck, 
            NoMiso, 
            mosi
        ), 
        MODE_0, 
        8.mhz(), 
        ccdr.peripheral.SPI1, &ccdr.clocks
    );

    let interface = display_interface_spi::SPIInterface::new(spi, dc, nss);

    let mut display = Ssd1306::new(
        interface,
        DisplaySize128x64,
        DisplayRotation::Rotate0,
    ).into_buffered_graphics_mode();

    display.reset(&mut rst, &mut delay).unwrap();
    display.init().unwrap();

    // Top side
    display.set_pixel(0, 0, true);
    display.set_pixel(1, 0, true);
    display.set_pixel(2, 0, true);
    display.set_pixel(3, 0, true);

    // Right side
    display.set_pixel(3, 0, true);
    display.set_pixel(3, 1, true);
    display.set_pixel(3, 2, true);
    display.set_pixel(3, 3, true);

    // Bottom side
    display.set_pixel(0, 3, true);
    display.set_pixel(1, 3, true);
    display.set_pixel(2, 3, true);
    display.set_pixel(3, 3, true);

    // Left side
    display.set_pixel(0, 0, true);
    display.set_pixel(0, 1, true);
    display.set_pixel(0, 2, true);
    display.set_pixel(0, 3, true);

    display.flush().unwrap();

    loop {}
}

#[exception]
fn HardFault(ef: &ExceptionFrame) -> ! {
    panic!("{:#?}", ef);
}

And if it’s any useful insight: So all three versions of the blinky example work, so in one of my attempts of troubleshooting, i tried putting the blinky logic in the same code above, and basically blinky functionality wont work once I initialize the spi variable here

    let spi: hal::spi::Spi<pac::SPI1, hal::spi::Enabled, u8> = dp.SPI1.spi(
        (
            sck, 
            NoMiso, 
            mosi
        ), 
        MODE_0, 
        8.mhz(), 
        ccdr.peripheral.SPI1, &ccdr.clocks
    );

So i’m thinking maybe something is wrong inside the spi variable?

Idk, been at it all weekend and haven’t figured it out :sob:

i have
DataSPI1 MOSI (seed pin 10)
ClkSPI1 SCK (seed pin 8)
DCSPI1 MISO (seed pin 9)
CSSPI1 CS (seed pin 7)

for RST i’ve tried 2 approaches
1: connecting the display RST to daisy pin 30 (which is gpiob PB15 i believe)
2: tried implementing a “NoOutputPin” because the C++ version doesn’t require any rst connections, here’s that implementation

/// Represents an unused output pin.
#[derive(Clone, Copy)]
pub struct NoOutputPin<PinE = ()> {
    _m: PhantomData<PinE>,
}

impl<PinE> NoOutputPin<PinE> {
    /// Create a new instance of `NoOutputPin`
    pub fn new() -> Self {
        Self { _m: PhantomData }
    }
}

impl<PinE> OutputPin for NoOutputPin<PinE> {
    type Error = PinE;
    fn set_low(&mut self) -> Result<(), PinE> {
        Ok(())
    }
    fn set_high(&mut self) -> Result<(), PinE> {
        Ok(())
    }
}

While I was able to get the adc_hal.rs example to run correctly, I was not able to use the examples to pass audio through, neither using the audio_passthrough.rs example, nor the audio_testsignal.rs example. In both cases, I just get nothing on the output.

Setup:
On Ubuntu

Following @sharps instructions - I am able to flash on adc_hal.rs example by moving the whole file to ./src/main.rs

Everything works properly here. The LED blinks at a rate according to the voltage at the pot wiper.

I am not currently using a debugger, but am doing the extra steps to use plane usb. Here is my build utility script:

put device in debug mode
run this script
#!/bin/zsh
cargo build --release
cargo objcopy --release – -O binary hello-daisy.bin
dfu-util -a 0 -s 0x08000000:leave -D hello-daisy.bin
rm hello-daisy.bin

=======
Next, I copied audio_testsignal.rs in ./src/main/, and also copied the dsp folder into source. Running the same steps as above, the program compiles and flashes successfully, the LEDs blink. But no audio.

I also tried leaving out the :leave command as was mentioned in another comment, and then manually hitting the reset button. But still nothing. No audio gets generated - I have an oscilloscope on seed pins 18 and 19, which are audio output pins. Nothing. It’s a flat voltage.

Anyone have any insights or other things to try? I will post here if I figure it out.

1 Like

When did you get your board? If relatively recently, then you have the latest revision that has a different codec.

As of the other day, someone has created a fork with a working solution, I used their same code for my board and it worked (I have the latest board).

2 Likes

Yeah, there’s a new board revision with a different audio codec. Audio examples don't seem to work with new codec · Issue #8 · antoinevg/daisy_bsp · GitHub ← this is where the fix is being worked on. Presumably there’ll be a new version of daisy_bsp released soonish with support.

I wrote a new driver script for the Daisy Seed rev5 (with the WM8731 codec) months ago and didn’t know about this thread. I made an account just now to spread the news!

It is a fork of Matthew Meyer’s libdaisy-rust crate, which is not the daisy-bsp crate from Antoine.

My fork is fully working (only) for the rev5. I’m planning on updating the library, so it also supports the old codec. Unfortunately, I own just rev5 boards, so I would be grateful if someone with a rev4 board could flash an example and report to me if everything is working correctly, once I updated the library!

1 Like

I tried to cargo +nightly build --example blinky --release but got this error:

   Compiling stm32h7xx-hal v0.11.0
error[E0606]: casting `BusWidth` as `u32` is invalid
    --> /Users/chaosprint/.cargo/registry/src/github.com-1ecc6299db9ec823/stm32h7xx-hal-0.11.0/src/sdmmc.rs:429:61
     |
429  |                       let sdmmc_bus_bandwidth = new_clock.0 * (width as u32);
     |                                                               ^^^^^^^^^^^^^^
...
1206 | / sdmmc! {
1207 | |     SDMMC1: (sdmmc1, Sdmmc1),
1208 | |     SDMMC2: (sdmmc2, Sdmmc2),
1209 | | }
     | |_- in this macro invocation
     |
     = note: cannot cast an enum with a non-exhaustive variant when it's defined in another crate
     = note: this error originates in the macro `sdmmc` (in Nightly builds, run with -Z macro-backtrace for more info)

My Daisy Seed is Rev 5.

Hi chaosprint,

It seems like you have a problem building the stm32h7xx-hal crate and not the libdaisy-rust crate. What version of Rust are you using? Maybe you need to bump it up.

On another note, I don’t build with the nightly toolchain. Also, maybe try building another example, i.e. passthru. I know, that one worked for me.

This definitely doesn’t have anything to do with your revision of the board!

My rust version:

rustc 1.64.0 (a55dd71d5 2022-09-19)

If I don’t use nightly, there will be even more errors. I guess for embedded Rust to use nightly is quite common.
Now I disable the sdmmc feature and it compiles fine. But what would be the expected behaviour of the passthru example? I only have a headphone with me. It may be nice to have a testing sine tone.

I guess, delay, passthru and volume are the only examples with audio output. However, they depend on audio input. So you would need to have an audio source for that (like a phone or laptop).

I would like to use a customised DSP engine for playing the sound and I don’t need any input for now.

How can we create a variable to store the DSP engine?

I try to create the DSP instance during #[init], then return it to local:

let oscillator = SinOsc::new(440.0);
Local {
   oscillator
}

But in the audio_handler, when I call:

for _ in 0..1024 {
    let s = oscillator.next();
    audio.push_stereo((s, s)).unwrap();
}

No sound is played. Is it because I need to enable some features? Or any other reasons?

The audio callback should look something like this:

// Interrupt handler for audio
#[task(binds = DMA1_STR1, local = [audio, buffer, oscillator], priority = 8)] 
fn audio_handler(mut ctx: audio_handler::Context) {
    let audio = &mut ctx.local.audio;
    let buffer = &mut ctx.local.buffer;
    let oscillator = &mut ctx.local.oscillator;

    // this needs to be called
    audio.get_stereo(buffer);

    for _ in buffer {
        let s = oscillator.next();
        audio.push((s,s)).unwrap();
    }
}

audio.get_stereo(buffer) is mandatory. I never got it working, if I didn’t call this function.

And buffer should be initialized in the #[init] task like this:

let buffer = [(0.0, 0.0); audio::BLOCK_SIZE_MAX]; // audio ring buffer

I tried to simplify the code but it is still not working:

    #[task(binds = DMA1_STR1, shared = [], local = [audio, buffer, phase], priority = 8)]
    fn audio_handler(mut ctx: audio_handler::Context) {
        let audio = &mut ctx.local.audio;
        let buffer = &mut ctx.local.buffer;
        let phase = &mut ctx.local.phase;
        audio.get_stereo(buffer);
        for i in 0..buffer.len() {
            match i {
                0 => {
                    phase[i].0 = phase[phase.len()-1].0 + 440.0 / 48000.0;
                    phase[i].1 = phase[phase.len()-1].1 + 440.0 / 48000.0;
                },
                _ => {
                    phase[i].0 = phase[i-1].0 + 440.0 / 48000.0;
                    phase[i].1 = phase[i-1].1 + 440.0 / 48000.0;
                }
            }
            
            let l = libm::sin((phase[i].0 * 2.0 * core::f32::consts::PI).into()) as f32;
            let r = libm::sin((phase[i].1 * 2.0 * core::f32::consts::PI).into()) as f32;
            audio.push_stereo((l, r)).unwrap();
        };
    }

Here I have init phase exactly same as buffer, a stereo f32 tuple.
When I flash it, it seems that there is a very short time of beeping.
I am sure my HW connection is correct as I can play the DSP example from the browser programmer.

Hi, @backtail

I now further edit the audio.rs in src:

    /// Push data to the DMA buffer for output
    /// Call this once per sample per call to [get_stereo()](Audio#get_stereo)
    pub fn push_stereo(&mut self, data: (f32, f32)) -> Result<(), ()> {
        self.phase += 440.0 / 48000.0;
        let v = libm::sin((self.phase * 2.0 * core::f32::consts::PI).into()) as f32;
        if self.phase >= 1.0 {
            self.phase -= 1.0;
        }
        self.output.push((v, v))
    }

This should in theory overwrite the audio with a sin tone for the examples passthru or volume, but I just got a very short and low-volume beep, the same as the result from the code above. Since I don’t have a ST-LINK yet, what would be the cause in your opinion? Anyway, I would assume it’s good to add some sound synthesis in the example given the usage of Daisy Seed.

Does the Daisy Seed board expose SWD port that can be used with pico-probe

The Daisy Seed has a mini JTAG Header pins (2x5). Is that what you’re looking for your pico-probe? I haven’t used that type of probe before so I cannot concretely tell you that it would work with the Daisy.

Those pins are used for this debug probe by the way: ST LINK-V3 MINI Debugger — Electro-Smith (and you can see how it’s connected in the photo).

Ok thanks for the info. Assuming that it is this port: Documentation – Arm Developer and the reading that I have done, it should work out.

I will give it a try an report back here

How do you disable sdmmc?

This topic may help:

Daisy Seed Rev5 Rust Example

I can’t get the link in a post - error msg says 429 Too Many Requests

1 Like