diff --git a/docs/pages/imxrt.adoc b/docs/pages/imxrt.adoc index 9fcb6725c..bbd65e494 100644 --- a/docs/pages/imxrt.adoc +++ b/docs/pages/imxrt.adoc @@ -9,5 +9,6 @@ The link: link:https://github.com/embassy-rs/embassy/tree/main/embassy-imxrt[Emb The following peripherals have a HAL implementation at present +* CRC * GPIO * RNG diff --git a/embassy-imxrt/src/crc.rs b/embassy-imxrt/src/crc.rs new file mode 100644 index 000000000..24d6ba5bd --- /dev/null +++ b/embassy-imxrt/src/crc.rs @@ -0,0 +1,190 @@ +//! Cyclic Redundancy Check (CRC) + +use core::marker::PhantomData; + +use crate::clocks::{enable_and_reset, SysconPeripheral}; +pub use crate::pac::crc_engine::mode::CrcPolynomial as Polynomial; +use crate::{peripherals, Peri, PeripheralType}; + +/// CRC driver. +pub struct Crc<'d> { + info: Info, + _config: Config, + _lifetime: PhantomData<&'d ()>, +} + +/// CRC configuration +pub struct Config { + /// Polynomial to be used + pub polynomial: Polynomial, + + /// Reverse bit order of input? + pub reverse_in: bool, + + /// 1's complement input? + pub complement_in: bool, + + /// Reverse CRC bit order? + pub reverse_out: bool, + + /// 1's complement CRC? + pub complement_out: bool, + + /// CRC Seed + pub seed: u32, +} + +impl Config { + /// Create a new CRC config. + #[must_use] + pub fn new( + polynomial: Polynomial, + reverse_in: bool, + complement_in: bool, + reverse_out: bool, + complement_out: bool, + seed: u32, + ) -> Self { + Config { + polynomial, + reverse_in, + complement_in, + reverse_out, + complement_out, + seed, + } + } +} + +impl Default for Config { + fn default() -> Self { + Self { + polynomial: Polynomial::CrcCcitt, + reverse_in: false, + complement_in: false, + reverse_out: false, + complement_out: false, + seed: 0xffff, + } + } +} + +impl<'d> Crc<'d> { + /// Instantiates new CRC peripheral and initializes to default values. + pub fn new(_peripheral: Peri<'d, T>, config: Config) -> Self { + // enable CRC clock + enable_and_reset::(); + + let mut instance = Self { + info: T::info(), + _config: config, + _lifetime: PhantomData, + }; + + instance.reconfigure(); + instance + } + + /// Reconfigured the CRC peripheral. + fn reconfigure(&mut self) { + self.info.regs.mode().write(|w| { + w.crc_poly() + .variant(self._config.polynomial) + .bit_rvs_wr() + .variant(self._config.reverse_in) + .cmpl_wr() + .variant(self._config.complement_in) + .bit_rvs_sum() + .variant(self._config.reverse_out) + .cmpl_sum() + .variant(self._config.complement_out) + }); + + // Init CRC value + self.info + .regs + .seed() + .write(|w| unsafe { w.crc_seed().bits(self._config.seed) }); + } + + /// Feeds a byte into the CRC peripheral. Returns the computed checksum. + pub fn feed_byte(&mut self, byte: u8) -> u32 { + self.info.regs.wr_data8().write(|w| unsafe { w.bits(byte) }); + + self.info.regs.sum().read().bits() + } + + /// Feeds an slice of bytes into the CRC peripheral. Returns the computed checksum. + pub fn feed_bytes(&mut self, bytes: &[u8]) -> u32 { + let (prefix, data, suffix) = unsafe { bytes.align_to::() }; + + for b in prefix { + self.info.regs.wr_data8().write(|w| unsafe { w.bits(*b) }); + } + + for d in data { + self.info.regs.wr_data32().write(|w| unsafe { w.bits(*d) }); + } + + for b in suffix { + self.info.regs.wr_data8().write(|w| unsafe { w.bits(*b) }); + } + + self.info.regs.sum().read().bits() + } + + /// Feeds a halfword into the CRC peripheral. Returns the computed checksum. + pub fn feed_halfword(&mut self, halfword: u16) -> u32 { + self.info.regs.wr_data16().write(|w| unsafe { w.bits(halfword) }); + + self.info.regs.sum().read().bits() + } + + /// Feeds an slice of halfwords into the CRC peripheral. Returns the computed checksum. + pub fn feed_halfwords(&mut self, halfwords: &[u16]) -> u32 { + for halfword in halfwords { + self.info.regs.wr_data16().write(|w| unsafe { w.bits(*halfword) }); + } + + self.info.regs.sum().read().bits() + } + + /// Feeds a words into the CRC peripheral. Returns the computed checksum. + pub fn feed_word(&mut self, word: u32) -> u32 { + self.info.regs.wr_data32().write(|w| unsafe { w.bits(word) }); + + self.info.regs.sum().read().bits() + } + + /// Feeds an slice of words into the CRC peripheral. Returns the computed checksum. + pub fn feed_words(&mut self, words: &[u32]) -> u32 { + for word in words { + self.info.regs.wr_data32().write(|w| unsafe { w.bits(*word) }); + } + + self.info.regs.sum().read().bits() + } +} + +struct Info { + regs: crate::pac::CrcEngine, +} + +trait SealedInstance { + fn info() -> Info; +} + +/// CRC instance trait. +#[allow(private_bounds)] +pub trait Instance: SealedInstance + PeripheralType + SysconPeripheral + 'static + Send {} + +impl Instance for peripherals::CRC {} + +impl SealedInstance for peripherals::CRC { + fn info() -> Info { + // SAFETY: safe from single executor + Info { + regs: unsafe { crate::pac::CrcEngine::steal() }, + } + } +} diff --git a/embassy-imxrt/src/lib.rs b/embassy-imxrt/src/lib.rs index f728ba060..b1183d8fc 100644 --- a/embassy-imxrt/src/lib.rs +++ b/embassy-imxrt/src/lib.rs @@ -18,6 +18,7 @@ compile_error!( pub(crate) mod fmt; pub mod clocks; +pub mod crc; pub mod gpio; pub mod iopctl; pub mod rng; diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 0103fa7ae..99cf29487 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -262,6 +262,9 @@ embassy_hal_internal::peripherals! { PPI_GROUP4, PPI_GROUP5, + // IPC + IPC, + // GPIO port 0 #[cfg(feature = "lfxo-pins-as-gpio")] P0_00, @@ -327,6 +330,8 @@ embassy_hal_internal::peripherals! { EGU5, } +impl_ipc!(IPC, IPC, IPC); + impl_usb!(USBD, USBD, USBD); impl_uarte!(SERIAL0, UARTE0, SERIAL0); diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index 22d33d080..c2932be31 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -141,6 +141,9 @@ embassy_hal_internal::peripherals! { PPI_GROUP4, PPI_GROUP5, + // IPC + IPC, + // GPIO port 0 P0_00, P0_01, @@ -200,6 +203,8 @@ embassy_hal_internal::peripherals! { EGU0, } +impl_ipc!(IPC, IPC, IPC); + impl_uarte!(SERIAL0, UARTE0, SERIAL0); impl_spim!(SERIAL0, SPIM0, SERIAL0); impl_spis!(SERIAL0, SPIS0, SERIAL0); diff --git a/embassy-nrf/src/ipc.rs b/embassy-nrf/src/ipc.rs new file mode 100644 index 000000000..a8a08c911 --- /dev/null +++ b/embassy-nrf/src/ipc.rs @@ -0,0 +1,363 @@ +//! InterProcessor Communication (IPC) + +#![macro_use] + +use core::future::poll_fn; +use core::marker::PhantomData; +use core::task::Poll; + +use embassy_hal_internal::{Peri, PeripheralType}; +use embassy_sync::waitqueue::AtomicWaker; + +use crate::interrupt::typelevel::Interrupt; +use crate::{interrupt, pac, ppi}; + +const EVENT_COUNT: usize = 16; + +/// IPC Event +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum EventNumber { + /// IPC Event 0 + Event0 = 0, + /// IPC Event 1 + Event1 = 1, + /// IPC Event 2 + Event2 = 2, + /// IPC Event 3 + Event3 = 3, + /// IPC Event 4 + Event4 = 4, + /// IPC Event 5 + Event5 = 5, + /// IPC Event 6 + Event6 = 6, + /// IPC Event 7 + Event7 = 7, + /// IPC Event 8 + Event8 = 8, + /// IPC Event 9 + Event9 = 9, + /// IPC Event 10 + Event10 = 10, + /// IPC Event 11 + Event11 = 11, + /// IPC Event 12 + Event12 = 12, + /// IPC Event 13 + Event13 = 13, + /// IPC Event 14 + Event14 = 14, + /// IPC Event 15 + Event15 = 15, +} + +const EVENTS: [EventNumber; EVENT_COUNT] = [ + EventNumber::Event0, + EventNumber::Event1, + EventNumber::Event2, + EventNumber::Event3, + EventNumber::Event4, + EventNumber::Event5, + EventNumber::Event6, + EventNumber::Event7, + EventNumber::Event8, + EventNumber::Event9, + EventNumber::Event10, + EventNumber::Event11, + EventNumber::Event12, + EventNumber::Event13, + EventNumber::Event14, + EventNumber::Event15, +]; + +/// IPC Channel +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum IpcChannel { + /// IPC Channel 0 + Channel0, + /// IPC Channel 1 + Channel1, + /// IPC Channel 2 + Channel2, + /// IPC Channel 3 + Channel3, + /// IPC Channel 4 + Channel4, + /// IPC Channel 5 + Channel5, + /// IPC Channel 6 + Channel6, + /// IPC Channel 7 + Channel7, + /// IPC Channel 8 + Channel8, + /// IPC Channel 9 + Channel9, + /// IPC Channel 10 + Channel10, + /// IPC Channel 11 + Channel11, + /// IPC Channel 12 + Channel12, + /// IPC Channel 13 + Channel13, + /// IPC Channel 14 + Channel14, + /// IPC Channel 15 + Channel15, +} + +impl IpcChannel { + fn mask(self) -> u32 { + 1 << (self as u32) + } +} + +/// Interrupt Handler +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let regs = T::regs(); + + // Check if an event was generated, and if it was, trigger the corresponding waker + for event in EVENTS { + if regs.events_receive(event as usize).read() & 0x01 == 0x01 { + regs.intenclr().write(|w| w.0 = 0x01 << event as u32); + T::state().wakers[event as usize].wake(); + } + } + } +} + +/// IPC driver +#[non_exhaustive] +pub struct Ipc<'d, T: Instance> { + /// Event 0 + pub event0: Event<'d, T>, + /// Event 1 + pub event1: Event<'d, T>, + /// Event 2 + pub event2: Event<'d, T>, + /// Event 3 + pub event3: Event<'d, T>, + /// Event 4 + pub event4: Event<'d, T>, + /// Event 5 + pub event5: Event<'d, T>, + /// Event 6 + pub event6: Event<'d, T>, + /// Event 7 + pub event7: Event<'d, T>, + /// Event 8 + pub event8: Event<'d, T>, + /// Event 9 + pub event9: Event<'d, T>, + /// Event 10 + pub event10: Event<'d, T>, + /// Event 11 + pub event11: Event<'d, T>, + /// Event 12 + pub event12: Event<'d, T>, + /// Event 13 + pub event13: Event<'d, T>, + /// Event 14 + pub event14: Event<'d, T>, + /// Event 15 + pub event15: Event<'d, T>, +} + +impl<'d, T: Instance> Ipc<'d, T> { + /// Create a new IPC driver. + pub fn new( + _p: Peri<'d, T>, + _irq: impl interrupt::typelevel::Binding> + 'd, + ) -> Self { + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + let _phantom = PhantomData; + #[rustfmt::skip] + let r = Self { // attributes on expressions are experimental + event0: Event { number: EventNumber::Event0, _phantom }, + event1: Event { number: EventNumber::Event1, _phantom }, + event2: Event { number: EventNumber::Event2, _phantom }, + event3: Event { number: EventNumber::Event3, _phantom }, + event4: Event { number: EventNumber::Event4, _phantom }, + event5: Event { number: EventNumber::Event5, _phantom }, + event6: Event { number: EventNumber::Event6, _phantom }, + event7: Event { number: EventNumber::Event7, _phantom }, + event8: Event { number: EventNumber::Event8, _phantom }, + event9: Event { number: EventNumber::Event9, _phantom }, + event10: Event { number: EventNumber::Event10, _phantom }, + event11: Event { number: EventNumber::Event11, _phantom }, + event12: Event { number: EventNumber::Event12, _phantom }, + event13: Event { number: EventNumber::Event13, _phantom }, + event14: Event { number: EventNumber::Event14, _phantom }, + event15: Event { number: EventNumber::Event15, _phantom }, + }; + r + } +} + +/// IPC event +pub struct Event<'d, T: Instance> { + number: EventNumber, + _phantom: PhantomData<&'d T>, +} + +impl<'d, T: Instance> Event<'d, T> { + /// Trigger the event. + pub fn trigger(&self) { + let nr = self.number; + T::regs().tasks_send(nr as usize).write_value(1); + } + + /// Wait for the event to be triggered. + pub async fn wait(&mut self) { + let regs = T::regs(); + let nr = self.number as usize; + regs.intenset().write(|w| w.0 = 1 << nr); + poll_fn(|cx| { + T::state().wakers[nr].register(cx.waker()); + + if regs.events_receive(nr).read() == 1 { + regs.events_receive(nr).write_value(0x00); + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + } + + /// Returns the [`EventNumber`] of the event. + pub fn number(&self) -> EventNumber { + self.number + } + + /// Create a handle that can trigger the event. + pub fn trigger_handle(&self) -> EventTrigger<'d, T> { + EventTrigger { + number: self.number, + _phantom: PhantomData, + } + } + + /// Configure the channels the event will broadcast to + pub fn configure_trigger>(&mut self, channels: I) { + T::regs().send_cnf(self.number as usize).write(|w| { + for channel in channels { + w.0 |= channel.mask(); + } + }) + } + + /// Configure the channels the event will listen on + pub fn configure_wait>(&mut self, channels: I) { + T::regs().receive_cnf(self.number as usize).write(|w| { + for channel in channels { + w.0 |= channel.mask(); + } + }); + } + + /// Get the task for the IPC event to use with PPI. + pub fn task(&self) -> ppi::Task<'d> { + let nr = self.number as usize; + let regs = T::regs(); + ppi::Task::from_reg(regs.tasks_send(nr)) + } + + /// Get the event for the IPC event to use with PPI. + pub fn event(&self) -> ppi::Event<'d> { + let nr = self.number as usize; + let regs = T::regs(); + ppi::Event::from_reg(regs.events_receive(nr)) + } + + /// Reborrow into a "child" Event. + /// + /// `self` will stay borrowed until the child Event is dropped. + pub fn reborrow(&mut self) -> Event<'_, T> { + Self { ..*self } + } + + /// Steal an IPC event by number. + /// + /// # Safety + /// + /// The event number must not be in use by another [`Event`]. + pub unsafe fn steal(number: EventNumber) -> Self { + Self { + number, + _phantom: PhantomData, + } + } +} + +/// A handle that can trigger an IPC event. +/// +/// This `struct` is returned by [`Event::trigger_handle`]. +#[derive(Debug, Copy, Clone)] +pub struct EventTrigger<'d, T: Instance> { + number: EventNumber, + _phantom: PhantomData<&'d T>, +} + +impl EventTrigger<'_, T> { + /// Trigger the event. + pub fn trigger(&self) { + let nr = self.number; + T::regs().tasks_send(nr as usize).write_value(1); + } + + /// Returns the [`EventNumber`] of the event. + pub fn number(&self) -> EventNumber { + self.number + } +} + +pub(crate) struct State { + wakers: [AtomicWaker; EVENT_COUNT], +} + +impl State { + pub(crate) const fn new() -> Self { + Self { + wakers: [const { AtomicWaker::new() }; EVENT_COUNT], + } + } +} + +pub(crate) trait SealedInstance { + fn regs() -> pac::ipc::Ipc; + fn state() -> &'static State; +} + +/// IPC peripheral instance. +#[allow(private_bounds)] +pub trait Instance: PeripheralType + SealedInstance + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: interrupt::typelevel::Interrupt; +} + +macro_rules! impl_ipc { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::ipc::SealedInstance for peripherals::$type { + fn regs() -> pac::ipc::Ipc { + pac::$pac_type + } + + fn state() -> &'static crate::ipc::State { + static STATE: crate::ipc::State = crate::ipc::State::new(); + &STATE + } + } + impl crate::ipc::Instance for peripherals::$type { + type Interrupt = crate::interrupt::typelevel::$irq; + } + }; +} diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 07ba2f6d4..5bce65a98 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -88,6 +88,8 @@ pub mod gpiote; #[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] pub mod i2s; +#[cfg(feature = "_nrf5340")] +pub mod ipc; #[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(any( feature = "nrf52832", diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index b440591cf..8fb8a50fd 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -26,7 +26,10 @@ features = ["defmt", "unstable-pac", "time-driver", "rp2040"] [features] default = [ "rt" ] -## Enable the rt feature of [`rp-pac`](https://docs.rs/rp-pac). This brings in the [`cortex-m-rt`](https://docs.rs/cortex-m-rt) crate, which adds startup code and minimal runtime initialization. + +## Enable the `rt` feature of [`rp-pac`](https://docs.rs/rp-pac). +## With `rt` enabled the PAC provides interrupt vectors instead of letting [`cortex-m-rt`](https://docs.rs/cortex-m-rt) do that. +## See for more info. rt = [ "rp-pac/rt" ] ## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers. diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 67aa5e540..6694aab66 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -1,4 +1,67 @@ -//! Clock configuration for the RP2040 +//! # Clock configuration for the RP2040 and RP235x microcontrollers. +//! +//! # Clock Configuration API +//! +//! This module provides both high-level convenience functions and low-level manual +//! configuration options for the RP2040 clock system. +//! +//! ## High-Level Convenience Functions +//! +//! For most users, these functions provide an easy way to configure clocks: +//! +//! - `ClockConfig::system_freq(125_000_000)` - Set system clock to a specific frequency with automatic voltage scaling +//! - `ClockConfig::crystal(12_000_000)` - Default configuration with 12MHz crystal giving 125MHz system clock +//! +//! ## Manual Configuration +//! +//! For advanced users who need precise control: +//! +//! ```rust,ignore +//! // Start with default configuration and customize it +//! let mut config = ClockConfig::default(); +//! +//! // Set custom PLL parameters +//! config.xosc = Some(XoscConfig { +//! hz: 12_000_000, +//! sys_pll: Some(PllConfig { +//! refdiv: 1, +//! fbdiv: 200, +//! post_div1: 6, +//! post_div2: 2, +//! }), +//! // ... other fields +//! }); +//! +//! // Set voltage for overclocking +//! config.core_voltage = CoreVoltage::V1_15; +//! ``` +//! +//! ## Examples +//! +//! ### Standard 125MHz configuration +//! ```rust,ignore +//! let config = ClockConfig::crystal(12_000_000); +//! ``` +//! +//! Or using the default configuration: +//! ```rust,ignore +//! let config = ClockConfig::default(); +//! ``` +//! +//! ### Overclock to 200MHz +//! ```rust,ignore +//! let config = ClockConfig::system_freq(200_000_000); +//! ``` +//! +//! ### Manual configuration for advanced scenarios +//! ```rust,ignore +//! use embassy_rp::clocks::{ClockConfig, XoscConfig, PllConfig, CoreVoltage}; +//! +//! // Start with defaults and customize +//! let mut config = ClockConfig::default(); +//! config.core_voltage = CoreVoltage::V1_15; +//! // Set other parameters as needed... +//! ``` #[cfg(feature = "rp2040")] use core::arch::asm; @@ -18,6 +81,7 @@ use crate::{pac, reset, Peri}; // gpin is not usually safe to use during the boot init() call, so it won't // be very useful until we have runtime clock reconfiguration. once this // happens we can resurrect the commented-out gpin bits. + struct Clocks { xosc: AtomicU32, sys: AtomicU32, @@ -26,6 +90,7 @@ struct Clocks { pll_usb: AtomicU32, usb: AtomicU32, adc: AtomicU32, + // See above re gpin handling being commented out // gpin0: AtomicU32, // gpin1: AtomicU32, rosc: AtomicU32, @@ -42,6 +107,7 @@ static CLOCKS: Clocks = Clocks { pll_usb: AtomicU32::new(0), usb: AtomicU32::new(0), adc: AtomicU32::new(0), + // See above re gpin handling being commented out // gpin0: AtomicU32::new(0), // gpin1: AtomicU32::new(0), rosc: AtomicU32::new(0), @@ -65,10 +131,64 @@ pub enum PeriClkSrc { Rosc = ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH as _, /// XOSC. Xosc = ClkPeriCtrlAuxsrc::XOSC_CLKSRC as _, + // See above re gpin handling being commented out // Gpin0 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN0 as _ , // Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1 as _ , } +/// Core voltage regulator settings for RP2040. +/// +/// The RP2040 voltage regulator can be configured for different output voltages. +/// Higher voltages allow for higher clock frequencies but increase power consumption and heat. +#[cfg(feature = "rp2040")] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u8)] +pub enum CoreVoltage { + /// 0.80V - Suitable for lower frequencies + V0_80 = 0b0000, + /// 0.85V + V0_85 = 0b0110, + /// 0.90V + V0_90 = 0b0111, + /// 0.95V + V0_95 = 0b1000, + /// 1.00V + V1_00 = 0b1001, + /// 1.05V + V1_05 = 0b1010, + /// 1.10V - Default voltage level + V1_10 = 0b1011, + /// 1.15V - Required for overclocking to 133-200MHz + V1_15 = 0b1100, + /// 1.20V + V1_20 = 0b1101, + /// 1.25V + V1_25 = 0b1110, + /// 1.30V + V1_30 = 0b1111, +} + +#[cfg(feature = "rp2040")] +impl CoreVoltage { + /// Get the recommended Brown-Out Detection (BOD) setting for this voltage. + /// Sets the BOD threshold to approximately 80% of the core voltage. + fn recommended_bod(self) -> u8 { + match self { + CoreVoltage::V0_80 => 0b0100, // 0.645V (~81% of 0.80V) + CoreVoltage::V0_85 => 0b0101, // 0.688V (~81% of 0.85V) + CoreVoltage::V0_90 => 0b0110, // 0.731V (~81% of 0.90V) + CoreVoltage::V0_95 => 0b0111, // 0.774V (~81% of 0.95V) + CoreVoltage::V1_00 => 0b1000, // 0.817V (~82% of 1.00V) + CoreVoltage::V1_05 => 0b1000, // 0.817V (~78% of 1.05V) + CoreVoltage::V1_10 => 0b1001, // 0.860V (~78% of 1.10V) + CoreVoltage::V1_15 => 0b1010, // 0.903V (~79% of 1.15V) + CoreVoltage::V1_20 => 0b1011, // 0.946V (~79% of 1.20V) + CoreVoltage::V1_25 => 0b1100, // 0.989V (~79% of 1.25V) + CoreVoltage::V1_30 => 0b1101, // 1.032V (~79% of 1.30V) + } + } +} + /// CLock configuration. #[non_exhaustive] pub struct ClockConfig { @@ -89,12 +209,63 @@ pub struct ClockConfig { /// RTC clock configuration. #[cfg(feature = "rp2040")] pub rtc_clk: Option, + /// Core voltage scaling (RP2040 only). Defaults to 1.10V. + #[cfg(feature = "rp2040")] + pub core_voltage: CoreVoltage, + /// Voltage stabilization delay in microseconds. + /// If not set, defaults will be used based on voltage level. + #[cfg(feature = "rp2040")] + pub voltage_stabilization_delay_us: Option, + // See above re gpin handling being commented out // gpin0: Option<(u32, Gpin<'static, AnyPin>)>, // gpin1: Option<(u32, Gpin<'static, AnyPin>)>, } +impl Default for ClockConfig { + /// Creates a minimal default configuration with safe values. + /// + /// This configuration uses the ring oscillator (ROSC) as the clock source + /// and sets minimal defaults that guarantee a working system. It's intended + /// as a starting point for manual configuration. + /// + /// Most users should use one of the more specific configuration functions: + /// - `ClockConfig::crystal()` - Standard configuration with external crystal + /// - `ClockConfig::rosc()` - Configuration using only the internal oscillator + /// - `ClockConfig::system_freq()` - Configuration for a specific system frequency + fn default() -> Self { + Self { + rosc: None, + xosc: None, + ref_clk: RefClkConfig { + src: RefClkSrc::Rosc, + div: 1, + }, + sys_clk: SysClkConfig { + src: SysClkSrc::Rosc, + div_int: 1, + div_frac: 0, + }, + peri_clk_src: None, + usb_clk: None, + adc_clk: None, + #[cfg(feature = "rp2040")] + rtc_clk: None, + #[cfg(feature = "rp2040")] + core_voltage: CoreVoltage::V1_10, + #[cfg(feature = "rp2040")] + voltage_stabilization_delay_us: None, + // See above re gpin handling being commented out + // gpin0: None, + // gpin1: None, + } + } +} + impl ClockConfig { /// Clock configuration derived from external crystal. + /// + /// This uses default settings for most parameters, suitable for typical use cases. + /// For manual control of PLL parameters, use `new_manual()` or modify the struct fields directly. pub fn crystal(crystal_hz: u32) -> Self { Self { rosc: Some(RoscConfig { @@ -152,6 +323,11 @@ impl ClockConfig { div_frac: 0, phase: 0, }), + #[cfg(feature = "rp2040")] + core_voltage: CoreVoltage::V1_10, // Use hardware default (1.10V) + #[cfg(feature = "rp2040")] + voltage_stabilization_delay_us: None, + // See above re gpin handling being commented out // gpin0: None, // gpin1: None, } @@ -192,20 +368,157 @@ impl ClockConfig { div_frac: 171, phase: 0, }), + #[cfg(feature = "rp2040")] + core_voltage: CoreVoltage::V1_10, // Use hardware default (1.10V) + #[cfg(feature = "rp2040")] + voltage_stabilization_delay_us: None, + // See above re gpin handling being commented out // gpin0: None, // gpin1: None, } } - // pub fn bind_gpin(&mut self, gpin: Gpin<'static, P>, hz: u32) { - // match P::NR { - // 0 => self.gpin0 = Some((hz, gpin.into())), - // 1 => self.gpin1 = Some((hz, gpin.into())), - // _ => unreachable!(), - // } - // // pin is now provisionally bound. if the config is applied it must be forgotten, - // // or Gpin::drop will deconfigure the clock input. - // } + /// Configure clocks derived from an external crystal with specific system frequency. + /// + /// This function calculates optimal PLL parameters to achieve the requested system + /// frequency. This only works for the usual 12MHz crystal. In case a different crystal is used, + /// You will have to set the PLL parameters manually. + /// + /// # Arguments + /// + /// * `sys_freq_hz` - The desired system clock frequency in Hz + /// + /// # Returns + /// + /// A ClockConfig configured to achieve the requested system frequency using the + /// the usual 12Mhz crystal, or panic if no valid parameters can be found. + /// + /// # Note on core voltage: + /// To date the only officially documented core voltages (see Datasheet section 2.15.3.1. Instances) are: + /// - Up to 133MHz: V1_10 (default) + /// - Above 133MHz: V1_15, but in the context of the datasheet covering reaching up to 200Mhz + /// That way all other frequencies below 133MHz or above 200MHz are not explicitly documented and not covered here. + /// In case You want to go below 133MHz or above 200MHz and want a different voltage, You will have to set that manually and with caution. + #[cfg(feature = "rp2040")] + pub fn system_freq(hz: u32) -> Self { + // Start with the standard configuration from crystal() + const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000; + let mut config = Self::crystal(DEFAULT_CRYSTAL_HZ); + + // No need to modify anything if target frequency is already 125MHz + // (which is what crystal() configures by default) + if hz == 125_000_000 { + return config; + } + + // Find optimal PLL parameters for the requested frequency + let sys_pll_params = find_pll_params(DEFAULT_CRYSTAL_HZ, hz) + .unwrap_or_else(|| panic!("Could not find valid PLL parameters for system clock")); + + // Replace the sys_pll configuration with our custom parameters + if let Some(xosc) = &mut config.xosc { + xosc.sys_pll = Some(sys_pll_params); + } + + // Set the voltage scale based on the target frequency + // Higher frequencies require higher voltage + #[cfg(feature = "rp2040")] + { + config.core_voltage = match hz { + freq if freq > 133_000_000 => CoreVoltage::V1_15, + _ => CoreVoltage::V1_10, // Use default voltage (V1_10) + }; + } + + config + } + + /// Configure with manual PLL settings for full control over system clock + /// + /// This method provides a simple way to configure the system with custom PLL parameters + /// without needing to understand the full nested configuration structure. + /// + /// # Arguments + /// + /// * `xosc_hz` - The frequency of the external crystal in Hz + /// * `pll_config` - The PLL configuration parameters to achieve desired frequency + /// * `core_voltage` - Voltage scaling for overclocking (required for >133MHz) + /// + /// # Returns + /// + /// A ClockConfig configured with the specified PLL parameters + /// + /// # Example + /// + /// ```rust,ignore + /// // Configure for 200MHz operation + /// let config = Config::default(); + /// config.clocks = ClockConfig::manual_pll( + /// 12_000_000, + /// PllConfig { + /// refdiv: 1, // Reference divider (12 MHz / 1 = 12 MHz) + /// fbdiv: 100, // Feedback divider (12 MHz * 100 = 1200 MHz VCO) + /// post_div1: 3, // First post divider (1200 MHz / 3 = 400 MHz) + /// post_div2: 2, // Second post divider (400 MHz / 2 = 200 MHz) + /// }, + /// CoreVoltage::V1_15 + /// ); + /// ``` + #[cfg(feature = "rp2040")] + pub fn manual_pll(xosc_hz: u32, pll_config: PllConfig, core_voltage: CoreVoltage) -> Self { + // Validate PLL parameters + assert!(pll_config.is_valid(xosc_hz), "Invalid PLL parameters"); + + let mut config = Self::default(); + + config.xosc = Some(XoscConfig { + hz: xosc_hz, + sys_pll: Some(pll_config), + usb_pll: Some(PllConfig { + refdiv: 1, + fbdiv: 120, + post_div1: 6, + post_div2: 5, + }), + delay_multiplier: 128, + }); + + config.ref_clk = RefClkConfig { + src: RefClkSrc::Xosc, + div: 1, + }; + + config.sys_clk = SysClkConfig { + src: SysClkSrc::PllSys, + div_int: 1, + div_frac: 0, + }; + + config.core_voltage = core_voltage; + config.peri_clk_src = Some(PeriClkSrc::Sys); + + // Set reasonable defaults for other clocks + config.usb_clk = Some(UsbClkConfig { + src: UsbClkSrc::PllUsb, + div: 1, + phase: 0, + }); + + config.adc_clk = Some(AdcClkConfig { + src: AdcClkSrc::PllUsb, + div: 1, + phase: 0, + }); + + config.rtc_clk = Some(RtcClkConfig { + src: RtcClkSrc::PllUsb, + div_int: 1024, + div_frac: 0, + phase: 0, + }); + + config + } } /// ROSC freq range. @@ -251,6 +564,7 @@ pub struct XoscConfig { } /// PLL configuration. +#[derive(Clone, Copy, Debug)] pub struct PllConfig { /// Reference divisor. pub refdiv: u8, @@ -262,6 +576,50 @@ pub struct PllConfig { pub post_div2: u8, } +impl PllConfig { + /// Calculate the output frequency for this PLL configuration + /// given an input frequency. + pub fn output_frequency(&self, input_hz: u32) -> u32 { + let ref_freq = input_hz / self.refdiv as u32; + let vco_freq = ref_freq * self.fbdiv as u32; + vco_freq / ((self.post_div1 * self.post_div2) as u32) + } + + /// Check if this PLL configuration is valid for the given input frequency. + pub fn is_valid(&self, input_hz: u32) -> bool { + // Check divisor constraints + if self.refdiv < 1 || self.refdiv > 63 { + return false; + } + if self.fbdiv < 16 || self.fbdiv > 320 { + return false; + } + if self.post_div1 < 1 || self.post_div1 > 7 { + return false; + } + if self.post_div2 < 1 || self.post_div2 > 7 { + return false; + } + if self.post_div2 > self.post_div1 { + return false; + } + + // Calculate reference frequency + let ref_freq = input_hz / self.refdiv as u32; + + // Check reference frequency range + if ref_freq < 5_000_000 || ref_freq > 800_000_000 { + return false; + } + + // Calculate VCO frequency + let vco_freq = ref_freq * self.fbdiv as u32; + + // Check VCO frequency range + vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000 + } +} + /// Reference clock config. pub struct RefClkConfig { /// Reference clock source. @@ -280,6 +638,7 @@ pub enum RefClkSrc { Rosc, /// PLL USB. PllUsb, + // See above re gpin handling being commented out // Gpin0, // Gpin1, } @@ -298,6 +657,7 @@ pub enum SysClkSrc { Rosc, /// XOSC. Xosc, + // See above re gpin handling being commented out // Gpin0, // Gpin1, } @@ -333,6 +693,7 @@ pub enum UsbClkSrc { Rosc = ClkUsbCtrlAuxsrc::ROSC_CLKSRC_PH as _, /// XOSC. Xosc = ClkUsbCtrlAuxsrc::XOSC_CLKSRC as _, + // See above re gpin handling being commented out // Gpin0 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN0 as _ , // Gpin1 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN1 as _ , } @@ -360,6 +721,7 @@ pub enum AdcClkSrc { Rosc = ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH as _, /// XOSC. Xosc = ClkAdcCtrlAuxsrc::XOSC_CLKSRC as _, + // See above re gpin handling being commented out // Gpin0 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN0 as _ , // Gpin1 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN1 as _ , } @@ -388,6 +750,7 @@ pub enum RtcClkSrc { Rosc = ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH as _, /// XOSC. Xosc = ClkRtcCtrlAuxsrc::XOSC_CLKSRC as _, + // See above re gpin handling being commented out // Gpin0 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN0 as _ , // Gpin1 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN1 as _ , } @@ -405,6 +768,103 @@ pub struct RtcClkConfig { pub phase: u8, } +/// Find valid PLL parameters (refdiv, fbdiv, post_div1, post_div2) for a target output frequency +/// based on the input frequency. +/// +/// This function searches for the best PLL configuration to achieve the requested target frequency +/// while staying within the VCO frequency range of 750MHz to 1800MHz. It prioritizes stability +/// over exact frequency matching by using larger divisors where possible. +/// +/// # Parameters +/// +/// * `input_hz`: The input frequency in Hz (typically the crystal frequency, e.g. 12MHz for th most common one used on rp2040 boards) +/// * `target_hz`: The desired output frequency in Hz (e.g. 125MHz for standard RP2040 operation) +/// +/// # Returns +/// +/// * `Some(PllConfig)` if valid parameters were found +/// * `None` if no valid parameters could be found for the requested combination +/// +/// # Example +/// +/// ```rust,ignore +/// // Find parameters for 133MHz system clock from 12MHz crystal +/// let pll_params = find_pll_params(12_000_000, 133_000_000).unwrap(); +/// ``` +#[cfg(feature = "rp2040")] +fn find_pll_params(input_hz: u32, target_hz: u32) -> Option { + // Fixed reference divider for system PLL + const PLL_SYS_REFDIV: u8 = 1; + + // Calculate reference frequency + let reference_freq = input_hz as u64 / PLL_SYS_REFDIV as u64; + + // Start from highest fbdiv for better stability (like SDK does) + for fbdiv in (16..=320).rev() { + let vco_freq = reference_freq * fbdiv; + + // Check VCO frequency is within valid range + if vco_freq < 750_000_000 || vco_freq > 1_800_000_000 { + continue; + } + + // Try all possible postdiv combinations starting from larger values + // (more conservative/stable approach) + for post_div1 in (1..=7).rev() { + for post_div2 in (1..=post_div1).rev() { + let out_freq = vco_freq / (post_div1 * post_div2); + + // Check if we get the exact target frequency without remainder + if out_freq == target_hz as u64 && (vco_freq % (post_div1 * post_div2) == 0) { + return Some(PllConfig { + refdiv: PLL_SYS_REFDIV, + fbdiv: fbdiv as u16, + post_div1: post_div1 as u8, + post_div2: post_div2 as u8, + }); + } + } + } + } + + // If we couldn't find an exact match, find the closest match + let mut best_config = None; + let mut min_diff = u32::MAX; + + for fbdiv in (16..=320).rev() { + let vco_freq = reference_freq * fbdiv; + + if vco_freq < 750_000_000 || vco_freq > 1_800_000_000 { + continue; + } + + for post_div1 in (1..=7).rev() { + for post_div2 in (1..=post_div1).rev() { + let out_freq = (vco_freq / (post_div1 * post_div2) as u64) as u32; + let diff = if out_freq > target_hz { + out_freq - target_hz + } else { + target_hz - out_freq + }; + + // If this is closer to the target, save it + if diff < min_diff { + min_diff = diff; + best_config = Some(PllConfig { + refdiv: PLL_SYS_REFDIV, + fbdiv: fbdiv as u16, + post_div1: post_div1 as u8, + post_div2: post_div2 as u8, + }); + } + } + } + } + + // Return the closest match if we found one + best_config +} + /// safety: must be called exactly once at bootup pub(crate) unsafe fn init(config: ClockConfig) { // Reset everything except: @@ -447,6 +907,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { reset::reset(peris); reset::unreset_wait(peris); + // See above re gpin handling being commented out // let gpin0_freq = config.gpin0.map_or(0, |p| { // core::mem::forget(p.1); // p.0 @@ -464,19 +925,60 @@ pub(crate) unsafe fn init(config: ClockConfig) { }; CLOCKS.rosc.store(rosc_freq, Ordering::Relaxed); + // Set Core Voltage (RP2040 only), if we have config for it and we're not using the default + #[cfg(feature = "rp2040")] + { + let voltage = config.core_voltage; + let vreg = pac::VREG_AND_CHIP_RESET; + let current_vsel = vreg.vreg().read().vsel(); + let target_vsel = voltage as u8; + + // If the target voltage is different from the current one, we need to change it + if target_vsel != current_vsel { + // Use modify() to preserve the HIZ and EN bits - otherwise we will disable the regulator when changing voltage + vreg.vreg().modify(|w| w.set_vsel(target_vsel)); + + // Wait for the voltage to stabilize. Use the provided delay or default based on voltage + let settling_time_us = config.voltage_stabilization_delay_us.unwrap_or_else(|| { + match voltage { + CoreVoltage::V1_15 => 1000, // 1ms for 1.15V + CoreVoltage::V1_20 | CoreVoltage::V1_25 | CoreVoltage::V1_30 => 2000, // 2ms for higher voltages + _ => 0, // no delay for all others + } + }); + + if settling_time_us != 0 { + // Delay in microseconds, using the ROSC frequency to calculate cycles + let cycles_per_us = rosc_freq / 1_000_000; + let delay_cycles = settling_time_us * cycles_per_us; + cortex_m::asm::delay(delay_cycles); + } + + // Only now set the BOD level. At this point the voltage is considered stable. + vreg.bod().write(|w| { + w.set_vsel(voltage.recommended_bod()); + w.set_en(true); // Enable brownout detection + }); + } + } + let (xosc_freq, pll_sys_freq, pll_usb_freq) = match config.xosc { Some(config) => { // start XOSC - // datasheet mentions support for clock inputs into XIN, but doesn't go into - // how this is achieved. pico-sdk doesn't support this at all. start_xosc(config.hz, config.delay_multiplier); let pll_sys_freq = match config.sys_pll { - Some(sys_pll_config) => configure_pll(pac::PLL_SYS, config.hz, sys_pll_config), + Some(sys_pll_config) => match configure_pll(pac::PLL_SYS, config.hz, sys_pll_config) { + Ok(freq) => freq, + Err(e) => panic!("Failed to configure PLL_SYS: {}", e), + }, None => 0, }; let pll_usb_freq = match config.usb_pll { - Some(usb_pll_config) => configure_pll(pac::PLL_USB, config.hz, usb_pll_config), + Some(usb_pll_config) => match configure_pll(pac::PLL_USB, config.hz, usb_pll_config) { + Ok(freq) => freq, + Err(e) => panic!("Failed to configure PLL_USB: {}", e), + }, None => 0, }; @@ -484,6 +986,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { } None => (0, 0, 0), }; + CLOCKS.xosc.store(xosc_freq, Ordering::Relaxed); CLOCKS.pll_sys.store(pll_sys_freq, Ordering::Relaxed); CLOCKS.pll_usb.store(pll_usb_freq, Ordering::Relaxed); @@ -496,6 +999,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { RefClkSrc::Xosc => (Src::XOSC_CLKSRC, Aux::CLKSRC_PLL_USB, xosc_freq / div), RefClkSrc::Rosc => (Src::ROSC_CLKSRC_PH, Aux::CLKSRC_PLL_USB, rosc_freq / div), RefClkSrc::PllUsb => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq / div), + // See above re gpin handling being commented out // RefClkSrc::Gpin0 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN0, gpin0_freq / div), // RefClkSrc::Gpin1 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN1, gpin1_freq / div), } @@ -540,6 +1044,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { SysClkSrc::PllUsb => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq), SysClkSrc::Rosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::ROSC_CLKSRC, rosc_freq), SysClkSrc::Xosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::XOSC_CLKSRC, xosc_freq), + // See above re gpin handling being commented out // SysClkSrc::Gpin0 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN0, gpin0_freq), // SysClkSrc::Gpin1 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN1, gpin1_freq), }; @@ -583,6 +1088,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { PeriClkSrc::PllUsb => pll_usb_freq, PeriClkSrc::Rosc => rosc_freq, PeriClkSrc::Xosc => xosc_freq, + // See above re gpin handling being commented out // PeriClkSrc::Gpin0 => gpin0_freq, // PeriClkSrc::Gpin1 => gpin1_freq, }; @@ -608,6 +1114,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { UsbClkSrc::PllSys => pll_sys_freq, UsbClkSrc::Rosc => rosc_freq, UsbClkSrc::Xosc => xosc_freq, + // See above re gpin handling being commented out // UsbClkSrc::Gpin0 => gpin0_freq, // UsbClkSrc::Gpin1 => gpin1_freq, }; @@ -631,6 +1138,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { AdcClkSrc::PllSys => pll_sys_freq, AdcClkSrc::Rosc => rosc_freq, AdcClkSrc::Xosc => xosc_freq, + // See above re gpin handling being commented out // AdcClkSrc::Gpin0 => gpin0_freq, // AdcClkSrc::Gpin1 => gpin1_freq, }; @@ -659,6 +1167,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { RtcClkSrc::PllSys => pll_sys_freq, RtcClkSrc::Rosc => rosc_freq, RtcClkSrc::Xosc => xosc_freq, + // See above re gpin handling being commented out // RtcClkSrc::Gpin0 => gpin0_freq, // RtcClkSrc::Gpin1 => gpin1_freq, }; @@ -725,6 +1234,7 @@ pub fn xosc_freq() -> u32 { CLOCKS.xosc.load(Ordering::Relaxed) } +// See above re gpin handling being commented out // pub fn gpin0_freq() -> u32 { // CLOCKS.gpin0.load(Ordering::Relaxed) // } @@ -783,46 +1293,100 @@ fn start_xosc(crystal_hz: u32, delay_multiplier: u32) { while !pac::XOSC.status().read().stable() {} } +/// PLL (Phase-Locked Loop) configuration #[inline(always)] -fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 { +fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result { + // Calculate reference frequency let ref_freq = input_freq / config.refdiv as u32; + + // Validate PLL parameters + // Feedback divider (FBDIV) must be between 16 and 320 assert!(config.fbdiv >= 16 && config.fbdiv <= 320); + + // Post divider 1 (POSTDIV1) must be between 1 and 7 assert!(config.post_div1 >= 1 && config.post_div1 <= 7); + + // Post divider 2 (POSTDIV2) must be between 1 and 7 assert!(config.post_div2 >= 1 && config.post_div2 <= 7); + + // Post divider 2 (POSTDIV2) must be less than or equal to post divider 1 (POSTDIV1) + assert!(config.post_div2 <= config.post_div1); + + // Reference divider (REFDIV) must be between 1 and 63 assert!(config.refdiv >= 1 && config.refdiv <= 63); + + // Reference frequency (REF_FREQ) must be between 5MHz and 800MHz assert!(ref_freq >= 5_000_000 && ref_freq <= 800_000_000); + + // Calculate VCO frequency let vco_freq = ref_freq.saturating_mul(config.fbdiv as u32); + + // VCO (Voltage Controlled Oscillator) frequency must be between 750MHz and 1800MHz assert!(vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000); - // Load VCO-related dividers before starting VCO - p.cs().write(|w| w.set_refdiv(config.refdiv as _)); - p.fbdiv_int().write(|w| w.set_fbdiv_int(config.fbdiv)); + // We follow the SDK's approach to PLL configuration which is: + // 1. Power down PLL + // 2. Configure the reference divider + // 3. Configure the feedback divider + // 4. Power up PLL and VCO + // 5. Wait for PLL to lock + // 6. Configure post-dividers + // 7. Enable post-divider output - // Turn on PLL - let pwr = p.pwr().write(|w| { - w.set_dsmpd(true); // "nothing is achieved by setting this low" - w.set_pd(false); - w.set_vcopd(false); - w.set_postdivpd(true); + // 1. Power down PLL before configuration + p.pwr().write(|w| { + w.set_pd(true); // Power down the PLL + w.set_vcopd(true); // Power down the VCO + w.set_postdivpd(true); // Power down the post divider + w.set_dsmpd(true); // Disable fractional mode *w }); - // Wait for PLL to lock - while !p.cs().read().lock() {} + // Short delay after powering down + cortex_m::asm::delay(10); - // Set post-dividers + // 2. Configure reference divider first + p.cs().write(|w| w.set_refdiv(config.refdiv as _)); + + // 3. Configure feedback divider + p.fbdiv_int().write(|w| w.set_fbdiv_int(config.fbdiv)); + + // 4. Power up PLL and VCO, but keep post divider powered down during initial lock + p.pwr().write(|w| { + w.set_pd(false); // Power up the PLL + w.set_vcopd(false); // Power up the VCO + w.set_postdivpd(true); // Keep post divider powered down during initial lock + w.set_dsmpd(true); // Disable fractional mode (simpler configuration) + *w + }); + + // 5. Wait for PLL to lock with a timeout + let mut timeout = 1_000_000; + while !p.cs().read().lock() { + timeout -= 1; + if timeout == 0 { + // PLL failed to lock, return 0 to indicate failure + return Err("PLL failed to lock"); + } + } + + // 6. Configure post dividers after PLL is locked p.prim().write(|w| { w.set_postdiv1(config.post_div1); w.set_postdiv2(config.post_div2); }); - // Turn on post divider - p.pwr().write(|w| { - *w = pwr; - w.set_postdivpd(false); + // 7. Enable the post divider output + p.pwr().modify(|w| { + w.set_postdivpd(false); // Power up post divider + *w }); - vco_freq / ((config.post_div1 * config.post_div2) as u32) + // Final delay to ensure everything is stable + cortex_m::asm::delay(100); + + // Calculate and return actual output frequency + Ok(vco_freq / ((config.post_div1 * config.post_div2) as u32)) } /// General purpose input clock pin. @@ -906,6 +1470,7 @@ impl_gpoutpin!(PIN_25, 3); pub enum GpoutSrc { /// Sys PLL. PllSys = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS as _, + // See above re gpin handling being commented out // Gpin0 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0 as _ , // Gpin1 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1 as _ , /// USB PLL. @@ -1001,6 +1566,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { let base = match src { ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), + // See above re gpin handling being commented out // ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), // ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), @@ -1009,7 +1575,6 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { ClkGpoutCtrlAuxsrc::CLK_SYS => clk_sys_freq(), ClkGpoutCtrlAuxsrc::CLK_USB => clk_usb_freq(), ClkGpoutCtrlAuxsrc::CLK_ADC => clk_adc_freq(), - //ClkGpoutCtrlAuxsrc::CLK_RTC => clk_rtc_freq() as _, ClkGpoutCtrlAuxsrc::CLK_REF => clk_ref_freq(), _ => unreachable!(), }; @@ -1069,6 +1634,7 @@ impl rand_core::RngCore for RoscRng { dest.fill_with(Self::next_u8) } } + /// Enter the `DORMANT` sleep state. This will stop *all* internal clocks /// and can only be exited through resets, dormant-wake GPIO interrupts, /// and RTC interrupts. If RTC is clocked from an internal clock source @@ -1197,3 +1763,196 @@ pub fn dormant_sleep() { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(feature = "rp2040")] + #[test] + fn test_find_pll_params() { + #[cfg(feature = "rp2040")] + { + // Test standard 125 MHz configuration with 12 MHz crystal + let params = find_pll_params(12_000_000, 125_000_000).unwrap(); + assert_eq!(params.refdiv, 1); + assert_eq!(params.fbdiv, 125); + + // Test USB PLL configuration for 48MHz + // The algorithm may find different valid parameters than the SDK defaults + // We'll check that it generates a valid configuration that produces 48MHz + let params = find_pll_params(12_000_000, 48_000_000).unwrap(); + assert_eq!(params.refdiv, 1); + + // Calculate the actual output frequency + let ref_freq = 12_000_000 / params.refdiv as u32; + let vco_freq = ref_freq as u64 * params.fbdiv as u64; + let output_freq = (vco_freq / ((params.post_div1 * params.post_div2) as u64)) as u32; + + // Verify the output frequency is correct + assert_eq!(output_freq, 48_000_000); + + // Verify VCO frequency is in valid range + assert!(vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000); + + // Test overclocked configuration for 200 MHz + let params = find_pll_params(12_000_000, 200_000_000).unwrap(); + assert_eq!(params.refdiv, 1); + let vco_freq = 12_000_000 as u64 * params.fbdiv as u64; + let output_freq = (vco_freq / ((params.post_div1 * params.post_div2) as u64)) as u32; + assert_eq!(output_freq, 200_000_000); + assert!(vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000); // VCO in valid range + + // Test non-standard crystal with 16 MHz + let params = find_pll_params(16_000_000, 125_000_000).unwrap(); + let vco_freq = (16_000_000 / params.refdiv as u32) as u64 * params.fbdiv as u64; + let output_freq = (vco_freq / ((params.post_div1 * params.post_div2) as u64)) as u32; + + // Test non-standard crystal with 15 MHz + let params = find_pll_params(15_000_000, 125_000_000).unwrap(); + let vco_freq = (15_000_000 / params.refdiv as u32) as u64 * params.fbdiv as u64; + let output_freq = (vco_freq / ((params.post_div1 * params.post_div2) as u64)) as u32; + + // With a 15 MHz crystal, we might not get exactly 125 MHz + // Check that it's close enough (within 0.2% margin) + let freq_diff = if output_freq > 125_000_000 { + output_freq - 125_000_000 + } else { + 125_000_000 - output_freq + }; + let error_percentage = (freq_diff as f64 / 125_000_000.0) * 100.0; + assert!( + error_percentage < 0.2, + "Output frequency {} is not close enough to target 125 MHz. Error: {:.2}%", + output_freq, + error_percentage + ); + + assert!(vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000); + } + } + + #[cfg(feature = "rp2040")] + #[test] + fn test_pll_config_validation() { + // Test PLL configuration validation logic + let valid_config = PllConfig { + refdiv: 1, + fbdiv: 125, + post_div1: 6, + post_div2: 2, + }; + + // Valid configuration should pass validation + assert!(valid_config.is_valid(12_000_000)); + + // Test fbdiv constraints + let mut invalid_config = valid_config; + invalid_config.fbdiv = 15; // Below minimum of 16 + assert!(!invalid_config.is_valid(12_000_000)); + + invalid_config.fbdiv = 321; // Above maximum of 320 + assert!(!invalid_config.is_valid(12_000_000)); + + // Test post_div constraints + invalid_config = valid_config; + invalid_config.post_div1 = 0; // Below minimum of 1 + assert!(!invalid_config.is_valid(12_000_000)); + + invalid_config = valid_config; + invalid_config.post_div1 = 8; // Above maximum of 7 + assert!(!invalid_config.is_valid(12_000_000)); + + // Test post_div2 must be <= post_div1 + invalid_config = valid_config; + invalid_config.post_div2 = 7; + invalid_config.post_div1 = 3; + assert!(!invalid_config.is_valid(12_000_000)); + + // Test reference frequency constraints + invalid_config = valid_config; + assert!(!invalid_config.is_valid(4_000_000)); // Below minimum of 5 MHz + assert!(!invalid_config.is_valid(900_000_000)); // Above maximum of 800 MHz + + // Test VCO frequency constraints + invalid_config = valid_config; + invalid_config.fbdiv = 16; + assert!(!invalid_config.is_valid(12_000_000)); // VCO too low: 12MHz * 16 = 192MHz + + // Test VCO frequency constraints - too high + invalid_config = valid_config; + invalid_config.fbdiv = 200; + invalid_config.refdiv = 1; + // This should be INVALID: 12MHz * 200 = 2400MHz exceeds max VCO of 1800MHz + assert!(!invalid_config.is_valid(12_000_000)); + + // Test a valid high VCO configuration + invalid_config.fbdiv = 150; // 12MHz * 150 = 1800MHz, exactly at the limit + assert!(invalid_config.is_valid(12_000_000)); + } + + #[cfg(feature = "rp2040")] + #[test] + fn test_manual_pll_helper() { + { + // Test the new manual_pll helper method + let config = ClockConfig::manual_pll( + 12_000_000, + PllConfig { + refdiv: 1, + fbdiv: 100, + post_div1: 3, + post_div2: 2, + }, + CoreVoltage::V1_15, + ); + + // Check voltage scale was set correctly + assert_eq!(config.core_voltage, CoreVoltage::V1_15); + + // Check PLL config was set correctly + assert_eq!(config.xosc.as_ref().unwrap().sys_pll.as_ref().unwrap().refdiv, 1); + assert_eq!(config.xosc.as_ref().unwrap().sys_pll.as_ref().unwrap().fbdiv, 100); + assert_eq!(config.xosc.as_ref().unwrap().sys_pll.as_ref().unwrap().post_div1, 3); + assert_eq!(config.xosc.as_ref().unwrap().sys_pll.as_ref().unwrap().post_div2, 2); + + // Check we get the expected frequency + assert_eq!( + config + .xosc + .as_ref() + .unwrap() + .sys_pll + .as_ref() + .unwrap() + .output_frequency(12_000_000), + 200_000_000 + ); + } + } + + #[cfg(feature = "rp2040")] + #[test] + fn test_auto_voltage_scaling() { + { + // Test automatic voltage scaling based on frequency + // Under 133 MHz should use default voltage (V1_10) + let config = ClockConfig::system_freq(125_000_000); + assert_eq!(config.core_voltage, CoreVoltage::V1_10); + + // 133-200 MHz should use V1_15 + let config = ClockConfig::system_freq(150_000_000); + assert_eq!(config.core_voltage, CoreVoltage::V1_15); + let config = ClockConfig::system_freq(200_000_000); + assert_eq!(config.core_voltage, CoreVoltage::V1_15); + + // Above 200 MHz should use V1_25 + let config = ClockConfig::system_freq(250_000_000); + assert_eq!(config.core_voltage, CoreVoltage::V1_15); + + // Below 125 MHz should use V1_10 + let config = ClockConfig::system_freq(100_000_000); + assert_eq!(config.core_voltage, CoreVoltage::V1_10); + } + } +} diff --git a/embassy-rp/src/pio_programs/clock_divider.rs b/embassy-rp/src/pio_programs/clock_divider.rs new file mode 100644 index 000000000..02e353f53 --- /dev/null +++ b/embassy-rp/src/pio_programs/clock_divider.rs @@ -0,0 +1,25 @@ +//! Helper functions for calculating PIO clock dividers + +use fixed::traits::ToFixed; +use fixed::types::extra::U8; + +use crate::clocks::clk_sys_freq; + +/// Calculate a PIO clock divider value based on the desired target frequency. +/// +/// # Arguments +/// +/// * `target_hz` - The desired PIO clock frequency in Hz +/// +/// # Returns +/// +/// A fixed-point divider value suitable for use in a PIO state machine configuration +#[inline] +pub fn calculate_pio_clock_divider(target_hz: u32) -> fixed::FixedU32 { + // Requires a non-zero frequency + assert!(target_hz > 0, "PIO clock frequency cannot be zero"); + + // Calculate the divider + let divider = (clk_sys_freq() + target_hz / 2) / target_hz; + divider.to_fixed() +} diff --git a/embassy-rp/src/pio_programs/hd44780.rs b/embassy-rp/src/pio_programs/hd44780.rs index 5846a8027..546c85a89 100644 --- a/embassy-rp/src/pio_programs/hd44780.rs +++ b/embassy-rp/src/pio_programs/hd44780.rs @@ -5,6 +5,7 @@ use crate::pio::{ Common, Config, Direction, FifoJoin, Instance, Irq, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine, }; +use crate::pio_programs::clock_divider::calculate_pio_clock_divider; use crate::Peri; /// This struct represents a HD44780 program that takes command words ( <0:4>) @@ -134,7 +135,10 @@ impl<'l, P: Instance, const S: usize> PioHD44780<'l, P, S> { let mut cfg = Config::default(); cfg.use_program(&word_prg.prg, &[&e]); - cfg.clock_divider = 125u8.into(); + + // Target 1 MHz PIO clock (each cycle is 1µs) + cfg.clock_divider = calculate_pio_clock_divider(1_000_000); + cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); cfg.shift_out = ShiftConfig { auto_fill: true, @@ -160,7 +164,10 @@ impl<'l, P: Instance, const S: usize> PioHD44780<'l, P, S> { let mut cfg = Config::default(); cfg.use_program(&seq_prg.prg, &[&e]); - cfg.clock_divider = 8u8.into(); // ~64ns/insn + + // Target ~15.6 MHz PIO clock (~64ns/insn) + cfg.clock_divider = calculate_pio_clock_divider(15_600_000); + cfg.set_jmp_pin(&db7); cfg.set_set_pins(&[&rs, &rw]); cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); diff --git a/embassy-rp/src/pio_programs/mod.rs b/embassy-rp/src/pio_programs/mod.rs index 74537825b..8eac328b3 100644 --- a/embassy-rp/src/pio_programs/mod.rs +++ b/embassy-rp/src/pio_programs/mod.rs @@ -1,5 +1,6 @@ //! Pre-built pio programs for common interfaces +pub mod clock_divider; pub mod hd44780; pub mod i2s; pub mod onewire; diff --git a/embassy-rp/src/pio_programs/rotary_encoder.rs b/embassy-rp/src/pio_programs/rotary_encoder.rs index e520da8a3..70b3795e9 100644 --- a/embassy-rp/src/pio_programs/rotary_encoder.rs +++ b/embassy-rp/src/pio_programs/rotary_encoder.rs @@ -1,11 +1,10 @@ //! PIO backed quadrature encoder -use fixed::traits::ToFixed; - use crate::gpio::Pull; use crate::pio::{ Common, Config, Direction as PioDirection, FifoJoin, Instance, LoadedProgram, PioPin, ShiftDirection, StateMachine, }; +use crate::pio_programs::clock_divider::calculate_pio_clock_divider; use crate::Peri; /// This struct represents an Encoder program loaded into pio instruction memory. @@ -48,7 +47,10 @@ impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> { cfg.set_in_pins(&[&pin_a, &pin_b]); cfg.fifo_join = FifoJoin::RxOnly; cfg.shift_in.direction = ShiftDirection::Left; - cfg.clock_divider = 10_000.to_fixed(); + + // Target 12.5 KHz PIO clock + cfg.clock_divider = calculate_pio_clock_divider(12_500); + cfg.use_program(&program.prg, &[]); sm.set_config(&cfg); sm.set_enable(true); diff --git a/embassy-rp/src/pio_programs/stepper.rs b/embassy-rp/src/pio_programs/stepper.rs index 495191659..0e9a8daf9 100644 --- a/embassy-rp/src/pio_programs/stepper.rs +++ b/embassy-rp/src/pio_programs/stepper.rs @@ -2,11 +2,8 @@ use core::mem::{self, MaybeUninit}; -use fixed::traits::ToFixed; -use fixed::types::extra::U8; -use fixed::FixedU32; - use crate::pio::{Common, Config, Direction, Instance, Irq, LoadedProgram, PioPin, StateMachine}; +use crate::pio_programs::clock_divider::calculate_pio_clock_divider; use crate::Peri; /// This struct represents a Stepper driver program loaded into pio instruction memory. @@ -64,7 +61,9 @@ impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> { sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]); let mut cfg = Config::default(); cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]); - cfg.clock_divider = (125_000_000 / (100 * 136)).to_fixed(); + + cfg.clock_divider = calculate_pio_clock_divider(100 * 136); + cfg.use_program(&program.prg, &[]); sm.set_config(&cfg); sm.set_enable(true); @@ -73,9 +72,11 @@ impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> { /// Set pulse frequency pub fn set_frequency(&mut self, freq: u32) { - let clock_divider: FixedU32 = (125_000_000 / (freq * 136)).to_fixed(); - assert!(clock_divider <= 65536, "clkdiv must be <= 65536"); - assert!(clock_divider >= 1, "clkdiv must be >= 1"); + let clock_divider = calculate_pio_clock_divider(freq * 136); + let divider_f32 = clock_divider.to_num::(); + assert!(divider_f32 <= 65536.0, "clkdiv must be <= 65536"); + assert!(divider_f32 >= 1.0, "clkdiv must be >= 1"); + self.sm.set_clock_divider(clock_divider); self.sm.clkdiv_restart(); } diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index da18138b5..02649ad81 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -34,28 +34,29 @@ impl State { } /// Buffered UART driver. -pub struct BufferedUart<'d, T: Instance> { - pub(crate) rx: BufferedUartRx<'d, T>, - pub(crate) tx: BufferedUartTx<'d, T>, +pub struct BufferedUart { + pub(super) rx: BufferedUartRx, + pub(super) tx: BufferedUartTx, } /// Buffered UART RX handle. -pub struct BufferedUartRx<'d, T: Instance> { - pub(crate) phantom: PhantomData<&'d mut T>, +pub struct BufferedUartRx { + pub(super) info: &'static Info, + pub(super) state: &'static State, } /// Buffered UART TX handle. -pub struct BufferedUartTx<'d, T: Instance> { - pub(crate) phantom: PhantomData<&'d mut T>, +pub struct BufferedUartTx { + pub(super) info: &'static Info, + pub(super) state: &'static State, } -pub(crate) fn init_buffers<'d, T: Instance + 'd>( - _irq: impl Binding>, +pub(super) fn init_buffers<'d>( + info: &Info, + state: &State, tx_buffer: Option<&'d mut [u8]>, rx_buffer: Option<&'d mut [u8]>, ) { - let state = T::buffered_state(); - if let Some(tx_buffer) = tx_buffer { let len = tx_buffer.len(); unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; @@ -76,61 +77,73 @@ pub(crate) fn init_buffers<'d, T: Instance + 'd>( // This means we can leave the interrupt enabled the whole time as long as // we clear it after it happens. The downside is that the we manually have // to pend the ISR when we want data transmission to start. - let regs = T::regs(); - regs.uartimsc().write(|w| { + info.regs.uartimsc().write(|w| { w.set_rxim(true); w.set_rtim(true); w.set_txim(true); }); - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; } -impl<'d, T: Instance> BufferedUart<'d, T> { +impl BufferedUart { /// Create a buffered UART instance. - pub fn new( + pub fn new<'d, T: Instance>( _uart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, rx: Peri<'d, impl RxPin>, - irq: impl Binding>, + _irq: impl Binding>, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], config: Config, ) -> Self { - super::Uart::<'d, T, Async>::init(Some(tx.into()), Some(rx.into()), None, None, config); - init_buffers::(irq, Some(tx_buffer), Some(rx_buffer)); + super::Uart::<'d, Async>::init(T::info(), Some(tx.into()), Some(rx.into()), None, None, config); + init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), Some(rx_buffer)); Self { - rx: BufferedUartRx { phantom: PhantomData }, - tx: BufferedUartTx { phantom: PhantomData }, + rx: BufferedUartRx { + info: T::info(), + state: T::buffered_state(), + }, + tx: BufferedUartTx { + info: T::info(), + state: T::buffered_state(), + }, } } /// Create a buffered UART instance with flow control. - pub fn new_with_rtscts( + pub fn new_with_rtscts<'d, T: Instance>( _uart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, rx: Peri<'d, impl RxPin>, rts: Peri<'d, impl RtsPin>, cts: Peri<'d, impl CtsPin>, - irq: impl Binding>, + _irq: impl Binding>, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], config: Config, ) -> Self { - super::Uart::<'d, T, Async>::init( + super::Uart::<'d, Async>::init( + T::info(), Some(tx.into()), Some(rx.into()), Some(rts.into()), Some(cts.into()), config, ); - init_buffers::(irq, Some(tx_buffer), Some(rx_buffer)); + init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), Some(rx_buffer)); Self { - rx: BufferedUartRx { phantom: PhantomData }, - tx: BufferedUartTx { phantom: PhantomData }, + rx: BufferedUartRx { + info: T::info(), + state: T::buffered_state(), + }, + tx: BufferedUartTx { + info: T::info(), + state: T::buffered_state(), + }, } } @@ -160,68 +173,75 @@ impl<'d, T: Instance> BufferedUart<'d, T> { } /// sets baudrate on runtime - pub fn set_baudrate(&mut self, baudrate: u32) { - super::Uart::<'d, T, Async>::set_baudrate_inner(baudrate); + pub fn set_baudrate<'d>(&mut self, baudrate: u32) { + super::Uart::<'d, Async>::set_baudrate_inner(self.rx.info, baudrate); } /// Split into separate RX and TX handles. - pub fn split(self) -> (BufferedUartTx<'d, T>, BufferedUartRx<'d, T>) { + pub fn split(self) -> (BufferedUartTx, BufferedUartRx) { (self.tx, self.rx) } /// Split the Uart into a transmitter and receiver by mutable reference, /// which is particularly useful when having two tasks correlating to /// transmitting and receiving. - pub fn split_ref(&mut self) -> (&mut BufferedUartTx<'d, T>, &mut BufferedUartRx<'d, T>) { + pub fn split_ref(&mut self) -> (&mut BufferedUartTx, &mut BufferedUartRx) { (&mut self.tx, &mut self.rx) } } -impl<'d, T: Instance> BufferedUartRx<'d, T> { +impl BufferedUartRx { /// Create a new buffered UART RX. - pub fn new( + pub fn new<'d, T: Instance>( _uart: Peri<'d, T>, - irq: impl Binding>, + _irq: impl Binding>, rx: Peri<'d, impl RxPin>, rx_buffer: &'d mut [u8], config: Config, ) -> Self { - super::Uart::<'d, T, Async>::init(None, Some(rx.into()), None, None, config); - init_buffers::(irq, None, Some(rx_buffer)); + super::Uart::<'d, Async>::init(T::info(), None, Some(rx.into()), None, None, config); + init_buffers(T::info(), T::buffered_state(), None, Some(rx_buffer)); - Self { phantom: PhantomData } + Self { + info: T::info(), + state: T::buffered_state(), + } } /// Create a new buffered UART RX with flow control. - pub fn new_with_rts( + pub fn new_with_rts<'d, T: Instance>( _uart: Peri<'d, T>, - irq: impl Binding>, + _irq: impl Binding>, rx: Peri<'d, impl RxPin>, rts: Peri<'d, impl RtsPin>, rx_buffer: &'d mut [u8], config: Config, ) -> Self { - super::Uart::<'d, T, Async>::init(None, Some(rx.into()), Some(rts.into()), None, config); - init_buffers::(irq, None, Some(rx_buffer)); + super::Uart::<'d, Async>::init(T::info(), None, Some(rx.into()), Some(rts.into()), None, config); + init_buffers(T::info(), T::buffered_state(), None, Some(rx_buffer)); - Self { phantom: PhantomData } + Self { + info: T::info(), + state: T::buffered_state(), + } } - fn read<'a>(buf: &'a mut [u8]) -> impl Future> + 'a - where - T: 'd, - { + fn read<'a>( + info: &'static Info, + state: &'static State, + buf: &'a mut [u8], + ) -> impl Future> + 'a { poll_fn(move |cx| { - if let Poll::Ready(r) = Self::try_read(buf) { + if let Poll::Ready(r) = Self::try_read(info, state, buf) { return Poll::Ready(r); } - T::buffered_state().rx_waker.register(cx.waker()); + state.rx_waker.register(cx.waker()); Poll::Pending }) } - fn get_rx_error() -> Option { - let errs = T::buffered_state().rx_error.swap(0, Ordering::Relaxed); + fn get_rx_error(state: &State) -> Option { + let errs = state.rx_error.swap(0, Ordering::Relaxed); if errs & RXE_OVERRUN != 0 { Some(Error::Overrun) } else if errs & RXE_BREAK != 0 { @@ -235,15 +255,11 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { } } - fn try_read(buf: &mut [u8]) -> Poll> - where - T: 'd, - { + fn try_read(info: &Info, state: &State, buf: &mut [u8]) -> Poll> { if buf.is_empty() { return Poll::Ready(Ok(0)); } - let state = T::buffered_state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; let n = rx_reader.pop(|data| { let n = data.len().min(buf.len()); @@ -252,7 +268,7 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { }); let result = if n == 0 { - match Self::get_rx_error() { + match Self::get_rx_error(state) { None => return Poll::Pending, Some(e) => Err(e), } @@ -262,8 +278,7 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { // (Re-)Enable the interrupt to receive more data in case it was // disabled because the buffer was full or errors were detected. - let regs = T::regs(); - regs.uartimsc().write_set(|w| { + info.regs.uartimsc().write_set(|w| { w.set_rxim(true); w.set_rtim(true); }); @@ -274,23 +289,19 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { /// Read from UART RX buffer blocking execution until done. pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result { loop { - match Self::try_read(buf) { + match Self::try_read(self.info, self.state, buf) { Poll::Ready(res) => return res, Poll::Pending => continue, } } } - fn fill_buf<'a>() -> impl Future> - where - T: 'd, - { + fn fill_buf<'a>(state: &'static State) -> impl Future> { poll_fn(move |cx| { - let state = T::buffered_state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; let (p, n) = rx_reader.pop_buf(); let result = if n == 0 { - match Self::get_rx_error() { + match Self::get_rx_error(state) { None => { state.rx_waker.register(cx.waker()); return Poll::Pending; @@ -306,64 +317,70 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { }) } - fn consume(amt: usize) { - let state = T::buffered_state(); + fn consume(info: &Info, state: &State, amt: usize) { let mut rx_reader = unsafe { state.rx_buf.reader() }; rx_reader.pop_done(amt); // (Re-)Enable the interrupt to receive more data in case it was // disabled because the buffer was full or errors were detected. - let regs = T::regs(); - regs.uartimsc().write_set(|w| { + info.regs.uartimsc().write_set(|w| { w.set_rxim(true); w.set_rtim(true); }); } /// we are ready to read if there is data in the buffer - fn read_ready() -> Result { - let state = T::buffered_state(); + fn read_ready(state: &State) -> Result { Ok(!state.rx_buf.is_empty()) } } -impl<'d, T: Instance> BufferedUartTx<'d, T> { +impl BufferedUartTx { /// Create a new buffered UART TX. - pub fn new( + pub fn new<'d, T: Instance>( _uart: Peri<'d, T>, - irq: impl Binding>, + _irq: impl Binding>, tx: Peri<'d, impl TxPin>, tx_buffer: &'d mut [u8], config: Config, ) -> Self { - super::Uart::<'d, T, Async>::init(Some(tx.into()), None, None, None, config); - init_buffers::(irq, Some(tx_buffer), None); + super::Uart::<'d, Async>::init(T::info(), Some(tx.into()), None, None, None, config); + init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), None); - Self { phantom: PhantomData } + Self { + info: T::info(), + state: T::buffered_state(), + } } /// Create a new buffered UART TX with flow control. - pub fn new_with_cts( + pub fn new_with_cts<'d, T: Instance>( _uart: Peri<'d, T>, - irq: impl Binding>, + _irq: impl Binding>, tx: Peri<'d, impl TxPin>, cts: Peri<'d, impl CtsPin>, tx_buffer: &'d mut [u8], config: Config, ) -> Self { - super::Uart::<'d, T, Async>::init(Some(tx.into()), None, None, Some(cts.into()), config); - init_buffers::(irq, Some(tx_buffer), None); + super::Uart::<'d, Async>::init(T::info(), Some(tx.into()), None, None, Some(cts.into()), config); + init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), None); - Self { phantom: PhantomData } + Self { + info: T::info(), + state: T::buffered_state(), + } } - fn write(buf: &[u8]) -> impl Future> + '_ { + fn write<'d>( + info: &'static Info, + state: &'static State, + buf: &'d [u8], + ) -> impl Future> + 'd { poll_fn(move |cx| { if buf.is_empty() { return Poll::Ready(Ok(0)); } - let state = T::buffered_state(); let mut tx_writer = unsafe { state.tx_buf.writer() }; let n = tx_writer.push(|data| { let n = data.len().min(buf.len()); @@ -379,14 +396,13 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { // FIFO and the number of bytes drops below a threshold. When the // FIFO was empty we have to manually pend the interrupt to shovel // TX data from the buffer into the FIFO. - T::Interrupt::pend(); + info.interrupt.pend(); Poll::Ready(Ok(n)) }) } - fn flush() -> impl Future> { + fn flush(state: &'static State) -> impl Future> { poll_fn(move |cx| { - let state = T::buffered_state(); if !state.tx_buf.is_empty() { state.tx_waker.register(cx.waker()); return Poll::Pending; @@ -403,8 +419,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { } loop { - let state = T::buffered_state(); - let mut tx_writer = unsafe { state.tx_buf.writer() }; + let mut tx_writer = unsafe { self.state.tx_buf.writer() }; let n = tx_writer.push(|data| { let n = data.len().min(buf.len()); data[..n].copy_from_slice(&buf[..n]); @@ -416,7 +431,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { // FIFO and the number of bytes drops below a threshold. When the // FIFO was empty we have to manually pend the interrupt to shovel // TX data from the buffer into the FIFO. - T::Interrupt::pend(); + self.info.interrupt.pend(); return Ok(n); } } @@ -425,8 +440,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { /// Flush UART TX blocking execution until done. pub fn blocking_flush(&mut self) -> Result<(), Error> { loop { - let state = T::buffered_state(); - if state.tx_buf.is_empty() { + if self.state.tx_buf.is_empty() { return Ok(()); } } @@ -434,7 +448,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { /// Check if UART is busy. pub fn busy(&self) -> bool { - T::regs().uartfr().read().busy() + self.info.regs.uartfr().read().busy() } /// Assert a break condition after waiting for the transmit buffers to empty, @@ -445,7 +459,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { /// This method may block for a long amount of time since it has to wait /// for the transmit fifo to empty, which may take a while on slow links. pub async fn send_break(&mut self, bits: u32) { - let regs = T::regs(); + let regs = self.info.regs; let bits = bits.max({ let lcr = regs.uartlcr_h().read(); let width = lcr.wlen() as u32 + 5; @@ -458,7 +472,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { let div_clk = clk_peri_freq() as u64 * 64; let wait_usecs = (1_000_000 * bits as u64 * divx64 * 16 + div_clk - 1) / div_clk; - Self::flush().await.unwrap(); + Self::flush(self.state).await.unwrap(); while self.busy() {} regs.uartlcr_h().write_set(|w| w.set_brk(true)); Timer::after_micros(wait_usecs).await; @@ -466,28 +480,26 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { } } -impl<'d, T: Instance> Drop for BufferedUartRx<'d, T> { +impl Drop for BufferedUartRx { fn drop(&mut self) { - let state = T::buffered_state(); - unsafe { state.rx_buf.deinit() } + unsafe { self.state.rx_buf.deinit() } // TX is inactive if the buffer is not available. // We can now unregister the interrupt handler - if !state.tx_buf.is_available() { - T::Interrupt::disable(); + if !self.state.tx_buf.is_available() { + self.info.interrupt.disable(); } } } -impl<'d, T: Instance> Drop for BufferedUartTx<'d, T> { +impl Drop for BufferedUartTx { fn drop(&mut self) { - let state = T::buffered_state(); - unsafe { state.tx_buf.deinit() } + unsafe { self.state.tx_buf.deinit() } // RX is inactive if the buffer is not available. // We can now unregister the interrupt handler - if !state.rx_buf.is_available() { - T::Interrupt::disable(); + if !self.state.rx_buf.is_available() { + self.info.interrupt.disable(); } } } @@ -499,7 +511,7 @@ pub struct BufferedInterruptHandler { impl interrupt::typelevel::Handler for BufferedInterruptHandler { unsafe fn on_interrupt() { - let r = T::regs(); + let r = T::info().regs; if r.uartdmacr().read().rxdmae() { return; } @@ -603,95 +615,95 @@ impl embedded_io::Error for Error { } } -impl<'d, T: Instance> embedded_io_async::ErrorType for BufferedUart<'d, T> { +impl embedded_io_async::ErrorType for BufferedUart { type Error = Error; } -impl<'d, T: Instance> embedded_io_async::ErrorType for BufferedUartRx<'d, T> { +impl embedded_io_async::ErrorType for BufferedUartRx { type Error = Error; } -impl<'d, T: Instance> embedded_io_async::ErrorType for BufferedUartTx<'d, T> { +impl embedded_io_async::ErrorType for BufferedUartTx { type Error = Error; } -impl<'d, T: Instance + 'd> embedded_io_async::Read for BufferedUart<'d, T> { +impl embedded_io_async::Read for BufferedUart { async fn read(&mut self, buf: &mut [u8]) -> Result { - BufferedUartRx::<'d, T>::read(buf).await + BufferedUartRx::read(self.rx.info, self.rx.state, buf).await } } -impl<'d, T: Instance + 'd> embedded_io_async::Read for BufferedUartRx<'d, T> { +impl embedded_io_async::Read for BufferedUartRx { async fn read(&mut self, buf: &mut [u8]) -> Result { - Self::read(buf).await + Self::read(self.info, self.state, buf).await } } -impl<'d, T: Instance + 'd> embedded_io_async::ReadReady for BufferedUart<'d, T> { +impl embedded_io_async::ReadReady for BufferedUart { fn read_ready(&mut self) -> Result { - BufferedUartRx::<'d, T>::read_ready() + BufferedUartRx::read_ready(self.rx.state) } } -impl<'d, T: Instance + 'd> embedded_io_async::ReadReady for BufferedUartRx<'d, T> { +impl embedded_io_async::ReadReady for BufferedUartRx { fn read_ready(&mut self) -> Result { - Self::read_ready() + Self::read_ready(self.state) } } -impl<'d, T: Instance + 'd> embedded_io_async::BufRead for BufferedUart<'d, T> { +impl embedded_io_async::BufRead for BufferedUart { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { - BufferedUartRx::<'d, T>::fill_buf().await + BufferedUartRx::fill_buf(self.rx.state).await } fn consume(&mut self, amt: usize) { - BufferedUartRx::<'d, T>::consume(amt) + BufferedUartRx::consume(self.rx.info, self.rx.state, amt) } } -impl<'d, T: Instance + 'd> embedded_io_async::BufRead for BufferedUartRx<'d, T> { +impl embedded_io_async::BufRead for BufferedUartRx { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { - Self::fill_buf().await + Self::fill_buf(self.state).await } fn consume(&mut self, amt: usize) { - Self::consume(amt) + Self::consume(self.info, self.state, amt) } } -impl<'d, T: Instance + 'd> embedded_io_async::Write for BufferedUart<'d, T> { +impl embedded_io_async::Write for BufferedUart { async fn write(&mut self, buf: &[u8]) -> Result { - BufferedUartTx::<'d, T>::write(buf).await + BufferedUartTx::write(self.tx.info, self.tx.state, buf).await } async fn flush(&mut self) -> Result<(), Self::Error> { - BufferedUartTx::<'d, T>::flush().await + BufferedUartTx::flush(self.tx.state).await } } -impl<'d, T: Instance + 'd> embedded_io_async::Write for BufferedUartTx<'d, T> { +impl embedded_io_async::Write for BufferedUartTx { async fn write(&mut self, buf: &[u8]) -> Result { - Self::write(buf).await + Self::write(self.info, self.state, buf).await } async fn flush(&mut self) -> Result<(), Self::Error> { - Self::flush().await + Self::flush(self.state).await } } -impl<'d, T: Instance + 'd> embedded_io::Read for BufferedUart<'d, T> { +impl embedded_io::Read for BufferedUart { fn read(&mut self, buf: &mut [u8]) -> Result { self.rx.blocking_read(buf) } } -impl<'d, T: Instance + 'd> embedded_io::Read for BufferedUartRx<'d, T> { +impl embedded_io::Read for BufferedUartRx { fn read(&mut self, buf: &mut [u8]) -> Result { self.blocking_read(buf) } } -impl<'d, T: Instance + 'd> embedded_io::Write for BufferedUart<'d, T> { +impl embedded_io::Write for BufferedUart { fn write(&mut self, buf: &[u8]) -> Result { self.tx.blocking_write(buf) } @@ -701,7 +713,7 @@ impl<'d, T: Instance + 'd> embedded_io::Write for BufferedUart<'d, T> { } } -impl<'d, T: Instance + 'd> embedded_io::Write for BufferedUartTx<'d, T> { +impl embedded_io::Write for BufferedUartTx { fn write(&mut self, buf: &[u8]) -> Result { self.blocking_write(buf) } @@ -711,11 +723,11 @@ impl<'d, T: Instance + 'd> embedded_io::Write for BufferedUartTx<'d, T> { } } -impl<'d, T: Instance> embedded_hal_02::serial::Read for BufferedUartRx<'d, T> { +impl embedded_hal_02::serial::Read for BufferedUartRx { type Error = Error; fn read(&mut self) -> Result> { - let r = T::regs(); + let r = self.info.regs; if r.uartfr().read().rxfe() { return Err(nb::Error::WouldBlock); } @@ -736,7 +748,7 @@ impl<'d, T: Instance> embedded_hal_02::serial::Read for BufferedUartRx<'d, T } } -impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write for BufferedUartTx<'d, T> { +impl embedded_hal_02::blocking::serial::Write for BufferedUartTx { type Error = Error; fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> { @@ -755,7 +767,7 @@ impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write for BufferedU } } -impl<'d, T: Instance> embedded_hal_02::serial::Read for BufferedUart<'d, T> { +impl embedded_hal_02::serial::Read for BufferedUart { type Error = Error; fn read(&mut self) -> Result> { @@ -763,7 +775,7 @@ impl<'d, T: Instance> embedded_hal_02::serial::Read for BufferedUart<'d, T> } } -impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write for BufferedUart<'d, T> { +impl embedded_hal_02::blocking::serial::Write for BufferedUart { type Error = Error; fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> { @@ -782,25 +794,25 @@ impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write for BufferedU } } -impl<'d, T: Instance> embedded_hal_nb::serial::ErrorType for BufferedUartRx<'d, T> { +impl embedded_hal_nb::serial::ErrorType for BufferedUartRx { type Error = Error; } -impl<'d, T: Instance> embedded_hal_nb::serial::ErrorType for BufferedUartTx<'d, T> { +impl embedded_hal_nb::serial::ErrorType for BufferedUartTx { type Error = Error; } -impl<'d, T: Instance> embedded_hal_nb::serial::ErrorType for BufferedUart<'d, T> { +impl embedded_hal_nb::serial::ErrorType for BufferedUart { type Error = Error; } -impl<'d, T: Instance> embedded_hal_nb::serial::Read for BufferedUartRx<'d, T> { +impl embedded_hal_nb::serial::Read for BufferedUartRx { fn read(&mut self) -> nb::Result { embedded_hal_02::serial::Read::read(self) } } -impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> { +impl embedded_hal_nb::serial::Write for BufferedUartTx { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) } @@ -810,13 +822,13 @@ impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> { } } -impl<'d, T: Instance> embedded_hal_nb::serial::Read for BufferedUart<'d, T> { +impl embedded_hal_nb::serial::Read for BufferedUart { fn read(&mut self) -> Result> { embedded_hal_02::serial::Read::read(&mut self.rx) } } -impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUart<'d, T> { +impl embedded_hal_nb::serial::Write for BufferedUart { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) } diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 7ce074a3f..c3a15fda5 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -13,7 +13,8 @@ use pac::uart::regs::Uartris; use crate::clocks::clk_peri_freq; use crate::dma::{AnyChannel, Channel}; use crate::gpio::{AnyPin, SealedPin}; -use crate::interrupt::typelevel::{Binding, Interrupt}; +use crate::interrupt::typelevel::{Binding, Interrupt as _}; +use crate::interrupt::{Interrupt, InterruptExt}; use crate::pac::io::vals::{Inover, Outover}; use crate::{interrupt, pac, peripherals, RegExt}; @@ -135,37 +136,41 @@ pub struct DmaState { } /// UART driver. -pub struct Uart<'d, T: Instance, M: Mode> { - tx: UartTx<'d, T, M>, - rx: UartRx<'d, T, M>, +pub struct Uart<'d, M: Mode> { + tx: UartTx<'d, M>, + rx: UartRx<'d, M>, } /// UART TX driver. -pub struct UartTx<'d, T: Instance, M: Mode> { +pub struct UartTx<'d, M: Mode> { + info: &'static Info, tx_dma: Option>, - phantom: PhantomData<(&'d mut T, M)>, + phantom: PhantomData, } /// UART RX driver. -pub struct UartRx<'d, T: Instance, M: Mode> { +pub struct UartRx<'d, M: Mode> { + info: &'static Info, + dma_state: &'static DmaState, rx_dma: Option>, - phantom: PhantomData<(&'d mut T, M)>, + phantom: PhantomData, } -impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { +impl<'d, M: Mode> UartTx<'d, M> { /// Create a new DMA-enabled UART which can only send data - pub fn new( + pub fn new( _uart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, tx_dma: Peri<'d, impl Channel>, config: Config, ) -> Self { - Uart::::init(Some(tx.into()), None, None, None, config); - Self::new_inner(Some(tx_dma.into())) + Uart::::init(T::info(), Some(tx.into()), None, None, None, config); + Self::new_inner(T::info(), Some(tx_dma.into())) } - fn new_inner(tx_dma: Option>) -> Self { + fn new_inner(info: &'static Info, tx_dma: Option>) -> Self { Self { + info, tx_dma, phantom: PhantomData, } @@ -173,7 +178,7 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { /// Transmit the provided buffer blocking execution until done. pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { - let r = T::regs(); + let r = self.info.regs; for &b in buffer { while r.uartfr().read().txff() {} r.uartdr().write(|w| w.set_data(b)); @@ -183,14 +188,13 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { /// Flush UART TX blocking execution until done. pub fn blocking_flush(&mut self) -> Result<(), Error> { - let r = T::regs(); - while !r.uartfr().read().txfe() {} + while !self.info.regs.uartfr().read().txfe() {} Ok(()) } /// Check if UART is busy transmitting. pub fn busy(&self) -> bool { - T::regs().uartfr().read().busy() + self.info.regs.uartfr().read().busy() } /// Assert a break condition after waiting for the transmit buffers to empty, @@ -201,7 +205,7 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { /// This method may block for a long amount of time since it has to wait /// for the transmit fifo to empty, which may take a while on slow links. pub async fn send_break(&mut self, bits: u32) { - let regs = T::regs(); + let regs = self.info.regs; let bits = bits.max({ let lcr = regs.uartlcr_h().read(); let width = lcr.wlen() as u32 + 5; @@ -222,65 +226,80 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { } } -impl<'d, T: Instance> UartTx<'d, T, Blocking> { +impl<'d> UartTx<'d, Blocking> { /// Create a new UART TX instance for blocking mode operations. - pub fn new_blocking(_uart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, config: Config) -> Self { - Uart::::init(Some(tx.into()), None, None, None, config); - Self::new_inner(None) + pub fn new_blocking(_uart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, config: Config) -> Self { + Uart::::init(T::info(), Some(tx.into()), None, None, None, config); + Self::new_inner(T::info(), None) } /// Convert this uart TX instance into a buffered uart using the provided /// irq and transmit buffer. - pub fn into_buffered( + pub fn into_buffered( self, - irq: impl Binding>, + _irq: impl Binding>, tx_buffer: &'d mut [u8], - ) -> BufferedUartTx<'d, T> { - buffered::init_buffers::(irq, Some(tx_buffer), None); + ) -> BufferedUartTx { + buffered::init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), None); - BufferedUartTx { phantom: PhantomData } + BufferedUartTx { + info: T::info(), + state: T::buffered_state(), + } } } -impl<'d, T: Instance> UartTx<'d, T, Async> { +impl<'d> UartTx<'d, Async> { /// Write to UART TX from the provided buffer using DMA. pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { let ch = self.tx_dma.as_mut().unwrap().reborrow(); let transfer = unsafe { - T::regs().uartdmacr().write_set(|reg| { + self.info.regs.uartdmacr().write_set(|reg| { reg.set_txdmae(true); }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::write(ch, buffer, T::regs().uartdr().as_ptr() as *mut _, T::TX_DREQ.into()) + crate::dma::write( + ch, + buffer, + self.info.regs.uartdr().as_ptr() as *mut _, + self.info.tx_dreq.into(), + ) }; transfer.await; Ok(()) } } -impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { +impl<'d, M: Mode> UartRx<'d, M> { /// Create a new DMA-enabled UART which can only receive data - pub fn new( + pub fn new( _uart: Peri<'d, T>, rx: Peri<'d, impl RxPin>, _irq: impl Binding>, rx_dma: Peri<'d, impl Channel>, config: Config, ) -> Self { - Uart::::init(None, Some(rx.into()), None, None, config); - Self::new_inner(true, Some(rx_dma.into())) + Uart::::init(T::info(), None, Some(rx.into()), None, None, config); + Self::new_inner(T::info(), T::dma_state(), true, Some(rx_dma.into())) } - fn new_inner(has_irq: bool, rx_dma: Option>) -> Self { + fn new_inner( + info: &'static Info, + dma_state: &'static DmaState, + has_irq: bool, + rx_dma: Option>, + ) -> Self { debug_assert_eq!(has_irq, rx_dma.is_some()); if has_irq { // disable all error interrupts initially - T::regs().uartimsc().write(|w| w.0 = 0); - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; + info.regs.uartimsc().write(|w| w.0 = 0); + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; } Self { + info, + dma_state, rx_dma, phantom: PhantomData, } @@ -299,7 +318,7 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { /// encountered. in both cases, `len` is the number of *good* bytes copied into /// `buffer`. fn drain_fifo(&mut self, buffer: &mut [u8]) -> Result { - let r = T::regs(); + let r = self.info.regs; for (i, b) in buffer.iter_mut().enumerate() { if r.uartfr().read().rxfe() { return Ok(i); @@ -323,12 +342,12 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { } } -impl<'d, T: Instance, M: Mode> Drop for UartRx<'d, T, M> { +impl<'d, M: Mode> Drop for UartRx<'d, M> { fn drop(&mut self) { if self.rx_dma.is_some() { - T::Interrupt::disable(); + self.info.interrupt.disable(); // clear dma flags. irq handlers use these to disambiguate among themselves. - T::regs().uartdmacr().write_clear(|reg| { + self.info.regs.uartdmacr().write_clear(|reg| { reg.set_rxdmae(true); reg.set_txdmae(true); reg.set_dmaonerr(true); @@ -337,23 +356,26 @@ impl<'d, T: Instance, M: Mode> Drop for UartRx<'d, T, M> { } } -impl<'d, T: Instance> UartRx<'d, T, Blocking> { +impl<'d> UartRx<'d, Blocking> { /// Create a new UART RX instance for blocking mode operations. - pub fn new_blocking(_uart: Peri<'d, T>, rx: Peri<'d, impl RxPin>, config: Config) -> Self { - Uart::::init(None, Some(rx.into()), None, None, config); - Self::new_inner(false, None) + pub fn new_blocking(_uart: Peri<'d, T>, rx: Peri<'d, impl RxPin>, config: Config) -> Self { + Uart::::init(T::info(), None, Some(rx.into()), None, None, config); + Self::new_inner(T::info(), T::dma_state(), false, None) } /// Convert this uart RX instance into a buffered uart using the provided /// irq and receive buffer. - pub fn into_buffered( + pub fn into_buffered( self, - irq: impl Binding>, + _irq: impl Binding>, rx_buffer: &'d mut [u8], - ) -> BufferedUartRx<'d, T> { - buffered::init_buffers::(irq, None, Some(rx_buffer)); + ) -> BufferedUartRx { + buffered::init_buffers(T::info(), T::buffered_state(), None, Some(rx_buffer)); - BufferedUartRx { phantom: PhantomData } + BufferedUartRx { + info: T::info(), + state: T::buffered_state(), + } } } @@ -364,7 +386,7 @@ pub struct InterruptHandler { impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { - let uart = T::regs(); + let uart = T::info().regs; if !uart.uartdmacr().read().rxdmae() { return; } @@ -380,13 +402,13 @@ impl interrupt::typelevel::Handler for InterruptHandl } } -impl<'d, T: Instance> UartRx<'d, T, Async> { +impl<'d> UartRx<'d, Async> { /// Read from UART RX into the provided buffer. pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { // clear error flags before we drain the fifo. errors that have accumulated // in the flags will also be present in the fifo. - T::dma_state().rx_errs.store(0, Ordering::Relaxed); - T::regs().uarticr().write(|w| { + self.dma_state.rx_errs.store(0, Ordering::Relaxed); + self.info.regs.uarticr().write(|w| { w.set_oeic(true); w.set_beic(true); w.set_peic(true); @@ -408,28 +430,33 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { // interrupt flags will have been raised, and those will be picked up immediately // by the interrupt handler. let ch = self.rx_dma.as_mut().unwrap().reborrow(); - T::regs().uartimsc().write_set(|w| { + self.info.regs.uartimsc().write_set(|w| { w.set_oeim(true); w.set_beim(true); w.set_peim(true); w.set_feim(true); }); - T::regs().uartdmacr().write_set(|reg| { + self.info.regs.uartdmacr().write_set(|reg| { reg.set_rxdmae(true); reg.set_dmaonerr(true); }); let transfer = unsafe { // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::read(ch, T::regs().uartdr().as_ptr() as *const _, buffer, T::RX_DREQ.into()) + crate::dma::read( + ch, + self.info.regs.uartdr().as_ptr() as *const _, + buffer, + self.info.rx_dreq.into(), + ) }; // wait for either the transfer to complete or an error to happen. let transfer_result = select( transfer, poll_fn(|cx| { - T::dma_state().rx_err_waker.register(cx.waker()); - match T::dma_state().rx_errs.swap(0, Ordering::Relaxed) { + self.dma_state.rx_err_waker.register(cx.waker()); + match self.dma_state.rx_errs.swap(0, Ordering::Relaxed) { 0 => Poll::Pending, e => Poll::Ready(Uartris(e as u32)), } @@ -441,7 +468,7 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { Either::First(()) => { // We're here because the DMA finished, BUT if an error occurred on the LAST // byte, then we may still need to grab the error state! - Uartris(T::dma_state().rx_errs.swap(0, Ordering::Relaxed) as u32) + Uartris(self.dma_state.rx_errs.swap(0, Ordering::Relaxed) as u32) } Either::Second(e) => { // We're here because we errored, which means this is the error that @@ -521,8 +548,8 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { ) -> Result { // clear error flags before we drain the fifo. errors that have accumulated // in the flags will also be present in the fifo. - T::dma_state().rx_errs.store(0, Ordering::Relaxed); - T::regs().uarticr().write(|w| { + self.dma_state.rx_errs.store(0, Ordering::Relaxed); + self.info.regs.uarticr().write(|w| { w.set_oeic(true); w.set_beic(true); w.set_peic(true); @@ -555,13 +582,13 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { // interrupt flags will have been raised, and those will be picked up immediately // by the interrupt handler. let ch = self.rx_dma.as_mut().unwrap(); - T::regs().uartimsc().write_set(|w| { + self.info.regs.uartimsc().write_set(|w| { w.set_oeim(true); w.set_beim(true); w.set_peim(true); w.set_feim(true); }); - T::regs().uartdmacr().write_set(|reg| { + self.info.regs.uartdmacr().write_set(|reg| { reg.set_rxdmae(true); reg.set_dmaonerr(true); }); @@ -572,9 +599,9 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { // is held across an await and makes the future non-Send. crate::dma::read( ch.reborrow(), - T::regs().uartdr().as_ptr() as *const _, + self.info.regs.uartdr().as_ptr() as *const _, sbuffer, - T::RX_DREQ.into(), + self.info.rx_dreq.into(), ) }; @@ -582,8 +609,8 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { let transfer_result = select( transfer, poll_fn(|cx| { - T::dma_state().rx_err_waker.register(cx.waker()); - match T::dma_state().rx_errs.swap(0, Ordering::Relaxed) { + self.dma_state.rx_err_waker.register(cx.waker()); + match self.dma_state.rx_errs.swap(0, Ordering::Relaxed) { 0 => Poll::Pending, e => Poll::Ready(Uartris(e as u32)), } @@ -596,7 +623,7 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { Either::First(()) => { // We're here because the DMA finished, BUT if an error occurred on the LAST // byte, then we may still need to grab the error state! - Uartris(T::dma_state().rx_errs.swap(0, Ordering::Relaxed) as u32) + Uartris(self.dma_state.rx_errs.swap(0, Ordering::Relaxed) as u32) } Either::Second(e) => { // We're here because we errored, which means this is the error that @@ -635,7 +662,7 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { continue; } - let regs = T::regs(); + let regs = self.info.regs; let all_full = next_addr == eval; // NOTE: This is off label usage of RSR! See the issue below for @@ -685,9 +712,9 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { } } -impl<'d, T: Instance> Uart<'d, T, Blocking> { +impl<'d> Uart<'d, Blocking> { /// Create a new UART without hardware flow control - pub fn new_blocking( + pub fn new_blocking( uart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, rx: Peri<'d, impl RxPin>, @@ -697,7 +724,7 @@ impl<'d, T: Instance> Uart<'d, T, Blocking> { } /// Create a new UART with hardware flow control (RTS/CTS) - pub fn new_with_rtscts_blocking( + pub fn new_with_rtscts_blocking( uart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, rx: Peri<'d, impl RxPin>, @@ -720,24 +747,30 @@ impl<'d, T: Instance> Uart<'d, T, Blocking> { /// Convert this uart instance into a buffered uart using the provided /// irq, transmit and receive buffers. - pub fn into_buffered( + pub fn into_buffered( self, - irq: impl Binding>, + _irq: impl Binding>, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], - ) -> BufferedUart<'d, T> { - buffered::init_buffers::(irq, Some(tx_buffer), Some(rx_buffer)); + ) -> BufferedUart { + buffered::init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), Some(rx_buffer)); BufferedUart { - rx: BufferedUartRx { phantom: PhantomData }, - tx: BufferedUartTx { phantom: PhantomData }, + rx: BufferedUartRx { + info: T::info(), + state: T::buffered_state(), + }, + tx: BufferedUartTx { + info: T::info(), + state: T::buffered_state(), + }, } } } -impl<'d, T: Instance> Uart<'d, T, Async> { +impl<'d> Uart<'d, Async> { /// Create a new DMA enabled UART without hardware flow control - pub fn new( + pub fn new( uart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, rx: Peri<'d, impl RxPin>, @@ -760,7 +793,7 @@ impl<'d, T: Instance> Uart<'d, T, Async> { } /// Create a new DMA enabled UART with hardware flow control (RTS/CTS) - pub fn new_with_rtscts( + pub fn new_with_rtscts( uart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, rx: Peri<'d, impl RxPin>, @@ -785,8 +818,8 @@ impl<'d, T: Instance> Uart<'d, T, Async> { } } -impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { - fn new_inner( +impl<'d, M: Mode> Uart<'d, M> { + fn new_inner( _uart: Peri<'d, T>, mut tx: Peri<'d, AnyPin>, mut rx: Peri<'d, AnyPin>, @@ -798,6 +831,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { config: Config, ) -> Self { Self::init( + T::info(), Some(tx.reborrow()), Some(rx.reborrow()), rts.as_mut().map(|x| x.reborrow()), @@ -806,19 +840,20 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { ); Self { - tx: UartTx::new_inner(tx_dma), - rx: UartRx::new_inner(has_irq, rx_dma), + tx: UartTx::new_inner(T::info(), tx_dma), + rx: UartRx::new_inner(T::info(), T::dma_state(), has_irq, rx_dma), } } fn init( + info: &Info, tx: Option>, rx: Option>, rts: Option>, cts: Option>, config: Config, ) { - let r = T::regs(); + let r = info.regs; if let Some(pin) = &tx { let funcsel = { let pin_number = ((pin.gpio().as_ptr() as u32) & 0x1FF) / 8; @@ -896,7 +931,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { }); } - Self::set_baudrate_inner(config.baudrate); + Self::set_baudrate_inner(info, config.baudrate); let (pen, eps) = match config.parity { Parity::ParityNone => (false, false), @@ -926,8 +961,8 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { }); } - fn lcr_modify(f: impl FnOnce(&mut crate::pac::uart::regs::UartlcrH) -> R) -> R { - let r = T::regs(); + fn lcr_modify(info: &Info, f: impl FnOnce(&mut crate::pac::uart::regs::UartlcrH) -> R) -> R { + let r = info.regs; // Notes from PL011 reference manual: // @@ -978,11 +1013,11 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { /// sets baudrate on runtime pub fn set_baudrate(&mut self, baudrate: u32) { - Self::set_baudrate_inner(baudrate); + Self::set_baudrate_inner(self.tx.info, baudrate); } - fn set_baudrate_inner(baudrate: u32) { - let r = T::regs(); + fn set_baudrate_inner(info: &Info, baudrate: u32) { + let r = info.regs; let clk_base = crate::clocks::clk_peri_freq(); @@ -1002,11 +1037,11 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { r.uartibrd().write_value(pac::uart::regs::Uartibrd(baud_ibrd)); r.uartfbrd().write_value(pac::uart::regs::Uartfbrd(baud_fbrd)); - Self::lcr_modify(|_| {}); + Self::lcr_modify(info, |_| {}); } } -impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { +impl<'d, M: Mode> Uart<'d, M> { /// Transmit the provided buffer blocking execution until done. pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { self.tx.blocking_write(buffer) @@ -1034,19 +1069,19 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { /// Split the Uart into a transmitter and receiver, which is particularly /// useful when having two tasks correlating to transmitting and receiving. - pub fn split(self) -> (UartTx<'d, T, M>, UartRx<'d, T, M>) { + pub fn split(self) -> (UartTx<'d, M>, UartRx<'d, M>) { (self.tx, self.rx) } /// Split the Uart into a transmitter and receiver by mutable reference, /// which is particularly useful when having two tasks correlating to /// transmitting and receiving. - pub fn split_ref(&mut self) -> (&mut UartTx<'d, T, M>, &mut UartRx<'d, T, M>) { + pub fn split_ref(&mut self) -> (&mut UartTx<'d, M>, &mut UartRx<'d, M>) { (&mut self.tx, &mut self.rx) } } -impl<'d, T: Instance> Uart<'d, T, Async> { +impl<'d> Uart<'d, Async> { /// Write to UART TX from the provided buffer. pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { self.tx.write(buffer).await @@ -1076,10 +1111,10 @@ impl<'d, T: Instance> Uart<'d, T, Async> { } } -impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read for UartRx<'d, T, M> { +impl<'d, M: Mode> embedded_hal_02::serial::Read for UartRx<'d, M> { type Error = Error; fn read(&mut self) -> Result> { - let r = T::regs(); + let r = self.info.regs; if r.uartfr().read().rxfe() { return Err(nb::Error::WouldBlock); } @@ -1100,11 +1135,11 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read for UartRx<'d, } } -impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Write for UartTx<'d, T, M> { +impl<'d, M: Mode> embedded_hal_02::serial::Write for UartTx<'d, M> { type Error = Error; fn write(&mut self, word: u8) -> Result<(), nb::Error> { - let r = T::regs(); + let r = self.info.regs; if r.uartfr().read().txff() { return Err(nb::Error::WouldBlock); } @@ -1114,7 +1149,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Write for UartTx<'d, } fn flush(&mut self) -> Result<(), nb::Error> { - let r = T::regs(); + let r = self.info.regs; if !r.uartfr().read().txfe() { return Err(nb::Error::WouldBlock); } @@ -1122,7 +1157,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Write for UartTx<'d, } } -impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write for UartTx<'d, T, M> { +impl<'d, M: Mode> embedded_hal_02::blocking::serial::Write for UartTx<'d, M> { type Error = Error; fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { @@ -1134,7 +1169,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write for } } -impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read for Uart<'d, T, M> { +impl<'d, M: Mode> embedded_hal_02::serial::Read for Uart<'d, M> { type Error = Error; fn read(&mut self) -> Result> { @@ -1142,7 +1177,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read for Uart<'d, T, } } -impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Write for Uart<'d, T, M> { +impl<'d, M: Mode> embedded_hal_02::serial::Write for Uart<'d, M> { type Error = Error; fn write(&mut self, word: u8) -> Result<(), nb::Error> { @@ -1154,7 +1189,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Write for Uart<'d, T } } -impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write for Uart<'d, T, M> { +impl<'d, M: Mode> embedded_hal_02::blocking::serial::Write for Uart<'d, M> { type Error = Error; fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { @@ -1177,21 +1212,21 @@ impl embedded_hal_nb::serial::Error for Error { } } -impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for UartRx<'d, T, M> { +impl<'d, M: Mode> embedded_hal_nb::serial::ErrorType for UartRx<'d, M> { type Error = Error; } -impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for UartTx<'d, T, M> { +impl<'d, M: Mode> embedded_hal_nb::serial::ErrorType for UartTx<'d, M> { type Error = Error; } -impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for Uart<'d, T, M> { +impl<'d, M: Mode> embedded_hal_nb::serial::ErrorType for Uart<'d, M> { type Error = Error; } -impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, T, M> { +impl<'d, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, M> { fn read(&mut self) -> nb::Result { - let r = T::regs(); + let r = self.info.regs; if r.uartfr().read().rxfe() { return Err(nb::Error::WouldBlock); } @@ -1212,7 +1247,7 @@ impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, T, M } } -impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, T, M> { +impl<'d, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, M> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) } @@ -1222,11 +1257,11 @@ impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, T, } } -impl<'d, T: Instance> embedded_io::ErrorType for UartTx<'d, T, Blocking> { +impl<'d> embedded_io::ErrorType for UartTx<'d, Blocking> { type Error = Error; } -impl<'d, T: Instance> embedded_io::Write for UartTx<'d, T, Blocking> { +impl<'d> embedded_io::Write for UartTx<'d, Blocking> { fn write(&mut self, buf: &[u8]) -> Result { self.blocking_write(buf).map(|_| buf.len()) } @@ -1236,13 +1271,13 @@ impl<'d, T: Instance> embedded_io::Write for UartTx<'d, T, Blocking> { } } -impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for Uart<'d, T, M> { +impl<'d, M: Mode> embedded_hal_nb::serial::Read for Uart<'d, M> { fn read(&mut self) -> Result> { embedded_hal_02::serial::Read::read(&mut self.rx) } } -impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, T, M> { +impl<'d, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, M> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) } @@ -1252,11 +1287,11 @@ impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, T, M> } } -impl<'d, T: Instance> embedded_io::ErrorType for Uart<'d, T, Blocking> { +impl<'d> embedded_io::ErrorType for Uart<'d, Blocking> { type Error = Error; } -impl<'d, T: Instance> embedded_io::Write for Uart<'d, T, Blocking> { +impl<'d> embedded_io::Write for Uart<'d, Blocking> { fn write(&mut self, buf: &[u8]) -> Result { self.blocking_write(buf).map(|_| buf.len()) } @@ -1266,13 +1301,17 @@ impl<'d, T: Instance> embedded_io::Write for Uart<'d, T, Blocking> { } } +struct Info { + regs: pac::uart::Uart, + tx_dreq: pac::dma::vals::TreqSel, + rx_dreq: pac::dma::vals::TreqSel, + interrupt: Interrupt, +} + trait SealedMode {} trait SealedInstance { - const TX_DREQ: pac::dma::vals::TreqSel; - const RX_DREQ: pac::dma::vals::TreqSel; - - fn regs() -> pac::uart::Uart; + fn info() -> &'static Info; fn buffered_state() -> &'static buffered::State; @@ -1308,11 +1347,14 @@ pub trait Instance: SealedInstance + PeripheralType { macro_rules! impl_instance { ($inst:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => { impl SealedInstance for peripherals::$inst { - const TX_DREQ: pac::dma::vals::TreqSel = $tx_dreq; - const RX_DREQ: pac::dma::vals::TreqSel = $rx_dreq; - - fn regs() -> pac::uart::Uart { - pac::$inst + fn info() -> &'static Info { + static INFO: Info = Info { + regs: pac::$inst, + tx_dreq: $tx_dreq, + rx_dreq: $rx_dreq, + interrupt: crate::interrupt::typelevel::$irq::IRQ, + }; + &INFO } fn buffered_state() -> &'static buffered::State { diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 54ab7d0d5..8fd7e8df4 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -31,6 +31,8 @@ pub struct PwmPin<'d, T, C> { /// PWM pin config /// /// This configures the pwm pin settings +#[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PwmPinConfig { /// PWM Pin output type pub output_type: OutputType, diff --git a/examples/mimxrt6/src/bin/crc.rs b/examples/mimxrt6/src/bin/crc.rs new file mode 100644 index 000000000..005a250e5 --- /dev/null +++ b/examples/mimxrt6/src/bin/crc.rs @@ -0,0 +1,175 @@ +#![no_std] +#![no_main] + +extern crate embassy_imxrt_examples; + +use defmt::*; +use embassy_executor::Spawner; +use embassy_imxrt::crc::{Config, Crc, Polynomial}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut p = embassy_imxrt::init(Default::default()); + let data = b"123456789"; + + info!("Initializing CRC"); + + // CRC-CCITT + let mut crc = Crc::new(p.CRC.reborrow(), Default::default()); + let output = crc.feed_bytes(data); + defmt::assert_eq!(output, 0x29b1); + + // CRC16-ARC + let mut crc = Crc::new( + p.CRC.reborrow(), + Config { + polynomial: Polynomial::Crc16, + reverse_in: true, + reverse_out: true, + complement_out: false, + seed: 0, + ..Default::default() + }, + ); + let output = crc.feed_bytes(data); + defmt::assert_eq!(output, 0xbb3d); + + // CRC16-CMS + let mut crc = Crc::new( + p.CRC.reborrow(), + Config { + polynomial: Polynomial::Crc16, + reverse_in: false, + reverse_out: false, + complement_out: false, + seed: 0xffff, + ..Default::default() + }, + ); + let output = crc.feed_bytes(data); + defmt::assert_eq!(output, 0xaee7); + + // CRC16-DDS-110 + let mut crc = Crc::new( + p.CRC.reborrow(), + Config { + polynomial: Polynomial::Crc16, + reverse_in: false, + reverse_out: false, + complement_out: false, + seed: 0x800d, + ..Default::default() + }, + ); + let output = crc.feed_bytes(data); + defmt::assert_eq!(output, 0x9ecf); + + // CRC16-MAXIM-DOW + let mut crc = Crc::new( + p.CRC.reborrow(), + Config { + polynomial: Polynomial::Crc16, + reverse_in: true, + reverse_out: true, + complement_out: true, + seed: 0, + ..Default::default() + }, + ); + let output = crc.feed_bytes(data); + defmt::assert_eq!(output, 0x44c2); + + // CRC16-MODBUS + let mut crc = Crc::new( + p.CRC.reborrow(), + Config { + polynomial: Polynomial::Crc16, + reverse_in: true, + reverse_out: true, + complement_out: false, + seed: 0xffff, + ..Default::default() + }, + ); + let output = crc.feed_bytes(data); + defmt::assert_eq!(output, 0x4b37); + + // CRC32-BZIP2 + let mut crc = Crc::new( + p.CRC.reborrow(), + Config { + polynomial: Polynomial::Crc32, + reverse_in: false, + reverse_out: false, + complement_out: true, + seed: 0xffff_ffff, + ..Default::default() + }, + ); + let output = crc.feed_bytes(data); + defmt::assert_eq!(output, 0xfc89_1918); + + // CRC32-CKSUM + let mut crc = Crc::new( + p.CRC.reborrow(), + Config { + polynomial: Polynomial::Crc32, + reverse_in: false, + reverse_out: false, + complement_out: true, + seed: 0, + ..Default::default() + }, + ); + let output = crc.feed_bytes(data); + defmt::assert_eq!(output, 0x765e_7680); + + // CRC32-ISO-HDLC + let mut crc = Crc::new( + p.CRC.reborrow(), + Config { + polynomial: Polynomial::Crc32, + reverse_in: true, + reverse_out: true, + complement_out: true, + seed: 0xffff_ffff, + ..Default::default() + }, + ); + let output = crc.feed_bytes(data); + defmt::assert_eq!(output, 0xcbf4_3926); + + // CRC32-JAMCRC + let mut crc = Crc::new( + p.CRC.reborrow(), + Config { + polynomial: Polynomial::Crc32, + reverse_in: true, + reverse_out: true, + complement_out: false, + seed: 0xffff_ffff, + ..Default::default() + }, + ); + let output = crc.feed_bytes(data); + defmt::assert_eq!(output, 0x340b_c6d9); + + // CRC32-MPEG-2 + let mut crc = Crc::new( + p.CRC.reborrow(), + Config { + polynomial: Polynomial::Crc32, + reverse_in: false, + reverse_out: false, + complement_out: false, + seed: 0xffff_ffff, + ..Default::default() + }, + ); + let output = crc.feed_bytes(data); + defmt::assert_eq!(output, 0x0376_e6e7); + + info!("end program"); + cortex_m::asm::bkpt(); +} diff --git a/examples/rp/src/bin/overclock.rs b/examples/rp/src/bin/overclock.rs new file mode 100644 index 000000000..9c78e0c9d --- /dev/null +++ b/examples/rp/src/bin/overclock.rs @@ -0,0 +1,64 @@ +//! # Overclocking the RP2040 to 200 MHz +//! +//! This example demonstrates how to configure the RP2040 to run at 200 MHz. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::clocks::{clk_sys_freq, ClockConfig}; +use embassy_rp::config::Config; +use embassy_rp::gpio::{Level, Output}; +use embassy_time::{Duration, Instant, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +const COUNT_TO: i64 = 10_000_000; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + // Set up for clock frequency of 200 MHz, setting all necessary defaults. + let config = Config::new(ClockConfig::system_freq(200_000_000)); + + // Show the voltage scale for verification + info!("System core voltage: {}", Debug2Format(&config.clocks.core_voltage)); + + // Initialize the peripherals + let p = embassy_rp::init(config); + + // Show CPU frequency for verification + let sys_freq = clk_sys_freq(); + info!("System clock frequency: {} MHz", sys_freq / 1_000_000); + + // LED to indicate the system is running + let mut led = Output::new(p.PIN_25, Level::Low); + + loop { + // Reset the counter at the start of measurement period + let mut counter = 0; + + // Turn LED on while counting + led.set_high(); + + let start = Instant::now(); + + // This is a busy loop that will take some time to complete + while counter < COUNT_TO { + counter += 1; + } + + let elapsed = Instant::now() - start; + + // Report the elapsed time + led.set_low(); + info!( + "At {}Mhz: Elapsed time to count to {}: {}ms", + sys_freq / 1_000_000, + counter, + elapsed.as_millis() + ); + + // Wait 2 seconds before starting the next measurement + Timer::after(Duration::from_secs(2)).await; + } +} diff --git a/examples/rp/src/bin/overclock_manual.rs b/examples/rp/src/bin/overclock_manual.rs new file mode 100644 index 000000000..35160b250 --- /dev/null +++ b/examples/rp/src/bin/overclock_manual.rs @@ -0,0 +1,79 @@ +//! # Overclocking the RP2040 to 200 MHz manually +//! +//! This example demonstrates how to manually configure the RP2040 to run at 200 MHz. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::clocks; +use embassy_rp::clocks::{ClockConfig, CoreVoltage, PllConfig}; +use embassy_rp::config::Config; +use embassy_rp::gpio::{Level, Output}; +use embassy_time::{Duration, Instant, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +const COUNT_TO: i64 = 10_000_000; + +/// Configure the RP2040 for 200 MHz operation by manually specifying the PLL settings. +fn configure_manual_overclock() -> Config { + // Set the PLL configuration manually, starting from default values + let mut config = Config::default(); + + // Set the system clock to 200 MHz + config.clocks = ClockConfig::manual_pll( + 12_000_000, // Crystal frequency, 12 MHz is common. If using custom, set to your value. + PllConfig { + refdiv: 1, // Reference divider + fbdiv: 100, // Feedback divider + post_div1: 3, // Post divider 1 + post_div2: 2, // Post divider 2 + }, + CoreVoltage::V1_15, // Core voltage, should be set to V1_15 for 200 MHz + ); + + config +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + // Initialize with our manual overclock configuration + let p = embassy_rp::init(configure_manual_overclock()); + + // Verify the actual system clock frequency + let sys_freq = clocks::clk_sys_freq(); + info!("System clock frequency: {} MHz", sys_freq / 1_000_000); + + // LED to indicate the system is running + let mut led = Output::new(p.PIN_25, Level::Low); + + loop { + // Reset the counter at the start of measurement period + let mut counter = 0; + + // Turn LED on while counting + led.set_high(); + + let start = Instant::now(); + + // This is a busy loop that will take some time to complete + while counter < COUNT_TO { + counter += 1; + } + + let elapsed = Instant::now() - start; + + // Report the elapsed time + led.set_low(); + info!( + "At {}Mhz: Elapsed time to count to {}: {}ms", + sys_freq / 1_000_000, + counter, + elapsed.as_millis() + ); + + // Wait 2 seconds before starting the next measurement + Timer::after(Duration::from_secs(2)).await; + } +} diff --git a/examples/rp/src/bin/sharing.rs b/examples/rp/src/bin/sharing.rs index 5416e20ce..497c4f845 100644 --- a/examples/rp/src/bin/sharing.rs +++ b/examples/rp/src/bin/sharing.rs @@ -31,7 +31,7 @@ use rand::RngCore; use static_cell::{ConstStaticCell, StaticCell}; use {defmt_rtt as _, panic_probe as _}; -type UartAsyncMutex = mutex::Mutex>; +type UartAsyncMutex = mutex::Mutex>; struct MyType { inner: u32, diff --git a/examples/rp/src/bin/uart_buffered_split.rs b/examples/rp/src/bin/uart_buffered_split.rs index da7e94139..3adbc18ab 100644 --- a/examples/rp/src/bin/uart_buffered_split.rs +++ b/examples/rp/src/bin/uart_buffered_split.rs @@ -48,7 +48,7 @@ async fn main(spawner: Spawner) { } #[embassy_executor::task] -async fn reader(mut rx: BufferedUartRx<'static, UART0>) { +async fn reader(mut rx: BufferedUartRx) { info!("Reading..."); loop { let mut buf = [0; 31]; diff --git a/examples/rp/src/bin/uart_unidir.rs b/examples/rp/src/bin/uart_unidir.rs index a45f40756..c2c8dfad8 100644 --- a/examples/rp/src/bin/uart_unidir.rs +++ b/examples/rp/src/bin/uart_unidir.rs @@ -39,7 +39,7 @@ async fn main(spawner: Spawner) { } #[embassy_executor::task] -async fn reader(mut rx: UartRx<'static, UART1, Async>) { +async fn reader(mut rx: UartRx<'static, Async>) { info!("Reading..."); loop { // read a total of 4 transmissions (32 / 8) and then print the result diff --git a/examples/rp235x/src/bin/sharing.rs b/examples/rp235x/src/bin/sharing.rs index 5416e20ce..497c4f845 100644 --- a/examples/rp235x/src/bin/sharing.rs +++ b/examples/rp235x/src/bin/sharing.rs @@ -31,7 +31,7 @@ use rand::RngCore; use static_cell::{ConstStaticCell, StaticCell}; use {defmt_rtt as _, panic_probe as _}; -type UartAsyncMutex = mutex::Mutex>; +type UartAsyncMutex = mutex::Mutex>; struct MyType { inner: u32, diff --git a/examples/rp235x/src/bin/uart_buffered_split.rs b/examples/rp235x/src/bin/uart_buffered_split.rs index f707c4b5e..7cad09f9b 100644 --- a/examples/rp235x/src/bin/uart_buffered_split.rs +++ b/examples/rp235x/src/bin/uart_buffered_split.rs @@ -48,7 +48,7 @@ async fn main(spawner: Spawner) { } #[embassy_executor::task] -async fn reader(mut rx: BufferedUartRx<'static, UART0>) { +async fn reader(mut rx: BufferedUartRx) { info!("Reading..."); loop { let mut buf = [0; 31]; diff --git a/examples/rp235x/src/bin/uart_unidir.rs b/examples/rp235x/src/bin/uart_unidir.rs index 4e98f9e1e..45c9c8407 100644 --- a/examples/rp235x/src/bin/uart_unidir.rs +++ b/examples/rp235x/src/bin/uart_unidir.rs @@ -39,7 +39,7 @@ async fn main(spawner: Spawner) { } #[embassy_executor::task] -async fn reader(mut rx: UartRx<'static, UART1, Async>) { +async fn reader(mut rx: UartRx<'static, Async>) { info!("Reading..."); loop { // read a total of 4 transmissions (32 / 8) and then print the result diff --git a/tests/rp/src/bin/overclock.rs b/tests/rp/src/bin/overclock.rs new file mode 100644 index 000000000..be8e85a3f --- /dev/null +++ b/tests/rp/src/bin/overclock.rs @@ -0,0 +1,70 @@ +#![no_std] +#![no_main] + +#[cfg(feature = "rp2040")] +teleprobe_meta::target!(b"rpi-pico"); +#[cfg(feature = "rp235xb")] +teleprobe_meta::target!(b"pimoroni-pico-plus-2"); + +use defmt::info; +#[cfg(feature = "rp2040")] +use defmt::{assert, assert_eq}; +use embassy_executor::Spawner; +use embassy_rp::clocks; +#[cfg(feature = "rp2040")] +use embassy_rp::clocks::ClockConfig; +#[cfg(feature = "rp2040")] +use embassy_rp::clocks::CoreVoltage; +use embassy_rp::config::Config; +use embassy_time::Instant; +use {defmt_rtt as _, panic_probe as _}; + +const COUNT_TO: i64 = 10_000_000; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + #[cfg(feature = "rp2040")] + let mut config = Config::default(); + #[cfg(not(feature = "rp2040"))] + let config = Config::default(); + + // Initialize with 200MHz clock configuration for RP2040, other chips will use default clock + #[cfg(feature = "rp2040")] + { + config.clocks = ClockConfig::system_freq(200_000_000); + let voltage = config.clocks.core_voltage; + assert!(matches!(voltage, CoreVoltage::V1_15), "Expected voltage scale V1_15"); + } + + let _p = embassy_rp::init(config); + + // Test the system speed + let (time_elapsed, clk_sys_freq) = { + let mut counter = 0; + let start = Instant::now(); + while counter < COUNT_TO { + counter += 1; + } + let elapsed = Instant::now() - start; + + (elapsed.as_millis(), clocks::clk_sys_freq()) + }; + + // Report the elapsed time, so that the compiler doesn't optimize it away for chips other than RP2040 + info!( + "At {}Mhz: Elapsed time to count to {}: {}ms", + clk_sys_freq / 1_000_000, + COUNT_TO, + time_elapsed + ); + + #[cfg(feature = "rp2040")] + { + // we should be at 200MHz + assert_eq!(clk_sys_freq, 200_000_000, "System clock frequency is not 200MHz"); + // At 200MHz, the time to count to 10_000_000 should be at 600ms, testing with 1% margin + assert!(time_elapsed <= 606, "Elapsed time is too long"); + } + + cortex_m::asm::bkpt(); +} diff --git a/tests/rp/src/bin/uart.rs b/tests/rp/src/bin/uart.rs index 84744ab77..80230f3fe 100644 --- a/tests/rp/src/bin/uart.rs +++ b/tests/rp/src/bin/uart.rs @@ -8,17 +8,17 @@ teleprobe_meta::target!(b"pimoroni-pico-plus-2"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; use embassy_rp::gpio::{Level, Output}; -use embassy_rp::uart::{Blocking, Config, Error, Instance, Parity, Uart, UartRx}; +use embassy_rp::uart::{Blocking, Config, Error, Parity, Uart, UartRx}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; -fn read(uart: &mut Uart<'_, impl Instance, Blocking>) -> Result<[u8; N], Error> { +fn read(uart: &mut Uart<'_, Blocking>) -> Result<[u8; N], Error> { let mut buf = [255; N]; uart.blocking_read(&mut buf)?; Ok(buf) } -fn read1(uart: &mut UartRx<'_, impl Instance, Blocking>) -> Result<[u8; N], Error> { +fn read1(uart: &mut UartRx<'_, Blocking>) -> Result<[u8; N], Error> { let mut buf = [255; N]; uart.blocking_read(&mut buf)?; Ok(buf) diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs index d5f655e9b..cb78fc142 100644 --- a/tests/rp/src/bin/uart_buffered.rs +++ b/tests/rp/src/bin/uart_buffered.rs @@ -10,7 +10,7 @@ use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::UART0; -use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config, Error, Instance, Parity}; +use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config, Error, Parity}; use embassy_time::Timer; use embedded_io_async::{Read, ReadExactError, Write}; use {defmt_rtt as _, panic_probe as _}; @@ -19,7 +19,7 @@ bind_interrupts!(struct Irqs { UART0_IRQ => BufferedInterruptHandler; }); -async fn read(uart: &mut BufferedUart<'_, impl Instance>) -> Result<[u8; N], Error> { +async fn read(uart: &mut BufferedUart) -> Result<[u8; N], Error> { let mut buf = [255; N]; match uart.read_exact(&mut buf).await { Ok(()) => Ok(buf), @@ -29,7 +29,7 @@ async fn read(uart: &mut BufferedUart<'_, impl Instance>) -> Res } } -async fn read1(uart: &mut BufferedUartRx<'_, impl Instance>) -> Result<[u8; N], Error> { +async fn read1(uart: &mut BufferedUartRx) -> Result<[u8; N], Error> { let mut buf = [255; N]; match uart.read_exact(&mut buf).await { Ok(()) => Ok(buf), diff --git a/tests/rp/src/bin/uart_dma.rs b/tests/rp/src/bin/uart_dma.rs index a09101223..a7af81f5f 100644 --- a/tests/rp/src/bin/uart_dma.rs +++ b/tests/rp/src/bin/uart_dma.rs @@ -10,7 +10,7 @@ use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::UART0; -use embassy_rp::uart::{Async, Config, Error, Instance, InterruptHandler, Parity, Uart, UartRx}; +use embassy_rp::uart::{Async, Config, Error, InterruptHandler, Parity, Uart, UartRx}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -18,13 +18,13 @@ bind_interrupts!(struct Irqs { UART0_IRQ => InterruptHandler; }); -async fn read(uart: &mut Uart<'_, impl Instance, Async>) -> Result<[u8; N], Error> { +async fn read(uart: &mut Uart<'_, Async>) -> Result<[u8; N], Error> { let mut buf = [255; N]; uart.read(&mut buf).await?; Ok(buf) } -async fn read1(uart: &mut UartRx<'_, impl Instance, Async>) -> Result<[u8; N], Error> { +async fn read1(uart: &mut UartRx<'_, Async>) -> Result<[u8; N], Error> { let mut buf = [255; N]; uart.read(&mut buf).await?; Ok(buf)