Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I2S write future never completes in examples/stm32f4/.../i2s_dma.rs #3681

Open
vinsynth opened this issue Dec 23, 2024 · 6 comments
Open

I2S write future never completes in examples/stm32f4/.../i2s_dma.rs #3681

vinsynth opened this issue Dec 23, 2024 · 6 comments

Comments

@vinsynth
Copy link
Contributor

I'm using an stm32f411ceu6 chip. Having replaced the necessary chip definitions in .cargo/config.toml and Cargo.toml (such that the blinky.rs program compiles and runs as expected), and remapped pins & DMA streams/channels as necessary, I find that i2s.write(...) in i2s_dma.rs never completes. Here's my slightly modified code in case anyone can try this on their own chip:

#![no_std]
#![no_main]

use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::i2s::{Config, I2S};
use embassy_stm32::time::Hertz;
use {defmt_rtt as _, panic_probe as _};

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
    let p = embassy_stm32::init(Default::default());
    info!("Hello World!");

    let mut dma_buffer = [0x00_u16; 128];
    let mut i2s = I2S::new_txonly(
        p.SPI3,
        p.PB5,  // sd
        p.PA15, // ws
        p.PB3,  // ck
        p.PB10, // mck
        p.DMA1_CH7,
        &mut dma_buffer,
        Hertz(48_000),
        Config::default(),
    );

    for i in 0_u16.. {
        i2s.write(&mut [i * 2; 64]).await.ok();
        defmt::panic!("if this message is reached, the write function returned!");
        i2s.write(&mut [i * 2 + 1; 64]).await.ok();
    }
}

i2s.write(...) still doesn't complete when I2S::new_txonly_nomck(...) is used instead. I'm connecting the pins to a PCM5102A chip that has worked with other i2s drivers. Is there something I'm missing with my configuration, or is this a library issue?

@elagil
Copy link
Contributor

elagil commented Jan 2, 2025

i2s.start() is never called, and I think it should.

@elagil
Copy link
Contributor

elagil commented Jan 2, 2025

Another thing I noticed is that I2S calculates its clock based on APB1 clock (default 42 MHz) which usually doesn't match the PLLI2S clock. This needs to be fixed at some point.

@vinsynth
Copy link
Contributor Author

vinsynth commented Jan 2, 2025

I tried calling i2s.start() before the write statement; the write future still doesn't complete. Calling it after the write statement also doesn't work (it would never make it there anyhow). I also thought of storing the write future to a temporary variable, then calling i2s.start(), then awaiting the temporary future, but that uses multiple mutable borrows at once, which rust of course doesn't allow. Unless there's another place to call i2s.start() that I'm missing, that function has no effect here.

@elagil
Copy link
Contributor

elagil commented Jan 2, 2025

By default, plli2s is None, so you have no clock for the peripheral.
Not sure how the example ever worked like this.

Try something like

    let mut peripheral_config = embassy_stm32::Config::default();
    {
        // Uses a 25 MHz external oscillator.
        use embassy_stm32::rcc::*;
        peripheral_config.rcc.hse = Some(Hse {
            freq: Hertz(25_000_000),
            mode: HseMode::Oscillator,
        });
        peripheral_config.rcc.sys = Sysclk::PLL1_P;

        peripheral_config.rcc.ahb_pre = AHBPrescaler::DIV1;
        peripheral_config.rcc.apb1_pre = APBPrescaler::DIV2;
        peripheral_config.rcc.apb2_pre = APBPrescaler::DIV1;

        peripheral_config.rcc.mux.clk48sel = mux::Clk48sel::PLL1_Q;

        peripheral_config.rcc.pll_src = PllSource::HSE;
        peripheral_config.rcc.pll = Some(Pll {
            prediv: PllPreDiv::DIV25,
            mul: PllMul::MUL336,
            divp: Some(PllPDiv::DIV4),
            divq: Some(PllQDiv::DIV7),
            divr: None,
        });

        peripheral_config.rcc.plli2s = Some(Pll { // <- The relevant section
            prediv: PllPreDiv::DIV25,
            mul: PllMul::MUL384,
            divp: None,
            divq: None,
            divr: Some(PllRDiv::DIV5),
        });

        peripheral_config.enable_debug_during_sleep = true;
    }
    let p = embassy_stm32::init(peripheral_config);

@vinsynth
Copy link
Contributor Author

vinsynth commented Jan 4, 2025

Aha, that did it! The default clock configuration must've changed after the example was created. I can create a PR to update the example.

@elagil
Copy link
Contributor

elagil commented Jan 4, 2025

Great! I could also add it here: #3716

It fixes the broken clock setup on F4 (but not other series chips, they are also broken).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants