Merge #1387
1387: rp: add PWM api r=Dirbaio a=pennae add PWM api ~~including interrupts and async support.~~ depends on https://github.com/embassy-rs/rp-pac/pull/1 **TODO**: - [x] example - [x] test - [x] move divmode to typelevel - [x] deduplicate `new_*` functions Co-authored-by: pennae <github@quasiparticle.net>
This commit is contained in:
		@@ -60,8 +60,9 @@ chrono = { version = "0.4", default-features = false, optional = true }
 | 
			
		||||
embedded-io = { version = "0.4.0", features = ["async"], optional = true }
 | 
			
		||||
embedded-storage = { version = "0.3" }
 | 
			
		||||
rand_core = "0.6.4"
 | 
			
		||||
fixed = "1.23.1"
 | 
			
		||||
 | 
			
		||||
rp-pac = { version = "1", features = ["rt"] }
 | 
			
		||||
rp-pac = { version = "2", features = ["rt"] }
 | 
			
		||||
 | 
			
		||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
 | 
			
		||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true}
 | 
			
		||||
 
 | 
			
		||||
@@ -155,8 +155,8 @@ unsafe fn configure_pll(p: pac::pll::Pll, refdiv: u32, vco_freq: u32, post_div1:
 | 
			
		||||
    let cs = p.cs().read();
 | 
			
		||||
    let prim = p.prim().read();
 | 
			
		||||
    if cs.lock()
 | 
			
		||||
        && cs.refdiv() == refdiv as _
 | 
			
		||||
        && p.fbdiv_int().read().fbdiv_int() == fbdiv as _
 | 
			
		||||
        && cs.refdiv() == refdiv as u8
 | 
			
		||||
        && p.fbdiv_int().read().fbdiv_int() == fbdiv as u16
 | 
			
		||||
        && prim.postdiv1() == post_div1
 | 
			
		||||
        && prim.postdiv2() == post_div2
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@ pub mod interrupt;
 | 
			
		||||
pub mod pio;
 | 
			
		||||
#[cfg(feature = "pio")]
 | 
			
		||||
pub mod pio_instr_util;
 | 
			
		||||
pub mod pwm;
 | 
			
		||||
#[cfg(feature = "pio")]
 | 
			
		||||
pub mod relocate;
 | 
			
		||||
 | 
			
		||||
@@ -109,6 +110,15 @@ embassy_hal_common::peripherals! {
 | 
			
		||||
    DMA_CH10,
 | 
			
		||||
    DMA_CH11,
 | 
			
		||||
 | 
			
		||||
    PWM_CH0,
 | 
			
		||||
    PWM_CH1,
 | 
			
		||||
    PWM_CH2,
 | 
			
		||||
    PWM_CH3,
 | 
			
		||||
    PWM_CH4,
 | 
			
		||||
    PWM_CH5,
 | 
			
		||||
    PWM_CH6,
 | 
			
		||||
    PWM_CH7,
 | 
			
		||||
 | 
			
		||||
    USB,
 | 
			
		||||
 | 
			
		||||
    RTC,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										338
									
								
								embassy-rp/src/pwm.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										338
									
								
								embassy-rp/src/pwm.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,338 @@
 | 
			
		||||
//! Pulse Width Modulation (PWM)
 | 
			
		||||
 | 
			
		||||
use embassy_embedded_hal::SetConfig;
 | 
			
		||||
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
 | 
			
		||||
use fixed::traits::ToFixed;
 | 
			
		||||
use fixed::FixedU16;
 | 
			
		||||
use pac::pwm::regs::{ChDiv, Intr};
 | 
			
		||||
use pac::pwm::vals::Divmode;
 | 
			
		||||
 | 
			
		||||
use crate::gpio::sealed::Pin as _;
 | 
			
		||||
use crate::gpio::{AnyPin, Pin as GpioPin};
 | 
			
		||||
use crate::{pac, peripherals, RegExt};
 | 
			
		||||
 | 
			
		||||
#[non_exhaustive]
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub struct Config {
 | 
			
		||||
    pub invert_a: bool,
 | 
			
		||||
    pub invert_b: bool,
 | 
			
		||||
    pub phase_correct: bool,
 | 
			
		||||
    pub enable: bool,
 | 
			
		||||
    pub divider: fixed::FixedU16<fixed::types::extra::U4>,
 | 
			
		||||
    pub compare_a: u16,
 | 
			
		||||
    pub compare_b: u16,
 | 
			
		||||
    pub top: u16,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for Config {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            invert_a: false,
 | 
			
		||||
            invert_b: false,
 | 
			
		||||
            phase_correct: false,
 | 
			
		||||
            enable: true, // differs from reset value
 | 
			
		||||
            divider: 1.to_fixed(),
 | 
			
		||||
            compare_a: 0,
 | 
			
		||||
            compare_b: 0,
 | 
			
		||||
            top: 0xffff,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub enum InputMode {
 | 
			
		||||
    Level,
 | 
			
		||||
    RisingEdge,
 | 
			
		||||
    FallingEdge,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<InputMode> for Divmode {
 | 
			
		||||
    fn from(value: InputMode) -> Self {
 | 
			
		||||
        match value {
 | 
			
		||||
            InputMode::Level => Divmode::LEVEL,
 | 
			
		||||
            InputMode::RisingEdge => Divmode::RISE,
 | 
			
		||||
            InputMode::FallingEdge => Divmode::FALL,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Pwm<'d, T: Channel> {
 | 
			
		||||
    inner: PeripheralRef<'d, T>,
 | 
			
		||||
    pin_a: Option<PeripheralRef<'d, AnyPin>>,
 | 
			
		||||
    pin_b: Option<PeripheralRef<'d, AnyPin>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, T: Channel> Pwm<'d, T> {
 | 
			
		||||
    fn new_inner(
 | 
			
		||||
        inner: impl Peripheral<P = T> + 'd,
 | 
			
		||||
        a: Option<PeripheralRef<'d, AnyPin>>,
 | 
			
		||||
        b: Option<PeripheralRef<'d, AnyPin>>,
 | 
			
		||||
        config: Config,
 | 
			
		||||
        divmode: Divmode,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        into_ref!(inner);
 | 
			
		||||
 | 
			
		||||
        let p = inner.regs();
 | 
			
		||||
        unsafe {
 | 
			
		||||
            p.csr().modify(|w| {
 | 
			
		||||
                w.set_divmode(divmode);
 | 
			
		||||
                w.set_en(false);
 | 
			
		||||
            });
 | 
			
		||||
            p.ctr().write(|w| w.0 = 0);
 | 
			
		||||
            Self::configure(p, &config);
 | 
			
		||||
 | 
			
		||||
            if let Some(pin) = &a {
 | 
			
		||||
                pin.io().ctrl().write(|w| w.set_funcsel(4));
 | 
			
		||||
            }
 | 
			
		||||
            if let Some(pin) = &b {
 | 
			
		||||
                pin.io().ctrl().write(|w| w.set_funcsel(4));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Self {
 | 
			
		||||
            inner,
 | 
			
		||||
            pin_a: a.into(),
 | 
			
		||||
            pin_b: b.into(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn new_free(inner: impl Peripheral<P = T> + 'd, config: Config) -> Self {
 | 
			
		||||
        Self::new_inner(inner, None, None, config, Divmode::DIV)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn new_output_a(
 | 
			
		||||
        inner: impl Peripheral<P = T> + 'd,
 | 
			
		||||
        a: impl Peripheral<P = impl PwmPinA<T>> + 'd,
 | 
			
		||||
        config: Config,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        into_ref!(a);
 | 
			
		||||
        Self::new_inner(inner, Some(a.map_into()), None, config, Divmode::DIV)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn new_output_b(
 | 
			
		||||
        inner: impl Peripheral<P = T> + 'd,
 | 
			
		||||
        b: impl Peripheral<P = impl PwmPinB<T>> + 'd,
 | 
			
		||||
        config: Config,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        into_ref!(b);
 | 
			
		||||
        Self::new_inner(inner, None, Some(b.map_into()), config, Divmode::DIV)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn new_output_ab(
 | 
			
		||||
        inner: impl Peripheral<P = T> + 'd,
 | 
			
		||||
        a: impl Peripheral<P = impl PwmPinA<T>> + 'd,
 | 
			
		||||
        b: impl Peripheral<P = impl PwmPinB<T>> + 'd,
 | 
			
		||||
        config: Config,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        into_ref!(a, b);
 | 
			
		||||
        Self::new_inner(inner, Some(a.map_into()), Some(b.map_into()), config, Divmode::DIV)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn new_input(
 | 
			
		||||
        inner: impl Peripheral<P = T> + 'd,
 | 
			
		||||
        b: impl Peripheral<P = impl PwmPinB<T>> + 'd,
 | 
			
		||||
        mode: InputMode,
 | 
			
		||||
        config: Config,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        into_ref!(b);
 | 
			
		||||
        Self::new_inner(inner, None, Some(b.map_into()), config, mode.into())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn new_output_input(
 | 
			
		||||
        inner: impl Peripheral<P = T> + 'd,
 | 
			
		||||
        a: impl Peripheral<P = impl PwmPinA<T>> + 'd,
 | 
			
		||||
        b: impl Peripheral<P = impl PwmPinB<T>> + 'd,
 | 
			
		||||
        mode: InputMode,
 | 
			
		||||
        config: Config,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        into_ref!(a, b);
 | 
			
		||||
        Self::new_inner(inner, Some(a.map_into()), Some(b.map_into()), config, mode.into())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn configure(p: pac::pwm::Channel, config: &Config) {
 | 
			
		||||
        if config.divider > FixedU16::<fixed::types::extra::U4>::from_bits(0xFF_F) {
 | 
			
		||||
            panic!("Requested divider is too large");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        unsafe {
 | 
			
		||||
            p.div().write_value(ChDiv(config.divider.to_bits() as u32));
 | 
			
		||||
            p.cc().write(|w| {
 | 
			
		||||
                w.set_a(config.compare_a);
 | 
			
		||||
                w.set_b(config.compare_b);
 | 
			
		||||
            });
 | 
			
		||||
            p.top().write(|w| w.set_top(config.top));
 | 
			
		||||
            p.csr().modify(|w| {
 | 
			
		||||
                w.set_a_inv(config.invert_a);
 | 
			
		||||
                w.set_b_inv(config.invert_b);
 | 
			
		||||
                w.set_ph_correct(config.phase_correct);
 | 
			
		||||
                w.set_en(config.enable);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub unsafe fn phase_advance(&mut self) {
 | 
			
		||||
        let p = self.inner.regs();
 | 
			
		||||
        p.csr().write_set(|w| w.set_ph_adv(true));
 | 
			
		||||
        while p.csr().read().ph_adv() {}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub unsafe fn phase_retard(&mut self) {
 | 
			
		||||
        let p = self.inner.regs();
 | 
			
		||||
        p.csr().write_set(|w| w.set_ph_ret(true));
 | 
			
		||||
        while p.csr().read().ph_ret() {}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn counter(&self) -> u16 {
 | 
			
		||||
        unsafe { self.inner.regs().ctr().read().ctr() }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn set_counter(&self, ctr: u16) {
 | 
			
		||||
        unsafe { self.inner.regs().ctr().write(|w| w.set_ctr(ctr)) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn wait_for_wrap(&mut self) {
 | 
			
		||||
        while !self.wrapped() {}
 | 
			
		||||
        self.clear_wrapped();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn wrapped(&mut self) -> bool {
 | 
			
		||||
        unsafe { pac::PWM.intr().read().0 & self.bit() != 0 }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn clear_wrapped(&mut self) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            pac::PWM.intr().write_value(Intr(self.bit() as _));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn bit(&self) -> u32 {
 | 
			
		||||
        1 << self.inner.number() as usize
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct PwmBatch(u32);
 | 
			
		||||
 | 
			
		||||
impl PwmBatch {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn enable(&mut self, pwm: &Pwm<'_, impl Channel>) {
 | 
			
		||||
        self.0 |= pwm.bit();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn set_enabled(enabled: bool, batch: impl FnOnce(&mut PwmBatch)) {
 | 
			
		||||
        let mut en = PwmBatch(0);
 | 
			
		||||
        batch(&mut en);
 | 
			
		||||
        unsafe {
 | 
			
		||||
            if enabled {
 | 
			
		||||
                pac::PWM.en().write_set(|w| w.0 = en.0);
 | 
			
		||||
            } else {
 | 
			
		||||
                pac::PWM.en().write_clear(|w| w.0 = en.0);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, T: Channel> Drop for Pwm<'d, T> {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.inner.regs().csr().write_clear(|w| w.set_en(false));
 | 
			
		||||
            if let Some(pin) = &self.pin_a {
 | 
			
		||||
                pin.io().ctrl().write(|w| w.set_funcsel(31));
 | 
			
		||||
            }
 | 
			
		||||
            if let Some(pin) = &self.pin_b {
 | 
			
		||||
                pin.io().ctrl().write(|w| w.set_funcsel(31));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
mod sealed {
 | 
			
		||||
    pub trait Channel {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait Channel: Peripheral<P = Self> + sealed::Channel + Sized + 'static {
 | 
			
		||||
    fn number(&self) -> u8;
 | 
			
		||||
 | 
			
		||||
    fn regs(&self) -> pac::pwm::Channel {
 | 
			
		||||
        pac::PWM.ch(self.number() as _)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! channel {
 | 
			
		||||
    ($name:ident, $num:expr) => {
 | 
			
		||||
        impl sealed::Channel for peripherals::$name {}
 | 
			
		||||
        impl Channel for peripherals::$name {
 | 
			
		||||
            fn number(&self) -> u8 {
 | 
			
		||||
                $num
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
channel!(PWM_CH0, 0);
 | 
			
		||||
channel!(PWM_CH1, 1);
 | 
			
		||||
channel!(PWM_CH2, 2);
 | 
			
		||||
channel!(PWM_CH3, 3);
 | 
			
		||||
channel!(PWM_CH4, 4);
 | 
			
		||||
channel!(PWM_CH5, 5);
 | 
			
		||||
channel!(PWM_CH6, 6);
 | 
			
		||||
channel!(PWM_CH7, 7);
 | 
			
		||||
 | 
			
		||||
pub trait PwmPinA<T: Channel>: GpioPin {}
 | 
			
		||||
pub trait PwmPinB<T: Channel>: GpioPin {}
 | 
			
		||||
 | 
			
		||||
macro_rules! impl_pin {
 | 
			
		||||
    ($pin:ident, $channel:ident, $kind:ident) => {
 | 
			
		||||
        impl $kind<peripherals::$channel> for peripherals::$pin {}
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl_pin!(PIN_0, PWM_CH0, PwmPinA);
 | 
			
		||||
impl_pin!(PIN_1, PWM_CH0, PwmPinB);
 | 
			
		||||
impl_pin!(PIN_2, PWM_CH1, PwmPinA);
 | 
			
		||||
impl_pin!(PIN_3, PWM_CH1, PwmPinB);
 | 
			
		||||
impl_pin!(PIN_4, PWM_CH2, PwmPinA);
 | 
			
		||||
impl_pin!(PIN_5, PWM_CH2, PwmPinB);
 | 
			
		||||
impl_pin!(PIN_6, PWM_CH3, PwmPinA);
 | 
			
		||||
impl_pin!(PIN_7, PWM_CH3, PwmPinB);
 | 
			
		||||
impl_pin!(PIN_8, PWM_CH4, PwmPinA);
 | 
			
		||||
impl_pin!(PIN_9, PWM_CH4, PwmPinB);
 | 
			
		||||
impl_pin!(PIN_10, PWM_CH5, PwmPinA);
 | 
			
		||||
impl_pin!(PIN_11, PWM_CH5, PwmPinB);
 | 
			
		||||
impl_pin!(PIN_12, PWM_CH6, PwmPinA);
 | 
			
		||||
impl_pin!(PIN_13, PWM_CH6, PwmPinB);
 | 
			
		||||
impl_pin!(PIN_14, PWM_CH7, PwmPinA);
 | 
			
		||||
impl_pin!(PIN_15, PWM_CH7, PwmPinB);
 | 
			
		||||
impl_pin!(PIN_16, PWM_CH0, PwmPinA);
 | 
			
		||||
impl_pin!(PIN_17, PWM_CH0, PwmPinB);
 | 
			
		||||
impl_pin!(PIN_18, PWM_CH1, PwmPinA);
 | 
			
		||||
impl_pin!(PIN_19, PWM_CH1, PwmPinB);
 | 
			
		||||
impl_pin!(PIN_20, PWM_CH2, PwmPinA);
 | 
			
		||||
impl_pin!(PIN_21, PWM_CH2, PwmPinB);
 | 
			
		||||
impl_pin!(PIN_22, PWM_CH3, PwmPinA);
 | 
			
		||||
impl_pin!(PIN_23, PWM_CH3, PwmPinB);
 | 
			
		||||
impl_pin!(PIN_24, PWM_CH4, PwmPinA);
 | 
			
		||||
impl_pin!(PIN_25, PWM_CH4, PwmPinB);
 | 
			
		||||
impl_pin!(PIN_26, PWM_CH5, PwmPinA);
 | 
			
		||||
impl_pin!(PIN_27, PWM_CH5, PwmPinB);
 | 
			
		||||
impl_pin!(PIN_28, PWM_CH6, PwmPinA);
 | 
			
		||||
impl_pin!(PIN_29, PWM_CH6, PwmPinB);
 | 
			
		||||
 | 
			
		||||
impl<'d, T: Channel> SetConfig for Pwm<'d, T> {
 | 
			
		||||
    type Config = Config;
 | 
			
		||||
    fn set_config(&mut self, config: &Self::Config) {
 | 
			
		||||
        Self::configure(self.inner.regs(), config);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -231,7 +231,7 @@ impl<'d, T: Instance> Driver<'d, T> {
 | 
			
		||||
        let len = (max_packet_size + 63) / 64 * 64;
 | 
			
		||||
 | 
			
		||||
        let addr = self.ep_mem_free;
 | 
			
		||||
        if addr + len > EP_MEMORY_SIZE as _ {
 | 
			
		||||
        if addr + len > EP_MEMORY_SIZE as u16 {
 | 
			
		||||
            warn!("Endpoint memory full");
 | 
			
		||||
            return Err(EndpointAllocError);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								examples/rp/src/bin/pwm.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								examples/rp/src/bin/pwm.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
#![no_std]
 | 
			
		||||
#![no_main]
 | 
			
		||||
#![feature(type_alias_impl_trait)]
 | 
			
		||||
 | 
			
		||||
use defmt::*;
 | 
			
		||||
use embassy_embedded_hal::SetConfig;
 | 
			
		||||
use embassy_executor::Spawner;
 | 
			
		||||
use embassy_rp::pwm::{Config, Pwm};
 | 
			
		||||
use embassy_time::{Duration, Timer};
 | 
			
		||||
use {defmt_rtt as _, panic_probe as _};
 | 
			
		||||
 | 
			
		||||
#[embassy_executor::main]
 | 
			
		||||
async fn main(_spawner: Spawner) {
 | 
			
		||||
    let p = embassy_rp::init(Default::default());
 | 
			
		||||
 | 
			
		||||
    let mut c: Config = Default::default();
 | 
			
		||||
    c.top = 0x8000;
 | 
			
		||||
    c.compare_b = 8;
 | 
			
		||||
    let mut pwm = Pwm::new_output_b(p.PWM_CH4, p.PIN_25, c.clone());
 | 
			
		||||
 | 
			
		||||
    loop {
 | 
			
		||||
        info!("current LED duty cycle: {}/32768", c.compare_b);
 | 
			
		||||
        Timer::after(Duration::from_secs(1)).await;
 | 
			
		||||
        c.compare_b = c.compare_b.rotate_left(4);
 | 
			
		||||
        pwm.set_config(&c);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										142
									
								
								tests/rp/src/bin/pwm.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								tests/rp/src/bin/pwm.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,142 @@
 | 
			
		||||
#![no_std]
 | 
			
		||||
#![no_main]
 | 
			
		||||
#![feature(type_alias_impl_trait)]
 | 
			
		||||
 | 
			
		||||
use defmt::{assert, assert_eq, assert_ne, *};
 | 
			
		||||
use embassy_executor::Spawner;
 | 
			
		||||
use embassy_rp::gpio::{Input, Level, Output, Pull};
 | 
			
		||||
use embassy_rp::pwm::{Config, InputMode, Pwm};
 | 
			
		||||
use embassy_time::{Duration, Timer};
 | 
			
		||||
use {defmt_rtt as _, panic_probe as _};
 | 
			
		||||
 | 
			
		||||
#[embassy_executor::main]
 | 
			
		||||
async fn main(_spawner: Spawner) {
 | 
			
		||||
    let mut p = embassy_rp::init(Default::default());
 | 
			
		||||
    info!("Hello World!");
 | 
			
		||||
 | 
			
		||||
    // Connections on CI device: 6 -> 9, 7 -> 11
 | 
			
		||||
    let (mut p6, mut p7, mut p9, mut p11) = (p.PIN_6, p.PIN_7, p.PIN_9, p.PIN_11);
 | 
			
		||||
 | 
			
		||||
    let cfg = {
 | 
			
		||||
        let mut c = Config::default();
 | 
			
		||||
        c.divider = 125.into();
 | 
			
		||||
        c.top = 10000;
 | 
			
		||||
        c.compare_a = 5000;
 | 
			
		||||
        c.compare_b = 5000;
 | 
			
		||||
        c
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Test free-running clock
 | 
			
		||||
    {
 | 
			
		||||
        let pwm = Pwm::new_free(&mut p.PWM_CH3, cfg.clone());
 | 
			
		||||
        cortex_m::asm::delay(125);
 | 
			
		||||
        let ctr = pwm.counter();
 | 
			
		||||
        assert!(ctr > 0);
 | 
			
		||||
        assert!(ctr < 100);
 | 
			
		||||
        cortex_m::asm::delay(125);
 | 
			
		||||
        assert!(ctr < pwm.counter());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for invert_a in [false, true] {
 | 
			
		||||
        info!("free-running, invert A: {}", invert_a);
 | 
			
		||||
        let mut cfg = cfg.clone();
 | 
			
		||||
        cfg.invert_a = invert_a;
 | 
			
		||||
        cfg.invert_b = !invert_a;
 | 
			
		||||
 | 
			
		||||
        // Test output from A
 | 
			
		||||
        {
 | 
			
		||||
            let pin1 = Input::new(&mut p9, Pull::None);
 | 
			
		||||
            let _pwm = Pwm::new_output_a(&mut p.PWM_CH3, &mut p6, cfg.clone());
 | 
			
		||||
            Timer::after(Duration::from_millis(1)).await;
 | 
			
		||||
            assert_eq!(pin1.is_low(), invert_a);
 | 
			
		||||
            Timer::after(Duration::from_millis(5)).await;
 | 
			
		||||
            assert_eq!(pin1.is_high(), invert_a);
 | 
			
		||||
            Timer::after(Duration::from_millis(5)).await;
 | 
			
		||||
            assert_eq!(pin1.is_low(), invert_a);
 | 
			
		||||
            Timer::after(Duration::from_millis(5)).await;
 | 
			
		||||
            assert_eq!(pin1.is_high(), invert_a);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Test output from B
 | 
			
		||||
        {
 | 
			
		||||
            let pin2 = Input::new(&mut p11, Pull::None);
 | 
			
		||||
            let _pwm = Pwm::new_output_b(&mut p.PWM_CH3, &mut p7, cfg.clone());
 | 
			
		||||
            Timer::after(Duration::from_millis(1)).await;
 | 
			
		||||
            assert_ne!(pin2.is_low(), invert_a);
 | 
			
		||||
            Timer::after(Duration::from_millis(5)).await;
 | 
			
		||||
            assert_ne!(pin2.is_high(), invert_a);
 | 
			
		||||
            Timer::after(Duration::from_millis(5)).await;
 | 
			
		||||
            assert_ne!(pin2.is_low(), invert_a);
 | 
			
		||||
            Timer::after(Duration::from_millis(5)).await;
 | 
			
		||||
            assert_ne!(pin2.is_high(), invert_a);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Test output from A+B
 | 
			
		||||
        {
 | 
			
		||||
            let pin1 = Input::new(&mut p9, Pull::None);
 | 
			
		||||
            let pin2 = Input::new(&mut p11, Pull::None);
 | 
			
		||||
            let _pwm = Pwm::new_output_ab(&mut p.PWM_CH3, &mut p6, &mut p7, cfg.clone());
 | 
			
		||||
            Timer::after(Duration::from_millis(1)).await;
 | 
			
		||||
            assert_eq!(pin1.is_low(), invert_a);
 | 
			
		||||
            assert_ne!(pin2.is_low(), invert_a);
 | 
			
		||||
            Timer::after(Duration::from_millis(5)).await;
 | 
			
		||||
            assert_eq!(pin1.is_high(), invert_a);
 | 
			
		||||
            assert_ne!(pin2.is_high(), invert_a);
 | 
			
		||||
            Timer::after(Duration::from_millis(5)).await;
 | 
			
		||||
            assert_eq!(pin1.is_low(), invert_a);
 | 
			
		||||
            assert_ne!(pin2.is_low(), invert_a);
 | 
			
		||||
            Timer::after(Duration::from_millis(5)).await;
 | 
			
		||||
            assert_eq!(pin1.is_high(), invert_a);
 | 
			
		||||
            assert_ne!(pin2.is_high(), invert_a);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Test level-gated
 | 
			
		||||
    {
 | 
			
		||||
        let mut pin2 = Output::new(&mut p11, Level::Low);
 | 
			
		||||
        let pwm = Pwm::new_input(&mut p.PWM_CH3, &mut p7, InputMode::Level, cfg.clone());
 | 
			
		||||
        assert_eq!(pwm.counter(), 0);
 | 
			
		||||
        Timer::after(Duration::from_millis(5)).await;
 | 
			
		||||
        assert_eq!(pwm.counter(), 0);
 | 
			
		||||
        pin2.set_high();
 | 
			
		||||
        Timer::after(Duration::from_millis(1)).await;
 | 
			
		||||
        pin2.set_low();
 | 
			
		||||
        let ctr = pwm.counter();
 | 
			
		||||
        assert!(ctr >= 1000);
 | 
			
		||||
        Timer::after(Duration::from_millis(1)).await;
 | 
			
		||||
        assert_eq!(pwm.counter(), ctr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Test rising-gated
 | 
			
		||||
    {
 | 
			
		||||
        let mut pin2 = Output::new(&mut p11, Level::Low);
 | 
			
		||||
        let pwm = Pwm::new_input(&mut p.PWM_CH3, &mut p7, InputMode::RisingEdge, cfg.clone());
 | 
			
		||||
        assert_eq!(pwm.counter(), 0);
 | 
			
		||||
        Timer::after(Duration::from_millis(5)).await;
 | 
			
		||||
        assert_eq!(pwm.counter(), 0);
 | 
			
		||||
        pin2.set_high();
 | 
			
		||||
        Timer::after(Duration::from_millis(1)).await;
 | 
			
		||||
        pin2.set_low();
 | 
			
		||||
        assert_eq!(pwm.counter(), 1);
 | 
			
		||||
        Timer::after(Duration::from_millis(1)).await;
 | 
			
		||||
        assert_eq!(pwm.counter(), 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Test falling-gated
 | 
			
		||||
    {
 | 
			
		||||
        let mut pin2 = Output::new(&mut p11, Level::High);
 | 
			
		||||
        let pwm = Pwm::new_input(&mut p.PWM_CH3, &mut p7, InputMode::FallingEdge, cfg.clone());
 | 
			
		||||
        assert_eq!(pwm.counter(), 0);
 | 
			
		||||
        Timer::after(Duration::from_millis(5)).await;
 | 
			
		||||
        assert_eq!(pwm.counter(), 0);
 | 
			
		||||
        pin2.set_low();
 | 
			
		||||
        Timer::after(Duration::from_millis(1)).await;
 | 
			
		||||
        pin2.set_high();
 | 
			
		||||
        assert_eq!(pwm.counter(), 1);
 | 
			
		||||
        Timer::after(Duration::from_millis(1)).await;
 | 
			
		||||
        assert_eq!(pwm.counter(), 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    info!("Test OK");
 | 
			
		||||
    cortex_m::asm::bkpt();
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user