Skip to content

Commit 04579df

Browse files
Merge #335
335: Fix `Pwm::set_output_pin` soundness, add `swap_output_pin` r=jonas-schievink a=jonas-schievink Fixes #320 by taking the pin by-value (cc @Dirbaio) To allow the same flexibility as before, this adds `swap_output_pin`, which returns the old pin (though I haven't tested this API). Co-authored-by: Jonas Schievink <[email protected]>
2 parents 851af59 + 7fcddf8 commit 04579df

File tree

4 files changed

+67
-20
lines changed

4 files changed

+67
-20
lines changed

examples/pwm-blinky-demo/src/main.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33

44
use hal::{gpio, prelude::*, pwm, pwm::Pwm, timer, timer::Timer};
55
use nb::block;
6-
#[cfg(feature = "52840")]
7-
use nrf52840_hal as hal;
86
#[cfg(feature = "52832")]
97
use nrf52832_hal as hal;
8+
#[cfg(feature = "52840")]
9+
use nrf52840_hal as hal;
1010
#[cfg(feature = "9160")]
1111
use nrf9160_hal as hal;
1212
use rtt_target::{rprintln, rtt_init_print};
@@ -29,7 +29,7 @@ fn main() -> ! {
2929
pwm.set_period(500u32.hz());
3030

3131
rprintln!("PWM Blinky demo starting");
32-
32+
3333
let wait_time = 1_000_000u32 / pwm.get_max_duty() as u32;
3434
loop {
3535
for duty in 0..pwm.get_max_duty() {
@@ -46,7 +46,7 @@ fn init_device(p: hal::pac::Peripherals) -> (Pwm<hal::pac::PWM0_NS>, Timer<hal::
4646
let pwm = Pwm::new(p.PWM0_NS);
4747
pwm.set_output_pin(
4848
pwm::Channel::C0,
49-
&p0.p0_02.into_push_pull_output(gpio::Level::High).degrade(),
49+
p0.p0_02.into_push_pull_output(gpio::Level::High).degrade(),
5050
);
5151

5252
let timer = Timer::new(p.TIMER0_NS);
@@ -61,7 +61,7 @@ fn init_device(p: hal::pac::Peripherals) -> (Pwm<hal::pac::PWM0>, Timer<hal::pac
6161
let pwm = Pwm::new(p.PWM0);
6262
pwm.set_output_pin(
6363
pwm::Channel::C0,
64-
&p0.p0_13.into_push_pull_output(gpio::Level::High).degrade(),
64+
p0.p0_13.into_push_pull_output(gpio::Level::High).degrade(),
6565
);
6666

6767
let timer = Timer::new(p.TIMER0);
@@ -76,15 +76,14 @@ fn init_device(p: hal::pac::Peripherals) -> (Pwm<hal::pac::PWM0>, Timer<hal::pac
7676
let pwm = Pwm::new(p.PWM0);
7777
pwm.set_output_pin(
7878
pwm::Channel::C0,
79-
&p0.p0_30.into_push_pull_output(gpio::Level::High).degrade(),
79+
p0.p0_30.into_push_pull_output(gpio::Level::High).degrade(),
8080
);
8181

8282
let timer = Timer::new(p.TIMER0);
8383

8484
(pwm, timer)
8585
}
8686

87-
8887
fn delay<T>(timer: &mut Timer<T>, cycles: u32)
8988
where
9089
T: timer::Instance,

examples/pwm-demo/src/main.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,20 @@ const APP: () = {
6767
let led3 = p0.p0_15.into_push_pull_output(Level::High).degrade();
6868
let led4 = p0.p0_16.into_push_pull_output(Level::High).degrade();
6969

70-
let pwm = Pwm::new(ctx.device.PWM0);
70+
let mut pwm = Pwm::new(ctx.device.PWM0);
7171
pwm.set_period(500u32.hz())
72-
.set_output_pin(Channel::C0, &led1)
73-
.set_output_pin(Channel::C1, &led2)
74-
.set_output_pin(Channel::C2, &led3)
75-
.set_output_pin(Channel::C3, &led4)
76-
.enable_interrupt(PwmEvent::Stopped)
77-
.enable();
72+
.set_output_pin(Channel::C0, led1)
73+
.set_output_pin(Channel::C1, led2)
74+
.set_output_pin(Channel::C2, led3)
75+
.set_output_pin(Channel::C3, led4)
76+
.enable_interrupt(PwmEvent::Stopped);
77+
78+
// In addition to `set_output_pin`, `swap_output_pin` and `clear_output_pin` can be used to
79+
// get the old pin back.
80+
let led1 = pwm.clear_output_pin(Channel::C0).unwrap();
81+
assert!(pwm.swap_output_pin(Channel::C0, led1).is_none());
82+
83+
pwm.enable();
7884

7985
let gpiote = Gpiote::new(ctx.device.GPIOTE);
8086
gpiote.port().input_pin(&btn1).low();

nrf-hal-common/src/gpio.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ pub enum Port {
5050
// ===============================================================
5151
/// Generic $PX pin
5252
pub struct Pin<MODE> {
53-
// Bit 7: Port, Bits 0-6: Pin
53+
/// 00AB BBBB
54+
/// A: Port
55+
/// B: Pin
5456
pin_port: u8,
5557
_mode: PhantomData<MODE>,
5658
}

nrf-hal-common/src/pwm.rs

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,16 +129,56 @@ where
129129
}
130130
}
131131

132-
/// Sets the associated output pin for the PWM channel and enables it.
132+
/// Sets the associated output pin for the PWM channel.
133+
///
134+
/// Modifying the pin configuration while the PWM instance is enabled is not recommended.
133135
#[inline(always)]
134-
pub fn set_output_pin(&self, channel: Channel, pin: &Pin<Output<PushPull>>) -> &Self {
136+
pub fn set_output_pin(&self, channel: Channel, pin: Pin<Output<PushPull>>) -> &Self {
135137
self.pwm.psel.out[usize::from(channel)].write(|w| {
136138
unsafe { w.bits(pin.psel_bits()) };
137139
w.connect().connected()
138140
});
139141
self
140142
}
141143

144+
/// Sets the output pin of `channel`, and returns the old pin (if any).
145+
///
146+
/// Modifying the pin configuration while the PWM instance is enabled is not recommended.
147+
pub fn swap_output_pin(
148+
&mut self,
149+
channel: Channel,
150+
pin: Pin<Output<PushPull>>,
151+
) -> Option<Pin<Output<PushPull>>> {
152+
// (needs `&mut self` because it reads, then writes, to the register)
153+
let psel = &self.pwm.psel.out[usize::from(channel)];
154+
let old = psel.read();
155+
let old = if old.connect().is_connected() {
156+
unsafe { Some(Pin::from_psel_bits(old.bits())) }
157+
} else {
158+
None
159+
};
160+
self.set_output_pin(channel, pin);
161+
old
162+
}
163+
164+
/// Disables the output pin of `channel`.
165+
///
166+
/// The output pin is returned, if one was previously configured.
167+
///
168+
/// Modifying the pin configuration while the PWM instance is enabled is not recommended.
169+
pub fn clear_output_pin(&mut self, channel: Channel) -> Option<Pin<Output<PushPull>>> {
170+
// (needs `&mut self` because it reads, then writes, to the register)
171+
let psel = &self.pwm.psel.out[usize::from(channel)];
172+
let old = psel.read();
173+
let old = if old.connect().is_connected() {
174+
unsafe { Some(Pin::from_psel_bits(old.bits())) }
175+
} else {
176+
None
177+
};
178+
psel.reset();
179+
old
180+
}
181+
142182
/// Enables the PWM generator.
143183
#[inline(always)]
144184
pub fn enable(&self) {
@@ -228,7 +268,7 @@ where
228268
self
229269
}
230270

231-
/// Returns selected operating mode of the wave counter.
271+
/// Returns selected operating mode of the wave counter.
232272
#[inline(always)]
233273
pub fn counter_mode(&self) -> CounterMode {
234274
match self.pwm.mode.read().updown().bit() {
@@ -351,7 +391,7 @@ where
351391
self.start_seq(Seq::Seq0);
352392
}
353393

354-
/// Returns duty cycle value for a PWM group.
394+
/// Returns duty cycle value for a PWM group.
355395
#[inline(always)]
356396
pub fn duty_on_group(&self, group: Group) -> u16 {
357397
self.duty_on_value(usize::from(group))
@@ -395,7 +435,7 @@ where
395435
self.start_seq(Seq::Seq0);
396436
}
397437

398-
/// Returns the duty cycle value for a PWM channel.
438+
/// Returns the duty cycle value for a PWM channel.
399439
#[inline(always)]
400440
pub fn duty_on(&self, channel: Channel) -> u16 {
401441
self.duty_on_value(usize::from(channel))

0 commit comments

Comments
 (0)