embedded-audio-core 0.2.0

no_std duty-modulated PWM audio: effect banks, tiered DSP, and mixing for Cortex-M
Documentation
# Embassy & Hardware Peripherals Integration Guide

This directory contains examples for driving hardware peripherals (DAC, PWM, I2S/SAI) using `embedded-audio` and `embassy-stm32` with DMA on microcontrollers such as the **STM32U585CIU6**.

## Running host examples

```bash
cargo run --example dac_dma_stream
```

---

## STM32U585CIU6 Embassy Async DMA Driver Example

The snippet below demonstrates continuous async DMA streaming to **DAC1** triggered by **TIM6** on an STM32U585 microcontroller using Embassy:

```rust
//! Embassy STM32U585CIU6 Audio Output Example with `embedded-audio`
//!
//! Demonstrates streaming synthesized wavetables and audio engine ticks
//! via DMA to DAC, PWM, or SAI peripherals on STM32U585.

#![no_std]
#![no_main]

use defmt::info;
use defmt_rtt as _;
use panic_probe as _;

use embassy_executor::Spawner;
use embassy_stm32::dac::{DacChannel, Value};
use embassy_stm32::peripherals::GPDMA1_CH0;
use embassy_stm32::rcc::{LsConfig, enable_and_reset, mux};
use embassy_stm32::time::Hertz;
use embassy_stm32::timer::low_level::{MasterMode, RoundTo, Timer};
use embassy_stm32::triggers::TIM6_TRGO;
use embassy_stm32::{Config, bind_interrupts, dma};

use embedded_audio::prelude::*;

bind_interrupts!(struct Irqs {
    GPDMA1_CHANNEL0 => dma::InterruptHandler<GPDMA1_CH0>;
});

/// Sample rate for audio synthesis (16 kHz)
const SAMPLE_RATE_HZ: u32 = 16_000;
/// Size of each DMA half-buffer (samples per channel chunk)
const DMA_HALF_SIZE: usize = 256;

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
    info!("Initializing STM32U585CIU6 Audio Engine with Embassy...");

    let mut config = Config::default();
    config.rcc.ls = LsConfig::default_lsi();
    config.rcc.mux.dac1sel = mux::Dacsel::Lsi;
    let p = embassy_stm32::init(config);

    // 1. Setup embedded-audio Engine & Wavetables
    let mut engine = AudioEngine::from_sample_rate(SAMPLE_RATE_HZ, 4095, DutyMode::Linear);

    // Custom wavetable using fixed-point generator:
    let custom_wavetable = generate_wavetable_fixed(|idx| {
        if idx < 128 { (idx as i16 - 64) as i8 } else { (191 - idx as i16) as i8 }
    });

    let adsr = AdsrSpec {
        attack_ms: 20,
        decay_ms: 100,
        sustain_q8: 180,
        release_ms: 300,
    };
    engine.play_wavetable(&custom_wavetable, 440, adsr).unwrap();
    engine.play_wavetable(&TRIANGLE_TABLE, 554, adsr).unwrap();

    // 2. Setup Peripheral & DMA (DAC1 with TIM6 trigger on PA4)
    let mut dac = DacChannel::new_triggered(p.DAC1, p.GPDMA1_CH0, TIM6_TRGO, Irqs, p.PA4);
    enable_and_reset::<embassy_stm32::peripherals::TIM6>();

    let timer = Timer::new(p.TIM6);
    timer.set_frequency(Hertz(SAMPLE_RATE_HZ), RoundTo::Faster);
    timer.set_master_mode(MasterMode::Update);
    timer.start();

    // 3. Double-Buffered Async DMA Audio Pump
    let mut dma_pump = DmaDoubleBuffer::<u16, DMA_HALF_SIZE>::new();

    loop {
        // Get next buffer half and fill with 12-bit DAC values (0..=4095)
        let buf = dma_pump.swap_and_get_next();
        engine.fill_dac_u12_buffer(buf);

        let dac_values: &[Value] = unsafe {
            core::slice::from_raw_parts(buf.as_ptr() as *const Value, buf.len())
        };

        // Await DMA transfer completion for active buffer chunk
        dac.write(dac_values).await;
    }
}
```