rp: add PWM api
This commit is contained in:
		
							parent
							
								
									54fe50c685
								
							
						
					
					
						commit
						a4866ad278
					
				| @ -60,8 +60,9 @@ chrono = { version = "0.4", default-features = false, optional = true } | |||||||
| embedded-io = { version = "0.4.0", features = ["async"], optional = true } | embedded-io = { version = "0.4.0", features = ["async"], optional = true } | ||||||
| embedded-storage = { version = "0.3" } | embedded-storage = { version = "0.3" } | ||||||
| rand_core = "0.6.4" | 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-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } | ||||||
| embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} | 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 cs = p.cs().read(); | ||||||
|     let prim = p.prim().read(); |     let prim = p.prim().read(); | ||||||
|     if cs.lock() |     if cs.lock() | ||||||
|         && cs.refdiv() == refdiv as _ |         && cs.refdiv() == refdiv as u8 | ||||||
|         && p.fbdiv_int().read().fbdiv_int() == fbdiv as _ |         && p.fbdiv_int().read().fbdiv_int() == fbdiv as u16 | ||||||
|         && prim.postdiv1() == post_div1 |         && prim.postdiv1() == post_div1 | ||||||
|         && prim.postdiv2() == post_div2 |         && prim.postdiv2() == post_div2 | ||||||
|     { |     { | ||||||
|  | |||||||
| @ -21,6 +21,7 @@ pub mod interrupt; | |||||||
| pub mod pio; | pub mod pio; | ||||||
| #[cfg(feature = "pio")] | #[cfg(feature = "pio")] | ||||||
| pub mod pio_instr_util; | pub mod pio_instr_util; | ||||||
|  | pub mod pwm; | ||||||
| #[cfg(feature = "pio")] | #[cfg(feature = "pio")] | ||||||
| pub mod relocate; | pub mod relocate; | ||||||
| 
 | 
 | ||||||
| @ -109,6 +110,15 @@ embassy_hal_common::peripherals! { | |||||||
|     DMA_CH10, |     DMA_CH10, | ||||||
|     DMA_CH11, |     DMA_CH11, | ||||||
| 
 | 
 | ||||||
|  |     PWM_CH0, | ||||||
|  |     PWM_CH1, | ||||||
|  |     PWM_CH2, | ||||||
|  |     PWM_CH3, | ||||||
|  |     PWM_CH4, | ||||||
|  |     PWM_CH5, | ||||||
|  |     PWM_CH6, | ||||||
|  |     PWM_CH7, | ||||||
|  | 
 | ||||||
|     USB, |     USB, | ||||||
| 
 | 
 | ||||||
|     RTC, |     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 len = (max_packet_size + 63) / 64 * 64; | ||||||
| 
 | 
 | ||||||
|         let addr = self.ep_mem_free; |         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"); |             warn!("Endpoint memory full"); | ||||||
|             return Err(EndpointAllocError); |             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(); | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user