From 1a1a43276e0c19cee73d468414753a27cdf91fb9 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Wed, 16 Jul 2025 09:24:08 +0100 Subject: [PATCH 01/20] Overhaul QSPI module --- hal/src/peripherals/qspi.rs | 294 ++++++++++++++++++++++++++---------- 1 file changed, 215 insertions(+), 79 deletions(-) diff --git a/hal/src/peripherals/qspi.rs b/hal/src/peripherals/qspi.rs index 3c11d684e6be..8752cbfb6464 100644 --- a/hal/src/peripherals/qspi.rs +++ b/hal/src/peripherals/qspi.rs @@ -1,17 +1,61 @@ +//! QSPI +//! +//! This module provides an interface to the QSPI peripheral, +//! which is mainly used for communicating with external flash chips. +//! +//! The QSPI peripheral supports 2 different modes: +//! * Oneshot (Default) - In this mode, the QSPI peripheral simply +//! sends and receives data to and from the flash chip. Quad-SPI +//! communication is only used for reading and writing data to the +//! flash chip. Other commands (Like erase, ID) are done in Single-SPI +//! mode. +//! * XIP (eXecute In Place) - In this mode, the processor can execute +//! code directly off the flash chip. Quad-SPI communication is forced +//! +//! ## IMPORTANT +//! When using QSPI commands, the QSPI peripheral will STALL the CPU. +//! Therefore, you should select the highest SPI communication frequency +//! that the flash chip supports. +//! +//! Erasing a chip can take sometimes up to 1-2 minutes, in which time +//! the CPU is stalled. Bare this in mind when issuing long-running commands +//! with the watchdog enabled! +//! +//! ## Using the QSPI peripheral +//! ``` +//! let pins = Pins::new(pac_peripherals.port); +//! let ahb_qspi = clocks.ahbs.qspi; +//! let (qspi, gclk0) = QspiBuilder::new( +//! pins.sck, +//! pins.cs, +//! pins.data0, +//! pins.data1, +//! pins.data2, +//! pins.data3, +//! ) +//! .with_freq(50_000_000) +//! .with_mode(atsamd_hal::qspi::QspiMode::_0) +//! .build(pac_peripherals.qspi, ahb_qspi, gclk0) +//! .unwrap(); +//! // QSPI is now in oneshot mode. +//! // ... +//! // Switch qspi to XIP mode if required +//! let qspi_xip = qspi.into_xip(); +//! ``` + use crate::{ - gpio::{AlternateH, AnyPin, PA08, PA09, PA10, PA11, PB10, PB11, Pin}, - pac::qspi::instrframe, - pac::{self, Mclk}, + clock::v2::{ + Enabled, Source, + ahb::AhbClk, + gclk::{EnabledGclk, EnabledGclk0, Gclk, Gclk0Id, Gclk0Io, GclkSourceId}, + types::Qspi as QspiClock, + }, + gpio::{AlternateH, PA08, PA09, PA10, PA11, PB10, PB11, Pin}, + pac::{self, qspi::instrframe}, + typelevel::{Decrement, Increment, PrivateDecrement, PrivateIncrement}, }; use core::marker::PhantomData; -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Error { - /// The command you selected cannot be performed by this function - CommandFunctionMismatch, -} - /// Qspi used for read/write of fixed-size octet buffers pub struct OneShot; /// Qspi is memory-mapped as read/execute @@ -19,6 +63,8 @@ pub struct XIP; pub struct Qspi { qspi: pac::Qspi, + _ahb: AhbClk, + gclk0_freq: u32, _sck: Pin, _cs: Pin, _io0: Pin, @@ -28,43 +74,129 @@ pub struct Qspi { _mode: PhantomData, } -impl Qspi { - /// Enable the clocks for the qspi peripheral in single data rate mode - /// assuming 120mhz system clock, for 4mhz spi mode 0 operation. - #[allow(clippy::too_many_arguments)] +/// QSPI signal operating modes +pub enum QspiMode { + /// * Shift SCK Edge: Falling, + /// * Capture SCK Edge: Falling + /// * SCK inactive level: Low + _0, + /// * Shift SCK Edge: Rising, + /// * Capture SCK Edge: Rising + /// * SCK inactive level: Low + _1, + /// * Shift SCK Edge: Rising, + /// * Capture SCK Edge: Rising + /// * SCK inactive level: High + _2, + /// * Shift SCK Edge: Falling, + /// * Capture SCK Edge: Falling + /// * SCK inactive level: High + _3, +} + +pub struct QspiBuilder { + sck: Pin, + cs: Pin, + io0: Pin, + io1: Pin, + io2: Pin, + io3: Pin, + freq: Option, + mode: Option, +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum QspiError { + /// No SPI Frequency provided + NoFreq, + /// No QSPI signal mode provided + NoMode, + /// Target SPI frequency could + /// not be achieved with the provided + /// CPU speed + SpiFreqNotValid, + /// The command you selected cannot be performed by this function + CommandFunctionMismatch, +} + +impl QspiBuilder { pub fn new( - mclk: &mut Mclk, - qspi: pac::Qspi, - _sck: impl AnyPin, - _cs: impl AnyPin, - _io0: impl AnyPin, - _io1: impl AnyPin, - _io2: impl AnyPin, - _io3: impl AnyPin, - ) -> Qspi { - mclk.apbcmask().modify(|_, w| w.qspi_().set_bit()); - // Enable the clocks for the qspi peripheral in single data rate mode. - mclk.ahbmask().modify(|_, w| { - w.qspi_().set_bit(); - w.qspi_2x_().clear_bit() - }); + sck: impl Into>, + cs: impl Into>, + io0: impl Into>, + io1: impl Into>, + io2: impl Into>, + io3: impl Into>, + ) -> Self { + Self { + sck: sck.into(), + cs: cs.into(), + io0: io0.into(), + io1: io1.into(), + io2: io2.into(), + io3: io3.into(), + freq: None, + mode: None, + } + } + + /// Sets the target frequency of the SPI communication + pub fn with_freq(mut self, freq: u32) -> Self { + self.freq = Some(freq); + self + } - let _sck = _sck.into().into_alternate(); - let _cs = _cs.into().into_alternate(); - let _io0 = _io0.into().into_alternate(); - let _io1 = _io1.into().into_alternate(); - let _io2 = _io2.into().into_alternate(); - let _io3 = _io3.into().into_alternate(); + /// Sets the SPI operation mode + pub fn with_mode(mut self, mode: QspiMode) -> Self { + self.mode = Some(mode); + self + } + /// Initialize the QSPI peripheral, and start communication + /// in regular SPI mode + pub fn build( + self, + qspi: pac::Qspi, + ahb: AhbClk, + gclk0: EnabledGclk, + ) -> Result<(Qspi, EnabledGclk), QspiError> { + Qspi::new(qspi, ahb, gclk0, self) + } +} + +impl Qspi { + pub(crate) fn new( + qspi: pac::Qspi, + ahb: AhbClk, + gclk0: Enabled, S>, + builder: QspiBuilder, + ) -> Result<(Qspi, Enabled, S::Inc>), QspiError> { + let targ_freq = builder.freq.ok_or(QspiError::NoFreq)?; + let mode = builder.mode.ok_or(QspiError::NoMode)?; + let gclk0_freq = gclk0.freq().to_Hz(); + // Ensure that the target SPI Freq can be achieved + if gclk0_freq % targ_freq != 0 { + return Err(QspiError::SpiFreqNotValid); + } + // Divider must be 0-254 + let div = gclk0_freq / targ_freq; + if div > 254 || div == 0 { + return Err(QspiError::SpiFreqNotValid); + } qspi.ctrla().write(|w| w.swrst().set_bit()); qspi.baud().write(|w| unsafe { - // TODO get system clock value instead of hardcoding - //(120_000_000u32 / 4_000_000u32) = 30 = BAUD + 1 - // BAUD = 29 - w.baud().bits(29); // 4Mhz - // SPI MODE 0 - w.cpol().clear_bit(); - w.cpha().clear_bit() + w.baud().bits(div as u8 - 1); + let cpol = match mode { + QspiMode::_0 | QspiMode::_1 => false, + QspiMode::_2 | QspiMode::_3 => true, + }; + let cpha = match mode { + QspiMode::_0 | QspiMode::_2 => false, + QspiMode::_1 | QspiMode::_3 => true, + }; + w.cpol().bit(cpol); + w.cpha().bit(cpha) }); qspi.ctrlb().write(|w| { @@ -76,27 +208,32 @@ impl Qspi { qspi.ctrla().modify(|_, w| w.enable().set_bit()); - Self { - qspi, - _sck, - _cs, - _io0, - _io1, - _io2, - _io3, - _mode: PhantomData, - } + Ok(( + Self { + qspi, + _ahb: ahb, + gclk0_freq, + _sck: builder.sck, + _cs: builder.cs, + _io0: builder.io0, + _io1: builder.io1, + _io2: builder.io2, + _io3: builder.io3, + _mode: PhantomData, + }, + gclk0.inc(), + )) } /// Run a generic command that neither takes nor receives data - pub fn run_command(&self, command: Command) -> Result<(), Error> { + pub fn run_command(&self, command: Command) -> Result<(), QspiError> { match command { //TODO verify this list of commands Command::WriteEnable | Command::WriteDisable | Command::Reset | Command::EnableReset => (), - _ => return Err(Error::CommandFunctionMismatch), + _ => return Err(QspiError::CommandFunctionMismatch), } let tfm = TransferMode { @@ -110,7 +247,7 @@ impl Qspi { } /// Run one of the read commands - pub fn read_command(&self, command: Command, response: &mut [u8]) -> Result<(), Error> { + pub fn read_command(&self, command: Command, response: &mut [u8]) -> Result<(), QspiError> { match command { //TODO verify this list of commands Command::Read @@ -118,7 +255,7 @@ impl Qspi { | Command::ReadId | Command::ReadStatus | Command::ReadStatus2 => (), - _ => return Err(Error::CommandFunctionMismatch), + _ => return Err(QspiError::CommandFunctionMismatch), } let tfm = TransferMode { @@ -133,14 +270,14 @@ impl Qspi { } /// Run one of the write commands - pub fn write_command(&self, command: Command, data: &[u8]) -> Result<(), Error> { + pub fn write_command(&self, command: Command, data: &[u8]) -> Result<(), QspiError> { match command { //TODO verify this list of commands Command::PageProgram | Command::QuadPageProgram | Command::WriteStatus | Command::WriteStatus2 => (), - _ => return Err(Error::CommandFunctionMismatch), + _ => return Err(QspiError::CommandFunctionMismatch), } let tfm = TransferMode { @@ -155,7 +292,7 @@ impl Qspi { } /// Run one of the erase commands - pub fn erase_command(&self, command: Command, address: u32) -> Result<(), Error> { + pub fn erase_command(&self, command: Command, address: u32) -> Result<(), QspiError> { match command { //TODO verify this list of commands Command::EraseSector | Command::EraseBlock => { @@ -177,7 +314,7 @@ impl Qspi { self.run_read_instruction(command, tfm, 0, &mut [], true); } } - _ => return Err(Error::CommandFunctionMismatch), + _ => return Err(QspiError::CommandFunctionMismatch), } Ok(()) @@ -231,6 +368,8 @@ impl Qspi { Qspi:: { qspi: self.qspi, + _ahb: self._ahb, + gclk0_freq: self.gclk0_freq, _sck: self._sck, _cs: self._cs, _io0: self._io0, @@ -243,12 +382,15 @@ impl Qspi { /// Return the consumed pins and the Qspi peripheral /// - /// Order: `(qspi, sck, cs, io0, io1, io2, io3)` + /// Order: `(qspi, apb, ahb, gclk0, sck, cs, io0, io1, io2, io3)` #[allow(clippy::type_complexity)] - pub fn free( + pub fn free( self, + gclk0: EnabledGclk0, ) -> ( pac::Qspi, + AhbClk, + EnabledGclk0, Pin, Pin, Pin, @@ -257,7 +399,15 @@ impl Qspi { Pin, ) { ( - self.qspi, self._sck, self._cs, self._io0, self._io1, self._io2, self._io3, + self.qspi, + self._ahb, + gclk0.dec(), + self._sck, + self._cs, + self._io0, + self._io1, + self._io2, + self._io3, ) } } @@ -271,6 +421,8 @@ impl Qspi { Qspi:: { qspi: self.qspi, + _ahb: self._ahb, + gclk0_freq: self.gclk0_freq, _sck: self._sck, _cs: self._cs, _io0: self._io0, @@ -363,22 +515,6 @@ impl Qspi { } } } - - /// Set the clock divider, relative to the main clock - /// - /// This fn safely subtracts 1 from your input value as the underlying fn is - /// SCK Baud = MCKL / (value + 1) - /// - /// ex if Mclk is 120mhz - /// value 0 is reduced to 0 results in 120mhz clock - /// value 1 is reduced to 0 results in 120mhz clock - /// value 2 is reduced to 1 results in 60mhz clock - pub fn set_clk_divider(&mut self, value: u8) { - // The baud register is divisor - 1 - self.qspi - .baud() - .write(|w| unsafe { w.baud().bits(value.saturating_sub(1)) }); - } } #[derive(Default, Debug, Copy, Clone)] From 3d48888aa834351619290655e9dbf2632096dace Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Wed, 16 Jul 2025 12:23:40 +0100 Subject: [PATCH 02/20] QSPI - Enable user selection of scramble mode --- hal/src/peripherals/qspi.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/hal/src/peripherals/qspi.rs b/hal/src/peripherals/qspi.rs index 8752cbfb6464..f0a7accf1eb2 100644 --- a/hal/src/peripherals/qspi.rs +++ b/hal/src/peripherals/qspi.rs @@ -103,6 +103,7 @@ pub struct QspiBuilder { io3: Pin, freq: Option, mode: Option, + scramble_mode: Option<(u32, bool)>, } #[derive(Debug, Clone, Copy, Eq, PartialEq)] @@ -138,6 +139,7 @@ impl QspiBuilder { io3: io3.into(), freq: None, mode: None, + scramble_mode: None, } } @@ -153,6 +155,20 @@ impl QspiBuilder { self } + /// Enables the optional scramble feature of QSPI + /// + /// * Key - 32 bit key to use for the scramble + /// * Random - Enable if the hardware based extra key should be + /// applied - This means that a QSPI chip will appear scrambled, + /// even to another processor. Disabling the random mode ensures + /// that whilst the QSPI chip itself is scrambled, it is only using + /// the user provided key - Thus allowing other processors with the + /// same key to read the QSPI chip + pub fn with_scramble(mut self, key: u32, random: bool) -> Self { + self.scramble_mode = Some((key, random)); + self + } + /// Initialize the QSPI peripheral, and start communication /// in regular SPI mode pub fn build( @@ -206,6 +222,15 @@ impl Qspi { w.datalen()._8bits() }); + // Enable scrambling if the user requested it + if let Some((key, random_en)) = builder.scramble_mode { + qspi.scrambctrl().write(|w| { + w.enable().set_bit(); + w.randomdis().bit(!random_en) + }); + qspi.scrambkey().write(|w| unsafe { w.key().bits(key) }); + } + qspi.ctrla().modify(|_, w| w.enable().set_bit()); Ok(( From bd8f324115bd69077b0f169a5a329e4305ce3bb0 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Thu, 17 Jul 2025 05:46:31 +0100 Subject: [PATCH 03/20] Add APB clock to QSPI --- hal/src/peripherals/qspi.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/hal/src/peripherals/qspi.rs b/hal/src/peripherals/qspi.rs index f0a7accf1eb2..5bdb6b86bf80 100644 --- a/hal/src/peripherals/qspi.rs +++ b/hal/src/peripherals/qspi.rs @@ -12,6 +12,12 @@ //! * XIP (eXecute In Place) - In this mode, the processor can execute //! code directly off the flash chip. Quad-SPI communication is forced //! +//! This module does not come with an API for easily communicating with +//! flash chips, due to the varying differences in timings and commands. +//! +//! For an example of actually communicating (R/W) with a QSPI flash chip, +//! look at the pygamer BSP QSPI example. +//! //! ## IMPORTANT //! When using QSPI commands, the QSPI peripheral will STALL the CPU. //! Therefore, you should select the highest SPI communication frequency @@ -25,6 +31,7 @@ //! ``` //! let pins = Pins::new(pac_peripherals.port); //! let ahb_qspi = clocks.ahbs.qspi; +//! let apb_qspi = clocks.apbs.qspi; //! let (qspi, gclk0) = QspiBuilder::new( //! pins.sck, //! pins.cs, @@ -35,7 +42,7 @@ //! ) //! .with_freq(50_000_000) //! .with_mode(atsamd_hal::qspi::QspiMode::_0) -//! .build(pac_peripherals.qspi, ahb_qspi, gclk0) +//! .build(pac_peripherals.qspi, ahb_qspi, apb_qspi, gclk0) //! .unwrap(); //! // QSPI is now in oneshot mode. //! // ... @@ -47,6 +54,7 @@ use crate::{ clock::v2::{ Enabled, Source, ahb::AhbClk, + apb::ApbClk, gclk::{EnabledGclk, EnabledGclk0, Gclk, Gclk0Id, Gclk0Io, GclkSourceId}, types::Qspi as QspiClock, }, @@ -64,6 +72,7 @@ pub struct XIP; pub struct Qspi { qspi: pac::Qspi, _ahb: AhbClk, + _apb: ApbClk, gclk0_freq: u32, _sck: Pin, _cs: Pin, @@ -175,9 +184,10 @@ impl QspiBuilder { self, qspi: pac::Qspi, ahb: AhbClk, + apb: ApbClk, gclk0: EnabledGclk, ) -> Result<(Qspi, EnabledGclk), QspiError> { - Qspi::new(qspi, ahb, gclk0, self) + Qspi::new(qspi, ahb, apb, gclk0, self) } } @@ -185,6 +195,7 @@ impl Qspi { pub(crate) fn new( qspi: pac::Qspi, ahb: AhbClk, + apb: ApbClk, gclk0: Enabled, S>, builder: QspiBuilder, ) -> Result<(Qspi, Enabled, S::Inc>), QspiError> { @@ -237,6 +248,7 @@ impl Qspi { Self { qspi, _ahb: ahb, + _apb: apb, gclk0_freq, _sck: builder.sck, _cs: builder.cs, @@ -394,6 +406,7 @@ impl Qspi { Qspi:: { qspi: self.qspi, _ahb: self._ahb, + _apb: self._apb, gclk0_freq: self.gclk0_freq, _sck: self._sck, _cs: self._cs, @@ -415,6 +428,7 @@ impl Qspi { ) -> ( pac::Qspi, AhbClk, + ApbClk, EnabledGclk0, Pin, Pin, @@ -426,6 +440,7 @@ impl Qspi { ( self.qspi, self._ahb, + self._apb, gclk0.dec(), self._sck, self._cs, @@ -447,6 +462,7 @@ impl Qspi { Qspi:: { qspi: self.qspi, _ahb: self._ahb, + _apb: self._apb, gclk0_freq: self.gclk0_freq, _sck: self._sck, _cs: self._cs, From d7702e23f381048c495f3f191b20c1d49e94af74 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Fri, 18 Jul 2025 12:31:08 +0100 Subject: [PATCH 04/20] Fix examples and remove QSPI builder in the BSPs --- boards/metro_m4/src/lib.rs | 26 ------------------------ boards/pygamer/examples/qspi.rs | 36 ++++++++++++++++++++++++--------- boards/pygamer/src/pins.rs | 8 -------- hal/src/peripherals/qspi.rs | 8 ++++++++ 4 files changed, 34 insertions(+), 44 deletions(-) diff --git a/boards/metro_m4/src/lib.rs b/boards/metro_m4/src/lib.rs index 7e972ef621ab..51ab81cc13a1 100644 --- a/boards/metro_m4/src/lib.rs +++ b/boards/metro_m4/src/lib.rs @@ -267,32 +267,6 @@ pub fn spi_master( .enable() } -/// Convenience for setting up the onboard QSPI flash. -/// Enables the clocks for the QSPI peripheral in single data rate mode -/// assuming 120MHz system clock, for 4MHz QSPI mode 0 operation. -#[allow(clippy::too_many_arguments)] -pub fn qspi_master( - mclk: &mut Mclk, - qspi: pac::Qspi, - sclk: impl Into, - cs: impl Into, - data0: impl Into, - data1: impl Into, - data2: impl Into, - data3: impl Into, -) -> Qspi { - Qspi::new( - mclk, - qspi, - sclk.into(), - cs.into(), - data0.into(), - data1.into(), - data2.into(), - data3.into(), - ) -} - /// I2C pads for the labelled I2C peripheral /// /// You can use these pads with other, user-defined [`i2c::Config`]urations. diff --git a/boards/pygamer/examples/qspi.rs b/boards/pygamer/examples/qspi.rs index 212aed377f95..6921bfc72ec3 100644 --- a/boards/pygamer/examples/qspi.rs +++ b/boards/pygamer/examples/qspi.rs @@ -22,7 +22,9 @@ #![no_std] #![no_main] +use atsamd_hal::qspi::QspiBuilder; use bsp::{entry, hal, pac, Pins}; +use hal::clock::v2::{clock_system_at_reset, pclk::Pclk}; #[cfg(not(feature = "panic_led"))] use panic_halt as _; use pygamer as bsp; @@ -37,18 +39,36 @@ use pac::{CorePeripherals, Peripherals}; fn main() -> ! { let mut peripherals = Peripherals::take().unwrap(); let core = CorePeripherals::take().unwrap(); - let mut clocks = GenericClockController::with_internal_32kosc( + + + let (mut buses, clocks, tokens) = clock_system_at_reset( + peripherals.oscctrl, + peripherals.osc32kctrl, peripherals.gclk, - &mut peripherals.mclk, - &mut peripherals.osc32kctrl, - &mut peripherals.oscctrl, + peripherals.mclk, &mut peripherals.nvmctrl, ); - let mut delay = Delay::new(core.SYST, &mut clocks); + + let (mut delay, gclk0) = Delay::new_with_source(core.SYST, clocks.gclk0); let sets = Pins::new(peripherals.port).split(); - let mut flash = sets.flash.init(&mut peripherals.mclk, peripherals.qspi); + let apb_qspi = clocks.apbs.qspi; + let ahb_qspi = clocks.ahbs.qspi; + + let (mut flash, gclk0) = QspiBuilder::new( + sets.flash.sclk, + sets.flash.cs, + sets.flash.data0, + sets.flash.data1, + sets.flash.data2, + sets.flash.data3 + ) + // 48Mhz since this is as fast as the CPU runs after reset, + .with_freq(48_000_000) + .with_mode(qspi::QspiMode::_0) + .build(peripherals.qspi, ahb_qspi, apb_qspi, gclk0) + .unwrap(); // Startup delay. Can't find documented but Adafruit use 5ms delay.delay_ms(5u8); @@ -64,10 +84,6 @@ fn main() -> ! { flash.read_command(Command::ReadId, &mut read_buf).unwrap(); assert_eq!(read_buf, [0x17, 0x40, 0xc8]); - // 120MHz / 2 = 60mhz - // faster than 104mhz at 3.3v would require High Performance Mode - flash.set_clk_divider(2); - // Enable Quad SPI mode. Requires write enable. Check WIP. flash.run_command(Command::WriteEnable).unwrap(); flash.write_command(Command::WriteStatus2, &[0x02]).unwrap(); diff --git a/boards/pygamer/src/pins.rs b/boards/pygamer/src/pins.rs index 2f1f5de4c63c..7ce71a7e5a81 100644 --- a/boards/pygamer/src/pins.rs +++ b/boards/pygamer/src/pins.rs @@ -883,14 +883,6 @@ pub struct QSPIFlash { pub data3: QspiD3Reset, } -impl QSPIFlash { - pub fn init(self, mclk: &mut pac::Mclk, qspi: pac::Qspi) -> qspi::Qspi { - qspi::Qspi::new( - mclk, qspi, self.sclk, self.cs, self.data0, self.data1, self.data2, self.data3, - ) - } -} - /// Button pins pub struct Buttons { /// Button Latch diff --git a/hal/src/peripherals/qspi.rs b/hal/src/peripherals/qspi.rs index 5bdb6b86bf80..dd6f7f4fc651 100644 --- a/hal/src/peripherals/qspi.rs +++ b/hal/src/peripherals/qspi.rs @@ -130,6 +130,14 @@ pub enum QspiError { CommandFunctionMismatch, } +/// # QSPI Configuration Builder +/// +/// This structure contains methods that configures the QSPI module. +/// +/// Setting frequency [`Self::with_freq`] and SPI mode [`Self::with_mode`] +/// are required to get QSPI running, without calling these, the [`Self::build`] +/// function will return a [`QspiError`]. Other configuration options are +/// optional, so are not required impl QspiBuilder { pub fn new( sck: impl Into>, From 4de6791dba37c965f17663458f57ec1a832a1c63 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Fri, 18 Jul 2025 13:14:20 +0100 Subject: [PATCH 05/20] Simplify gclk types --- boards/pygamer/examples/qspi.rs | 10 +++++----- hal/src/peripherals/qspi.rs | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/boards/pygamer/examples/qspi.rs b/boards/pygamer/examples/qspi.rs index 6921bfc72ec3..acfa249befa9 100644 --- a/boards/pygamer/examples/qspi.rs +++ b/boards/pygamer/examples/qspi.rs @@ -57,11 +57,11 @@ fn main() -> ! { let ahb_qspi = clocks.ahbs.qspi; let (mut flash, gclk0) = QspiBuilder::new( - sets.flash.sclk, - sets.flash.cs, - sets.flash.data0, - sets.flash.data1, - sets.flash.data2, + sets.flash.sclk, + sets.flash.cs, + sets.flash.data0, + sets.flash.data1, + sets.flash.data2, sets.flash.data3 ) // 48Mhz since this is as fast as the CPU runs after reset, diff --git a/hal/src/peripherals/qspi.rs b/hal/src/peripherals/qspi.rs index dd6f7f4fc651..3609268b0b93 100644 --- a/hal/src/peripherals/qspi.rs +++ b/hal/src/peripherals/qspi.rs @@ -55,7 +55,7 @@ use crate::{ Enabled, Source, ahb::AhbClk, apb::ApbClk, - gclk::{EnabledGclk, EnabledGclk0, Gclk, Gclk0Id, Gclk0Io, GclkSourceId}, + gclk::{EnabledGclk0, Gclk, Gclk0Id, Gclk0Io, GclkSourceId}, types::Qspi as QspiClock, }, gpio::{AlternateH, PA08, PA09, PA10, PA11, PB10, PB11, Pin}, @@ -193,8 +193,8 @@ impl QspiBuilder { qspi: pac::Qspi, ahb: AhbClk, apb: ApbClk, - gclk0: EnabledGclk, - ) -> Result<(Qspi, EnabledGclk), QspiError> { + gclk0: EnabledGclk0, + ) -> Result<(Qspi, EnabledGclk0), QspiError> { Qspi::new(qspi, ahb, apb, gclk0, self) } } @@ -206,7 +206,7 @@ impl Qspi { apb: ApbClk, gclk0: Enabled, S>, builder: QspiBuilder, - ) -> Result<(Qspi, Enabled, S::Inc>), QspiError> { + ) -> Result<(Qspi, EnabledGclk0), QspiError> { let targ_freq = builder.freq.ok_or(QspiError::NoFreq)?; let mode = builder.mode.ok_or(QspiError::NoMode)?; let gclk0_freq = gclk0.freq().to_Hz(); From d579b2198137a2e7fcfdce6d78f8b179de870299 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Fri, 18 Jul 2025 13:17:19 +0100 Subject: [PATCH 06/20] Fix rstfmt --- boards/pygamer/examples/qspi.rs | 1 - hal/src/peripherals/qspi.rs | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/boards/pygamer/examples/qspi.rs b/boards/pygamer/examples/qspi.rs index acfa249befa9..b794d3403eae 100644 --- a/boards/pygamer/examples/qspi.rs +++ b/boards/pygamer/examples/qspi.rs @@ -40,7 +40,6 @@ fn main() -> ! { let mut peripherals = Peripherals::take().unwrap(); let core = CorePeripherals::take().unwrap(); - let (mut buses, clocks, tokens) = clock_system_at_reset( peripherals.oscctrl, peripherals.osc32kctrl, diff --git a/hal/src/peripherals/qspi.rs b/hal/src/peripherals/qspi.rs index 3609268b0b93..f2578cb72c84 100644 --- a/hal/src/peripherals/qspi.rs +++ b/hal/src/peripherals/qspi.rs @@ -188,6 +188,7 @@ impl QspiBuilder { /// Initialize the QSPI peripheral, and start communication /// in regular SPI mode + #[allow(clippy::type_complexity)] pub fn build( self, qspi: pac::Qspi, @@ -200,6 +201,7 @@ impl QspiBuilder { } impl Qspi { + #[allow(clippy::type_complexity)] pub(crate) fn new( qspi: pac::Qspi, ahb: AhbClk, From d6ede4a49487a214ca12591293ed6a7cadd30aac Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Fri, 18 Jul 2025 13:20:36 +0100 Subject: [PATCH 07/20] Fix doc comment indent --- hal/src/peripherals/qspi.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hal/src/peripherals/qspi.rs b/hal/src/peripherals/qspi.rs index f2578cb72c84..e92600dc5700 100644 --- a/hal/src/peripherals/qspi.rs +++ b/hal/src/peripherals/qspi.rs @@ -176,11 +176,11 @@ impl QspiBuilder { /// /// * Key - 32 bit key to use for the scramble /// * Random - Enable if the hardware based extra key should be - /// applied - This means that a QSPI chip will appear scrambled, - /// even to another processor. Disabling the random mode ensures - /// that whilst the QSPI chip itself is scrambled, it is only using - /// the user provided key - Thus allowing other processors with the - /// same key to read the QSPI chip + /// applied - This means that a QSPI chip will appear scrambled, + /// even to another processor. Disabling the random mode ensures + /// that whilst the QSPI chip itself is scrambled, it is only using + /// the user provided key - Thus allowing other processors with the + /// same key to read the QSPI chip pub fn with_scramble(mut self, key: u32, random: bool) -> Self { self.scramble_mode = Some((key, random)); self From 5c164d1d4806409e220c78716cf671567a7c56d8 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Mon, 21 Jul 2025 06:30:26 +0100 Subject: [PATCH 08/20] Remove underscore in QSPI member names --- hal/src/peripherals/qspi.rs | 88 ++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/hal/src/peripherals/qspi.rs b/hal/src/peripherals/qspi.rs index e92600dc5700..8ebcb9b018fe 100644 --- a/hal/src/peripherals/qspi.rs +++ b/hal/src/peripherals/qspi.rs @@ -71,16 +71,16 @@ pub struct XIP; pub struct Qspi { qspi: pac::Qspi, - _ahb: AhbClk, - _apb: ApbClk, + ahb: AhbClk, + apb: ApbClk, gclk0_freq: u32, - _sck: Pin, - _cs: Pin, - _io0: Pin, - _io1: Pin, - _io2: Pin, - _io3: Pin, - _mode: PhantomData, + sck: Pin, + cs: Pin, + io0: Pin, + io1: Pin, + io2: Pin, + io3: Pin, + mode: PhantomData, } /// QSPI signal operating modes @@ -257,16 +257,16 @@ impl Qspi { Ok(( Self { qspi, - _ahb: ahb, - _apb: apb, + ahb, + apb, gclk0_freq, - _sck: builder.sck, - _cs: builder.cs, - _io0: builder.io0, - _io1: builder.io1, - _io2: builder.io2, - _io3: builder.io3, - _mode: PhantomData, + sck: builder.sck, + cs: builder.cs, + io0: builder.io0, + io1: builder.io1, + io2: builder.io2, + io3: builder.io3, + mode: PhantomData, }, gclk0.inc(), )) @@ -415,16 +415,16 @@ impl Qspi { Qspi:: { qspi: self.qspi, - _ahb: self._ahb, - _apb: self._apb, + ahb: self.ahb, + apb: self.apb, gclk0_freq: self.gclk0_freq, - _sck: self._sck, - _cs: self._cs, - _io0: self._io0, - _io1: self._io1, - _io2: self._io2, - _io3: self._io3, - _mode: PhantomData, + sck: self.sck, + cs: self.cs, + io0: self.io0, + io1: self.io1, + io2: self.io2, + io3: self.io3, + mode: PhantomData, } } @@ -449,15 +449,15 @@ impl Qspi { ) { ( self.qspi, - self._ahb, - self._apb, + self.ahb, + self.apb, gclk0.dec(), - self._sck, - self._cs, - self._io0, - self._io1, - self._io2, - self._io3, + self.sck, + self.cs, + self.io0, + self.io1, + self.io2, + self.io3, ) } } @@ -471,16 +471,16 @@ impl Qspi { Qspi:: { qspi: self.qspi, - _ahb: self._ahb, - _apb: self._apb, + ahb: self.ahb, + apb: self.apb, gclk0_freq: self.gclk0_freq, - _sck: self._sck, - _cs: self._cs, - _io0: self._io0, - _io1: self._io1, - _io2: self._io2, - _io3: self._io3, - _mode: PhantomData, + sck: self.sck, + cs: self.cs, + io0: self.io0, + io1: self.io1, + io2: self.io2, + io3: self.io3, + mode: PhantomData, } } } From c90558c7aa58638c53a84d89ffc5208bafac6435 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Wed, 16 Jul 2025 09:24:08 +0100 Subject: [PATCH 09/20] Overhaul QSPI module --- hal/src/peripherals/qspi.rs | 294 ++++++++++++++++++++++++++---------- 1 file changed, 215 insertions(+), 79 deletions(-) diff --git a/hal/src/peripherals/qspi.rs b/hal/src/peripherals/qspi.rs index 3c11d684e6be..8752cbfb6464 100644 --- a/hal/src/peripherals/qspi.rs +++ b/hal/src/peripherals/qspi.rs @@ -1,17 +1,61 @@ +//! QSPI +//! +//! This module provides an interface to the QSPI peripheral, +//! which is mainly used for communicating with external flash chips. +//! +//! The QSPI peripheral supports 2 different modes: +//! * Oneshot (Default) - In this mode, the QSPI peripheral simply +//! sends and receives data to and from the flash chip. Quad-SPI +//! communication is only used for reading and writing data to the +//! flash chip. Other commands (Like erase, ID) are done in Single-SPI +//! mode. +//! * XIP (eXecute In Place) - In this mode, the processor can execute +//! code directly off the flash chip. Quad-SPI communication is forced +//! +//! ## IMPORTANT +//! When using QSPI commands, the QSPI peripheral will STALL the CPU. +//! Therefore, you should select the highest SPI communication frequency +//! that the flash chip supports. +//! +//! Erasing a chip can take sometimes up to 1-2 minutes, in which time +//! the CPU is stalled. Bare this in mind when issuing long-running commands +//! with the watchdog enabled! +//! +//! ## Using the QSPI peripheral +//! ``` +//! let pins = Pins::new(pac_peripherals.port); +//! let ahb_qspi = clocks.ahbs.qspi; +//! let (qspi, gclk0) = QspiBuilder::new( +//! pins.sck, +//! pins.cs, +//! pins.data0, +//! pins.data1, +//! pins.data2, +//! pins.data3, +//! ) +//! .with_freq(50_000_000) +//! .with_mode(atsamd_hal::qspi::QspiMode::_0) +//! .build(pac_peripherals.qspi, ahb_qspi, gclk0) +//! .unwrap(); +//! // QSPI is now in oneshot mode. +//! // ... +//! // Switch qspi to XIP mode if required +//! let qspi_xip = qspi.into_xip(); +//! ``` + use crate::{ - gpio::{AlternateH, AnyPin, PA08, PA09, PA10, PA11, PB10, PB11, Pin}, - pac::qspi::instrframe, - pac::{self, Mclk}, + clock::v2::{ + Enabled, Source, + ahb::AhbClk, + gclk::{EnabledGclk, EnabledGclk0, Gclk, Gclk0Id, Gclk0Io, GclkSourceId}, + types::Qspi as QspiClock, + }, + gpio::{AlternateH, PA08, PA09, PA10, PA11, PB10, PB11, Pin}, + pac::{self, qspi::instrframe}, + typelevel::{Decrement, Increment, PrivateDecrement, PrivateIncrement}, }; use core::marker::PhantomData; -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Error { - /// The command you selected cannot be performed by this function - CommandFunctionMismatch, -} - /// Qspi used for read/write of fixed-size octet buffers pub struct OneShot; /// Qspi is memory-mapped as read/execute @@ -19,6 +63,8 @@ pub struct XIP; pub struct Qspi { qspi: pac::Qspi, + _ahb: AhbClk, + gclk0_freq: u32, _sck: Pin, _cs: Pin, _io0: Pin, @@ -28,43 +74,129 @@ pub struct Qspi { _mode: PhantomData, } -impl Qspi { - /// Enable the clocks for the qspi peripheral in single data rate mode - /// assuming 120mhz system clock, for 4mhz spi mode 0 operation. - #[allow(clippy::too_many_arguments)] +/// QSPI signal operating modes +pub enum QspiMode { + /// * Shift SCK Edge: Falling, + /// * Capture SCK Edge: Falling + /// * SCK inactive level: Low + _0, + /// * Shift SCK Edge: Rising, + /// * Capture SCK Edge: Rising + /// * SCK inactive level: Low + _1, + /// * Shift SCK Edge: Rising, + /// * Capture SCK Edge: Rising + /// * SCK inactive level: High + _2, + /// * Shift SCK Edge: Falling, + /// * Capture SCK Edge: Falling + /// * SCK inactive level: High + _3, +} + +pub struct QspiBuilder { + sck: Pin, + cs: Pin, + io0: Pin, + io1: Pin, + io2: Pin, + io3: Pin, + freq: Option, + mode: Option, +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum QspiError { + /// No SPI Frequency provided + NoFreq, + /// No QSPI signal mode provided + NoMode, + /// Target SPI frequency could + /// not be achieved with the provided + /// CPU speed + SpiFreqNotValid, + /// The command you selected cannot be performed by this function + CommandFunctionMismatch, +} + +impl QspiBuilder { pub fn new( - mclk: &mut Mclk, - qspi: pac::Qspi, - _sck: impl AnyPin, - _cs: impl AnyPin, - _io0: impl AnyPin, - _io1: impl AnyPin, - _io2: impl AnyPin, - _io3: impl AnyPin, - ) -> Qspi { - mclk.apbcmask().modify(|_, w| w.qspi_().set_bit()); - // Enable the clocks for the qspi peripheral in single data rate mode. - mclk.ahbmask().modify(|_, w| { - w.qspi_().set_bit(); - w.qspi_2x_().clear_bit() - }); + sck: impl Into>, + cs: impl Into>, + io0: impl Into>, + io1: impl Into>, + io2: impl Into>, + io3: impl Into>, + ) -> Self { + Self { + sck: sck.into(), + cs: cs.into(), + io0: io0.into(), + io1: io1.into(), + io2: io2.into(), + io3: io3.into(), + freq: None, + mode: None, + } + } + + /// Sets the target frequency of the SPI communication + pub fn with_freq(mut self, freq: u32) -> Self { + self.freq = Some(freq); + self + } - let _sck = _sck.into().into_alternate(); - let _cs = _cs.into().into_alternate(); - let _io0 = _io0.into().into_alternate(); - let _io1 = _io1.into().into_alternate(); - let _io2 = _io2.into().into_alternate(); - let _io3 = _io3.into().into_alternate(); + /// Sets the SPI operation mode + pub fn with_mode(mut self, mode: QspiMode) -> Self { + self.mode = Some(mode); + self + } + /// Initialize the QSPI peripheral, and start communication + /// in regular SPI mode + pub fn build( + self, + qspi: pac::Qspi, + ahb: AhbClk, + gclk0: EnabledGclk, + ) -> Result<(Qspi, EnabledGclk), QspiError> { + Qspi::new(qspi, ahb, gclk0, self) + } +} + +impl Qspi { + pub(crate) fn new( + qspi: pac::Qspi, + ahb: AhbClk, + gclk0: Enabled, S>, + builder: QspiBuilder, + ) -> Result<(Qspi, Enabled, S::Inc>), QspiError> { + let targ_freq = builder.freq.ok_or(QspiError::NoFreq)?; + let mode = builder.mode.ok_or(QspiError::NoMode)?; + let gclk0_freq = gclk0.freq().to_Hz(); + // Ensure that the target SPI Freq can be achieved + if gclk0_freq % targ_freq != 0 { + return Err(QspiError::SpiFreqNotValid); + } + // Divider must be 0-254 + let div = gclk0_freq / targ_freq; + if div > 254 || div == 0 { + return Err(QspiError::SpiFreqNotValid); + } qspi.ctrla().write(|w| w.swrst().set_bit()); qspi.baud().write(|w| unsafe { - // TODO get system clock value instead of hardcoding - //(120_000_000u32 / 4_000_000u32) = 30 = BAUD + 1 - // BAUD = 29 - w.baud().bits(29); // 4Mhz - // SPI MODE 0 - w.cpol().clear_bit(); - w.cpha().clear_bit() + w.baud().bits(div as u8 - 1); + let cpol = match mode { + QspiMode::_0 | QspiMode::_1 => false, + QspiMode::_2 | QspiMode::_3 => true, + }; + let cpha = match mode { + QspiMode::_0 | QspiMode::_2 => false, + QspiMode::_1 | QspiMode::_3 => true, + }; + w.cpol().bit(cpol); + w.cpha().bit(cpha) }); qspi.ctrlb().write(|w| { @@ -76,27 +208,32 @@ impl Qspi { qspi.ctrla().modify(|_, w| w.enable().set_bit()); - Self { - qspi, - _sck, - _cs, - _io0, - _io1, - _io2, - _io3, - _mode: PhantomData, - } + Ok(( + Self { + qspi, + _ahb: ahb, + gclk0_freq, + _sck: builder.sck, + _cs: builder.cs, + _io0: builder.io0, + _io1: builder.io1, + _io2: builder.io2, + _io3: builder.io3, + _mode: PhantomData, + }, + gclk0.inc(), + )) } /// Run a generic command that neither takes nor receives data - pub fn run_command(&self, command: Command) -> Result<(), Error> { + pub fn run_command(&self, command: Command) -> Result<(), QspiError> { match command { //TODO verify this list of commands Command::WriteEnable | Command::WriteDisable | Command::Reset | Command::EnableReset => (), - _ => return Err(Error::CommandFunctionMismatch), + _ => return Err(QspiError::CommandFunctionMismatch), } let tfm = TransferMode { @@ -110,7 +247,7 @@ impl Qspi { } /// Run one of the read commands - pub fn read_command(&self, command: Command, response: &mut [u8]) -> Result<(), Error> { + pub fn read_command(&self, command: Command, response: &mut [u8]) -> Result<(), QspiError> { match command { //TODO verify this list of commands Command::Read @@ -118,7 +255,7 @@ impl Qspi { | Command::ReadId | Command::ReadStatus | Command::ReadStatus2 => (), - _ => return Err(Error::CommandFunctionMismatch), + _ => return Err(QspiError::CommandFunctionMismatch), } let tfm = TransferMode { @@ -133,14 +270,14 @@ impl Qspi { } /// Run one of the write commands - pub fn write_command(&self, command: Command, data: &[u8]) -> Result<(), Error> { + pub fn write_command(&self, command: Command, data: &[u8]) -> Result<(), QspiError> { match command { //TODO verify this list of commands Command::PageProgram | Command::QuadPageProgram | Command::WriteStatus | Command::WriteStatus2 => (), - _ => return Err(Error::CommandFunctionMismatch), + _ => return Err(QspiError::CommandFunctionMismatch), } let tfm = TransferMode { @@ -155,7 +292,7 @@ impl Qspi { } /// Run one of the erase commands - pub fn erase_command(&self, command: Command, address: u32) -> Result<(), Error> { + pub fn erase_command(&self, command: Command, address: u32) -> Result<(), QspiError> { match command { //TODO verify this list of commands Command::EraseSector | Command::EraseBlock => { @@ -177,7 +314,7 @@ impl Qspi { self.run_read_instruction(command, tfm, 0, &mut [], true); } } - _ => return Err(Error::CommandFunctionMismatch), + _ => return Err(QspiError::CommandFunctionMismatch), } Ok(()) @@ -231,6 +368,8 @@ impl Qspi { Qspi:: { qspi: self.qspi, + _ahb: self._ahb, + gclk0_freq: self.gclk0_freq, _sck: self._sck, _cs: self._cs, _io0: self._io0, @@ -243,12 +382,15 @@ impl Qspi { /// Return the consumed pins and the Qspi peripheral /// - /// Order: `(qspi, sck, cs, io0, io1, io2, io3)` + /// Order: `(qspi, apb, ahb, gclk0, sck, cs, io0, io1, io2, io3)` #[allow(clippy::type_complexity)] - pub fn free( + pub fn free( self, + gclk0: EnabledGclk0, ) -> ( pac::Qspi, + AhbClk, + EnabledGclk0, Pin, Pin, Pin, @@ -257,7 +399,15 @@ impl Qspi { Pin, ) { ( - self.qspi, self._sck, self._cs, self._io0, self._io1, self._io2, self._io3, + self.qspi, + self._ahb, + gclk0.dec(), + self._sck, + self._cs, + self._io0, + self._io1, + self._io2, + self._io3, ) } } @@ -271,6 +421,8 @@ impl Qspi { Qspi:: { qspi: self.qspi, + _ahb: self._ahb, + gclk0_freq: self.gclk0_freq, _sck: self._sck, _cs: self._cs, _io0: self._io0, @@ -363,22 +515,6 @@ impl Qspi { } } } - - /// Set the clock divider, relative to the main clock - /// - /// This fn safely subtracts 1 from your input value as the underlying fn is - /// SCK Baud = MCKL / (value + 1) - /// - /// ex if Mclk is 120mhz - /// value 0 is reduced to 0 results in 120mhz clock - /// value 1 is reduced to 0 results in 120mhz clock - /// value 2 is reduced to 1 results in 60mhz clock - pub fn set_clk_divider(&mut self, value: u8) { - // The baud register is divisor - 1 - self.qspi - .baud() - .write(|w| unsafe { w.baud().bits(value.saturating_sub(1)) }); - } } #[derive(Default, Debug, Copy, Clone)] From e165b768bffcad10ce46f817013d5664cae523b3 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Wed, 16 Jul 2025 12:23:40 +0100 Subject: [PATCH 10/20] QSPI - Enable user selection of scramble mode --- hal/src/peripherals/qspi.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/hal/src/peripherals/qspi.rs b/hal/src/peripherals/qspi.rs index 8752cbfb6464..f0a7accf1eb2 100644 --- a/hal/src/peripherals/qspi.rs +++ b/hal/src/peripherals/qspi.rs @@ -103,6 +103,7 @@ pub struct QspiBuilder { io3: Pin, freq: Option, mode: Option, + scramble_mode: Option<(u32, bool)>, } #[derive(Debug, Clone, Copy, Eq, PartialEq)] @@ -138,6 +139,7 @@ impl QspiBuilder { io3: io3.into(), freq: None, mode: None, + scramble_mode: None, } } @@ -153,6 +155,20 @@ impl QspiBuilder { self } + /// Enables the optional scramble feature of QSPI + /// + /// * Key - 32 bit key to use for the scramble + /// * Random - Enable if the hardware based extra key should be + /// applied - This means that a QSPI chip will appear scrambled, + /// even to another processor. Disabling the random mode ensures + /// that whilst the QSPI chip itself is scrambled, it is only using + /// the user provided key - Thus allowing other processors with the + /// same key to read the QSPI chip + pub fn with_scramble(mut self, key: u32, random: bool) -> Self { + self.scramble_mode = Some((key, random)); + self + } + /// Initialize the QSPI peripheral, and start communication /// in regular SPI mode pub fn build( @@ -206,6 +222,15 @@ impl Qspi { w.datalen()._8bits() }); + // Enable scrambling if the user requested it + if let Some((key, random_en)) = builder.scramble_mode { + qspi.scrambctrl().write(|w| { + w.enable().set_bit(); + w.randomdis().bit(!random_en) + }); + qspi.scrambkey().write(|w| unsafe { w.key().bits(key) }); + } + qspi.ctrla().modify(|_, w| w.enable().set_bit()); Ok(( From f7a043d8820f065f1022d1a4b49163ecbe974d9b Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Thu, 17 Jul 2025 05:46:31 +0100 Subject: [PATCH 11/20] Add APB clock to QSPI --- hal/src/peripherals/qspi.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/hal/src/peripherals/qspi.rs b/hal/src/peripherals/qspi.rs index f0a7accf1eb2..5bdb6b86bf80 100644 --- a/hal/src/peripherals/qspi.rs +++ b/hal/src/peripherals/qspi.rs @@ -12,6 +12,12 @@ //! * XIP (eXecute In Place) - In this mode, the processor can execute //! code directly off the flash chip. Quad-SPI communication is forced //! +//! This module does not come with an API for easily communicating with +//! flash chips, due to the varying differences in timings and commands. +//! +//! For an example of actually communicating (R/W) with a QSPI flash chip, +//! look at the pygamer BSP QSPI example. +//! //! ## IMPORTANT //! When using QSPI commands, the QSPI peripheral will STALL the CPU. //! Therefore, you should select the highest SPI communication frequency @@ -25,6 +31,7 @@ //! ``` //! let pins = Pins::new(pac_peripherals.port); //! let ahb_qspi = clocks.ahbs.qspi; +//! let apb_qspi = clocks.apbs.qspi; //! let (qspi, gclk0) = QspiBuilder::new( //! pins.sck, //! pins.cs, @@ -35,7 +42,7 @@ //! ) //! .with_freq(50_000_000) //! .with_mode(atsamd_hal::qspi::QspiMode::_0) -//! .build(pac_peripherals.qspi, ahb_qspi, gclk0) +//! .build(pac_peripherals.qspi, ahb_qspi, apb_qspi, gclk0) //! .unwrap(); //! // QSPI is now in oneshot mode. //! // ... @@ -47,6 +54,7 @@ use crate::{ clock::v2::{ Enabled, Source, ahb::AhbClk, + apb::ApbClk, gclk::{EnabledGclk, EnabledGclk0, Gclk, Gclk0Id, Gclk0Io, GclkSourceId}, types::Qspi as QspiClock, }, @@ -64,6 +72,7 @@ pub struct XIP; pub struct Qspi { qspi: pac::Qspi, _ahb: AhbClk, + _apb: ApbClk, gclk0_freq: u32, _sck: Pin, _cs: Pin, @@ -175,9 +184,10 @@ impl QspiBuilder { self, qspi: pac::Qspi, ahb: AhbClk, + apb: ApbClk, gclk0: EnabledGclk, ) -> Result<(Qspi, EnabledGclk), QspiError> { - Qspi::new(qspi, ahb, gclk0, self) + Qspi::new(qspi, ahb, apb, gclk0, self) } } @@ -185,6 +195,7 @@ impl Qspi { pub(crate) fn new( qspi: pac::Qspi, ahb: AhbClk, + apb: ApbClk, gclk0: Enabled, S>, builder: QspiBuilder, ) -> Result<(Qspi, Enabled, S::Inc>), QspiError> { @@ -237,6 +248,7 @@ impl Qspi { Self { qspi, _ahb: ahb, + _apb: apb, gclk0_freq, _sck: builder.sck, _cs: builder.cs, @@ -394,6 +406,7 @@ impl Qspi { Qspi:: { qspi: self.qspi, _ahb: self._ahb, + _apb: self._apb, gclk0_freq: self.gclk0_freq, _sck: self._sck, _cs: self._cs, @@ -415,6 +428,7 @@ impl Qspi { ) -> ( pac::Qspi, AhbClk, + ApbClk, EnabledGclk0, Pin, Pin, @@ -426,6 +440,7 @@ impl Qspi { ( self.qspi, self._ahb, + self._apb, gclk0.dec(), self._sck, self._cs, @@ -447,6 +462,7 @@ impl Qspi { Qspi:: { qspi: self.qspi, _ahb: self._ahb, + _apb: self._apb, gclk0_freq: self.gclk0_freq, _sck: self._sck, _cs: self._cs, From 1f27473bf890a38efc5e31906d1d2b259cd8e4b6 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Fri, 18 Jul 2025 12:31:08 +0100 Subject: [PATCH 12/20] Fix examples and remove QSPI builder in the BSPs --- boards/metro_m4/src/lib.rs | 26 ------------------------ boards/pygamer/examples/qspi.rs | 36 ++++++++++++++++++++++++--------- boards/pygamer/src/pins.rs | 8 -------- hal/src/peripherals/qspi.rs | 8 ++++++++ 4 files changed, 34 insertions(+), 44 deletions(-) diff --git a/boards/metro_m4/src/lib.rs b/boards/metro_m4/src/lib.rs index 7e972ef621ab..51ab81cc13a1 100644 --- a/boards/metro_m4/src/lib.rs +++ b/boards/metro_m4/src/lib.rs @@ -267,32 +267,6 @@ pub fn spi_master( .enable() } -/// Convenience for setting up the onboard QSPI flash. -/// Enables the clocks for the QSPI peripheral in single data rate mode -/// assuming 120MHz system clock, for 4MHz QSPI mode 0 operation. -#[allow(clippy::too_many_arguments)] -pub fn qspi_master( - mclk: &mut Mclk, - qspi: pac::Qspi, - sclk: impl Into, - cs: impl Into, - data0: impl Into, - data1: impl Into, - data2: impl Into, - data3: impl Into, -) -> Qspi { - Qspi::new( - mclk, - qspi, - sclk.into(), - cs.into(), - data0.into(), - data1.into(), - data2.into(), - data3.into(), - ) -} - /// I2C pads for the labelled I2C peripheral /// /// You can use these pads with other, user-defined [`i2c::Config`]urations. diff --git a/boards/pygamer/examples/qspi.rs b/boards/pygamer/examples/qspi.rs index 212aed377f95..6921bfc72ec3 100644 --- a/boards/pygamer/examples/qspi.rs +++ b/boards/pygamer/examples/qspi.rs @@ -22,7 +22,9 @@ #![no_std] #![no_main] +use atsamd_hal::qspi::QspiBuilder; use bsp::{entry, hal, pac, Pins}; +use hal::clock::v2::{clock_system_at_reset, pclk::Pclk}; #[cfg(not(feature = "panic_led"))] use panic_halt as _; use pygamer as bsp; @@ -37,18 +39,36 @@ use pac::{CorePeripherals, Peripherals}; fn main() -> ! { let mut peripherals = Peripherals::take().unwrap(); let core = CorePeripherals::take().unwrap(); - let mut clocks = GenericClockController::with_internal_32kosc( + + + let (mut buses, clocks, tokens) = clock_system_at_reset( + peripherals.oscctrl, + peripherals.osc32kctrl, peripherals.gclk, - &mut peripherals.mclk, - &mut peripherals.osc32kctrl, - &mut peripherals.oscctrl, + peripherals.mclk, &mut peripherals.nvmctrl, ); - let mut delay = Delay::new(core.SYST, &mut clocks); + + let (mut delay, gclk0) = Delay::new_with_source(core.SYST, clocks.gclk0); let sets = Pins::new(peripherals.port).split(); - let mut flash = sets.flash.init(&mut peripherals.mclk, peripherals.qspi); + let apb_qspi = clocks.apbs.qspi; + let ahb_qspi = clocks.ahbs.qspi; + + let (mut flash, gclk0) = QspiBuilder::new( + sets.flash.sclk, + sets.flash.cs, + sets.flash.data0, + sets.flash.data1, + sets.flash.data2, + sets.flash.data3 + ) + // 48Mhz since this is as fast as the CPU runs after reset, + .with_freq(48_000_000) + .with_mode(qspi::QspiMode::_0) + .build(peripherals.qspi, ahb_qspi, apb_qspi, gclk0) + .unwrap(); // Startup delay. Can't find documented but Adafruit use 5ms delay.delay_ms(5u8); @@ -64,10 +84,6 @@ fn main() -> ! { flash.read_command(Command::ReadId, &mut read_buf).unwrap(); assert_eq!(read_buf, [0x17, 0x40, 0xc8]); - // 120MHz / 2 = 60mhz - // faster than 104mhz at 3.3v would require High Performance Mode - flash.set_clk_divider(2); - // Enable Quad SPI mode. Requires write enable. Check WIP. flash.run_command(Command::WriteEnable).unwrap(); flash.write_command(Command::WriteStatus2, &[0x02]).unwrap(); diff --git a/boards/pygamer/src/pins.rs b/boards/pygamer/src/pins.rs index 2f1f5de4c63c..7ce71a7e5a81 100644 --- a/boards/pygamer/src/pins.rs +++ b/boards/pygamer/src/pins.rs @@ -883,14 +883,6 @@ pub struct QSPIFlash { pub data3: QspiD3Reset, } -impl QSPIFlash { - pub fn init(self, mclk: &mut pac::Mclk, qspi: pac::Qspi) -> qspi::Qspi { - qspi::Qspi::new( - mclk, qspi, self.sclk, self.cs, self.data0, self.data1, self.data2, self.data3, - ) - } -} - /// Button pins pub struct Buttons { /// Button Latch diff --git a/hal/src/peripherals/qspi.rs b/hal/src/peripherals/qspi.rs index 5bdb6b86bf80..dd6f7f4fc651 100644 --- a/hal/src/peripherals/qspi.rs +++ b/hal/src/peripherals/qspi.rs @@ -130,6 +130,14 @@ pub enum QspiError { CommandFunctionMismatch, } +/// # QSPI Configuration Builder +/// +/// This structure contains methods that configures the QSPI module. +/// +/// Setting frequency [`Self::with_freq`] and SPI mode [`Self::with_mode`] +/// are required to get QSPI running, without calling these, the [`Self::build`] +/// function will return a [`QspiError`]. Other configuration options are +/// optional, so are not required impl QspiBuilder { pub fn new( sck: impl Into>, From 76cf1e68613aaef2d3713f4e6086a5091386b81a Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Fri, 18 Jul 2025 13:14:20 +0100 Subject: [PATCH 13/20] Simplify gclk types --- boards/pygamer/examples/qspi.rs | 10 +++++----- hal/src/peripherals/qspi.rs | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/boards/pygamer/examples/qspi.rs b/boards/pygamer/examples/qspi.rs index 6921bfc72ec3..acfa249befa9 100644 --- a/boards/pygamer/examples/qspi.rs +++ b/boards/pygamer/examples/qspi.rs @@ -57,11 +57,11 @@ fn main() -> ! { let ahb_qspi = clocks.ahbs.qspi; let (mut flash, gclk0) = QspiBuilder::new( - sets.flash.sclk, - sets.flash.cs, - sets.flash.data0, - sets.flash.data1, - sets.flash.data2, + sets.flash.sclk, + sets.flash.cs, + sets.flash.data0, + sets.flash.data1, + sets.flash.data2, sets.flash.data3 ) // 48Mhz since this is as fast as the CPU runs after reset, diff --git a/hal/src/peripherals/qspi.rs b/hal/src/peripherals/qspi.rs index dd6f7f4fc651..3609268b0b93 100644 --- a/hal/src/peripherals/qspi.rs +++ b/hal/src/peripherals/qspi.rs @@ -55,7 +55,7 @@ use crate::{ Enabled, Source, ahb::AhbClk, apb::ApbClk, - gclk::{EnabledGclk, EnabledGclk0, Gclk, Gclk0Id, Gclk0Io, GclkSourceId}, + gclk::{EnabledGclk0, Gclk, Gclk0Id, Gclk0Io, GclkSourceId}, types::Qspi as QspiClock, }, gpio::{AlternateH, PA08, PA09, PA10, PA11, PB10, PB11, Pin}, @@ -193,8 +193,8 @@ impl QspiBuilder { qspi: pac::Qspi, ahb: AhbClk, apb: ApbClk, - gclk0: EnabledGclk, - ) -> Result<(Qspi, EnabledGclk), QspiError> { + gclk0: EnabledGclk0, + ) -> Result<(Qspi, EnabledGclk0), QspiError> { Qspi::new(qspi, ahb, apb, gclk0, self) } } @@ -206,7 +206,7 @@ impl Qspi { apb: ApbClk, gclk0: Enabled, S>, builder: QspiBuilder, - ) -> Result<(Qspi, Enabled, S::Inc>), QspiError> { + ) -> Result<(Qspi, EnabledGclk0), QspiError> { let targ_freq = builder.freq.ok_or(QspiError::NoFreq)?; let mode = builder.mode.ok_or(QspiError::NoMode)?; let gclk0_freq = gclk0.freq().to_Hz(); From 9df4c2a3dff8b7c602bc0017fc31b06bc79c14eb Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Fri, 18 Jul 2025 13:17:19 +0100 Subject: [PATCH 14/20] Fix rstfmt --- boards/pygamer/examples/qspi.rs | 1 - hal/src/peripherals/qspi.rs | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/boards/pygamer/examples/qspi.rs b/boards/pygamer/examples/qspi.rs index acfa249befa9..b794d3403eae 100644 --- a/boards/pygamer/examples/qspi.rs +++ b/boards/pygamer/examples/qspi.rs @@ -40,7 +40,6 @@ fn main() -> ! { let mut peripherals = Peripherals::take().unwrap(); let core = CorePeripherals::take().unwrap(); - let (mut buses, clocks, tokens) = clock_system_at_reset( peripherals.oscctrl, peripherals.osc32kctrl, diff --git a/hal/src/peripherals/qspi.rs b/hal/src/peripherals/qspi.rs index 3609268b0b93..f2578cb72c84 100644 --- a/hal/src/peripherals/qspi.rs +++ b/hal/src/peripherals/qspi.rs @@ -188,6 +188,7 @@ impl QspiBuilder { /// Initialize the QSPI peripheral, and start communication /// in regular SPI mode + #[allow(clippy::type_complexity)] pub fn build( self, qspi: pac::Qspi, @@ -200,6 +201,7 @@ impl QspiBuilder { } impl Qspi { + #[allow(clippy::type_complexity)] pub(crate) fn new( qspi: pac::Qspi, ahb: AhbClk, From 6d348c9e71dae4bcd8073bf94c1ef2ea0120bc67 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Fri, 18 Jul 2025 13:20:36 +0100 Subject: [PATCH 15/20] Fix doc comment indent --- hal/src/peripherals/qspi.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hal/src/peripherals/qspi.rs b/hal/src/peripherals/qspi.rs index f2578cb72c84..e92600dc5700 100644 --- a/hal/src/peripherals/qspi.rs +++ b/hal/src/peripherals/qspi.rs @@ -176,11 +176,11 @@ impl QspiBuilder { /// /// * Key - 32 bit key to use for the scramble /// * Random - Enable if the hardware based extra key should be - /// applied - This means that a QSPI chip will appear scrambled, - /// even to another processor. Disabling the random mode ensures - /// that whilst the QSPI chip itself is scrambled, it is only using - /// the user provided key - Thus allowing other processors with the - /// same key to read the QSPI chip + /// applied - This means that a QSPI chip will appear scrambled, + /// even to another processor. Disabling the random mode ensures + /// that whilst the QSPI chip itself is scrambled, it is only using + /// the user provided key - Thus allowing other processors with the + /// same key to read the QSPI chip pub fn with_scramble(mut self, key: u32, random: bool) -> Self { self.scramble_mode = Some((key, random)); self From 622b7822d773b078c2a95ad3af26bf5de78d5fd8 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Mon, 21 Jul 2025 06:30:26 +0100 Subject: [PATCH 16/20] Remove underscore in QSPI member names --- hal/src/peripherals/qspi.rs | 88 ++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/hal/src/peripherals/qspi.rs b/hal/src/peripherals/qspi.rs index e92600dc5700..8ebcb9b018fe 100644 --- a/hal/src/peripherals/qspi.rs +++ b/hal/src/peripherals/qspi.rs @@ -71,16 +71,16 @@ pub struct XIP; pub struct Qspi { qspi: pac::Qspi, - _ahb: AhbClk, - _apb: ApbClk, + ahb: AhbClk, + apb: ApbClk, gclk0_freq: u32, - _sck: Pin, - _cs: Pin, - _io0: Pin, - _io1: Pin, - _io2: Pin, - _io3: Pin, - _mode: PhantomData, + sck: Pin, + cs: Pin, + io0: Pin, + io1: Pin, + io2: Pin, + io3: Pin, + mode: PhantomData, } /// QSPI signal operating modes @@ -257,16 +257,16 @@ impl Qspi { Ok(( Self { qspi, - _ahb: ahb, - _apb: apb, + ahb, + apb, gclk0_freq, - _sck: builder.sck, - _cs: builder.cs, - _io0: builder.io0, - _io1: builder.io1, - _io2: builder.io2, - _io3: builder.io3, - _mode: PhantomData, + sck: builder.sck, + cs: builder.cs, + io0: builder.io0, + io1: builder.io1, + io2: builder.io2, + io3: builder.io3, + mode: PhantomData, }, gclk0.inc(), )) @@ -415,16 +415,16 @@ impl Qspi { Qspi:: { qspi: self.qspi, - _ahb: self._ahb, - _apb: self._apb, + ahb: self.ahb, + apb: self.apb, gclk0_freq: self.gclk0_freq, - _sck: self._sck, - _cs: self._cs, - _io0: self._io0, - _io1: self._io1, - _io2: self._io2, - _io3: self._io3, - _mode: PhantomData, + sck: self.sck, + cs: self.cs, + io0: self.io0, + io1: self.io1, + io2: self.io2, + io3: self.io3, + mode: PhantomData, } } @@ -449,15 +449,15 @@ impl Qspi { ) { ( self.qspi, - self._ahb, - self._apb, + self.ahb, + self.apb, gclk0.dec(), - self._sck, - self._cs, - self._io0, - self._io1, - self._io2, - self._io3, + self.sck, + self.cs, + self.io0, + self.io1, + self.io2, + self.io3, ) } } @@ -471,16 +471,16 @@ impl Qspi { Qspi:: { qspi: self.qspi, - _ahb: self._ahb, - _apb: self._apb, + ahb: self.ahb, + apb: self.apb, gclk0_freq: self.gclk0_freq, - _sck: self._sck, - _cs: self._cs, - _io0: self._io0, - _io1: self._io1, - _io2: self._io2, - _io3: self._io3, - _mode: PhantomData, + sck: self.sck, + cs: self.cs, + io0: self.io0, + io1: self.io1, + io2: self.io2, + io3: self.io3, + mode: PhantomData, } } } From cecee8bcadacdd1ccbea83057e8d53df8b3cd8e5 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Tue, 25 Nov 2025 07:54:18 +0100 Subject: [PATCH 17/20] Correctly specify EnabledGclk0 --- hal/src/peripherals/qspi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hal/src/peripherals/qspi.rs b/hal/src/peripherals/qspi.rs index 8ebcb9b018fe..47a5d40de99d 100644 --- a/hal/src/peripherals/qspi.rs +++ b/hal/src/peripherals/qspi.rs @@ -206,7 +206,7 @@ impl Qspi { qspi: pac::Qspi, ahb: AhbClk, apb: ApbClk, - gclk0: Enabled, S>, + gclk0: EnabledGclk0, builder: QspiBuilder, ) -> Result<(Qspi, EnabledGclk0), QspiError> { let targ_freq = builder.freq.ok_or(QspiError::NoFreq)?; From e6bca6e92710314d1aa9c6360008fa2a9872ace4 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Tue, 25 Nov 2025 16:11:01 +0100 Subject: [PATCH 18/20] QSPI - Ensure QSPI freq cannot be more than 1/2 of CPU freq --- hal/src/peripherals/qspi.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/hal/src/peripherals/qspi.rs b/hal/src/peripherals/qspi.rs index 47a5d40de99d..1cddcc309c0c 100644 --- a/hal/src/peripherals/qspi.rs +++ b/hal/src/peripherals/qspi.rs @@ -213,7 +213,10 @@ impl Qspi { let mode = builder.mode.ok_or(QspiError::NoMode)?; let gclk0_freq = gclk0.freq().to_Hz(); // Ensure that the target SPI Freq can be achieved - if gclk0_freq % targ_freq != 0 { + // The second check is done to ensure the CPU can R/W from QSPI. + // If QSPI signal is at the same freq as the CPU's, then the CPU + // can never distinguish bits coming from QSPI + if gclk0_freq % targ_freq != 0 || targ_freq > gclk0_freq / 2 { return Err(QspiError::SpiFreqNotValid); } // Divider must be 0-254 @@ -272,6 +275,27 @@ impl Qspi { )) } + pub fn change_freq( + &mut self, + gclk0: &EnabledGclk0, + freq: u32, + ) -> Result<(), QspiError> { + let gclk0_freq = gclk0.freq().to_Hz(); + // Ensure that the target SPI Freq can be achieved + if gclk0_freq % freq != 0 || freq > gclk0_freq / 2 { + return Err(QspiError::SpiFreqNotValid); + } + // Divider must be 0-254 + let div = gclk0_freq / freq; + if div > 254 || div == 0 { + return Err(QspiError::SpiFreqNotValid); + } + self.qspi + .baud() + .write(|w| unsafe { w.baud().bits(div as u8 - 1) }); + Ok(()) + } + /// Run a generic command that neither takes nor receives data pub fn run_command(&self, command: Command) -> Result<(), QspiError> { match command { From 9495aad48957dcfe27b39e516a2dead778cabfde Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Tue, 25 Nov 2025 16:11:12 +0100 Subject: [PATCH 19/20] FIx compile warnings of pygamer QSPI example --- boards/pygamer/examples/qspi.rs | 13 +++++++------ boards/pygamer/src/pins.rs | 1 - 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/boards/pygamer/examples/qspi.rs b/boards/pygamer/examples/qspi.rs index b794d3403eae..e0cad6ba9291 100644 --- a/boards/pygamer/examples/qspi.rs +++ b/boards/pygamer/examples/qspi.rs @@ -24,12 +24,11 @@ use atsamd_hal::qspi::QspiBuilder; use bsp::{entry, hal, pac, Pins}; -use hal::clock::v2::{clock_system_at_reset, pclk::Pclk}; +use hal::clock::v2::clock_system_at_reset; #[cfg(not(feature = "panic_led"))] use panic_halt as _; use pygamer as bsp; -use hal::clock::GenericClockController; use hal::delay::Delay; use hal::prelude::*; use hal::qspi::{self, Command}; @@ -40,7 +39,7 @@ fn main() -> ! { let mut peripherals = Peripherals::take().unwrap(); let core = CorePeripherals::take().unwrap(); - let (mut buses, clocks, tokens) = clock_system_at_reset( + let (_buses, clocks, _tokens) = clock_system_at_reset( peripherals.oscctrl, peripherals.osc32kctrl, peripherals.gclk, @@ -55,7 +54,7 @@ fn main() -> ! { let apb_qspi = clocks.apbs.qspi; let ahb_qspi = clocks.ahbs.qspi; - let (mut flash, gclk0) = QspiBuilder::new( + let (mut flash, _gclk0) = QspiBuilder::new( sets.flash.sclk, sets.flash.cs, sets.flash.data0, @@ -63,8 +62,10 @@ fn main() -> ! { sets.flash.data2, sets.flash.data3 ) - // 48Mhz since this is as fast as the CPU runs after reset, - .with_freq(48_000_000) + // QSPI freq can never be more than 1/2 of the CPU freq. + // CPU is running at 48Mhz by default, so max QSPI speed + // like this is 24Mhz + .with_freq(24_000_000) .with_mode(qspi::QspiMode::_0) .build(peripherals.qspi, ahb_qspi, apb_qspi, gclk0) .unwrap(); diff --git a/boards/pygamer/src/pins.rs b/boards/pygamer/src/pins.rs index 7ce71a7e5a81..55e81ebdc691 100644 --- a/boards/pygamer/src/pins.rs +++ b/boards/pygamer/src/pins.rs @@ -8,7 +8,6 @@ use embedded_hal_bus::spi as bspi; use hal::clock::GenericClockController; use hal::gpio::PA01; use hal::pwm; -use hal::qspi; use hal::sercom::uart::{self, BaudMode, Oversampling}; use hal::sercom::{i2c, spi, Sercom1, Sercom4}; use hal::time::Hertz; From 8a5f734e658de3806c513e71d3d9bc32d2708368 Mon Sep 17 00:00:00 2001 From: Ashcon Mohseninia Date: Tue, 25 Nov 2025 16:14:33 +0100 Subject: [PATCH 20/20] QSPI remove unused imports --- hal/src/peripherals/qspi.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hal/src/peripherals/qspi.rs b/hal/src/peripherals/qspi.rs index 1cddcc309c0c..edcbed40782d 100644 --- a/hal/src/peripherals/qspi.rs +++ b/hal/src/peripherals/qspi.rs @@ -52,10 +52,10 @@ use crate::{ clock::v2::{ - Enabled, Source, + Source, ahb::AhbClk, apb::ApbClk, - gclk::{EnabledGclk0, Gclk, Gclk0Id, Gclk0Io, GclkSourceId}, + gclk::{EnabledGclk0, Gclk0Io, GclkSourceId}, types::Qspi as QspiClock, }, gpio::{AlternateH, PA08, PA09, PA10, PA11, PB10, PB11, Pin},