aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-rp/Cargo.toml3
-rw-r--r--embassy-rp/src/clocks.rs4
-rw-r--r--embassy-rp/src/lib.rs10
-rw-r--r--embassy-rp/src/pwm.rs338
-rw-r--r--embassy-rp/src/usb.rs2
-rw-r--r--examples/rp/src/bin/pwm.rs27
-rw-r--r--tests/rp/src/bin/pwm.rs142
7 files changed, 522 insertions, 4 deletions
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml
index b7ed6ccbb..f784ab33d 100644
--- a/embassy-rp/Cargo.toml
+++ b/embassy-rp/Cargo.toml
@@ -60,8 +60,9 @@ chrono = { version = "0.4", default-features = false, optional = true }
60embedded-io = { version = "0.4.0", features = ["async"], optional = true } 60embedded-io = { version = "0.4.0", features = ["async"], optional = true }
61embedded-storage = { version = "0.3" } 61embedded-storage = { version = "0.3" }
62rand_core = "0.6.4" 62rand_core = "0.6.4"
63fixed = "1.23.1"
63 64
64rp-pac = { version = "1", features = ["rt"] } 65rp-pac = { version = "2", features = ["rt"] }
65 66
66embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 67embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
67embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} 68embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true}
diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs
index 85c9bbb7a..8a34b293d 100644
--- a/embassy-rp/src/clocks.rs
+++ b/embassy-rp/src/clocks.rs
@@ -155,8 +155,8 @@ unsafe fn configure_pll(p: pac::pll::Pll, refdiv: u32, vco_freq: u32, post_div1:
155 let cs = p.cs().read(); 155 let cs = p.cs().read();
156 let prim = p.prim().read(); 156 let prim = p.prim().read();
157 if cs.lock() 157 if cs.lock()
158 && cs.refdiv() == refdiv as _ 158 && cs.refdiv() == refdiv as u8
159 && p.fbdiv_int().read().fbdiv_int() == fbdiv as _ 159 && p.fbdiv_int().read().fbdiv_int() == fbdiv as u16
160 && prim.postdiv1() == post_div1 160 && prim.postdiv1() == post_div1
161 && prim.postdiv2() == post_div2 161 && prim.postdiv2() == post_div2
162 { 162 {
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs
index 3841bb83a..aa8660320 100644
--- a/embassy-rp/src/lib.rs
+++ b/embassy-rp/src/lib.rs
@@ -21,6 +21,7 @@ pub mod interrupt;
21pub mod pio; 21pub mod pio;
22#[cfg(feature = "pio")] 22#[cfg(feature = "pio")]
23pub mod pio_instr_util; 23pub mod pio_instr_util;
24pub mod pwm;
24#[cfg(feature = "pio")] 25#[cfg(feature = "pio")]
25pub mod relocate; 26pub mod relocate;
26 27
@@ -109,6 +110,15 @@ embassy_hal_common::peripherals! {
109 DMA_CH10, 110 DMA_CH10,
110 DMA_CH11, 111 DMA_CH11,
111 112
113 PWM_CH0,
114 PWM_CH1,
115 PWM_CH2,
116 PWM_CH3,
117 PWM_CH4,
118 PWM_CH5,
119 PWM_CH6,
120 PWM_CH7,
121
112 USB, 122 USB,
113 123
114 RTC, 124 RTC,
diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs
new file mode 100644
index 000000000..d2bf79584
--- /dev/null
+++ b/embassy-rp/src/pwm.rs
@@ -0,0 +1,338 @@
1//! Pulse Width Modulation (PWM)
2
3use embassy_embedded_hal::SetConfig;
4use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
5use fixed::traits::ToFixed;
6use fixed::FixedU16;
7use pac::pwm::regs::{ChDiv, Intr};
8use pac::pwm::vals::Divmode;
9
10use crate::gpio::sealed::Pin as _;
11use crate::gpio::{AnyPin, Pin as GpioPin};
12use crate::{pac, peripherals, RegExt};
13
14#[non_exhaustive]
15#[derive(Clone)]
16pub struct Config {
17 pub invert_a: bool,
18 pub invert_b: bool,
19 pub phase_correct: bool,
20 pub enable: bool,
21 pub divider: fixed::FixedU16<fixed::types::extra::U4>,
22 pub compare_a: u16,
23 pub compare_b: u16,
24 pub top: u16,
25}
26
27impl Default for Config {
28 fn default() -> Self {
29 Self {
30 invert_a: false,
31 invert_b: false,
32 phase_correct: false,
33 enable: true, // differs from reset value
34 divider: 1.to_fixed(),
35 compare_a: 0,
36 compare_b: 0,
37 top: 0xffff,
38 }
39 }
40}
41
42pub enum InputMode {
43 Level,
44 RisingEdge,
45 FallingEdge,
46}
47
48impl From<InputMode> for Divmode {
49 fn from(value: InputMode) -> Self {
50 match value {
51 InputMode::Level => Divmode::LEVEL,
52 InputMode::RisingEdge => Divmode::RISE,
53 InputMode::FallingEdge => Divmode::FALL,
54 }
55 }
56}
57
58pub struct Pwm<'d, T: Channel> {
59 inner: PeripheralRef<'d, T>,
60 pin_a: Option<PeripheralRef<'d, AnyPin>>,
61 pin_b: Option<PeripheralRef<'d, AnyPin>>,
62}
63
64impl<'d, T: Channel> Pwm<'d, T> {
65 fn new_inner(
66 inner: impl Peripheral<P = T> + 'd,
67 a: Option<PeripheralRef<'d, AnyPin>>,
68 b: Option<PeripheralRef<'d, AnyPin>>,
69 config: Config,
70 divmode: Divmode,
71 ) -> Self {
72 into_ref!(inner);
73
74 let p = inner.regs();
75 unsafe {
76 p.csr().modify(|w| {
77 w.set_divmode(divmode);
78 w.set_en(false);
79 });
80 p.ctr().write(|w| w.0 = 0);
81 Self::configure(p, &config);
82
83 if let Some(pin) = &a {
84 pin.io().ctrl().write(|w| w.set_funcsel(4));
85 }
86 if let Some(pin) = &b {
87 pin.io().ctrl().write(|w| w.set_funcsel(4));
88 }
89 }
90 Self {
91 inner,
92 pin_a: a.into(),
93 pin_b: b.into(),
94 }
95 }
96
97 #[inline]
98 pub fn new_free(inner: impl Peripheral<P = T> + 'd, config: Config) -> Self {
99 Self::new_inner(inner, None, None, config, Divmode::DIV)
100 }
101
102 #[inline]
103 pub fn new_output_a(
104 inner: impl Peripheral<P = T> + 'd,
105 a: impl Peripheral<P = impl PwmPinA<T>> + 'd,
106 config: Config,
107 ) -> Self {
108 into_ref!(a);
109 Self::new_inner(inner, Some(a.map_into()), None, config, Divmode::DIV)
110 }
111
112 #[inline]
113 pub fn new_output_b(
114 inner: impl Peripheral<P = T> + 'd,
115 b: impl Peripheral<P = impl PwmPinB<T>> + 'd,
116 config: Config,
117 ) -> Self {
118 into_ref!(b);
119 Self::new_inner(inner, None, Some(b.map_into()), config, Divmode::DIV)
120 }
121
122 #[inline]
123 pub fn new_output_ab(
124 inner: impl Peripheral<P = T> + 'd,
125 a: impl Peripheral<P = impl PwmPinA<T>> + 'd,
126 b: impl Peripheral<P = impl PwmPinB<T>> + 'd,
127 config: Config,
128 ) -> Self {
129 into_ref!(a, b);
130 Self::new_inner(inner, Some(a.map_into()), Some(b.map_into()), config, Divmode::DIV)
131 }
132
133 #[inline]
134 pub fn new_input(
135 inner: impl Peripheral<P = T> + 'd,
136 b: impl Peripheral<P = impl PwmPinB<T>> + 'd,
137 mode: InputMode,
138 config: Config,
139 ) -> Self {
140 into_ref!(b);
141 Self::new_inner(inner, None, Some(b.map_into()), config, mode.into())
142 }
143
144 #[inline]
145 pub fn new_output_input(
146 inner: impl Peripheral<P = T> + 'd,
147 a: impl Peripheral<P = impl PwmPinA<T>> + 'd,
148 b: impl Peripheral<P = impl PwmPinB<T>> + 'd,
149 mode: InputMode,
150 config: Config,
151 ) -> Self {
152 into_ref!(a, b);
153 Self::new_inner(inner, Some(a.map_into()), Some(b.map_into()), config, mode.into())
154 }
155
156 fn configure(p: pac::pwm::Channel, config: &Config) {
157 if config.divider > FixedU16::<fixed::types::extra::U4>::from_bits(0xFF_F) {
158 panic!("Requested divider is too large");
159 }
160
161 unsafe {
162 p.div().write_value(ChDiv(config.divider.to_bits() as u32));
163 p.cc().write(|w| {
164 w.set_a(config.compare_a);
165 w.set_b(config.compare_b);
166 });
167 p.top().write(|w| w.set_top(config.top));
168 p.csr().modify(|w| {
169 w.set_a_inv(config.invert_a);
170 w.set_b_inv(config.invert_b);
171 w.set_ph_correct(config.phase_correct);
172 w.set_en(config.enable);
173 });
174 }
175 }
176
177 #[inline]
178 pub unsafe fn phase_advance(&mut self) {
179 let p = self.inner.regs();
180 p.csr().write_set(|w| w.set_ph_adv(true));
181 while p.csr().read().ph_adv() {}
182 }
183
184 #[inline]
185 pub unsafe fn phase_retard(&mut self) {
186 let p = self.inner.regs();
187 p.csr().write_set(|w| w.set_ph_ret(true));
188 while p.csr().read().ph_ret() {}
189 }
190
191 #[inline]
192 pub fn counter(&self) -> u16 {
193 unsafe { self.inner.regs().ctr().read().ctr() }
194 }
195
196 #[inline]
197 pub fn set_counter(&self, ctr: u16) {
198 unsafe { self.inner.regs().ctr().write(|w| w.set_ctr(ctr)) }
199 }
200
201 #[inline]
202 pub fn wait_for_wrap(&mut self) {
203 while !self.wrapped() {}
204 self.clear_wrapped();
205 }
206
207 #[inline]
208 pub fn wrapped(&mut self) -> bool {
209 unsafe { pac::PWM.intr().read().0 & self.bit() != 0 }
210 }
211
212 #[inline]
213 pub fn clear_wrapped(&mut self) {
214 unsafe {
215 pac::PWM.intr().write_value(Intr(self.bit() as _));
216 }
217 }
218
219 #[inline]
220 fn bit(&self) -> u32 {
221 1 << self.inner.number() as usize
222 }
223}
224
225pub struct PwmBatch(u32);
226
227impl PwmBatch {
228 #[inline]
229 pub fn enable(&mut self, pwm: &Pwm<'_, impl Channel>) {
230 self.0 |= pwm.bit();
231 }
232
233 #[inline]
234 pub fn set_enabled(enabled: bool, batch: impl FnOnce(&mut PwmBatch)) {
235 let mut en = PwmBatch(0);
236 batch(&mut en);
237 unsafe {
238 if enabled {
239 pac::PWM.en().write_set(|w| w.0 = en.0);
240 } else {
241 pac::PWM.en().write_clear(|w| w.0 = en.0);
242 }
243 }
244 }
245}
246
247impl<'d, T: Channel> Drop for Pwm<'d, T> {
248 fn drop(&mut self) {
249 unsafe {
250 self.inner.regs().csr().write_clear(|w| w.set_en(false));
251 if let Some(pin) = &self.pin_a {
252 pin.io().ctrl().write(|w| w.set_funcsel(31));
253 }
254 if let Some(pin) = &self.pin_b {
255 pin.io().ctrl().write(|w| w.set_funcsel(31));
256 }
257 }
258 }
259}
260
261mod sealed {
262 pub trait Channel {}
263}
264
265pub trait Channel: Peripheral<P = Self> + sealed::Channel + Sized + 'static {
266 fn number(&self) -> u8;
267
268 fn regs(&self) -> pac::pwm::Channel {
269 pac::PWM.ch(self.number() as _)
270 }
271}
272
273macro_rules! channel {
274 ($name:ident, $num:expr) => {
275 impl sealed::Channel for peripherals::$name {}
276 impl Channel for peripherals::$name {
277 fn number(&self) -> u8 {
278 $num
279 }
280 }
281 };
282}
283
284channel!(PWM_CH0, 0);
285channel!(PWM_CH1, 1);
286channel!(PWM_CH2, 2);
287channel!(PWM_CH3, 3);
288channel!(PWM_CH4, 4);
289channel!(PWM_CH5, 5);
290channel!(PWM_CH6, 6);
291channel!(PWM_CH7, 7);
292
293pub trait PwmPinA<T: Channel>: GpioPin {}
294pub trait PwmPinB<T: Channel>: GpioPin {}
295
296macro_rules! impl_pin {
297 ($pin:ident, $channel:ident, $kind:ident) => {
298 impl $kind<peripherals::$channel> for peripherals::$pin {}
299 };
300}
301
302impl_pin!(PIN_0, PWM_CH0, PwmPinA);
303impl_pin!(PIN_1, PWM_CH0, PwmPinB);
304impl_pin!(PIN_2, PWM_CH1, PwmPinA);
305impl_pin!(PIN_3, PWM_CH1, PwmPinB);
306impl_pin!(PIN_4, PWM_CH2, PwmPinA);
307impl_pin!(PIN_5, PWM_CH2, PwmPinB);
308impl_pin!(PIN_6, PWM_CH3, PwmPinA);
309impl_pin!(PIN_7, PWM_CH3, PwmPinB);
310impl_pin!(PIN_8, PWM_CH4, PwmPinA);
311impl_pin!(PIN_9, PWM_CH4, PwmPinB);
312impl_pin!(PIN_10, PWM_CH5, PwmPinA);
313impl_pin!(PIN_11, PWM_CH5, PwmPinB);
314impl_pin!(PIN_12, PWM_CH6, PwmPinA);
315impl_pin!(PIN_13, PWM_CH6, PwmPinB);
316impl_pin!(PIN_14, PWM_CH7, PwmPinA);
317impl_pin!(PIN_15, PWM_CH7, PwmPinB);
318impl_pin!(PIN_16, PWM_CH0, PwmPinA);
319impl_pin!(PIN_17, PWM_CH0, PwmPinB);
320impl_pin!(PIN_18, PWM_CH1, PwmPinA);
321impl_pin!(PIN_19, PWM_CH1, PwmPinB);
322impl_pin!(PIN_20, PWM_CH2, PwmPinA);
323impl_pin!(PIN_21, PWM_CH2, PwmPinB);
324impl_pin!(PIN_22, PWM_CH3, PwmPinA);
325impl_pin!(PIN_23, PWM_CH3, PwmPinB);
326impl_pin!(PIN_24, PWM_CH4, PwmPinA);
327impl_pin!(PIN_25, PWM_CH4, PwmPinB);
328impl_pin!(PIN_26, PWM_CH5, PwmPinA);
329impl_pin!(PIN_27, PWM_CH5, PwmPinB);
330impl_pin!(PIN_28, PWM_CH6, PwmPinA);
331impl_pin!(PIN_29, PWM_CH6, PwmPinB);
332
333impl<'d, T: Channel> SetConfig for Pwm<'d, T> {
334 type Config = Config;
335 fn set_config(&mut self, config: &Self::Config) {
336 Self::configure(self.inner.regs(), config);
337 }
338}
diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs
index 2e3708eff..a049e4769 100644
--- a/embassy-rp/src/usb.rs
+++ b/embassy-rp/src/usb.rs
@@ -231,7 +231,7 @@ impl<'d, T: Instance> Driver<'d, T> {
231 let len = (max_packet_size + 63) / 64 * 64; 231 let len = (max_packet_size + 63) / 64 * 64;
232 232
233 let addr = self.ep_mem_free; 233 let addr = self.ep_mem_free;
234 if addr + len > EP_MEMORY_SIZE as _ { 234 if addr + len > EP_MEMORY_SIZE as u16 {
235 warn!("Endpoint memory full"); 235 warn!("Endpoint memory full");
236 return Err(EndpointAllocError); 236 return Err(EndpointAllocError);
237 } 237 }
diff --git a/examples/rp/src/bin/pwm.rs b/examples/rp/src/bin/pwm.rs
new file mode 100644
index 000000000..69d315553
--- /dev/null
+++ b/examples/rp/src/bin/pwm.rs
@@ -0,0 +1,27 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_embedded_hal::SetConfig;
7use embassy_executor::Spawner;
8use embassy_rp::pwm::{Config, Pwm};
9use embassy_time::{Duration, Timer};
10use {defmt_rtt as _, panic_probe as _};
11
12#[embassy_executor::main]
13async fn main(_spawner: Spawner) {
14 let p = embassy_rp::init(Default::default());
15
16 let mut c: Config = Default::default();
17 c.top = 0x8000;
18 c.compare_b = 8;
19 let mut pwm = Pwm::new_output_b(p.PWM_CH4, p.PIN_25, c.clone());
20
21 loop {
22 info!("current LED duty cycle: {}/32768", c.compare_b);
23 Timer::after(Duration::from_secs(1)).await;
24 c.compare_b = c.compare_b.rotate_left(4);
25 pwm.set_config(&c);
26 }
27}
diff --git a/tests/rp/src/bin/pwm.rs b/tests/rp/src/bin/pwm.rs
new file mode 100644
index 000000000..b8cbe74c8
--- /dev/null
+++ b/tests/rp/src/bin/pwm.rs
@@ -0,0 +1,142 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{assert, assert_eq, assert_ne, *};
6use embassy_executor::Spawner;
7use embassy_rp::gpio::{Input, Level, Output, Pull};
8use embassy_rp::pwm::{Config, InputMode, Pwm};
9use embassy_time::{Duration, Timer};
10use {defmt_rtt as _, panic_probe as _};
11
12#[embassy_executor::main]
13async fn main(_spawner: Spawner) {
14 let mut p = embassy_rp::init(Default::default());
15 info!("Hello World!");
16
17 // Connections on CI device: 6 -> 9, 7 -> 11
18 let (mut p6, mut p7, mut p9, mut p11) = (p.PIN_6, p.PIN_7, p.PIN_9, p.PIN_11);
19
20 let cfg = {
21 let mut c = Config::default();
22 c.divider = 125.into();
23 c.top = 10000;
24 c.compare_a = 5000;
25 c.compare_b = 5000;
26 c
27 };
28
29 // Test free-running clock
30 {
31 let pwm = Pwm::new_free(&mut p.PWM_CH3, cfg.clone());
32 cortex_m::asm::delay(125);
33 let ctr = pwm.counter();
34 assert!(ctr > 0);
35 assert!(ctr < 100);
36 cortex_m::asm::delay(125);
37 assert!(ctr < pwm.counter());
38 }
39
40 for invert_a in [false, true] {
41 info!("free-running, invert A: {}", invert_a);
42 let mut cfg = cfg.clone();
43 cfg.invert_a = invert_a;
44 cfg.invert_b = !invert_a;
45
46 // Test output from A
47 {
48 let pin1 = Input::new(&mut p9, Pull::None);
49 let _pwm = Pwm::new_output_a(&mut p.PWM_CH3, &mut p6, cfg.clone());
50 Timer::after(Duration::from_millis(1)).await;
51 assert_eq!(pin1.is_low(), invert_a);
52 Timer::after(Duration::from_millis(5)).await;
53 assert_eq!(pin1.is_high(), invert_a);
54 Timer::after(Duration::from_millis(5)).await;
55 assert_eq!(pin1.is_low(), invert_a);
56 Timer::after(Duration::from_millis(5)).await;
57 assert_eq!(pin1.is_high(), invert_a);
58 }
59
60 // Test output from B
61 {
62 let pin2 = Input::new(&mut p11, Pull::None);
63 let _pwm = Pwm::new_output_b(&mut p.PWM_CH3, &mut p7, cfg.clone());
64 Timer::after(Duration::from_millis(1)).await;
65 assert_ne!(pin2.is_low(), invert_a);
66 Timer::after(Duration::from_millis(5)).await;
67 assert_ne!(pin2.is_high(), invert_a);
68 Timer::after(Duration::from_millis(5)).await;
69 assert_ne!(pin2.is_low(), invert_a);
70 Timer::after(Duration::from_millis(5)).await;
71 assert_ne!(pin2.is_high(), invert_a);
72 }
73
74 // Test output from A+B
75 {
76 let pin1 = Input::new(&mut p9, Pull::None);
77 let pin2 = Input::new(&mut p11, Pull::None);
78 let _pwm = Pwm::new_output_ab(&mut p.PWM_CH3, &mut p6, &mut p7, cfg.clone());
79 Timer::after(Duration::from_millis(1)).await;
80 assert_eq!(pin1.is_low(), invert_a);
81 assert_ne!(pin2.is_low(), invert_a);
82 Timer::after(Duration::from_millis(5)).await;
83 assert_eq!(pin1.is_high(), invert_a);
84 assert_ne!(pin2.is_high(), invert_a);
85 Timer::after(Duration::from_millis(5)).await;
86 assert_eq!(pin1.is_low(), invert_a);
87 assert_ne!(pin2.is_low(), invert_a);
88 Timer::after(Duration::from_millis(5)).await;
89 assert_eq!(pin1.is_high(), invert_a);
90 assert_ne!(pin2.is_high(), invert_a);
91 }
92 }
93
94 // Test level-gated
95 {
96 let mut pin2 = Output::new(&mut p11, Level::Low);
97 let pwm = Pwm::new_input(&mut p.PWM_CH3, &mut p7, InputMode::Level, cfg.clone());
98 assert_eq!(pwm.counter(), 0);
99 Timer::after(Duration::from_millis(5)).await;
100 assert_eq!(pwm.counter(), 0);
101 pin2.set_high();
102 Timer::after(Duration::from_millis(1)).await;
103 pin2.set_low();
104 let ctr = pwm.counter();
105 assert!(ctr >= 1000);
106 Timer::after(Duration::from_millis(1)).await;
107 assert_eq!(pwm.counter(), ctr);
108 }
109
110 // Test rising-gated
111 {
112 let mut pin2 = Output::new(&mut p11, Level::Low);
113 let pwm = Pwm::new_input(&mut p.PWM_CH3, &mut p7, InputMode::RisingEdge, cfg.clone());
114 assert_eq!(pwm.counter(), 0);
115 Timer::after(Duration::from_millis(5)).await;
116 assert_eq!(pwm.counter(), 0);
117 pin2.set_high();
118 Timer::after(Duration::from_millis(1)).await;
119 pin2.set_low();
120 assert_eq!(pwm.counter(), 1);
121 Timer::after(Duration::from_millis(1)).await;
122 assert_eq!(pwm.counter(), 1);
123 }
124
125 // Test falling-gated
126 {
127 let mut pin2 = Output::new(&mut p11, Level::High);
128 let pwm = Pwm::new_input(&mut p.PWM_CH3, &mut p7, InputMode::FallingEdge, cfg.clone());
129 assert_eq!(pwm.counter(), 0);
130 Timer::after(Duration::from_millis(5)).await;
131 assert_eq!(pwm.counter(), 0);
132 pin2.set_low();
133 Timer::after(Duration::from_millis(1)).await;
134 pin2.set_high();
135 assert_eq!(pwm.counter(), 1);
136 Timer::after(Duration::from_millis(1)).await;
137 assert_eq!(pwm.counter(), 1);
138 }
139
140 info!("Test OK");
141 cortex_m::asm::bkpt();
142}