Documentation / Integrate / Rust examples

Rust examples

Real excerpts from the buildable STM32G474 targets. The linked source files contain complete imports, clock setup, interrupt bindings, and error handling.

Configure classical CAN

FDCAN2 is wired to PB12/PB13. Both nodes must use the same nominal bit rate.

let mut can = can::CanConfigurator::new(
    p.FDCAN2,
    p.PB12,
    p.PB13,
    Irqs,
);
can.set_bitrate(500_000);
can.properties().set_standard_filter(
    StandardFilterSlot::_0,
    StandardFilter::accept_all_into_fifo0(),
);
let mut can: Can = can.into_normal_mode();

Send a frame and await an echo

let data = [0xCA, 0xFE, seq, 0x00];
let frame = Frame::new_standard(0x123, &data).unwrap();
can.write(&frame).await;

match with_timeout(Duration::from_millis(700), can.read()).await {
    Ok(Ok(envelope)) => {
        let is_echo = match envelope.frame.id() {
            embedded_can::Id::Standard(id) => id.as_raw() == 0x456,
            embedded_can::Id::Extended(_) => false,
        };
        if is_echo
            && envelope.frame.data().get(0..3) == Some(&data[0..3])
        {
            info!("echo received");
        } else {
            warn!("unexpected response");
        }
    }
    Ok(Err(error)) => error!("RX error: {:?}", error),
    Err(_) => warn!("response timeout"),
}

Receive and reply

loop {
    match can.read().await {
        Ok(envelope) => {
            let echo = Frame::new_standard(
                0x456,
                envelope.frame.data(),
            ).unwrap();
            can.write(&echo).await;
        }
        Err(error) => error!("RX error: {:?}", error),
    }
}

Configure CAN FD with BRS

The FD examples route FDCAN to the 170 MHz PCLK1 source, then configure separate arbitration and data rates.

config.rcc.mux.fdcansel = Fdcansel::PCLK1;
let p = embassy_stm32::init(config);

let mut can = can::CanConfigurator::new(
    p.FDCAN2,
    p.PB12,
    p.PB13,
    Irqs,
);
can.set_bitrate(1_000_000);
can.set_fd_data_bitrate(2_000_000, true); // TDC enabled
let mut can: Can = can.into_normal_mode();

Build a 64-byte FD frame

let mut data = [0xAAu8; 64];
data[0..4].copy_from_slice(&seq.to_le_bytes());
data[4..8].copy_from_slice(&tx_ticks.to_le_bytes());

let header = Header::new_fd(
    embedded_can::StandardId::new(0x100).unwrap().into(),
    64,
    false, // data frame, not RTR
    true,  // enable bit-rate switching
);
let frame = FdFrame::new(header, &data).unwrap();
can.write_fd(&frame).await;

Check every CAN target

cd firmware/rust_bringup/stm32g4
cargo check --release \
  --bin can_tx --bin can_rx \
  --bin can_fd_tx --bin can_fd_rx
API stability

These examples use the Embassy revision pinned by the repository submodule. Upstream Embassy CAN APIs may differ, so build against the checkout rather than copying the snippets into an unrelated dependency version.