Rust starter for Daisy Seed

I got my Daisy Seed earlier this week (Tx Michael!) and put together a simple starter project for using the Daisy Seed with the Rust programming language:

Currently it just implements a basic “blinky” which flashes the Seed’s user led.

A good place to go digging for more example code is the examples directory in the stm32h7xx-hal crate.

The instructions for use go something like this:


Dependencies

Install arm target for Daisy

rustup target add thumbv7em-none-eabihf

Install gdb

# install via macports (TODO homebrew, linux, windows)
sudo port install arm-none-eabi-gdb

Install openocd

Install from source or get it from: https://xpack.github.io/openocd/install/

Generate project

cargo install cargo-generate

cargo generate \
    --git https://github.com/antoinevg/hello-daisy \
    --name your-project-name

Flash and run

  1. Plug a 2x10 mini adapter into the Daisy Seed with the red stripe facing up towards the white stripe.

  2. Connect the other end to a ST-Link or similiar programmer.

  3. Start openocd in one terminal:

     cd /path/to/your-project-name
     /path/to/openocd
    
  4. Run program in another terminal:

     cd /path/to/your-project-name
     cargo build
    
     # some programmers may need you to hit the reset button on the Daisy Seed at this point
    
     cargo run
6 Likes

Update 24 November 2020

I’ve started working on an Embedded Rust “Board Support Crate” which adds support for working with audio & midi.

The hello-daisy starter now makes use of the daisy_bsp crate and you can find some examples of how to use it here:

4 Likes

So you’re clocking it to 480Mhz, but libDaisy uses 400Mhz. I thought it has a chip from the older revision that doesn’t support this frequency. Are you overclocking on purpose or are Daisy developers too conservative?

Good question, I don’t actually know!

You’re right about the revisions though, 480 MHz has only been supported since revision V of the STM32H7 chips which came out April last year.

All my Daisy hardware has the revision V chip but I only bought a couple months ago so it’s possible some of the earlier hardware has an older revision?

I could probably add a compile-time feature to drop down to 400MHz if it ends up being a problem for anyone.

It very well may be that libDaisy code uses such settings to be compatible with older board revisions. Let’s hear what @shensley has to say about this.

The very early development daisy boards were done using the older, original revisions and so we had our clock tree set up for 400MHz since that was the max at the time.

I have an updated, ‘boosted’ clock tree pretty much ready to go, but it needs some testing to verify external memories still behave, and it may require adjusting things in some of the peripheral classes (i2c timing values come to mind). It also tightens up the SAI clocks to be well within 1% of the target sample rates, etc.

The commented out lines 116 and 132 in src/system.c can be swapped out for the lines directly above them to run at 480MHz if you need it.

3 Likes

For reference, this must be the branch mentioned above. I guess I’ll change clocks accordingly for OWL firmware fork. Free MCU upgrade FTW!

2 Likes

That is indeed the branch. Totally meant to link to it in my comment, and then got pulled away from my desk and forgot.

There were some weird things on that branch with the new clock tree, but I think most of it was the build I was using didn’t tell the HAL what frequency the HSE was (which is now in the CFLAGS). But it’d be worth running some tests on the external memories, and making sure typical things still function as expected (audio, adcs, etc.).

I’ll try to get this added into the modern libdaisy ASAP. I want to compare power consumption between 400/480 as well so we can make it optional (if it matters). Though at some point I want to add some more in-depth configuration of clock speeds to the PAI make this a bit more power-friendly for lightweight DSP that might want to run on a battery.

2 Likes

Definitely make clock speed programmable, or even adaptive like the Pi later on, as the H750 gets pretty warm at 480MHz. Running flat out for no purpose isn’t a good move if you don’t need the performance.

1 Like

I want to code for the Daisy in Rust, and I gave this starter a try.

At first, I struggled to see how to get it working, because the instructions use an ST-link programmer, openOCD, and GDB on a mac. Well, I don’t have a mac or a st-link, and I wanted to get something going that was simple (like the daisy web programmer).

It took a lot of googling and experimentation, but I’ve got it working! I want to report back how you can make this work from linux mint with just a USB cable.

Disclaimer: I have no idea what I’m doing with rust, or with embedded development. However, I’m pretty sure it’s actually working.

Here’s what I did. First get the rust toolchain:

rustup target add thumbv7em-none-eabihf

Then get the daisy-bsp github repo:

cargo install cargo-generate

cargo generate \
    --git https://github.com/antoinevg/hello-daisy \
    --name your-project-name

(so far it’s the same as the original instructions)

Make sure you have dfu-util:

sudo apt-get install dfu-util

(or equivalent for non-ubuntu systems)

Then get bin-utils for cargo:

rustup component add llvm-tools-preview

cargo install cargo-binutils

In the cloned repo, compile as usual:

cargo build --release

But then you need to take the ELF binary and dump out the compiled code in a raw binary format:

cargo objcopy --release -- -O binary hello-daisy.bin

At this point, put the daisy seed into DFU mode by holding boot and hitting reset. You should see two entries with the same ID (0483:df11) when running dfu-util --list

Then you can flash the binary-dump file with:

dfu-util -a 0 -s 0x08000000:leave -D hello-daisy.bin

That’s it!

I’ve only tested with variants on the basic blinker example in the BSP repo, but I have the toolchain going at this point so I’m pretty confident :slight_smile:

Next up is audio examples from the BSP repo (EDIT: working!), then figuring out how to access the hardware on the daisy pod.

Massive thanks to @antoine antoine for writing the BSP. I’m excited to see where this goes.

6 Likes

Something about :leave in dfu-util was causing issues with the daisy. I was getting audio errors until I stopped doing that and started hitting the hardware RESET button. I’m now flashing without it, which prevents the dfu-util error that usually show up when finishing flashing.

So, uh, if you’re trying this and can’t get any sound, or get weird and inconsistent errors when flashing, try resetting it with the hardware button after flashing.

1 Like

I’ve got a Daisy Pod on order and I’m using it as an excuse to learn Rust. What’s the best way to get started, assuming I know a lot of digital signal processing and have been programming in other languages for years?

1 Like

Probably taking a look at daisy_bsp and libdaisy_rust on github, and/or asking questions on the #rust channel on the daisy slack.

For general rust embedded, there’s a work-in-progress book. You should also look into embeddded_hal.

… at least that’s what I’d suggest. I’m pretty new to both rust and embedded dev.

1 Like

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.