embassy-rs/embassy · error
QSPI transfer width exceeds configured IO lanes
Error message
QSPI transfer width exceeds configured IO lanes
What it means
The QSPI controller only has as many IO lines wired up as configured (1, 2 or 4). assert_transfer_widths() checks the instruction, address, and data widths of every transaction against that maximum and panics if any of them requests more lanes than exist. This keeps bus configuration consistent with the hardware wiring.
Solutions
- Set transaction iwidth/awidth/dwidth to widths <= the configured max_transfer_width() (check your Qspi wiring/pins).
- Reconfigure the Qspi instance with four IO pins if quad transfers are genuinely required.
- For memory-mapped mode, ensure the memory configuration's widths match the wired lanes before calling enable_memory_map().
Example fix
// before (2-line QSPI) let mut t = TransferConfig::new(cmd, QspiWidth::SING, None, QspiWidth::NONE, QspiWidth::FOUR); qspi.blocking_command(t); // after let mut t = TransferConfig::new(cmd, QspiWidth::SING, None, QspiWidth::NONE, QspiWidth::DUAL); qspi.blocking_command(t);
Defensive patterns
Strategy: validation
Validate before calling
fn widths_fit(transaction: &TransferConfig, max: QspiWidth) -> bool {
transaction.iwidth <= max && transaction.awidth <= max && transaction.dwidth <= max
} Prevention
- Derive allowed widths from your board's wired QSPI pins; document them next to the driver setup.
- Never copy quad-mode transaction configs onto single/dual-wired boards.
- Validate TransferConfigs in one helper before handing them to any QSPI call.
When it happens
Trigger: Submitting a TransferConfig with iwidth, awidth, or dwidth of FOURBITS (or any width above the configured max) to a Qspi set up for one- or two-line mode, via blocking_command/blocking_read/blocking_write, setup_auto_poll, or entering memory-mapped mode (enable_memory_map).
Common situations: Copy-pasting a quad-mode flash driver config onto a board wired for dual/single SPI; flash chip datasheet commands (e.g. quad fast read) used without the quad IO lanes being configured; enabling memory-mapped mode with a quad transaction on a dual-lane setup.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- QSPI address can't be sent with an address width of NONE
- QSPI address too large to be represented with the given…
- QSPI address is not set, so the address width should be NONE
- QSPI data must be at least one byte
- QSPI data can't be sent with a data width of NONE
AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10).
Data as JSON: /api/errors/9d7b7b076367bef0.
Report an issue: GitHub.
Appendix: source
Thrown at embassy-stm32/src/qspi/mod.rs:210
self.bk1d0.as_ref(),
self.bk1d1.as_ref(),
self.bk1d2.as_ref(),
self.bk1d3.as_ref(),
);
let bk2 = bank_max(
self.bk2d0.as_ref(),
self.bk2d1.as_ref(),
self.bk2d2.as_ref(),
self.bk2d3.as_ref(),
);
bk1.max(bk2)
}
/// Panic if any width in `transaction` exceeds the wired-up IO lanes.
fn assert_transfer_widths(&self, transaction: &TransferConfig) {
let max = self.max_transfer_width();
if transaction.iwidth > max || transaction.awidth > max || transaction.dwidth > max {
panic!("QSPI transfer width exceeds configured IO lanes");
}
}
/// Do a QSPI command.
pub fn blocking_command(&mut self, transaction: TransferConfig) {
self.setup_command(transaction);
while !T::REGS.sr().read().tcf() {}
T::REGS.fcr().modify(|v| v.set_ctcf(true));
}
/// Blocking read data.
pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) {
#[cfg(not(stm32h7))]
T::REGS.cr().modify(|v| v.set_dmaen(false));
self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len()));
let current_ar = T::REGS.ar().read().address();View on GitHub (pinned to 463a07b963)