diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index 5b913f15..21360bf6 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -1,3 +1,4 @@ +//! ADC driver. use core::future::poll_fn; use core::marker::PhantomData; use core::mem; @@ -16,6 +17,7 @@ use crate::{dma, interrupt, pac, peripherals, Peripheral, RegExt}; static WAKER: AtomicWaker = AtomicWaker::new(); +/// ADC config. #[non_exhaustive] pub struct Config {} @@ -30,9 +32,11 @@ enum Source<'p> { TempSensor(PeripheralRef<'p, ADC_TEMP_SENSOR>), } +/// ADC channel. pub struct Channel<'p>(Source<'p>); impl<'p> Channel<'p> { + /// Create a new ADC channel from pin with the provided [Pull] configuration. pub fn new_pin(pin: impl Peripheral

+ 'p, pull: Pull) -> Self { into_ref!(pin); pin.pad_ctrl().modify(|w| { @@ -49,6 +53,7 @@ impl<'p> Channel<'p> { Self(Source::Pin(pin.map_into())) } + /// Create a new ADC channel for the internal temperature sensor. pub fn new_temp_sensor(s: impl Peripheral

+ 'p) -> Self { let r = pac::ADC; r.cs().write_set(|w| w.set_ts_en(true)); @@ -83,35 +88,44 @@ impl<'p> Drop for Source<'p> { } } +/// ADC sample. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(transparent)] pub struct Sample(u16); impl Sample { + /// Sample is valid. pub fn good(&self) -> bool { self.0 < 0x8000 } + /// Sample value. pub fn value(&self) -> u16 { self.0 & !0x8000 } } +/// ADC error. #[derive(Debug, Eq, PartialEq, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { + /// Error converting value. ConversionFailed, } +/// ADC mode. pub trait Mode {} +/// ADC async mode. pub struct Async; impl Mode for Async {} +/// ADC blocking mode. pub struct Blocking; impl Mode for Blocking {} +/// ADC driver. pub struct Adc<'d, M: Mode> { phantom: PhantomData<(&'d ADC, M)>, } @@ -150,6 +164,7 @@ impl<'d, M: Mode> Adc<'d, M> { while !r.cs().read().ready() {} } + /// Sample a value from a channel in blocking mode. pub fn blocking_read(&mut self, ch: &mut Channel) -> Result { let r = Self::regs(); r.cs().modify(|w| { @@ -166,6 +181,7 @@ impl<'d, M: Mode> Adc<'d, M> { } impl<'d> Adc<'d, Async> { + /// Create ADC driver in async mode. pub fn new( _inner: impl Peripheral

+ 'd, _irq: impl Binding, @@ -194,6 +210,7 @@ impl<'d> Adc<'d, Async> { .await; } + /// Sample a value from a channel until completed. pub async fn read(&mut self, ch: &mut Channel<'_>) -> Result { let r = Self::regs(); r.cs().modify(|w| { @@ -272,6 +289,7 @@ impl<'d> Adc<'d, Async> { } } + /// Sample multiple values from a channel using DMA. #[inline] pub async fn read_many( &mut self, @@ -283,6 +301,7 @@ impl<'d> Adc<'d, Async> { self.read_many_inner(ch, buf, false, div, dma).await } + /// Sample multiple values from a channel using DMA with errors inlined in samples. #[inline] pub async fn read_many_raw( &mut self, @@ -299,6 +318,7 @@ impl<'d> Adc<'d, Async> { } impl<'d> Adc<'d, Blocking> { + /// Create ADC driver in blocking mode. pub fn new_blocking(_inner: impl Peripheral

+ 'd, _config: Config) -> Self { Self::setup(); @@ -306,6 +326,7 @@ impl<'d> Adc<'d, Blocking> { } } +/// Interrupt handler. pub struct InterruptHandler { _empty: (), } @@ -324,6 +345,7 @@ mod sealed { pub trait AdcChannel {} } +/// ADC sample. pub trait AdcSample: sealed::AdcSample {} impl sealed::AdcSample for u16 {} @@ -332,7 +354,9 @@ impl AdcSample for u16 {} impl sealed::AdcSample for u8 {} impl AdcSample for u8 {} +/// ADC channel. pub trait AdcChannel: sealed::AdcChannel {} +/// ADC pin. pub trait AdcPin: AdcChannel + gpio::Pin {} macro_rules! impl_pin { diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 2f064561..19232b80 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -45,15 +45,20 @@ static CLOCKS: Clocks = Clocks { rtc: AtomicU16::new(0), }; -/// Enumeration of supported clock sources. +/// Peripheral clock sources. #[repr(u8)] #[non_exhaustive] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum PeriClkSrc { + /// SYS. Sys = ClkPeriCtrlAuxsrc::CLK_SYS as _, + /// PLL SYS. PllSys = ClkPeriCtrlAuxsrc::CLKSRC_PLL_SYS as _, + /// PLL USB. PllUsb = ClkPeriCtrlAuxsrc::CLKSRC_PLL_USB as _, + /// ROSC. Rosc = ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH as _, + /// XOSC. Xosc = ClkPeriCtrlAuxsrc::XOSC_CLKSRC as _, // Gpin0 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN0 as _ , // Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1 as _ , @@ -83,6 +88,7 @@ pub struct ClockConfig { } impl ClockConfig { + /// Clock configuration derived from external crystal. pub fn crystal(crystal_hz: u32) -> Self { Self { rosc: Some(RoscConfig { @@ -141,6 +147,7 @@ impl ClockConfig { } } + /// Clock configuration from internal oscillator. pub fn rosc() -> Self { Self { rosc: Some(RoscConfig { @@ -190,13 +197,18 @@ impl ClockConfig { // } } +/// ROSC freq range. #[repr(u16)] #[non_exhaustive] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum RoscRange { + /// Low range. Low = pac::rosc::vals::FreqRange::LOW.0, + /// Medium range (1.33x low) Medium = pac::rosc::vals::FreqRange::MEDIUM.0, + /// High range (2x low) High = pac::rosc::vals::FreqRange::HIGH.0, + /// Too high. Should not be used. TooHigh = pac::rosc::vals::FreqRange::TOOHIGH.0, } @@ -239,96 +251,136 @@ pub struct PllConfig { pub post_div2: u8, } -/// Reference +/// Reference clock config. pub struct RefClkConfig { + /// Reference clock source. pub src: RefClkSrc, + /// Reference clock divider. pub div: u8, } +/// Reference clock source. #[non_exhaustive] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum RefClkSrc { - // main sources + /// XOSC. Xosc, + /// ROSC. Rosc, - // aux sources + /// PLL USB. PllUsb, // Gpin0, // Gpin1, } +/// SYS clock source. #[non_exhaustive] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum SysClkSrc { - // main sources + /// REF. Ref, - // aux sources + /// PLL SYS. PllSys, + /// PLL USB. PllUsb, + /// ROSC. Rosc, + /// XOSC. Xosc, // Gpin0, // Gpin1, } +/// SYS clock config. pub struct SysClkConfig { + /// SYS clock source. pub src: SysClkSrc, + /// SYS clock divider. pub div_int: u32, + /// SYS clock fraction. pub div_frac: u8, } +/// USB clock source. #[repr(u8)] #[non_exhaustive] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum UsbClkSrc { + /// PLL USB. PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB as _, + /// PLL SYS. PllSys = ClkUsbCtrlAuxsrc::CLKSRC_PLL_SYS as _, + /// ROSC. Rosc = ClkUsbCtrlAuxsrc::ROSC_CLKSRC_PH as _, + /// XOSC. Xosc = ClkUsbCtrlAuxsrc::XOSC_CLKSRC as _, // Gpin0 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN0 as _ , // Gpin1 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN1 as _ , } +/// USB clock config. pub struct UsbClkConfig { + /// USB clock source. pub src: UsbClkSrc, + /// USB clock divider. pub div: u8, + /// USB clock phase. pub phase: u8, } +/// ADC clock source. #[repr(u8)] #[non_exhaustive] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum AdcClkSrc { + /// PLL USB. PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB as _, + /// PLL SYS. PllSys = ClkAdcCtrlAuxsrc::CLKSRC_PLL_SYS as _, + /// ROSC. Rosc = ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH as _, + /// XOSC. Xosc = ClkAdcCtrlAuxsrc::XOSC_CLKSRC as _, // Gpin0 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN0 as _ , // Gpin1 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN1 as _ , } +/// ADC clock config. pub struct AdcClkConfig { + /// ADC clock source. pub src: AdcClkSrc, + /// ADC clock divider. pub div: u8, + /// ADC clock phase. pub phase: u8, } +/// RTC clock source. #[repr(u8)] #[non_exhaustive] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum RtcClkSrc { + /// PLL USB. PllUsb = ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB as _, + /// PLL SYS. PllSys = ClkRtcCtrlAuxsrc::CLKSRC_PLL_SYS as _, + /// ROSC. Rosc = ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH as _, + /// XOSC. Xosc = ClkRtcCtrlAuxsrc::XOSC_CLKSRC as _, // Gpin0 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN0 as _ , // Gpin1 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN1 as _ , } +/// RTC clock config. pub struct RtcClkConfig { + /// RTC clock source. pub src: RtcClkSrc, + /// RTC clock divider. pub div_int: u32, + /// RTC clock divider fraction. pub div_frac: u8, + /// RTC clock phase. pub phase: u8, } @@ -605,10 +657,12 @@ fn configure_rosc(config: RoscConfig) -> u32 { config.hz } +/// ROSC clock frequency. pub fn rosc_freq() -> u32 { CLOCKS.rosc.load(Ordering::Relaxed) } +/// XOSC clock frequency. pub fn xosc_freq() -> u32 { CLOCKS.xosc.load(Ordering::Relaxed) } @@ -620,34 +674,42 @@ pub fn xosc_freq() -> u32 { // CLOCKS.gpin1.load(Ordering::Relaxed) // } +/// PLL SYS clock frequency. pub fn pll_sys_freq() -> u32 { CLOCKS.pll_sys.load(Ordering::Relaxed) } +/// PLL USB clock frequency. pub fn pll_usb_freq() -> u32 { CLOCKS.pll_usb.load(Ordering::Relaxed) } +/// SYS clock frequency. pub fn clk_sys_freq() -> u32 { CLOCKS.sys.load(Ordering::Relaxed) } +/// REF clock frequency. pub fn clk_ref_freq() -> u32 { CLOCKS.reference.load(Ordering::Relaxed) } +/// Peripheral clock frequency. pub fn clk_peri_freq() -> u32 { CLOCKS.peri.load(Ordering::Relaxed) } +/// USB clock frequency. pub fn clk_usb_freq() -> u32 { CLOCKS.usb.load(Ordering::Relaxed) } +/// ADC clock frequency. pub fn clk_adc_freq() -> u32 { CLOCKS.adc.load(Ordering::Relaxed) } +/// RTC clock frequency. pub fn clk_rtc_freq() -> u16 { CLOCKS.rtc.load(Ordering::Relaxed) } @@ -708,7 +770,9 @@ fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 { vco_freq / ((config.post_div1 * config.post_div2) as u32) } +/// General purpose input clock pin. pub trait GpinPin: crate::gpio::Pin { + /// Pin number. const NR: usize; } @@ -723,12 +787,14 @@ macro_rules! impl_gpinpin { impl_gpinpin!(PIN_20, 20, 0); impl_gpinpin!(PIN_22, 22, 1); +/// General purpose clock input driver. pub struct Gpin<'d, T: Pin> { gpin: PeripheralRef<'d, AnyPin>, _phantom: PhantomData, } impl<'d, T: Pin> Gpin<'d, T> { + /// Create new gpin driver. pub fn new(gpin: impl Peripheral

+ 'd) -> Gpin<'d, P> { into_ref!(gpin); @@ -754,7 +820,9 @@ impl<'d, T: Pin> Drop for Gpin<'d, T> { } } +/// General purpose clock output pin. pub trait GpoutPin: crate::gpio::Pin { + /// Pin number. fn number(&self) -> usize; } @@ -773,26 +841,38 @@ impl_gpoutpin!(PIN_23, 1); impl_gpoutpin!(PIN_24, 2); impl_gpoutpin!(PIN_25, 3); +/// Gpout clock source. #[repr(u8)] pub enum GpoutSrc { + /// Sys PLL. PllSys = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS as _, // Gpin0 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0 as _ , // Gpin1 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1 as _ , + /// USB PLL. PllUsb = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB as _, + /// ROSC. Rosc = ClkGpoutCtrlAuxsrc::ROSC_CLKSRC as _, + /// XOSC. Xosc = ClkGpoutCtrlAuxsrc::XOSC_CLKSRC as _, + /// SYS. Sys = ClkGpoutCtrlAuxsrc::CLK_SYS as _, + /// USB. Usb = ClkGpoutCtrlAuxsrc::CLK_USB as _, + /// ADC. Adc = ClkGpoutCtrlAuxsrc::CLK_ADC as _, + /// RTC. Rtc = ClkGpoutCtrlAuxsrc::CLK_RTC as _, + /// REF. Ref = ClkGpoutCtrlAuxsrc::CLK_REF as _, } +/// General purpose clock output driver. pub struct Gpout<'d, T: GpoutPin> { gpout: PeripheralRef<'d, T>, } impl<'d, T: GpoutPin> Gpout<'d, T> { + /// Create new general purpose cloud output. pub fn new(gpout: impl Peripheral

+ 'd) -> Self { into_ref!(gpout); @@ -801,6 +881,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { Self { gpout } } + /// Set clock divider. pub fn set_div(&self, int: u32, frac: u8) { let c = pac::CLOCKS; c.clk_gpout_div(self.gpout.number()).write(|w| { @@ -809,6 +890,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { }); } + /// Set clock source. pub fn set_src(&self, src: GpoutSrc) { let c = pac::CLOCKS; c.clk_gpout_ctrl(self.gpout.number()).modify(|w| { @@ -816,6 +898,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { }); } + /// Enable clock. pub fn enable(&self) { let c = pac::CLOCKS; c.clk_gpout_ctrl(self.gpout.number()).modify(|w| { @@ -823,6 +906,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { }); } + /// Disable clock. pub fn disable(&self) { let c = pac::CLOCKS; c.clk_gpout_ctrl(self.gpout.number()).modify(|w| { @@ -830,6 +914,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { }); } + /// Clock frequency. pub fn get_freq(&self) -> u32 { let c = pac::CLOCKS; let src = c.clk_gpout_ctrl(self.gpout.number()).read().auxsrc(); diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 45ca21a7..088a842a 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -38,6 +38,9 @@ pub(crate) unsafe fn init() { interrupt::DMA_IRQ_0.enable(); } +/// DMA read. +/// +/// SAFETY: Slice must point to a valid location reachable by DMA. pub unsafe fn read<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, from: *const W, @@ -57,6 +60,9 @@ pub unsafe fn read<'a, C: Channel, W: Word>( ) } +/// DMA write. +/// +/// SAFETY: Slice must point to a valid location reachable by DMA. pub unsafe fn write<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, from: *const [W], @@ -79,6 +85,9 @@ pub unsafe fn write<'a, C: Channel, W: Word>( // static mut so that this is allocated in RAM. static mut DUMMY: u32 = 0; +/// DMA repeated write. +/// +/// SAFETY: Slice must point to a valid location reachable by DMA. pub unsafe fn write_repeated<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, to: *mut W, @@ -97,6 +106,9 @@ pub unsafe fn write_repeated<'a, C: Channel, W: Word>( ) } +/// DMA copy between slices. +/// +/// SAFETY: Slices must point to locations reachable by DMA. pub unsafe fn copy<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, from: &[W], @@ -152,6 +164,7 @@ fn copy_inner<'a, C: Channel>( Transfer::new(ch) } +/// DMA transfer driver. #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Transfer<'a, C: Channel> { channel: PeripheralRef<'a, C>, @@ -201,19 +214,25 @@ mod sealed { pub trait Word {} } +/// DMA channel interface. pub trait Channel: Peripheral

+ sealed::Channel + Into + Sized + 'static { + /// Channel number. fn number(&self) -> u8; + /// Channel registry block. fn regs(&self) -> pac::dma::Channel { pac::DMA.ch(self.number() as _) } + /// Convert into type-erased [AnyChannel]. fn degrade(self) -> AnyChannel { AnyChannel { number: self.number() } } } +/// DMA word. pub trait Word: sealed::Word { + /// Word size. fn size() -> vals::DataSize; } @@ -238,6 +257,7 @@ impl Word for u32 { } } +/// Type erased DMA channel. pub struct AnyChannel { number: u8, } diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 1b20561d..2d673cf6 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -1,3 +1,4 @@ +//! Flash driver. use core::future::Future; use core::marker::PhantomData; use core::pin::Pin; @@ -13,9 +14,10 @@ use crate::dma::{AnyChannel, Channel, Transfer}; use crate::pac; use crate::peripherals::FLASH; +/// Flash base address. pub const FLASH_BASE: *const u32 = 0x10000000 as _; -// If running from RAM, we might have no boot2. Use bootrom `flash_enter_cmd_xip` instead. +/// If running from RAM, we might have no boot2. Use bootrom `flash_enter_cmd_xip` instead. // TODO: when run-from-ram is set, completely skip the "pause cores and jumpp to RAM" dance. pub const USE_BOOT2: bool = !cfg!(feature = "run-from-ram"); @@ -24,10 +26,15 @@ pub const USE_BOOT2: bool = !cfg!(feature = "run-from-ram"); // These limitations are currently enforced because of using the // RP2040 boot-rom flash functions, that are optimized for flash compatibility // rather than performance. +/// Flash page size. pub const PAGE_SIZE: usize = 256; +/// Flash write size. pub const WRITE_SIZE: usize = 1; +/// Flash read size. pub const READ_SIZE: usize = 1; +/// Flash erase size. pub const ERASE_SIZE: usize = 4096; +/// Flash DMA read size. pub const ASYNC_READ_SIZE: usize = 4; /// Error type for NVMC operations. @@ -38,7 +45,9 @@ pub enum Error { OutOfBounds, /// Unaligned operation or using unaligned buffers. Unaligned, + /// Accessed from the wrong core. InvalidCore, + /// Other error Other, } @@ -96,12 +105,18 @@ impl<'a, 'd, T: Instance, const FLASH_SIZE: usize> Drop for BackgroundRead<'a, ' } } +/// Flash driver. pub struct Flash<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> { dma: Option>, phantom: PhantomData<(&'d mut T, M)>, } impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SIZE> { + /// Blocking read. + /// + /// The offset and buffer must be aligned. + /// + /// NOTE: `offset` is an offset from the flash start, NOT an absolute address. pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { trace!( "Reading from 0x{:x} to 0x{:x}", @@ -116,10 +131,14 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI Ok(()) } + /// Flash capacity. pub fn capacity(&self) -> usize { FLASH_SIZE } + /// Blocking erase. + /// + /// NOTE: `offset` is an offset from the flash start, NOT an absolute address. pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { check_erase(self, from, to)?; @@ -136,6 +155,11 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI Ok(()) } + /// Blocking write. + /// + /// The offset and buffer must be aligned. + /// + /// NOTE: `offset` is an offset from the flash start, NOT an absolute address. pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { check_write(self, offset, bytes.len())?; @@ -219,6 +243,7 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI } impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Blocking, FLASH_SIZE> { + /// Create a new flash driver in blocking mode. pub fn new_blocking(_flash: impl Peripheral

+ 'd) -> Self { Self { dma: None, @@ -228,6 +253,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Blocking, FLASH_SIZE } impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Async, FLASH_SIZE> { + /// Create a new flash driver in async mode. pub fn new(_flash: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd) -> Self { into_ref!(dma); Self { @@ -236,6 +262,11 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Async, FLASH_SIZE> { } } + /// Start a background read operation. + /// + /// The offset and buffer must be aligned. + /// + /// NOTE: `offset` is an offset from the flash start, NOT an absolute address. pub fn background_read<'a>( &'a mut self, offset: u32, @@ -279,6 +310,11 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Async, FLASH_SIZE> { }) } + /// Async read. + /// + /// The offset and buffer must be aligned. + /// + /// NOTE: `offset` is an offset from the flash start, NOT an absolute address. pub async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { use core::mem::MaybeUninit; @@ -874,7 +910,9 @@ mod sealed { pub trait Mode {} } +/// Flash instance. pub trait Instance: sealed::Instance {} +/// Flash mode. pub trait Mode: sealed::Mode {} impl sealed::Instance for FLASH {} @@ -887,7 +925,9 @@ macro_rules! impl_mode { }; } +/// Flash blocking mode. pub struct Blocking; +/// Flash async mode. pub struct Async; impl_mode!(Blocking); diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 23273e62..2e6692ab 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -1,3 +1,4 @@ +//! GPIO driver. #![macro_use] use core::convert::Infallible; use core::future::Future; @@ -23,7 +24,9 @@ static QSPI_WAKERS: [AtomicWaker; QSPI_PIN_COUNT] = [NEW_AW; QSPI_PIN_COUNT]; /// Represents a digital input or output level. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Level { + /// Logical low. Low, + /// Logical high. High, } @@ -48,48 +51,66 @@ impl From for bool { /// Represents a pull setting for an input. #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum Pull { + /// No pull. None, + /// Internal pull-up resistor. Up, + /// Internal pull-down resistor. Down, } /// Drive strength of an output #[derive(Debug, Eq, PartialEq)] pub enum Drive { + /// 2 mA drive. _2mA, + /// 4 mA drive. _4mA, + /// 8 mA drive. _8mA, + /// 1 2mA drive. _12mA, } /// Slew rate of an output #[derive(Debug, Eq, PartialEq)] pub enum SlewRate { + /// Fast slew rate. Fast, + /// Slow slew rate. Slow, } /// A GPIO bank with up to 32 pins. #[derive(Debug, Eq, PartialEq)] pub enum Bank { + /// Bank 0. Bank0 = 0, + /// QSPI. #[cfg(feature = "qspi-as-gpio")] Qspi = 1, } +/// Dormant mode config. #[derive(Debug, Eq, PartialEq, Copy, Clone, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DormantWakeConfig { + /// Wake on edge high. pub edge_high: bool, + /// Wake on edge low. pub edge_low: bool, + /// Wake on level high. pub level_high: bool, + /// Wake on level low. pub level_low: bool, } +/// GPIO input driver. pub struct Input<'d, T: Pin> { pin: Flex<'d, T>, } impl<'d, T: Pin> Input<'d, T> { + /// Create GPIO input driver for a [Pin] with the provided [Pull] configuration. #[inline] pub fn new(pin: impl Peripheral

+ 'd, pull: Pull) -> Self { let mut pin = Flex::new(pin); @@ -104,11 +125,13 @@ impl<'d, T: Pin> Input<'d, T> { self.pin.set_schmitt(enable) } + /// Get whether the pin input level is high. #[inline] pub fn is_high(&mut self) -> bool { self.pin.is_high() } + /// Get whether the pin input level is low. #[inline] pub fn is_low(&mut self) -> bool { self.pin.is_low() @@ -120,31 +143,37 @@ impl<'d, T: Pin> Input<'d, T> { self.pin.get_level() } + /// Wait until the pin is high. If it is already high, return immediately. #[inline] pub async fn wait_for_high(&mut self) { self.pin.wait_for_high().await; } + /// Wait until the pin is low. If it is already low, return immediately. #[inline] pub async fn wait_for_low(&mut self) { self.pin.wait_for_low().await; } + /// Wait for the pin to undergo a transition from low to high. #[inline] pub async fn wait_for_rising_edge(&mut self) { self.pin.wait_for_rising_edge().await; } + /// Wait for the pin to undergo a transition from high to low. #[inline] pub async fn wait_for_falling_edge(&mut self) { self.pin.wait_for_falling_edge().await; } + /// Wait for the pin to undergo any transition, i.e low to high OR high to low. #[inline] pub async fn wait_for_any_edge(&mut self) { self.pin.wait_for_any_edge().await; } + /// Configure dormant wake. #[inline] pub fn dormant_wake(&mut self, cfg: DormantWakeConfig) -> DormantWake { self.pin.dormant_wake(cfg) @@ -155,10 +184,15 @@ impl<'d, T: Pin> Input<'d, T> { #[derive(Debug, Eq, PartialEq, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum InterruptTrigger { + /// Trigger on pin low. LevelLow, + /// Trigger on pin high. LevelHigh, + /// Trigger on high to low transition. EdgeLow, + /// Trigger on low to high transition. EdgeHigh, + /// Trigger on any transition. AnyEdge, } @@ -226,6 +260,7 @@ struct InputFuture<'a, T: Pin> { } impl<'d, T: Pin> InputFuture<'d, T> { + /// Create a new future wiating for input trigger. pub fn new(pin: impl Peripheral

+ 'd, level: InterruptTrigger) -> Self { into_ref!(pin); let pin_group = (pin.pin() % 8) as usize; @@ -308,11 +343,13 @@ impl<'d, T: Pin> Future for InputFuture<'d, T> { } } +/// GPIO output driver. pub struct Output<'d, T: Pin> { pin: Flex<'d, T>, } impl<'d, T: Pin> Output<'d, T> { + /// Create GPIO output driver for a [Pin] with the provided [Level]. #[inline] pub fn new(pin: impl Peripheral

+ 'd, initial_output: Level) -> Self { let mut pin = Flex::new(pin); @@ -331,7 +368,7 @@ impl<'d, T: Pin> Output<'d, T> { self.pin.set_drive_strength(strength) } - // Set the pin's slew rate. + /// Set the pin's slew rate. #[inline] pub fn set_slew_rate(&mut self, slew_rate: SlewRate) { self.pin.set_slew_rate(slew_rate) @@ -386,6 +423,7 @@ pub struct OutputOpenDrain<'d, T: Pin> { } impl<'d, T: Pin> OutputOpenDrain<'d, T> { + /// Create GPIO output driver for a [Pin] in open drain mode with the provided [Level]. #[inline] pub fn new(pin: impl Peripheral

+ 'd, initial_output: Level) -> Self { let mut pin = Flex::new(pin); @@ -403,7 +441,7 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> { self.pin.set_drive_strength(strength) } - // Set the pin's slew rate. + /// Set the pin's slew rate. #[inline] pub fn set_slew_rate(&mut self, slew_rate: SlewRate) { self.pin.set_slew_rate(slew_rate) @@ -456,11 +494,13 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> { self.pin.toggle_set_as_output() } + /// Get whether the pin input level is high. #[inline] pub fn is_high(&mut self) -> bool { self.pin.is_high() } + /// Get whether the pin input level is low. #[inline] pub fn is_low(&mut self) -> bool { self.pin.is_low() @@ -472,26 +512,31 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> { self.is_high().into() } + /// Wait until the pin is high. If it is already high, return immediately. #[inline] pub async fn wait_for_high(&mut self) { self.pin.wait_for_high().await; } + /// Wait until the pin is low. If it is already low, return immediately. #[inline] pub async fn wait_for_low(&mut self) { self.pin.wait_for_low().await; } + /// Wait for the pin to undergo a transition from low to high. #[inline] pub async fn wait_for_rising_edge(&mut self) { self.pin.wait_for_rising_edge().await; } + /// Wait for the pin to undergo a transition from high to low. #[inline] pub async fn wait_for_falling_edge(&mut self) { self.pin.wait_for_falling_edge().await; } + /// Wait for the pin to undergo any transition, i.e low to high OR high to low. #[inline] pub async fn wait_for_any_edge(&mut self) { self.pin.wait_for_any_edge().await; @@ -508,6 +553,10 @@ pub struct Flex<'d, T: Pin> { } impl<'d, T: Pin> Flex<'d, T> { + /// Wrap the pin in a `Flex`. + /// + /// The pin remains disconnected. The initial output level is unspecified, but can be changed + /// before the pin is put into output mode. #[inline] pub fn new(pin: impl Peripheral

+ 'd) -> Self { into_ref!(pin); @@ -556,7 +605,7 @@ impl<'d, T: Pin> Flex<'d, T> { }); } - // Set the pin's slew rate. + /// Set the pin's slew rate. #[inline] pub fn set_slew_rate(&mut self, slew_rate: SlewRate) { self.pin.pad_ctrl().modify(|w| { @@ -589,6 +638,7 @@ impl<'d, T: Pin> Flex<'d, T> { self.pin.sio_oe().value_set().write_value(self.bit()) } + /// Set as output pin. #[inline] pub fn is_set_as_output(&mut self) -> bool { self.ref_is_set_as_output() @@ -599,15 +649,18 @@ impl<'d, T: Pin> Flex<'d, T> { (self.pin.sio_oe().value().read() & self.bit()) != 0 } + /// Toggle output pin. #[inline] pub fn toggle_set_as_output(&mut self) { self.pin.sio_oe().value_xor().write_value(self.bit()) } + /// Get whether the pin input level is high. #[inline] pub fn is_high(&mut self) -> bool { !self.is_low() } + /// Get whether the pin input level is low. #[inline] pub fn is_low(&mut self) -> bool { @@ -675,31 +728,37 @@ impl<'d, T: Pin> Flex<'d, T> { self.pin.sio_out().value_xor().write_value(self.bit()) } + /// Wait until the pin is high. If it is already high, return immediately. #[inline] pub async fn wait_for_high(&mut self) { InputFuture::new(&mut self.pin, InterruptTrigger::LevelHigh).await; } + /// Wait until the pin is low. If it is already low, return immediately. #[inline] pub async fn wait_for_low(&mut self) { InputFuture::new(&mut self.pin, InterruptTrigger::LevelLow).await; } + /// Wait for the pin to undergo a transition from low to high. #[inline] pub async fn wait_for_rising_edge(&mut self) { InputFuture::new(&mut self.pin, InterruptTrigger::EdgeHigh).await; } + /// Wait for the pin to undergo a transition from high to low. #[inline] pub async fn wait_for_falling_edge(&mut self) { InputFuture::new(&mut self.pin, InterruptTrigger::EdgeLow).await; } + /// Wait for the pin to undergo any transition, i.e low to high OR high to low. #[inline] pub async fn wait_for_any_edge(&mut self) { InputFuture::new(&mut self.pin, InterruptTrigger::AnyEdge).await; } + /// Configure dormant wake. #[inline] pub fn dormant_wake(&mut self, cfg: DormantWakeConfig) -> DormantWake { let idx = self.pin._pin() as usize; @@ -737,6 +796,7 @@ impl<'d, T: Pin> Drop for Flex<'d, T> { } } +/// Dormant wake driver. pub struct DormantWake<'w, T: Pin> { pin: PeripheralRef<'w, T>, cfg: DormantWakeConfig, @@ -818,6 +878,7 @@ pub(crate) mod sealed { } } +/// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an [AnyPin]. pub trait Pin: Peripheral

+ Into + sealed::Pin + Sized + 'static { /// Degrade to a generic pin struct fn degrade(self) -> AnyPin { @@ -839,6 +900,7 @@ pub trait Pin: Peripheral

+ Into + sealed::Pin + Sized + 'stat } } +/// Type-erased GPIO pin pub struct AnyPin { pin_bank: u8, } diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 15095236..74d01579 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -1,3 +1,4 @@ +//! I2C driver. use core::future; use core::marker::PhantomData; use core::task::Poll; @@ -22,6 +23,7 @@ pub enum AbortReason { ArbitrationLoss, /// Transmit ended with data still in fifo TxNotEmpty(u16), + /// Other reason. Other(u32), } @@ -41,9 +43,11 @@ pub enum Error { AddressReserved(u16), } +/// I2C config. #[non_exhaustive] #[derive(Copy, Clone)] pub struct Config { + /// Frequency. pub frequency: u32, } @@ -53,13 +57,16 @@ impl Default for Config { } } +/// Size of I2C FIFO. pub const FIFO_SIZE: u8 = 16; +/// I2C driver. pub struct I2c<'d, T: Instance, M: Mode> { phantom: PhantomData<(&'d mut T, M)>, } impl<'d, T: Instance> I2c<'d, T, Blocking> { + /// Create a new driver instance in blocking mode. pub fn new_blocking( peri: impl Peripheral

+ 'd, scl: impl Peripheral

> + 'd, @@ -72,6 +79,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { } impl<'d, T: Instance> I2c<'d, T, Async> { + /// Create a new driver instance in async mode. pub fn new_async( peri: impl Peripheral

+ 'd, scl: impl Peripheral

> + 'd, @@ -292,16 +300,19 @@ impl<'d, T: Instance> I2c<'d, T, Async> { } } + /// Read from address into buffer using DMA. pub async fn read_async(&mut self, addr: u16, buffer: &mut [u8]) -> Result<(), Error> { Self::setup(addr)?; self.read_async_internal(buffer, true, true).await } + /// Write to address from buffer using DMA. pub async fn write_async(&mut self, addr: u16, bytes: impl IntoIterator) -> Result<(), Error> { Self::setup(addr)?; self.write_async_internal(bytes, true).await } + /// Write to address from bytes and read from address into buffer using DMA. pub async fn write_read_async( &mut self, addr: u16, @@ -314,6 +325,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { } } +/// Interrupt handler. pub struct InterruptHandler { _uart: PhantomData, } @@ -569,17 +581,20 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { // Blocking public API // ========================= + /// Read from address into buffer blocking caller until done. pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> { Self::setup(address.into())?; self.read_blocking_internal(read, true, true) // Automatic Stop } + /// Write to address from buffer blocking caller until done. pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { Self::setup(address.into())?; self.write_blocking_internal(write, true) } + /// Write to address from bytes and read from address into buffer blocking caller until done. pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { Self::setup(address.into())?; self.write_blocking_internal(write, false)?; @@ -742,6 +757,7 @@ where } } +/// Check if address is reserved. pub fn i2c_reserved_addr(addr: u16) -> bool { ((addr & 0x78) == 0 || (addr & 0x78) == 0x78) && addr != 0 } @@ -768,6 +784,7 @@ mod sealed { pub trait SclPin {} } +/// Driver mode. pub trait Mode: sealed::Mode {} macro_rules! impl_mode { @@ -777,12 +794,15 @@ macro_rules! impl_mode { }; } +/// Blocking mode. pub struct Blocking; +/// Async mode. pub struct Async; impl_mode!(Blocking); impl_mode!(Async); +/// I2C instance. pub trait Instance: sealed::Instance {} macro_rules! impl_instance { @@ -819,7 +839,9 @@ macro_rules! impl_instance { impl_instance!(I2C0, I2C0_IRQ, set_i2c0, 32, 33); impl_instance!(I2C1, I2C1_IRQ, set_i2c1, 34, 35); +/// SDA pin. pub trait SdaPin: sealed::SdaPin + crate::gpio::Pin {} +/// SCL pin. pub trait SclPin: sealed::SclPin + crate::gpio::Pin {} macro_rules! impl_pin { diff --git a/embassy-rp/src/i2c_slave.rs b/embassy-rp/src/i2c_slave.rs index 9271ede3..721b7a1f 100644 --- a/embassy-rp/src/i2c_slave.rs +++ b/embassy-rp/src/i2c_slave.rs @@ -1,3 +1,4 @@ +//! I2C slave driver. use core::future; use core::marker::PhantomData; use core::task::Poll; @@ -63,11 +64,13 @@ impl Default for Config { } } +/// I2CSlave driver. pub struct I2cSlave<'d, T: Instance> { phantom: PhantomData<&'d mut T>, } impl<'d, T: Instance> I2cSlave<'d, T> { + /// Create a new instance. pub fn new( _peri: impl Peripheral

+ 'd, scl: impl Peripheral

> + 'd, diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 2c49787d..004b9458 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -1,6 +1,7 @@ #![no_std] #![allow(async_fn_in_trait)] #![doc = include_str!("../README.md")] +#![warn(missing_docs)] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs index ae91d1e8..ca979502 100644 --- a/embassy-rp/src/pio/mod.rs +++ b/embassy-rp/src/pio/mod.rs @@ -1,3 +1,4 @@ +//! PIO driver. use core::future::Future; use core::marker::PhantomData; use core::pin::Pin as FuturePin; diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 5b96557a..784a05f9 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -119,11 +119,13 @@ impl<'d, T: Channel> Pwm<'d, T> { } } + /// Create PWM driver without any configured pins. #[inline] pub fn new_free(inner: impl Peripheral

+ 'd, config: Config) -> Self { Self::new_inner(inner, None, None, config, Divmode::DIV) } + /// Create PWM driver with a single 'a' as output. #[inline] pub fn new_output_a( inner: impl Peripheral

+ 'd, @@ -134,6 +136,7 @@ impl<'d, T: Channel> Pwm<'d, T> { Self::new_inner(inner, Some(a.map_into()), None, config, Divmode::DIV) } + /// Create PWM driver with a single 'b' pin as output. #[inline] pub fn new_output_b( inner: impl Peripheral

+ 'd, @@ -144,6 +147,7 @@ impl<'d, T: Channel> Pwm<'d, T> { Self::new_inner(inner, None, Some(b.map_into()), config, Divmode::DIV) } + /// Create PWM driver with a 'a' and 'b' pins as output. #[inline] pub fn new_output_ab( inner: impl Peripheral

+ 'd, @@ -155,6 +159,7 @@ impl<'d, T: Channel> Pwm<'d, T> { Self::new_inner(inner, Some(a.map_into()), Some(b.map_into()), config, Divmode::DIV) } + /// Create PWM driver with a single 'b' as input pin. #[inline] pub fn new_input( inner: impl Peripheral

+ 'd, @@ -166,6 +171,7 @@ impl<'d, T: Channel> Pwm<'d, T> { Self::new_inner(inner, None, Some(b.map_into()), config, mode.into()) } + /// Create PWM driver with a 'a' and 'b' pins in the desired input mode. #[inline] pub fn new_output_input( inner: impl Peripheral

+ 'd, @@ -178,6 +184,7 @@ impl<'d, T: Channel> Pwm<'d, T> { Self::new_inner(inner, Some(a.map_into()), Some(b.map_into()), config, mode.into()) } + /// Set the PWM config. pub fn set_config(&mut self, config: &Config) { Self::configure(self.inner.regs(), config); } @@ -221,28 +228,33 @@ impl<'d, T: Channel> Pwm<'d, T> { while p.csr().read().ph_ret() {} } + /// Read PWM counter. #[inline] pub fn counter(&self) -> u16 { self.inner.regs().ctr().read().ctr() } + /// Write PWM counter. #[inline] pub fn set_counter(&self, ctr: u16) { self.inner.regs().ctr().write(|w| w.set_ctr(ctr)) } + /// Wait for channel interrupt. #[inline] pub fn wait_for_wrap(&mut self) { while !self.wrapped() {} self.clear_wrapped(); } + /// Check if interrupt for channel is set. #[inline] pub fn wrapped(&mut self) -> bool { pac::PWM.intr().read().0 & self.bit() != 0 } #[inline] + /// Clear interrupt flag. pub fn clear_wrapped(&mut self) { pac::PWM.intr().write_value(Intr(self.bit() as _)); } @@ -253,15 +265,18 @@ impl<'d, T: Channel> Pwm<'d, T> { } } +/// Batch representation of PWM channels. pub struct PwmBatch(u32); impl PwmBatch { #[inline] + /// Enable a PWM channel in this batch. pub fn enable(&mut self, pwm: &Pwm<'_, impl Channel>) { self.0 |= pwm.bit(); } #[inline] + /// Enable channels in this batch in a PWM. pub fn set_enabled(enabled: bool, batch: impl FnOnce(&mut PwmBatch)) { let mut en = PwmBatch(0); batch(&mut en); @@ -289,9 +304,12 @@ mod sealed { pub trait Channel {} } +/// PWM Channel. pub trait Channel: Peripheral

+ sealed::Channel + Sized + 'static { + /// Channel number. fn number(&self) -> u8; + /// Channel register block. fn regs(&self) -> pac::pwm::Channel { pac::PWM.ch(self.number() as _) } @@ -317,7 +335,9 @@ channel!(PWM_CH5, 5); channel!(PWM_CH6, 6); channel!(PWM_CH7, 7); +/// PWM Pin A. pub trait PwmPinA: GpioPin {} +/// PWM Pin B. pub trait PwmPinB: GpioPin {} macro_rules! impl_pin { diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index c3df3ee5..b696989f 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs @@ -1,3 +1,4 @@ +//! RTC driver. mod filter; use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; diff --git a/embassy-rp/src/timer.rs b/embassy-rp/src/timer.rs index faa8df03..69c0c85b 100644 --- a/embassy-rp/src/timer.rs +++ b/embassy-rp/src/timer.rs @@ -1,3 +1,4 @@ +//! Timer driver. use core::cell::Cell; use atomic_polyfill::{AtomicU8, Ordering}; diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index f14e0852..99c95812 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -1,3 +1,4 @@ +//! Buffered UART driver. use core::future::{poll_fn, Future}; use core::slice; use core::task::Poll; diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 32be7661..99fce0fc 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -1,3 +1,4 @@ +//! UART driver. use core::future::poll_fn; use core::marker::PhantomData; use core::task::Poll; @@ -947,7 +948,7 @@ pub struct Async; impl_mode!(Blocking); impl_mode!(Async); -/// UART instance trait. +/// UART instance. pub trait Instance: sealed::Instance {} macro_rules! impl_instance { diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index bcd84822..905661d6 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -1,3 +1,4 @@ +//! USB driver. use core::future::poll_fn; use core::marker::PhantomData; use core::slice;