Compare commits
	
		
			4 Commits
		
	
	
		
			embassy-rp
			...
			embassy-rp
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | f4b77c967f | ||
|  | ca2e3759ad | ||
|  | 6f21f0680e | ||
|  | fc724dd707 | 
| @@ -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 = impl AdcPin + 'p> + '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 = ADC_TEMP_SENSOR> + '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<u16, Error> { | ||||
|         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<P = ADC> + 'd, | ||||
|         _irq: impl Binding<interrupt::typelevel::ADC_IRQ_FIFO, InterruptHandler>, | ||||
| @@ -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<u16, Error> { | ||||
|         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<S: AdcSample>( | ||||
|         &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<P = ADC> + '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 { | ||||
|   | ||||
| @@ -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<T>, | ||||
| } | ||||
|  | ||||
| impl<'d, T: Pin> Gpin<'d, T> { | ||||
|     /// Create new gpin driver. | ||||
|     pub fn new<P: GpinPin>(gpin: impl Peripheral<P = P> + '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<P = T> + '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(); | ||||
|   | ||||
| @@ -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<P = C> + '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<P = C> + '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<P = C> + '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<P = C> + '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<P = Self> + sealed::Channel + Into<AnyChannel> + 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, | ||||
| } | ||||
|   | ||||
| @@ -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<PeripheralRef<'d, AnyChannel>>, | ||||
|     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<P = T> + '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<P = T> + 'd, dma: impl Peripheral<P = impl Channel> + '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); | ||||
|   | ||||
| @@ -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<Level> 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<P = T> + '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<T> { | ||||
|         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<P = T> + '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<P = T> + '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<P = T> + '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<P = T> + '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<T> { | ||||
|         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<P = Self> + Into<AnyPin> + sealed::Pin + Sized + 'static { | ||||
|     /// Degrade to a generic pin struct | ||||
|     fn degrade(self) -> AnyPin { | ||||
| @@ -839,6 +900,7 @@ pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + sealed::Pin + Sized + 'stat | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Type-erased GPIO pin | ||||
| pub struct AnyPin { | ||||
|     pin_bank: u8, | ||||
| } | ||||
|   | ||||
| @@ -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<P = T> + 'd, | ||||
|         scl: impl Peripheral<P = impl SclPin<T>> + '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<P = T> + 'd, | ||||
|         scl: impl Peripheral<P = impl SclPin<T>> + '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<Item = u8>) -> 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<T: Instance> { | ||||
|     _uart: PhantomData<T>, | ||||
| } | ||||
| @@ -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<T: Instance> {} | ||||
| } | ||||
|  | ||||
| /// 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<T: Instance>: sealed::SdaPin<T> + crate::gpio::Pin {} | ||||
| /// SCL pin. | ||||
| pub trait SclPin<T: Instance>: sealed::SclPin<T> + crate::gpio::Pin {} | ||||
|  | ||||
| macro_rules! impl_pin { | ||||
|   | ||||
| @@ -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<P = T> + 'd, | ||||
|         scl: impl Peripheral<P = impl SclPin<T>> + 'd, | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| //! PIO driver. | ||||
| use core::future::Future; | ||||
| use core::marker::PhantomData; | ||||
| use core::pin::Pin as FuturePin; | ||||
|   | ||||
| @@ -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<P = T> + '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<P = T> + '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<P = T> + '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<P = T> + '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<P = T> + '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<P = T> + '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<P = Self> + 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<T: Channel>: GpioPin {} | ||||
| /// PWM Pin B. | ||||
| pub trait PwmPinB<T: Channel>: GpioPin {} | ||||
|  | ||||
| macro_rules! impl_pin { | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| //! RTC driver. | ||||
| mod filter; | ||||
|  | ||||
| use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| //! Timer driver. | ||||
| use core::cell::Cell; | ||||
|  | ||||
| use atomic_polyfill::{AtomicU8, Ordering}; | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| //! Buffered UART driver. | ||||
| use core::future::{poll_fn, Future}; | ||||
| use core::slice; | ||||
| use core::task::Poll; | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| //! USB driver. | ||||
| use core::future::poll_fn; | ||||
| use core::marker::PhantomData; | ||||
| use core::slice; | ||||
|   | ||||
| @@ -148,31 +148,20 @@ struct Timeout { | ||||
|  | ||||
| #[allow(dead_code)] | ||||
| impl Timeout { | ||||
|     #[cfg(not(feature = "time"))] | ||||
|     #[inline] | ||||
|     fn check(self) -> Result<(), Error> { | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|         #[cfg(feature = "time")] | ||||
|     #[inline] | ||||
|     fn check(self) -> Result<(), Error> { | ||||
|         if Instant::now() > self.deadline { | ||||
|             Err(Error::Timeout) | ||||
|         } else { | ||||
|             return Err(Error::Timeout); | ||||
|         } | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|     } | ||||
|  | ||||
|     #[cfg(not(feature = "time"))] | ||||
|     #[inline] | ||||
|     fn with<R>(self, fut: impl Future<Output = Result<R, Error>>) -> impl Future<Output = Result<R, Error>> { | ||||
|         fut | ||||
|     } | ||||
|  | ||||
|         #[cfg(feature = "time")] | ||||
|     #[inline] | ||||
|     fn with<R>(self, fut: impl Future<Output = Result<R, Error>>) -> impl Future<Output = Result<R, Error>> { | ||||
|         { | ||||
|             use futures::FutureExt; | ||||
|  | ||||
|             embassy_futures::select::select(embassy_time::Timer::at(self.deadline), fut).map(|r| match r { | ||||
| @@ -180,6 +169,10 @@ impl Timeout { | ||||
|                 embassy_futures::select::Either::Second(r) => r, | ||||
|             }) | ||||
|         } | ||||
|  | ||||
|         #[cfg(not(feature = "time"))] | ||||
|         fut | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub(crate) mod sealed { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user