From b34aa404e3cf3e817122010a4055429878ada507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Mon, 10 Aug 2020 13:16:43 +0200 Subject: [PATCH 01/12] Add PWM module --- Cargo.toml | 1 + examples/pwm-demo/Cargo.toml | 17 + examples/pwm-demo/Embed.toml | 21 + examples/pwm-demo/src/main.rs | 256 +++++++++ nrf-hal-common/src/lib.rs | 2 + nrf-hal-common/src/pwm.rs | 1000 +++++++++++++++++++++++++++++++++ 6 files changed, 1297 insertions(+) create mode 100644 examples/pwm-demo/Cargo.toml create mode 100755 examples/pwm-demo/Embed.toml create mode 100644 examples/pwm-demo/src/main.rs create mode 100644 nrf-hal-common/src/pwm.rs diff --git a/Cargo.toml b/Cargo.toml index 36bf029b..aee33faa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ members = [ "examples/gpiote-demo", "examples/wdt-demo", "examples/comp-demo", + "examples/pwm-demo", ] [profile.dev] diff --git a/examples/pwm-demo/Cargo.toml b/examples/pwm-demo/Cargo.toml new file mode 100644 index 00000000..69739705 --- /dev/null +++ b/examples/pwm-demo/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "pwm-demo" +version = "0.1.0" +authors = ["Henrik AlseĢr"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cortex-m = "0.6.2" +cortex-m-rtic = "0.5.3" +rtt-target = {version = "0.2.0", features = ["cortex-m"] } +nrf52840-hal = { features = ["rt"], path = "../../nrf52840-hal" } + +[dependencies.embedded-hal] +version = "0.2.3" +features = ["unproven"] diff --git a/examples/pwm-demo/Embed.toml b/examples/pwm-demo/Embed.toml new file mode 100755 index 00000000..eda5e3e0 --- /dev/null +++ b/examples/pwm-demo/Embed.toml @@ -0,0 +1,21 @@ +[default.probe] +protocol = "Swd" + +[default.flashing] +enabled = true +halt_afterwards = false +restore_unwritten_bytes = false + +[default.general] +chip = "nRF52840" +chip_descriptions = [] +log_level = "Warn" + +[default.rtt] +enabled = true +channels = [] +timeout = 3000 +show_timestamps = true + +[default.gdb] +enabled = false \ No newline at end of file diff --git a/examples/pwm-demo/src/main.rs b/examples/pwm-demo/src/main.rs new file mode 100644 index 00000000..11d96964 --- /dev/null +++ b/examples/pwm-demo/src/main.rs @@ -0,0 +1,256 @@ +#![no_std] +#![no_main] + +use embedded_hal::digital::v2::InputPin; +use { + core::{ + panic::PanicInfo, + sync::atomic::{compiler_fence, Ordering}, + }, + hal::{ + gpio::{p0::Parts, Input, Level, Pin, PullUp}, + gpiote::Gpiote, + pac::PWM0, + pwm::*, + time::*, + }, + nrf52840_hal as hal, + rtic::cyccnt::U32Ext as _, + rtt_target::{rprintln, rtt_init_print}, +}; + +#[derive(Debug, PartialEq)] +pub enum AppStatus { + Idle, + Demo1A, + Demo1B, + Demo1C, + Demo2A, + Demo2B, + Demo2C, + Demo3, + Demo4, +} + +#[rtic::app(device = crate::hal::pac, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)] +const APP: () = { + struct Resources { + gpiote: Gpiote, + btn1: Pin>, + btn2: Pin>, + btn3: Pin>, + btn4: Pin>, + pwm: Pwm, + #[init(AppStatus::Idle)] + status: AppStatus, + } + + #[init] + fn init(mut ctx: init::Context) -> init::LateResources { + let _clocks = hal::clocks::Clocks::new(ctx.device.CLOCK).enable_ext_hfosc(); + ctx.core.DCB.enable_trace(); + ctx.core.DWT.enable_cycle_counter(); + rtt_init_print!(); + + let p0 = Parts::new(ctx.device.P0); + let btn1 = p0.p0_11.into_pullup_input().degrade(); + let btn2 = p0.p0_12.into_pullup_input().degrade(); + let btn3 = p0.p0_24.into_pullup_input().degrade(); + let btn4 = p0.p0_25.into_pullup_input().degrade(); + let led1 = p0.p0_13.into_push_pull_output(Level::High).degrade(); + let led2 = p0.p0_14.into_push_pull_output(Level::High).degrade(); + let led3 = p0.p0_15.into_push_pull_output(Level::High).degrade(); + let led4 = p0.p0_16.into_push_pull_output(Level::High).degrade(); + + let pwm = Pwm::new(ctx.device.PWM0); + pwm.set_period(100u32.hz().into()) + .set_output_pin(Channel::C0, &led1) + .set_output_pin(Channel::C1, &led2) + .set_output_pin(Channel::C2, &led3) + .set_output_pin(Channel::C3, &led4) + .enable_interrupt(PwmEvent::Stopped) + .enable(); + + let gpiote = Gpiote::new(ctx.device.GPIOTE); + gpiote.port().input_pin(&btn1).low(); + gpiote.port().input_pin(&btn2).low(); + gpiote.port().input_pin(&btn3).low(); + gpiote.port().input_pin(&btn4).low(); + gpiote.port().enable_interrupt(); + + init::LateResources { + gpiote, + btn1, + btn2, + btn3, + btn4, + pwm, + } + } + + #[idle] + fn idle(_: idle::Context) -> ! { + rprintln!("Press a button to start a demo"); + loop { + cortex_m::asm::wfi(); + } + } + + #[task(binds = PWM0, resources = [pwm])] + fn on_pwm(ctx: on_pwm::Context) { + let pwm = ctx.resources.pwm; + if pwm.is_event_triggered(PwmEvent::Stopped) { + pwm.reset_event(PwmEvent::Stopped); + rprintln!("PWM generation stopped"); + } + } + + #[task(binds = GPIOTE, resources = [gpiote], schedule = [debounce])] + fn on_gpiote(ctx: on_gpiote::Context) { + ctx.resources.gpiote.reset_events(); + ctx.schedule.debounce(ctx.start + 3_000_000.cycles()).ok(); + } + + #[task(resources = [btn1, btn2, btn3, btn4, pwm, status])] + fn debounce(ctx: debounce::Context) { + static mut BUF: [u16; 48] = [0u16; 48]; + let status = ctx.resources.status; + + let pwm = ctx.resources.pwm; + let max_duty = pwm.get_max_duty(); + let (ch0, ch1, ch2, ch3) = pwm.split_channels(); + let (grp0, grp1) = pwm.split_groups(); + + if ctx.resources.btn1.is_low().unwrap() { + match status { + AppStatus::Demo1B => { + rprintln!("DEMO 1C: Individual channel duty cycle"); + *status = AppStatus::Demo1C; + ch0.set_duty(max_duty / 10); + ch1.set_duty(max_duty / 50); + ch2.set_duty(max_duty / 100); + ch3.set_duty(max_duty / 500); + } + AppStatus::Demo1A => { + rprintln!("DEMO 1B: Group duty cycle"); + *status = AppStatus::Demo1B; + grp1.set_duty(max_duty / 10); + grp0.set_duty(max_duty / 300); + } + _ => { + rprintln!("DEMO 1A: Common duty cycle for all channels"); + *status = AppStatus::Demo1A; + pwm.set_duty_on_common(max_duty / 10); + } + } + } + if ctx.resources.btn2.is_low().unwrap() { + match status { + AppStatus::Demo2A => { + rprintln!("DEMO 2B: Loop individual sequences"); + *status = AppStatus::Demo2B; + let amplitude = max_duty as i32 / 5; + let offset = max_duty as i32 / 300; + for x in 0..12 { + BUF[4 * x] = triangle_wave(x as i32, 12, amplitude, 0, 0) as u16; + BUF[4 * x + 1] = triangle_wave(x as i32, 12, amplitude, 3, offset) as u16; + BUF[4 * x + 2] = triangle_wave(x as i32, 12, amplitude, 6, offset) as u16; + BUF[4 * x + 3] = triangle_wave(x as i32, 12, amplitude, 9, offset) as u16; + } + pwm.set_load_mode(LoadMode::Individual) + .set_seq_refresh(Seq::Seq0, 30) + .set_seq_refresh(Seq::Seq1, 30) + .loop_inf(); + pwm.load_seq(Seq::Seq0, &BUF[..48]).ok(); + pwm.load_seq(Seq::Seq1, &BUF[..48]).ok(); + pwm.start_seq(Seq::Seq0); + } + _ => { + rprintln!("DEMO 2A: Play sequence once"); + *status = AppStatus::Demo2A; + for x in 0..10 { + BUF[x] = triangle_wave(x as i32, 10, 2000, 0, 100) as u16; + } + pwm.set_load_mode(LoadMode::Common) + .one_shot() + .set_seq_refresh(Seq::Seq0, 50) + .set_step_mode(StepMode::Auto) + .load_seq(Seq::Seq0, &BUF[..10]) + .ok(); + pwm.start_seq(Seq::Seq0); + } + } + } + if ctx.resources.btn3.is_low().unwrap() { + match status { + AppStatus::Demo3 => { + rprintln!("DEMO 3: Next step"); + if pwm.is_event_triggered(PwmEvent::SeqEnd(Seq::Seq1)) { + rprintln!("DEMO 3: End"); + pwm.reset_event(PwmEvent::SeqEnd(Seq::Seq1)); + pwm.stop(); + *status = AppStatus::Idle; + } else { + pwm.next_step(); + } + } + _ => { + rprintln!("DEMO 3: Manually step through sequence"); + *status = AppStatus::Demo3; + for x in 0..8 { + BUF[x] = triangle_wave( + x as i32, + 8, + max_duty as i32 / 50, + 0, + max_duty as i32 / 800, + ) as u16; + } + pwm.set_load_mode(LoadMode::Common) + .loop_inf() + .set_step_mode(StepMode::NextStep); + pwm.load_seq(Seq::Seq0, &BUF[..4]).ok(); + pwm.load_seq(Seq::Seq1, &BUF[4..8]).ok(); + pwm.start_seq(Seq::Seq0); + } + } + } + if ctx.resources.btn4.is_low().unwrap() { + rprintln!("DEMO 4: Play complex sequence 4 times"); + *status = AppStatus::Demo4; + for x in 0..12 { + BUF[x] = triangle_wave(x as i32, 12, max_duty as i32 / 20, 0, max_duty as i32 / 800) + as u16; + } + pwm.set_load_mode(LoadMode::Common) + .set_step_mode(StepMode::Auto) + .set_seq_refresh(Seq::Seq0, 100) + .set_seq_refresh(Seq::Seq1, 20) + .repeat(4); + pwm.load_seq(Seq::Seq0, &BUF[..6]).ok(); + pwm.load_seq(Seq::Seq1, &BUF[6..12]).ok(); + pwm.start_seq(Seq::Seq0); + } + } + + extern "C" { + fn SWI0_EGU0(); + fn SWI1_EGU1(); + fn SWI2_EGU2(); + } +}; + +fn triangle_wave(x: i32, length: i32, amplitude: i32, phase: i32, y_offset: i32) -> i32 { + (amplitude - (((x + phase) * amplitude / (length / (2))) % (2 * amplitude) - amplitude).abs()) + + y_offset +} + +#[inline(never)] +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + cortex_m::interrupt::disable(); + rprintln!("{}", info); + loop { + compiler_fence(Ordering::SeqCst); + } +} diff --git a/nrf-hal-common/src/lib.rs b/nrf-hal-common/src/lib.rs index a307827d..34182d5c 100644 --- a/nrf-hal-common/src/lib.rs +++ b/nrf-hal-common/src/lib.rs @@ -39,6 +39,8 @@ pub mod gpio; pub mod gpiote; #[cfg(not(feature = "9160"))] pub mod ppi; +#[cfg(any(feature = "52833", feature = "52840"))] +pub mod pwm; #[cfg(not(feature = "9160"))] pub mod rng; pub mod rtc; diff --git a/nrf-hal-common/src/pwm.rs b/nrf-hal-common/src/pwm.rs new file mode 100644 index 00000000..9668ffb4 --- /dev/null +++ b/nrf-hal-common/src/pwm.rs @@ -0,0 +1,1000 @@ +//! HAL interface to the PWM peripheral. +//! +//! The pulse with modulation (PWM) module enables the generation of pulse width modulated signals on GPIO. + +use core::sync::atomic::{compiler_fence, Ordering}; + +#[cfg(any(feature = "52833", feature = "52840"))] +use crate::{ + gpio::{Output, Pin, Port, PushPull}, + pac::{ + generic::Reg, + pwm0::{ + _EVENTS_LOOPSDONE, _EVENTS_PWMPERIODEND, _EVENTS_SEQEND, _EVENTS_SEQSTARTED, + _EVENTS_STOPPED, _TASKS_NEXTSTEP, _TASKS_SEQSTART, _TASKS_STOP, + }, + PWM0, PWM1, PWM2, PWM3, + }, + target_constants::{SRAM_LOWER, SRAM_UPPER}, + time::*, +}; + +/// A safe wrapper around the raw peripheral. +#[derive(Debug)] +pub struct Pwm { + pwm: T, + duty: [u16; 4], +} + +impl Pwm +where + T: sealed::Instance, +{ + /// Takes ownership of the peripheral and applies sane defaults. + pub fn new(pwm: T) -> Pwm { + compiler_fence(Ordering::SeqCst); + let duty = [0u16; 4]; + pwm.enable.write(|w| w.enable().enabled()); + pwm.mode.write(|w| w.updown().up()); + pwm.prescaler.write(|w| w.prescaler().div_1()); + pwm.countertop + .write(|w| unsafe { w.countertop().bits(32767) }); + pwm.loop_.write(|w| w.cnt().disabled()); + pwm.decoder.write(|w| { + w.load().individual(); + w.mode().refresh_count() + }); + pwm.seq0.refresh.write(|w| unsafe { w.bits(0) }); + pwm.seq0.enddelay.write(|w| unsafe { w.bits(0) }); + pwm.seq1.refresh.write(|w| unsafe { w.bits(0) }); + pwm.seq1.enddelay.write(|w| unsafe { w.bits(0) }); + + pwm.seq0 + .ptr + .write(|w| unsafe { w.bits(duty.as_ptr() as u32) }); + pwm.seq0.cnt.write(|w| unsafe { w.bits(4) }); + pwm.seq1 + .ptr + .write(|w| unsafe { w.bits(duty.as_ptr() as u32) }); + pwm.seq1.cnt.write(|w| unsafe { w.bits(4) }); + + Self { pwm, duty } + } + + /// Sets the PWM clock prescaler. + #[inline(always)] + pub fn set_prescaler(&self, div: Prescaler) -> &Self { + self.pwm.prescaler.write(|w| w.prescaler().bits(div.into())); + self + } + + /// Sets the PWM clock prescaler. + #[inline(always)] + pub fn get_prescaler(&self) -> Prescaler { + match self.pwm.prescaler.read().prescaler().bits() { + 0 => Prescaler::Div1, + 1 => Prescaler::Div2, + 2 => Prescaler::Div4, + 3 => Prescaler::Div8, + 4 => Prescaler::Div16, + 5 => Prescaler::Div32, + 6 => Prescaler::Div64, + 7 => Prescaler::Div128, + _ => unreachable!(), + } + } + + /// Sets the maximum duty cycle value. + #[inline(always)] + pub fn set_max_duty(&self, duty: u16) -> &Self { + self.pwm + .countertop + .write(|w| unsafe { w.countertop().bits(duty.min(32767u16)) }); + self + } + /// Returns the maximum duty cycle value. + #[inline(always)] + pub fn get_max_duty(&self) -> u16 { + self.pwm.countertop.read().countertop().bits() + } + + /// Sets the PWM output frequency. + #[inline(always)] + pub fn set_period(&self, freq: Hertz) -> &Self { + let duty = match self.get_prescaler() { + Prescaler::Div1 => 16_000_000u32 / freq.0, + Prescaler::Div2 => 8_000_000u32 / freq.0, + Prescaler::Div4 => 4_000_000u32 / freq.0, + Prescaler::Div8 => 2_000_000u32 / freq.0, + Prescaler::Div16 => 1_000_000u32 / freq.0, + Prescaler::Div32 => 500_000u32 / freq.0, + Prescaler::Div64 => 250_000u32 / freq.0, + Prescaler::Div128 => 125_000u32 / freq.0, + }; + match self.get_counter_mode() { + CounterMode::Up => self.set_max_duty(duty.min(32767) as u16), + CounterMode::UpAndDown => self.set_max_duty((duty / 2).min(32767) as u16), + }; + self + } + + /// Returns the PWM output frequency. + #[inline(always)] + pub fn get_period(&self) -> Hertz { + let max_duty = self.get_max_duty() as u32; + let freq = match self.get_prescaler() { + Prescaler::Div1 => 16_000_000u32 / max_duty, + Prescaler::Div2 => 8_000_000u32 / max_duty, + Prescaler::Div4 => 4_000_000u32 / max_duty, + Prescaler::Div8 => 2_000_000u32 / max_duty, + Prescaler::Div16 => 1_000_000u32 / max_duty, + Prescaler::Div32 => 500_000u32 / max_duty, + Prescaler::Div64 => 250_000u32 / max_duty, + Prescaler::Div128 => 125_000u32 / max_duty, + }; + match self.get_counter_mode() { + CounterMode::Up => freq.hz(), + CounterMode::UpAndDown => (freq / 2).hz(), + } + } + + /// Sets the associated output pin for the PWM channel and enables it. + #[inline(always)] + pub fn set_output_pin(&self, channel: Channel, pin: &Pin>) -> &Self { + self.pwm.psel.out[usize::from(channel)].write(|w| { + #[cfg(any(feature = "52833", feature = "52840"))] + match pin.port() { + Port::Port0 => w.port().clear_bit(), + Port::Port1 => w.port().set_bit(), + }; + unsafe { + w.pin().bits(pin.pin()); + } + w.connect().connected() + }); + self + } + + /// Enables the PWM generator. + #[inline(always)] + pub fn enable(&self) { + self.pwm.enable.write(|w| w.enable().enabled()); + } + + /// Disables the PWM generator. + #[inline(always)] + pub fn disable(&self) { + self.pwm.enable.write(|w| w.enable().disabled()); + } + + /// Enables a PWM channel. + #[inline(always)] + pub fn enable_channel(&self, channel: Channel) -> &Self { + self.pwm.psel.out[usize::from(channel)].modify(|_r, w| w.connect().connected()); + self + } + + /// Disables a PWM channel. + #[inline(always)] + pub fn disable_channel(&self, channel: Channel) -> &Self { + self.pwm.psel.out[usize::from(channel)].modify(|_r, w| w.connect().disconnected()); + self + } + + /// Enables a PWM group. + #[inline(always)] + pub fn enable_group(&self, group: Group) -> &Self { + match group { + Group::G0 => { + self.pwm.psel.out[0].modify(|_r, w| w.connect().connected()); + self.pwm.psel.out[1].modify(|_r, w| w.connect().connected()); + } + Group::G1 => { + self.pwm.psel.out[2].modify(|_r, w| w.connect().connected()); + self.pwm.psel.out[3].modify(|_r, w| w.connect().connected()); + } + } + self + } + + /// Disables a PWM group. + #[inline(always)] + pub fn disable_group(&self, group: Group) -> &Self { + match group { + Group::G0 => { + self.pwm.psel.out[0].modify(|_r, w| w.connect().disconnected()); + self.pwm.psel.out[1].modify(|_r, w| w.connect().disconnected()); + } + Group::G1 => { + self.pwm.psel.out[2].modify(|_r, w| w.connect().disconnected()); + self.pwm.psel.out[3].modify(|_r, w| w.connect().disconnected()); + } + } + self + } + + /// Cofigures how a sequence is read from RAM and is spread to the compare register. + #[inline(always)] + pub fn set_load_mode(&self, mode: LoadMode) -> &Self { + self.pwm.decoder.modify(|_r, w| w.load().bits(mode.into())); + if mode == LoadMode::Waveform { + self.disable_channel(Channel::C3); + } else { + self.enable_channel(Channel::C3); + } + self + } + + /// Returns how a sequence is read from RAM and is spread to the compare register. + #[inline(always)] + pub fn get_load_mode(&self) -> LoadMode { + match self.pwm.decoder.read().load().bits() { + 0 => LoadMode::Common, + 1 => LoadMode::Grouped, + 2 => LoadMode::Individual, + 3 => LoadMode::Waveform, + _ => unreachable!(), + } + } + + /// Selects operating mode of the wave counter. + #[inline(always)] + pub fn set_counter_mode(&self, mode: CounterMode) -> &Self { + self.pwm.mode.write(|w| w.updown().bit(mode.into())); + self + } + + /// Returns selected operating mode of the wave counter. + #[inline(always)] + pub fn get_counter_mode(&self) -> CounterMode { + match self.pwm.mode.read().updown().bit() { + false => CounterMode::Up, + true => CounterMode::UpAndDown, + } + } + + /// Selects source for advancing the active sequence. + #[inline(always)] + pub fn set_step_mode(&self, mode: StepMode) -> &Self { + self.pwm.decoder.modify(|_r, w| w.mode().bit(mode.into())); + self + } + + /// Returns selected source for advancing the active sequence. + #[inline(always)] + pub fn get_step_mode(&self) -> StepMode { + match self.pwm.decoder.read().mode().bit() { + false => StepMode::Auto, + true => StepMode::NextStep, + } + } + + // Internal helper function that returns 15 bit duty cycle value. + #[inline(always)] + fn get_duty_on_value(&self, index: usize) -> u16 { + let val = self.duty[index]; + let is_inverted = (val >> 15) & 1 == 0; + match is_inverted { + false => val, + true => self.get_max_duty() - (val & 0x7FFF), + } + } + + // Internal helper function that returns 15 bit inverted duty cycle value. + #[inline(always)] + fn get_duty_off_value(&self, index: usize) -> u16 { + let val = self.duty[index]; + let is_inverted = (val >> 15) & 1 == 0; + match is_inverted { + false => self.get_max_duty() - val, + true => val & 0x7FFF, + } + } + + /// Sets duty cycle (15 bit) for all PWM channels. + pub fn set_duty_on_common(&self, duty: u16) { + compiler_fence(Ordering::SeqCst); + unsafe { + *(self.duty.as_ptr() as *mut u16) = duty.min(self.get_max_duty()) & 0x7FFF; + } + self.one_shot(); + self.set_load_mode(LoadMode::Common); + self.pwm + .seq0 + .ptr + .write(|w| unsafe { w.bits(self.duty.as_ptr() as u32) }); + self.pwm.seq0.cnt.write(|w| unsafe { w.bits(1) }); + self.start_seq(Seq::Seq0); + } + + /// Sets inverted duty cycle (15 bit) for all PWM channels. + pub fn set_duty_off_common(&self, duty: u16) { + compiler_fence(Ordering::SeqCst); + unsafe { + *(self.duty.as_ptr() as *mut u16) = duty.min(self.get_max_duty()) | 0x8000; + } + self.one_shot(); + self.set_load_mode(LoadMode::Common); + self.pwm + .seq0 + .ptr + .write(|w| unsafe { w.bits(self.duty.as_ptr() as u32) }); + self.pwm.seq0.cnt.write(|w| unsafe { w.bits(1) }); + self.start_seq(Seq::Seq0); + } + + /// Returns the common duty cycle value for all PWM channels in `Common` load mode. + #[inline(always)] + pub fn get_duty_on_common(&self) -> u16 { + self.get_duty_on_value(0) + } + + /// Returns the inverted common duty cycle value for all PWM channels in `Common` load mode. + #[inline(always)] + pub fn get_duty_off_common(&self) -> u16 { + self.get_duty_off_value(0) + } + + /// Sets duty cycle (15 bit) for a PWM group. + pub fn set_duty_on_group(&self, group: Group, duty: u16) { + compiler_fence(Ordering::SeqCst); + unsafe { + *(self.duty.as_ptr().offset(group.into()) as *mut u16) = + duty.min(self.get_max_duty()) & 0x7FFF; + } + self.one_shot(); + self.set_load_mode(LoadMode::Grouped); + self.pwm + .seq0 + .ptr + .write(|w| unsafe { w.bits(self.duty.as_ptr() as u32) }); + self.pwm.seq0.cnt.write(|w| unsafe { w.bits(2) }); + self.start_seq(Seq::Seq0); + } + + /// Sets inverted duty cycle (15 bit) for a PWM group. + pub fn set_duty_off_group(&self, group: Group, duty: u16) { + compiler_fence(Ordering::SeqCst); + unsafe { + *(self.duty.as_ptr().offset(group.into()) as *mut u16) = + duty.min(self.get_max_duty()) | 0x8000; + } + self.one_shot(); + self.set_load_mode(LoadMode::Grouped); + self.pwm + .seq0 + .ptr + .write(|w| unsafe { w.bits(self.duty.as_ptr() as u32) }); + self.pwm.seq0.cnt.write(|w| unsafe { w.bits(2) }); + self.start_seq(Seq::Seq0); + } + + /// Returns duty cycle value for a PWM group. + #[inline(always)] + pub fn get_duty_on_group(&self, group: Group) -> u16 { + self.get_duty_on_value(usize::from(group)) + } + + /// Returns inverted duty cycle value for a PWM group. + #[inline(always)] + pub fn get_duty_off_group(&self, group: Group) -> u16 { + self.get_duty_off_value(usize::from(group)) + } + + /// Sets duty cycle (15 bit) for a PWM channel. + pub fn set_duty_on(&self, channel: Channel, duty: u16) { + compiler_fence(Ordering::SeqCst); + unsafe { + *(self.duty.as_ptr().offset(channel.into()) as *mut u16) = + duty.min(self.get_max_duty()) & 0x7FFF; + } + self.one_shot(); + self.set_load_mode(LoadMode::Individual); + if self.load_seq(Seq::Seq0, &self.duty).is_ok() { + self.start_seq(Seq::Seq0); + } + } + + /// Sets inverted duty cycle (15 bit) for a PWM channel. + pub fn set_duty_off(&self, channel: Channel, duty: u16) { + compiler_fence(Ordering::SeqCst); + unsafe { + *(self.duty.as_ptr().offset(channel.into()) as *mut u16) = + duty.min(self.get_max_duty()) | 0x8000; + } + self.one_shot(); + self.set_load_mode(LoadMode::Individual); + if self.load_seq(Seq::Seq0, &self.duty).is_ok() { + self.start_seq(Seq::Seq0); + } + } + + /// Returns the duty cycle value for a PWM channel. + #[inline(always)] + pub fn get_duty_on(&self, channel: Channel) -> u16 { + self.get_duty_on_value(usize::from(channel)) + } + + /// Returns the inverted duty cycle value for a PWM group. + #[inline(always)] + pub fn get_duty_off(&self, channel: Channel) -> u16 { + self.get_duty_off_value(usize::from(channel)) + } + + /// Sets number of playbacks of sequences. + #[inline(always)] + pub fn set_loop(&self, mode: Loop) { + self.pwm.loop_.write(|w| match mode { + Loop::Disabled => w.cnt().disabled(), + Loop::Times(n) => unsafe { w.cnt().bits(n) }, + Loop::Inf => unsafe { w.cnt().bits(2) }, + }); + self.pwm.shorts.write(|w| match mode { + Loop::Inf => w.loopsdone_seqstart0().enabled(), + _ => w.loopsdone_seqstart0().disabled(), + }); + } + + /// Looping disabled (stop at the end of the sequence). + #[inline(always)] + pub fn one_shot(&self) -> &Self { + self.set_loop(Loop::Disabled); + self + } + + /// Loops playback of sequences indefinately. + #[inline(always)] + pub fn loop_inf(&self) -> &Self { + self.set_loop(Loop::Inf); + self + } + + /// Sets number of playbacks of sequences. + #[inline(always)] + pub fn repeat(&self, times: u16) -> &Self { + self.set_loop(Loop::Times(times)); + self + } + + /// Sets number of additional PWM periods between samples loaded into compare register. + #[inline(always)] + pub fn set_seq_refresh(&self, seq: Seq, periods: u32) -> &Self { + match seq { + Seq::Seq0 => self.pwm.seq0.refresh.write(|w| unsafe { w.bits(periods) }), + Seq::Seq1 => self.pwm.seq1.refresh.write(|w| unsafe { w.bits(periods) }), + } + self + } + + /// Sets number of additional PWM periods after the sequence ends. + #[inline(always)] + pub fn set_seq_end_delay(&self, seq: Seq, periods: u32) -> &Self { + match seq { + Seq::Seq0 => self.pwm.seq0.enddelay.write(|w| unsafe { w.bits(periods) }), + Seq::Seq1 => self.pwm.seq1.enddelay.write(|w| unsafe { w.bits(periods) }), + } + self + } + + /// Loads a sequence buffer. + /// NOTE: `buf` must live until the sequence is done playing. + pub fn load_seq(&self, seq: Seq, buf: &[u16]) -> Result<(), Error> { + if ((buf.as_ptr() as usize) < SRAM_LOWER) || ((buf.as_ptr() as usize) > SRAM_UPPER) { + return Err(Error::DMABufferNotInDataMemory); + } + + compiler_fence(Ordering::SeqCst); + + match seq { + Seq::Seq0 => { + self.pwm + .seq0 + .ptr + .write(|w| unsafe { w.bits(buf.as_ptr() as u32) }); + self.pwm + .seq0 + .cnt + .write(|w| unsafe { w.bits(buf.len() as u32) }); + } + Seq::Seq1 => { + self.pwm + .seq1 + .ptr + .write(|w| unsafe { w.bits(buf.as_ptr() as u32) }); + self.pwm + .seq1 + .cnt + .write(|w| unsafe { w.bits(buf.len() as u32) }); + } + } + Ok(()) + } + + /// Loads the first PWM value on all enabled channels from a sequence and starts playing that sequence. + /// Causes PWM generation to start if not running. + #[inline(always)] + pub fn start_seq(&self, seq: Seq) { + compiler_fence(Ordering::SeqCst); + self.pwm.tasks_seqstart[usize::from(seq)].write(|w| w.tasks_seqstart().set_bit()); + while self.pwm.events_seqstarted[usize::from(seq)].read().bits() == 0 {} + self.pwm.events_seqend[0].write(|w| w); + self.pwm.events_seqend[1].write(|w| w); + } + + /// Steps by one value in the current sequence on all enabled channels, if the `NextStep` step mode is selected. + /// Does not cause PWM generation to start if not running. + #[inline(always)] + pub fn next_step(&self) { + self.pwm + .tasks_nextstep + .write(|w| w.tasks_nextstep().set_bit()); + } + + /// Stops PWM pulse generation on all channels at the end of current PWM period, and stops sequence playback. + #[inline(always)] + pub fn stop(&self) { + compiler_fence(Ordering::SeqCst); + self.pwm.tasks_stop.write(|w| w.tasks_stop().set_bit()); + while self.pwm.events_stopped.read().bits() == 0 {} + } + + /// Enables interrupt triggering on the specified event. + #[inline(always)] + pub fn enable_interrupt(&self, event: PwmEvent) -> &Self { + match event { + PwmEvent::Stopped => self.pwm.intenset.modify(|_r, w| w.stopped().set()), + PwmEvent::LoopsDone => self.pwm.intenset.modify(|_r, w| w.loopsdone().set()), + PwmEvent::PwmPeriodEnd => self.pwm.intenset.modify(|_r, w| w.pwmperiodend().set()), + PwmEvent::SeqStarted(seq) => match seq { + Seq::Seq0 => self.pwm.intenset.modify(|_r, w| w.seqstarted0().set()), + Seq::Seq1 => self.pwm.intenset.modify(|_r, w| w.seqstarted1().set()), + }, + PwmEvent::SeqEnd(seq) => match seq { + Seq::Seq0 => self.pwm.intenset.modify(|_r, w| w.seqend0().set()), + Seq::Seq1 => self.pwm.intenset.modify(|_r, w| w.seqend1().set()), + }, + }; + self + } + + /// Disables interrupt triggering on the specified event. + #[inline(always)] + pub fn disable_interrupt(&self, event: PwmEvent) -> &Self { + match event { + PwmEvent::Stopped => self.pwm.intenclr.modify(|_r, w| w.stopped().clear()), + PwmEvent::LoopsDone => self.pwm.intenclr.modify(|_r, w| w.loopsdone().clear()), + PwmEvent::PwmPeriodEnd => self.pwm.intenclr.modify(|_r, w| w.pwmperiodend().clear()), + PwmEvent::SeqStarted(seq) => match seq { + Seq::Seq0 => self.pwm.intenclr.modify(|_r, w| w.seqstarted0().clear()), + Seq::Seq1 => self.pwm.intenclr.modify(|_r, w| w.seqstarted1().clear()), + }, + PwmEvent::SeqEnd(seq) => match seq { + Seq::Seq0 => self.pwm.intenclr.modify(|_r, w| w.seqend0().clear()), + Seq::Seq1 => self.pwm.intenclr.modify(|_r, w| w.seqend1().clear()), + }, + }; + self + } + + /// Checks if an event has been triggered. + #[inline(always)] + pub fn is_event_triggered(&self, event: PwmEvent) -> bool { + match event { + PwmEvent::Stopped => self.pwm.events_stopped.read().bits() != 0, + PwmEvent::LoopsDone => self.pwm.events_loopsdone.read().bits() != 0, + PwmEvent::PwmPeriodEnd => self.pwm.events_pwmperiodend.read().bits() != 0, + PwmEvent::SeqStarted(seq) => { + self.pwm.events_seqstarted[usize::from(seq)].read().bits() != 0 + } + PwmEvent::SeqEnd(seq) => self.pwm.events_seqend[usize::from(seq)].read().bits() != 0, + } + } + + /// Marks event as handled. + #[inline(always)] + pub fn reset_event(&self, event: PwmEvent) { + match event { + PwmEvent::Stopped => self.pwm.events_stopped.write(|w| w), + PwmEvent::LoopsDone => self.pwm.events_loopsdone.write(|w| w), + PwmEvent::PwmPeriodEnd => self.pwm.events_pwmperiodend.write(|w| w), + PwmEvent::SeqStarted(seq) => self.pwm.events_seqstarted[usize::from(seq)].write(|w| w), + PwmEvent::SeqEnd(seq) => self.pwm.events_seqend[usize::from(seq)].write(|w| w), + } + } + + /// Returns reference to `Stopped` event endpoint for PPI. + #[inline(always)] + pub fn event_stopped(&self) -> &Reg { + &self.pwm.events_stopped + } + + /// Returns reference to `LoopsDone` event endpoint for PPI. + #[inline(always)] + pub fn event_loops_done(&self) -> &Reg { + &self.pwm.events_loopsdone + } + + /// Returns reference to `PwmPeriodEnd` event endpoint for PPI. + #[inline(always)] + pub fn event_pwm_period_end(&self) -> &Reg { + &self.pwm.events_pwmperiodend + } + + /// Returns reference to `Seq0 End` event endpoint for PPI. + #[inline(always)] + pub fn event_seq0_end(&self) -> &Reg { + &self.pwm.events_seqend[0] + } + + /// Returns reference to `Seq1 End` event endpoint for PPI. + #[inline(always)] + pub fn event_seq1_end(&self) -> &Reg { + &self.pwm.events_seqend[1] + } + + /// Returns reference to `Seq0 Started` event endpoint for PPI. + #[inline(always)] + pub fn event_seq0_started(&self) -> &Reg { + &self.pwm.events_seqstarted[0] + } + + /// Returns reference to `Seq1 Started` event endpoint for PPI. + #[inline(always)] + pub fn event_seq1_started(&self) -> &Reg { + &self.pwm.events_seqstarted[1] + } + + /// Returns reference to `Seq0 Start` task endpoint for PPI. + #[cfg(any(feature = "52833", feature = "52840"))] + #[inline(always)] + pub fn task_start_seq0(&self) -> &Reg { + &self.pwm.tasks_seqstart[0] + } + + /// Returns reference to `Seq1 Started` task endpoint for PPI. + #[cfg(any(feature = "52833", feature = "52840"))] + #[inline(always)] + pub fn task_start_seq1(&self) -> &Reg { + &self.pwm.tasks_seqstart[1] + } + + /// Returns reference to `NextStep` task endpoint for PPI. + #[inline(always)] + pub fn task_next_step(&self) -> &Reg { + &self.pwm.tasks_nextstep + } + + /// Returns reference to `Stop` task endpoint for PPI. + #[inline(always)] + pub fn task_stop(&self) -> &Reg { + &self.pwm.tasks_stop + } + + /// Returns individual handles to the four PWM channels. + #[inline(always)] + pub fn split_channels(&self) -> (PwmChannel, PwmChannel, PwmChannel, PwmChannel) { + ( + PwmChannel::new(self, Channel::C0), + PwmChannel::new(self, Channel::C1), + PwmChannel::new(self, Channel::C2), + PwmChannel::new(self, Channel::C3), + ) + } + + /// Returns individual handles to the two PWM groups. + pub fn split_groups(&self) -> (PwmGroup, PwmGroup) { + ( + PwmGroup::new(self, Group::G0), + PwmGroup::new(self, Group::G1), + ) + } + + /// Consumes `self` and returns back the raw peripheral. + pub fn free(self) -> T { + self.pwm + } +} + +impl embedded_hal::Pwm for Pwm { + type Channel = Channel; + type Duty = u16; + type Time = Hertz; + + fn enable(&mut self, channel: Self::Channel) { + self.enable_channel(channel); + } + + fn disable(&mut self, channel: Self::Channel) { + self.disable_channel(channel); + } + + fn get_duty(&self, channel: Self::Channel) -> Self::Duty { + self.get_duty_on(channel) + } + + fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { + self.set_duty_on(channel, duty); + } + + fn get_max_duty(&self) -> Self::Duty { + self.get_max_duty() + } + + fn get_period(&self) -> Self::Time { + self.get_period() + } + + fn set_period

(&mut self, period: P) + where + P: Into, + { + Self::set_period(self, period.into()); + } +} + +/// PWM channel +#[derive(Debug)] +pub struct PwmChannel<'a, T: sealed::Instance> { + pwm: &'a Pwm, + channel: Channel, +} + +impl<'a, T: sealed::Instance> PwmChannel<'a, T> { + pub fn new(pwm: &'a Pwm, channel: Channel) -> Self { + Self { pwm, channel } + } + + pub fn enable(&self) { + self.pwm.enable_channel(self.channel); + } + + pub fn disable(&self) { + self.pwm.disable_channel(self.channel); + } + + pub fn get_max_duty(&self) -> u16 { + self.pwm.get_max_duty() + } + pub fn set_duty(&self, duty: u16) { + self.pwm.set_duty_on(self.channel, duty); + } + pub fn set_duty_on(&self, duty: u16) { + self.pwm.set_duty_on(self.channel, duty); + } + pub fn set_duty_off(&self, duty: u16) { + self.pwm.set_duty_off(self.channel, duty); + } + pub fn get_duty_on(&self) -> u16 { + self.pwm.get_duty_on(self.channel) + } + pub fn get_duty_off(&self) -> u16 { + self.pwm.get_duty_off(self.channel) + } +} + +impl<'a, T: sealed::Instance> embedded_hal::PwmPin for PwmChannel<'a, T> { + type Duty = u16; + + fn disable(&mut self) { + Self::disable(self); + } + + fn enable(&mut self) { + Self::enable(self); + } + + fn get_duty(&self) -> Self::Duty { + self.get_duty_on() + } + + fn get_max_duty(&self) -> Self::Duty { + self.get_max_duty() + } + + fn set_duty(&mut self, duty: u16) { + self.set_duty_on(duty) + } +} + +/// PWM group +#[derive(Debug)] +pub struct PwmGroup<'a, T: sealed::Instance> { + pwm: &'a Pwm, + group: Group, +} + +impl<'a, T: sealed::Instance> PwmGroup<'a, T> { + pub fn new(pwm: &'a Pwm, group: Group) -> Self { + Self { pwm, group } + } + + pub fn enable(&self) { + self.pwm.enable_group(self.group); + } + + pub fn disable(&self) { + self.pwm.disable_group(self.group); + } + pub fn get_max_duty(&self) -> u16 { + self.pwm.get_max_duty() + } + pub fn set_duty(&self, duty: u16) { + self.pwm.set_duty_on_group(self.group, duty); + } + pub fn set_duty_on(&self, duty: u16) { + self.pwm.set_duty_on_group(self.group, duty); + } + pub fn set_duty_off(&self, duty: u16) { + self.pwm.set_duty_off_group(self.group, duty); + } + pub fn get_duty_on(&self) -> u16 { + self.pwm.get_duty_on_group(self.group) + } + pub fn get_duty_off(&self) -> u16 { + self.pwm.get_duty_off_group(self.group) + } +} + +impl<'a, T: sealed::Instance> embedded_hal::PwmPin for PwmGroup<'a, T> { + type Duty = u16; + + fn disable(&mut self) { + Self::disable(self); + } + + fn enable(&mut self) { + Self::enable(self); + } + + fn get_duty(&self) -> Self::Duty { + self.get_duty_on() + } + + fn get_max_duty(&self) -> Self::Duty { + self.get_max_duty() + } + + fn set_duty(&mut self, duty: u16) { + self.set_duty_on(duty) + } +} + +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Channel { + C0, + C1, + C2, + C3, +} +impl From for isize { + fn from(variant: Channel) -> Self { + variant as _ + } +} +impl From for usize { + fn from(variant: Channel) -> Self { + variant as _ + } +} + +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Group { + G0, + G1, +} +impl From for isize { + fn from(variant: Group) -> Self { + variant as _ + } +} +impl From for usize { + fn from(variant: Group) -> Self { + variant as _ + } +} + +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum LoadMode { + Common, + Grouped, + Individual, + Waveform, +} +impl From for u8 { + fn from(variant: LoadMode) -> Self { + variant as _ + } +} + +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Seq { + Seq0, + Seq1, +} +impl From for usize { + fn from(variant: Seq) -> Self { + variant as _ + } +} + +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Prescaler { + Div1, + Div2, + Div4, + Div8, + Div16, + Div32, + Div64, + Div128, +} +impl From for u8 { + fn from(variant: Prescaler) -> Self { + variant as _ + } +} + +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum CounterMode { + Up, + UpAndDown, +} +impl From for bool { + fn from(variant: CounterMode) -> Self { + match variant { + CounterMode::Up => false, + CounterMode::UpAndDown => true, + } + } +} + +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Loop { + Disabled, + Times(u16), + Inf, +} + +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum PwmEvent { + Stopped, + LoopsDone, + PwmPeriodEnd, + SeqStarted(Seq), + SeqEnd(Seq), +} + +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum StepMode { + Auto, + NextStep, +} +impl From for bool { + fn from(variant: StepMode) -> Self { + match variant { + StepMode::Auto => false, + StepMode::NextStep => true, + } + } +} + +#[derive(Debug)] +pub enum Error { + DMABufferNotInDataMemory, +} + +mod sealed { + use core::ops::Deref; + pub trait Instance: Deref {} +} + +impl sealed::Instance for PWM0 {} + +#[cfg(not(any(feature = "52810")))] +impl sealed::Instance for PWM1 {} + +#[cfg(not(any(feature = "52810")))] +impl sealed::Instance for PWM2 {} + +#[cfg(not(any(feature = "52810", feature = "52832")))] +impl sealed::Instance for PWM3 {} From 643835d231cc4884098a02e689e5273b1b2b3dd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Mon, 10 Aug 2020 13:54:59 +0200 Subject: [PATCH 02/12] Cleanup demo --- examples/pwm-demo/src/main.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/examples/pwm-demo/src/main.rs b/examples/pwm-demo/src/main.rs index 11d96964..34a0482b 100644 --- a/examples/pwm-demo/src/main.rs +++ b/examples/pwm-demo/src/main.rs @@ -27,7 +27,6 @@ pub enum AppStatus { Demo1C, Demo2A, Demo2B, - Demo2C, Demo3, Demo4, } @@ -63,7 +62,7 @@ const APP: () = { let led4 = p0.p0_16.into_push_pull_output(Level::High).degrade(); let pwm = Pwm::new(ctx.device.PWM0); - pwm.set_period(100u32.hz().into()) + pwm.set_period(500u32.hz().into()) .set_output_pin(Channel::C0, &led1) .set_output_pin(Channel::C1, &led2) .set_output_pin(Channel::C2, &led3) @@ -235,8 +234,6 @@ const APP: () = { extern "C" { fn SWI0_EGU0(); - fn SWI1_EGU1(); - fn SWI2_EGU2(); } }; From d89eb96daaeb6ef48684e625230a751fb98022b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Mon, 10 Aug 2020 18:17:48 +0200 Subject: [PATCH 03/12] Add demo for Waveform mode --- examples/pwm-demo/src/main.rs | 69 +++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/examples/pwm-demo/src/main.rs b/examples/pwm-demo/src/main.rs index 34a0482b..acedf342 100644 --- a/examples/pwm-demo/src/main.rs +++ b/examples/pwm-demo/src/main.rs @@ -27,6 +27,7 @@ pub enum AppStatus { Demo1C, Demo2A, Demo2B, + Demo2C, Demo3, Demo4, } @@ -100,7 +101,7 @@ const APP: () = { let pwm = ctx.resources.pwm; if pwm.is_event_triggered(PwmEvent::Stopped) { pwm.reset_event(PwmEvent::Stopped); - rprintln!("PWM generation stopped"); + rprintln!("PWM generation was stopped"); } } @@ -133,8 +134,8 @@ const APP: () = { AppStatus::Demo1A => { rprintln!("DEMO 1B: Group duty cycle"); *status = AppStatus::Demo1B; - grp1.set_duty(max_duty / 10); grp0.set_duty(max_duty / 300); + grp1.set_duty(max_duty / 10); } _ => { rprintln!("DEMO 1A: Common duty cycle for all channels"); @@ -145,13 +146,34 @@ const APP: () = { } if ctx.resources.btn2.is_low().unwrap() { match status { + AppStatus::Demo2B => { + rprintln!("DEMO 2C: Play complex sequence 4 times"); + *status = AppStatus::Demo2C; + for x in 0..12 { + BUF[x] = triangle_wave( + x as i32, + 12, + max_duty as i32 / 20, + 0, + max_duty as i32 / 800, + ) as u16; + } + pwm.set_load_mode(LoadMode::Common) + .set_step_mode(StepMode::Auto) + .set_seq_refresh(Seq::Seq0, 100) + .set_seq_refresh(Seq::Seq1, 10) + .repeat(4); + pwm.load_seq(Seq::Seq0, &BUF[..6]).ok(); + pwm.load_seq(Seq::Seq1, &BUF[6..12]).ok(); + pwm.start_seq(Seq::Seq0); + } AppStatus::Demo2A => { rprintln!("DEMO 2B: Loop individual sequences"); *status = AppStatus::Demo2B; let amplitude = max_duty as i32 / 5; let offset = max_duty as i32 / 300; for x in 0..12 { - BUF[4 * x] = triangle_wave(x as i32, 12, amplitude, 0, 0) as u16; + BUF[4 * x] = triangle_wave(x as i32, 12, amplitude, 0, offset) as u16; BUF[4 * x + 1] = triangle_wave(x as i32, 12, amplitude, 3, offset) as u16; BUF[4 * x + 2] = triangle_wave(x as i32, 12, amplitude, 6, offset) as u16; BUF[4 * x + 3] = triangle_wave(x as i32, 12, amplitude, 9, offset) as u16; @@ -184,50 +206,49 @@ const APP: () = { match status { AppStatus::Demo3 => { rprintln!("DEMO 3: Next step"); + pwm.next_step(); if pwm.is_event_triggered(PwmEvent::SeqEnd(Seq::Seq1)) { rprintln!("DEMO 3: End"); pwm.reset_event(PwmEvent::SeqEnd(Seq::Seq1)); pwm.stop(); *status = AppStatus::Idle; - } else { - pwm.next_step(); } } _ => { rprintln!("DEMO 3: Manually step through sequence"); *status = AppStatus::Demo3; - for x in 0..8 { - BUF[x] = triangle_wave( - x as i32, - 8, - max_duty as i32 / 50, - 0, - max_duty as i32 / 800, - ) as u16; + let amplitude = max_duty as i32 / 20; + let offset = max_duty as i32 / 300; + for x in 0..6 { + BUF[x] = triangle_wave(x as i32, 6, amplitude, 0, offset) as u16; } pwm.set_load_mode(LoadMode::Common) .loop_inf() .set_step_mode(StepMode::NextStep); - pwm.load_seq(Seq::Seq0, &BUF[..4]).ok(); - pwm.load_seq(Seq::Seq1, &BUF[4..8]).ok(); + pwm.load_seq(Seq::Seq0, &BUF[..3]).ok(); + pwm.load_seq(Seq::Seq1, &BUF[3..6]).ok(); pwm.start_seq(Seq::Seq0); } } } if ctx.resources.btn4.is_low().unwrap() { - rprintln!("DEMO 4: Play complex sequence 4 times"); + rprintln!("DEMO 4: Waveform mode"); *status = AppStatus::Demo4; for x in 0..12 { - BUF[x] = triangle_wave(x as i32, 12, max_duty as i32 / 20, 0, max_duty as i32 / 800) - as u16; + let current_max = x * 2_200 + 5_000; + BUF[4 * x] = ((x % 3) * current_max / (5 * (x + 1))) as u16; + BUF[4 * x + 1] = (((x + 1) % 3) * current_max / (5 * (x + 1))) as u16; + BUF[4 * x + 2] = (((x + 2) % 3) * current_max / (5 * (x + 1))) as u16; + // In waveform mode, the 4th sample is current max_duty + BUF[4 * x + 3] = current_max as u16; } - pwm.set_load_mode(LoadMode::Common) + pwm.set_load_mode(LoadMode::Waveform) .set_step_mode(StepMode::Auto) - .set_seq_refresh(Seq::Seq0, 100) - .set_seq_refresh(Seq::Seq1, 20) - .repeat(4); - pwm.load_seq(Seq::Seq0, &BUF[..6]).ok(); - pwm.load_seq(Seq::Seq1, &BUF[6..12]).ok(); + .set_seq_refresh(Seq::Seq0, 150) + .set_seq_refresh(Seq::Seq1, 150) + .loop_inf(); + pwm.load_seq(Seq::Seq0, &BUF[..48]).ok(); + pwm.load_seq(Seq::Seq1, &BUF[..48]).ok(); pwm.start_seq(Seq::Seq0); } } From cabaeb1973a7cfe2dc62e1e9471fc19e0e562790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Mon, 10 Aug 2020 20:41:59 +0200 Subject: [PATCH 04/12] Add demo comments and grouped sequence example --- examples/pwm-demo/src/main.rs | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/examples/pwm-demo/src/main.rs b/examples/pwm-demo/src/main.rs index acedf342..510e116b 100644 --- a/examples/pwm-demo/src/main.rs +++ b/examples/pwm-demo/src/main.rs @@ -63,7 +63,7 @@ const APP: () = { let led4 = p0.p0_16.into_push_pull_output(Level::High).degrade(); let pwm = Pwm::new(ctx.device.PWM0); - pwm.set_period(500u32.hz().into()) + pwm.set_period(500u32.hz()) .set_output_pin(Channel::C0, &led1) .set_output_pin(Channel::C1, &led2) .set_output_pin(Channel::C2, &led3) @@ -147,24 +147,22 @@ const APP: () = { if ctx.resources.btn2.is_low().unwrap() { match status { AppStatus::Demo2B => { - rprintln!("DEMO 2C: Play complex sequence 4 times"); + rprintln!("DEMO 2C: Play grouped sequence 4 times"); *status = AppStatus::Demo2C; + let amplitude = max_duty as i32 / 20; + let offset = 0; + // In `Grouped` mode, each step consists of two values [G0, G1] for x in 0..12 { - BUF[x] = triangle_wave( - x as i32, - 12, - max_duty as i32 / 20, - 0, - max_duty as i32 / 800, - ) as u16; + BUF[x * 2] = triangle_wave(x as i32, 12, amplitude, 6, offset) as u16; + BUF[x * 2 + 1] = triangle_wave(x as i32, 12, amplitude, 0, offset) as u16; } - pwm.set_load_mode(LoadMode::Common) + pwm.set_load_mode(LoadMode::Grouped) .set_step_mode(StepMode::Auto) - .set_seq_refresh(Seq::Seq0, 100) - .set_seq_refresh(Seq::Seq1, 10) + .set_seq_refresh(Seq::Seq0, 70) + .set_seq_refresh(Seq::Seq1, 30) .repeat(4); - pwm.load_seq(Seq::Seq0, &BUF[..6]).ok(); - pwm.load_seq(Seq::Seq1, &BUF[6..12]).ok(); + pwm.load_seq(Seq::Seq0, &BUF[..12]).ok(); + pwm.load_seq(Seq::Seq1, &BUF[12..24]).ok(); pwm.start_seq(Seq::Seq0); } AppStatus::Demo2A => { @@ -172,6 +170,7 @@ const APP: () = { *status = AppStatus::Demo2B; let amplitude = max_duty as i32 / 5; let offset = max_duty as i32 / 300; + // In `Individual` mode, each step consists of four values [C0, C1, C2, C3] for x in 0..12 { BUF[4 * x] = triangle_wave(x as i32, 12, amplitude, 0, offset) as u16; BUF[4 * x + 1] = triangle_wave(x as i32, 12, amplitude, 3, offset) as u16; @@ -187,8 +186,9 @@ const APP: () = { pwm.start_seq(Seq::Seq0); } _ => { - rprintln!("DEMO 2A: Play sequence once"); + rprintln!("DEMO 2A: Play common sequence once"); *status = AppStatus::Demo2A; + // In `Common` mode, each step consists of one value for all channels. for x in 0..10 { BUF[x] = triangle_wave(x as i32, 10, 2000, 0, 100) as u16; } @@ -234,12 +234,13 @@ const APP: () = { if ctx.resources.btn4.is_low().unwrap() { rprintln!("DEMO 4: Waveform mode"); *status = AppStatus::Demo4; + // In `Waveform` mode, each step consists of four values [C0, C1, C2, MAX_DUTY] + // So the maximum duty cycle can be set on a per step basis, affecting the PWM frequency for x in 0..12 { let current_max = x * 2_200 + 5_000; BUF[4 * x] = ((x % 3) * current_max / (5 * (x + 1))) as u16; BUF[4 * x + 1] = (((x + 1) % 3) * current_max / (5 * (x + 1))) as u16; BUF[4 * x + 2] = (((x + 2) % 3) * current_max / (5 * (x + 1))) as u16; - // In waveform mode, the 4th sample is current max_duty BUF[4 * x + 3] = current_max as u16; } pwm.set_load_mode(LoadMode::Waveform) From e7a90a6b4e2aa11c65e901f041a788f982a50a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Fri, 21 Aug 2020 00:52:43 +0200 Subject: [PATCH 05/12] Make Instance trait public but requiring a private trait, to allow for generic naming in user code --- nrf-hal-common/src/pwm.rs | 46 ++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/nrf-hal-common/src/pwm.rs b/nrf-hal-common/src/pwm.rs index 9668ffb4..33c8c7ad 100644 --- a/nrf-hal-common/src/pwm.rs +++ b/nrf-hal-common/src/pwm.rs @@ -21,14 +21,14 @@ use crate::{ /// A safe wrapper around the raw peripheral. #[derive(Debug)] -pub struct Pwm { +pub struct Pwm { pwm: T, duty: [u16; 4], } impl Pwm where - T: sealed::Instance, + T: Instance, { /// Takes ownership of the peripheral and applies sane defaults. pub fn new(pwm: T) -> Pwm { @@ -695,7 +695,7 @@ where } } -impl embedded_hal::Pwm for Pwm { +impl embedded_hal::Pwm for Pwm { type Channel = Channel; type Duty = u16; type Time = Hertz; @@ -734,12 +734,12 @@ impl embedded_hal::Pwm for Pwm { /// PWM channel #[derive(Debug)] -pub struct PwmChannel<'a, T: sealed::Instance> { +pub struct PwmChannel<'a, T: Instance> { pwm: &'a Pwm, channel: Channel, } -impl<'a, T: sealed::Instance> PwmChannel<'a, T> { +impl<'a, T: Instance> PwmChannel<'a, T> { pub fn new(pwm: &'a Pwm, channel: Channel) -> Self { Self { pwm, channel } } @@ -772,7 +772,7 @@ impl<'a, T: sealed::Instance> PwmChannel<'a, T> { } } -impl<'a, T: sealed::Instance> embedded_hal::PwmPin for PwmChannel<'a, T> { +impl<'a, T: Instance> embedded_hal::PwmPin for PwmChannel<'a, T> { type Duty = u16; fn disable(&mut self) { @@ -798,12 +798,12 @@ impl<'a, T: sealed::Instance> embedded_hal::PwmPin for PwmChannel<'a, T> { /// PWM group #[derive(Debug)] -pub struct PwmGroup<'a, T: sealed::Instance> { +pub struct PwmGroup<'a, T: Instance> { pwm: &'a Pwm, group: Group, } -impl<'a, T: sealed::Instance> PwmGroup<'a, T> { +impl<'a, T: Instance> PwmGroup<'a, T> { pub fn new(pwm: &'a Pwm, group: Group) -> Self { Self { pwm, group } } @@ -835,7 +835,7 @@ impl<'a, T: sealed::Instance> PwmGroup<'a, T> { } } -impl<'a, T: sealed::Instance> embedded_hal::PwmPin for PwmGroup<'a, T> { +impl<'a, T: Instance> embedded_hal::PwmPin for PwmGroup<'a, T> { type Duty = u16; fn disable(&mut self) { @@ -983,18 +983,30 @@ pub enum Error { DMABufferNotInDataMemory, } -mod sealed { - use core::ops::Deref; - pub trait Instance: Deref {} -} +pub trait Instance: private::Sealed {} -impl sealed::Instance for PWM0 {} +impl Instance for PWM0 {} #[cfg(not(any(feature = "52810")))] -impl sealed::Instance for PWM1 {} +impl Instance for PWM1 {} #[cfg(not(any(feature = "52810")))] -impl sealed::Instance for PWM2 {} +impl Instance for PWM2 {} #[cfg(not(any(feature = "52810", feature = "52832")))] -impl sealed::Instance for PWM3 {} +impl Instance for PWM3 {} + +mod private { + pub trait Sealed: core::ops::Deref {} + + impl Sealed for crate::pwm::PWM0 {} + + #[cfg(not(any(feature = "52810")))] + impl Sealed for crate::pwm::PWM1 {} + + #[cfg(not(any(feature = "52810")))] + impl Sealed for crate::pwm::PWM2 {} + + #[cfg(not(any(feature = "52810", feature = "52832")))] + impl Sealed for crate::pwm::PWM3 {} +} From d1995998feb983f9c7f766dc337a4260f36a3533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Fri, 21 Aug 2020 01:08:50 +0200 Subject: [PATCH 06/12] Check DMA buffer size --- nrf-hal-common/src/pwm.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nrf-hal-common/src/pwm.rs b/nrf-hal-common/src/pwm.rs index 33c8c7ad..2e24170a 100644 --- a/nrf-hal-common/src/pwm.rs +++ b/nrf-hal-common/src/pwm.rs @@ -479,10 +479,14 @@ where /// Loads a sequence buffer. /// NOTE: `buf` must live until the sequence is done playing. pub fn load_seq(&self, seq: Seq, buf: &[u16]) -> Result<(), Error> { - if ((buf.as_ptr() as usize) < SRAM_LOWER) || ((buf.as_ptr() as usize) > SRAM_UPPER) { + if (buf.as_ptr() as usize) < SRAM_LOWER || (buf.as_ptr() as usize) > SRAM_UPPER { return Err(Error::DMABufferNotInDataMemory); } + if buf.len() > 2048 { + return Err(Error::BufferTooLong); + } + compiler_fence(Ordering::SeqCst); match seq { @@ -981,6 +985,7 @@ impl From for bool { #[derive(Debug)] pub enum Error { DMABufferNotInDataMemory, + BufferTooLong, } pub trait Instance: private::Sealed {} From 7ef09c6c56e69791e132f36599e9310ac685c76e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Fri, 21 Aug 2020 01:24:56 +0200 Subject: [PATCH 07/12] Correct maximum seq maxcnt --- nrf-hal-common/src/pwm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nrf-hal-common/src/pwm.rs b/nrf-hal-common/src/pwm.rs index 2e24170a..aea93dd3 100644 --- a/nrf-hal-common/src/pwm.rs +++ b/nrf-hal-common/src/pwm.rs @@ -483,7 +483,7 @@ where return Err(Error::DMABufferNotInDataMemory); } - if buf.len() > 2048 { + if buf.len() > 32_768 { return Err(Error::BufferTooLong); } From 0be82763131bca9386565b48db52417dd9fd69f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Mon, 24 Aug 2020 22:08:58 +0200 Subject: [PATCH 08/12] Omit get_ prefix in getter functions --- nrf-hal-common/src/pwm.rs | 104 +++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/nrf-hal-common/src/pwm.rs b/nrf-hal-common/src/pwm.rs index aea93dd3..fbaf58b7 100644 --- a/nrf-hal-common/src/pwm.rs +++ b/nrf-hal-common/src/pwm.rs @@ -70,7 +70,7 @@ where /// Sets the PWM clock prescaler. #[inline(always)] - pub fn get_prescaler(&self) -> Prescaler { + pub fn prescaler(&self) -> Prescaler { match self.pwm.prescaler.read().prescaler().bits() { 0 => Prescaler::Div1, 1 => Prescaler::Div2, @@ -94,14 +94,14 @@ where } /// Returns the maximum duty cycle value. #[inline(always)] - pub fn get_max_duty(&self) -> u16 { + pub fn max_duty(&self) -> u16 { self.pwm.countertop.read().countertop().bits() } /// Sets the PWM output frequency. #[inline(always)] pub fn set_period(&self, freq: Hertz) -> &Self { - let duty = match self.get_prescaler() { + let duty = match self.prescaler() { Prescaler::Div1 => 16_000_000u32 / freq.0, Prescaler::Div2 => 8_000_000u32 / freq.0, Prescaler::Div4 => 4_000_000u32 / freq.0, @@ -111,7 +111,7 @@ where Prescaler::Div64 => 250_000u32 / freq.0, Prescaler::Div128 => 125_000u32 / freq.0, }; - match self.get_counter_mode() { + match self.counter_mode() { CounterMode::Up => self.set_max_duty(duty.min(32767) as u16), CounterMode::UpAndDown => self.set_max_duty((duty / 2).min(32767) as u16), }; @@ -120,9 +120,9 @@ where /// Returns the PWM output frequency. #[inline(always)] - pub fn get_period(&self) -> Hertz { - let max_duty = self.get_max_duty() as u32; - let freq = match self.get_prescaler() { + pub fn period(&self) -> Hertz { + let max_duty = self.max_duty() as u32; + let freq = match self.prescaler() { Prescaler::Div1 => 16_000_000u32 / max_duty, Prescaler::Div2 => 8_000_000u32 / max_duty, Prescaler::Div4 => 4_000_000u32 / max_duty, @@ -132,7 +132,7 @@ where Prescaler::Div64 => 250_000u32 / max_duty, Prescaler::Div128 => 125_000u32 / max_duty, }; - match self.get_counter_mode() { + match self.counter_mode() { CounterMode::Up => freq.hz(), CounterMode::UpAndDown => (freq / 2).hz(), } @@ -227,7 +227,7 @@ where /// Returns how a sequence is read from RAM and is spread to the compare register. #[inline(always)] - pub fn get_load_mode(&self) -> LoadMode { + pub fn load_mode(&self) -> LoadMode { match self.pwm.decoder.read().load().bits() { 0 => LoadMode::Common, 1 => LoadMode::Grouped, @@ -246,7 +246,7 @@ where /// Returns selected operating mode of the wave counter. #[inline(always)] - pub fn get_counter_mode(&self) -> CounterMode { + pub fn counter_mode(&self) -> CounterMode { match self.pwm.mode.read().updown().bit() { false => CounterMode::Up, true => CounterMode::UpAndDown, @@ -262,7 +262,7 @@ where /// Returns selected source for advancing the active sequence. #[inline(always)] - pub fn get_step_mode(&self) -> StepMode { + pub fn step_mode(&self) -> StepMode { match self.pwm.decoder.read().mode().bit() { false => StepMode::Auto, true => StepMode::NextStep, @@ -271,22 +271,22 @@ where // Internal helper function that returns 15 bit duty cycle value. #[inline(always)] - fn get_duty_on_value(&self, index: usize) -> u16 { + fn duty_on_value(&self, index: usize) -> u16 { let val = self.duty[index]; let is_inverted = (val >> 15) & 1 == 0; match is_inverted { false => val, - true => self.get_max_duty() - (val & 0x7FFF), + true => self.max_duty() - (val & 0x7FFF), } } // Internal helper function that returns 15 bit inverted duty cycle value. #[inline(always)] - fn get_duty_off_value(&self, index: usize) -> u16 { + fn duty_off_value(&self, index: usize) -> u16 { let val = self.duty[index]; let is_inverted = (val >> 15) & 1 == 0; match is_inverted { - false => self.get_max_duty() - val, + false => self.max_duty() - val, true => val & 0x7FFF, } } @@ -295,7 +295,7 @@ where pub fn set_duty_on_common(&self, duty: u16) { compiler_fence(Ordering::SeqCst); unsafe { - *(self.duty.as_ptr() as *mut u16) = duty.min(self.get_max_duty()) & 0x7FFF; + *(self.duty.as_ptr() as *mut u16) = duty.min(self.max_duty()) & 0x7FFF; } self.one_shot(); self.set_load_mode(LoadMode::Common); @@ -311,7 +311,7 @@ where pub fn set_duty_off_common(&self, duty: u16) { compiler_fence(Ordering::SeqCst); unsafe { - *(self.duty.as_ptr() as *mut u16) = duty.min(self.get_max_duty()) | 0x8000; + *(self.duty.as_ptr() as *mut u16) = duty.min(self.max_duty()) | 0x8000; } self.one_shot(); self.set_load_mode(LoadMode::Common); @@ -325,14 +325,14 @@ where /// Returns the common duty cycle value for all PWM channels in `Common` load mode. #[inline(always)] - pub fn get_duty_on_common(&self) -> u16 { - self.get_duty_on_value(0) + pub fn duty_on_common(&self) -> u16 { + self.duty_on_value(0) } /// Returns the inverted common duty cycle value for all PWM channels in `Common` load mode. #[inline(always)] - pub fn get_duty_off_common(&self) -> u16 { - self.get_duty_off_value(0) + pub fn duty_off_common(&self) -> u16 { + self.duty_off_value(0) } /// Sets duty cycle (15 bit) for a PWM group. @@ -340,7 +340,7 @@ where compiler_fence(Ordering::SeqCst); unsafe { *(self.duty.as_ptr().offset(group.into()) as *mut u16) = - duty.min(self.get_max_duty()) & 0x7FFF; + duty.min(self.max_duty()) & 0x7FFF; } self.one_shot(); self.set_load_mode(LoadMode::Grouped); @@ -357,7 +357,7 @@ where compiler_fence(Ordering::SeqCst); unsafe { *(self.duty.as_ptr().offset(group.into()) as *mut u16) = - duty.min(self.get_max_duty()) | 0x8000; + duty.min(self.max_duty()) | 0x8000; } self.one_shot(); self.set_load_mode(LoadMode::Grouped); @@ -371,14 +371,14 @@ where /// Returns duty cycle value for a PWM group. #[inline(always)] - pub fn get_duty_on_group(&self, group: Group) -> u16 { - self.get_duty_on_value(usize::from(group)) + pub fn duty_on_group(&self, group: Group) -> u16 { + self.duty_on_value(usize::from(group)) } /// Returns inverted duty cycle value for a PWM group. #[inline(always)] - pub fn get_duty_off_group(&self, group: Group) -> u16 { - self.get_duty_off_value(usize::from(group)) + pub fn duty_off_group(&self, group: Group) -> u16 { + self.duty_off_value(usize::from(group)) } /// Sets duty cycle (15 bit) for a PWM channel. @@ -386,7 +386,7 @@ where compiler_fence(Ordering::SeqCst); unsafe { *(self.duty.as_ptr().offset(channel.into()) as *mut u16) = - duty.min(self.get_max_duty()) & 0x7FFF; + duty.min(self.max_duty()) & 0x7FFF; } self.one_shot(); self.set_load_mode(LoadMode::Individual); @@ -400,7 +400,7 @@ where compiler_fence(Ordering::SeqCst); unsafe { *(self.duty.as_ptr().offset(channel.into()) as *mut u16) = - duty.min(self.get_max_duty()) | 0x8000; + duty.min(self.max_duty()) | 0x8000; } self.one_shot(); self.set_load_mode(LoadMode::Individual); @@ -411,14 +411,14 @@ where /// Returns the duty cycle value for a PWM channel. #[inline(always)] - pub fn get_duty_on(&self, channel: Channel) -> u16 { - self.get_duty_on_value(usize::from(channel)) + pub fn duty_on(&self, channel: Channel) -> u16 { + self.duty_on_value(usize::from(channel)) } /// Returns the inverted duty cycle value for a PWM group. #[inline(always)] - pub fn get_duty_off(&self, channel: Channel) -> u16 { - self.get_duty_off_value(usize::from(channel)) + pub fn duty_off(&self, channel: Channel) -> u16 { + self.duty_off_value(usize::from(channel)) } /// Sets number of playbacks of sequences. @@ -713,7 +713,7 @@ impl embedded_hal::Pwm for Pwm { } fn get_duty(&self, channel: Self::Channel) -> Self::Duty { - self.get_duty_on(channel) + self.duty_on(channel) } fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { @@ -721,11 +721,11 @@ impl embedded_hal::Pwm for Pwm { } fn get_max_duty(&self) -> Self::Duty { - self.get_max_duty() + self.max_duty() } fn get_period(&self) -> Self::Time { - self.get_period() + self.period() } fn set_period

(&mut self, period: P) @@ -756,8 +756,8 @@ impl<'a, T: Instance> PwmChannel<'a, T> { self.pwm.disable_channel(self.channel); } - pub fn get_max_duty(&self) -> u16 { - self.pwm.get_max_duty() + pub fn max_duty(&self) -> u16 { + self.pwm.max_duty() } pub fn set_duty(&self, duty: u16) { self.pwm.set_duty_on(self.channel, duty); @@ -768,11 +768,11 @@ impl<'a, T: Instance> PwmChannel<'a, T> { pub fn set_duty_off(&self, duty: u16) { self.pwm.set_duty_off(self.channel, duty); } - pub fn get_duty_on(&self) -> u16 { - self.pwm.get_duty_on(self.channel) + pub fn duty_on(&self) -> u16 { + self.pwm.duty_on(self.channel) } - pub fn get_duty_off(&self) -> u16 { - self.pwm.get_duty_off(self.channel) + pub fn duty_off(&self) -> u16 { + self.pwm.duty_off(self.channel) } } @@ -788,11 +788,11 @@ impl<'a, T: Instance> embedded_hal::PwmPin for PwmChannel<'a, T> { } fn get_duty(&self) -> Self::Duty { - self.get_duty_on() + self.duty_on() } fn get_max_duty(&self) -> Self::Duty { - self.get_max_duty() + self.max_duty() } fn set_duty(&mut self, duty: u16) { @@ -819,8 +819,8 @@ impl<'a, T: Instance> PwmGroup<'a, T> { pub fn disable(&self) { self.pwm.disable_group(self.group); } - pub fn get_max_duty(&self) -> u16 { - self.pwm.get_max_duty() + pub fn max_duty(&self) -> u16 { + self.pwm.max_duty() } pub fn set_duty(&self, duty: u16) { self.pwm.set_duty_on_group(self.group, duty); @@ -831,11 +831,11 @@ impl<'a, T: Instance> PwmGroup<'a, T> { pub fn set_duty_off(&self, duty: u16) { self.pwm.set_duty_off_group(self.group, duty); } - pub fn get_duty_on(&self) -> u16 { - self.pwm.get_duty_on_group(self.group) + pub fn duty_on(&self) -> u16 { + self.pwm.duty_on_group(self.group) } - pub fn get_duty_off(&self) -> u16 { - self.pwm.get_duty_off_group(self.group) + pub fn duty_off(&self) -> u16 { + self.pwm.duty_off_group(self.group) } } @@ -851,11 +851,11 @@ impl<'a, T: Instance> embedded_hal::PwmPin for PwmGroup<'a, T> { } fn get_duty(&self) -> Self::Duty { - self.get_duty_on() + self.duty_on() } fn get_max_duty(&self) -> Self::Duty { - self.get_max_duty() + self.max_duty() } fn set_duty(&mut self, duty: u16) { From 7242ff3a2a1c943173bbb03d5e38af690ba03f5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Mon, 24 Aug 2020 22:26:40 +0200 Subject: [PATCH 09/12] Update demo with renamed functions --- examples/pwm-demo/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pwm-demo/src/main.rs b/examples/pwm-demo/src/main.rs index 42c74b1f..7effb08d 100644 --- a/examples/pwm-demo/src/main.rs +++ b/examples/pwm-demo/src/main.rs @@ -117,7 +117,7 @@ const APP: () = { let status = ctx.resources.status; let pwm = ctx.resources.pwm; - let max_duty = pwm.get_max_duty(); + let max_duty = pwm.max_duty(); let (ch0, ch1, ch2, ch3) = pwm.split_channels(); let (grp0, grp1) = pwm.split_groups(); From 2dda3099fbd1b04fd81055d85dc129f99a6f73c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Mon, 24 Aug 2020 23:03:56 +0200 Subject: [PATCH 10/12] Use RefCell for internal duty buffer instead of raw pointers --- nrf-hal-common/src/pwm.rs | 59 ++++++++++++++------------------------- 1 file changed, 21 insertions(+), 38 deletions(-) diff --git a/nrf-hal-common/src/pwm.rs b/nrf-hal-common/src/pwm.rs index fbaf58b7..1d2291ec 100644 --- a/nrf-hal-common/src/pwm.rs +++ b/nrf-hal-common/src/pwm.rs @@ -2,6 +2,7 @@ //! //! The pulse with modulation (PWM) module enables the generation of pulse width modulated signals on GPIO. +use core::cell::RefCell; use core::sync::atomic::{compiler_fence, Ordering}; #[cfg(any(feature = "52833", feature = "52840"))] @@ -23,7 +24,7 @@ use crate::{ #[derive(Debug)] pub struct Pwm { pwm: T, - duty: [u16; 4], + duty: RefCell<[u16; 4]>, } impl Pwm @@ -33,7 +34,7 @@ where /// Takes ownership of the peripheral and applies sane defaults. pub fn new(pwm: T) -> Pwm { compiler_fence(Ordering::SeqCst); - let duty = [0u16; 4]; + let duty = RefCell::new([0u16; 4]); pwm.enable.write(|w| w.enable().enabled()); pwm.mode.write(|w| w.updown().up()); pwm.prescaler.write(|w| w.prescaler().div_1()); @@ -272,7 +273,7 @@ where // Internal helper function that returns 15 bit duty cycle value. #[inline(always)] fn duty_on_value(&self, index: usize) -> u16 { - let val = self.duty[index]; + let val = self.duty.borrow()[index]; let is_inverted = (val >> 15) & 1 == 0; match is_inverted { false => val, @@ -283,7 +284,7 @@ where // Internal helper function that returns 15 bit inverted duty cycle value. #[inline(always)] fn duty_off_value(&self, index: usize) -> u16 { - let val = self.duty[index]; + let val = self.duty.borrow()[index]; let is_inverted = (val >> 15) & 1 == 0; match is_inverted { false => self.max_duty() - val, @@ -294,9 +295,9 @@ where /// Sets duty cycle (15 bit) for all PWM channels. pub fn set_duty_on_common(&self, duty: u16) { compiler_fence(Ordering::SeqCst); - unsafe { - *(self.duty.as_ptr() as *mut u16) = duty.min(self.max_duty()) & 0x7FFF; - } + self.duty + .borrow_mut() + .copy_from_slice(&[duty.min(self.max_duty()) & 0x7FFF; 4][..]); self.one_shot(); self.set_load_mode(LoadMode::Common); self.pwm @@ -310,9 +311,9 @@ where /// Sets inverted duty cycle (15 bit) for all PWM channels. pub fn set_duty_off_common(&self, duty: u16) { compiler_fence(Ordering::SeqCst); - unsafe { - *(self.duty.as_ptr() as *mut u16) = duty.min(self.max_duty()) | 0x8000; - } + self.duty + .borrow_mut() + .copy_from_slice(&[duty.min(self.max_duty()) | 0x8000; 4][..]); self.one_shot(); self.set_load_mode(LoadMode::Common); self.pwm @@ -338,10 +339,11 @@ where /// Sets duty cycle (15 bit) for a PWM group. pub fn set_duty_on_group(&self, group: Group, duty: u16) { compiler_fence(Ordering::SeqCst); - unsafe { - *(self.duty.as_ptr().offset(group.into()) as *mut u16) = - duty.min(self.max_duty()) & 0x7FFF; - } + // unsafe { + // *(self.duty.as_ptr().offset(group.into()) as *mut u16) = + // duty.min(self.max_duty()) & 0x7FFF; + // } + self.duty.borrow_mut()[usize::from(group)] = duty.min(self.max_duty()) & 0x7FFF; self.one_shot(); self.set_load_mode(LoadMode::Grouped); self.pwm @@ -355,10 +357,7 @@ where /// Sets inverted duty cycle (15 bit) for a PWM group. pub fn set_duty_off_group(&self, group: Group, duty: u16) { compiler_fence(Ordering::SeqCst); - unsafe { - *(self.duty.as_ptr().offset(group.into()) as *mut u16) = - duty.min(self.max_duty()) | 0x8000; - } + self.duty.borrow_mut()[usize::from(group)] = duty.min(self.max_duty()) | 0x8000; self.one_shot(); self.set_load_mode(LoadMode::Grouped); self.pwm @@ -384,13 +383,10 @@ where /// Sets duty cycle (15 bit) for a PWM channel. pub fn set_duty_on(&self, channel: Channel, duty: u16) { compiler_fence(Ordering::SeqCst); - unsafe { - *(self.duty.as_ptr().offset(channel.into()) as *mut u16) = - duty.min(self.max_duty()) & 0x7FFF; - } + self.duty.borrow_mut()[usize::from(channel)] = duty.min(self.max_duty()) & 0x7FFF; self.one_shot(); self.set_load_mode(LoadMode::Individual); - if self.load_seq(Seq::Seq0, &self.duty).is_ok() { + if self.load_seq(Seq::Seq0, &*self.duty.borrow()).is_ok() { self.start_seq(Seq::Seq0); } } @@ -398,13 +394,10 @@ where /// Sets inverted duty cycle (15 bit) for a PWM channel. pub fn set_duty_off(&self, channel: Channel, duty: u16) { compiler_fence(Ordering::SeqCst); - unsafe { - *(self.duty.as_ptr().offset(channel.into()) as *mut u16) = - duty.min(self.max_duty()) | 0x8000; - } + self.duty.borrow_mut()[usize::from(channel)] = duty.min(self.max_duty()) | 0x8000; self.one_shot(); self.set_load_mode(LoadMode::Individual); - if self.load_seq(Seq::Seq0, &self.duty).is_ok() { + if self.load_seq(Seq::Seq0, &*self.duty.borrow()).is_ok() { self.start_seq(Seq::Seq0); } } @@ -870,11 +863,6 @@ pub enum Channel { C2, C3, } -impl From for isize { - fn from(variant: Channel) -> Self { - variant as _ - } -} impl From for usize { fn from(variant: Channel) -> Self { variant as _ @@ -886,11 +874,6 @@ pub enum Group { G0, G1, } -impl From for isize { - fn from(variant: Group) -> Self { - variant as _ - } -} impl From for usize { fn from(variant: Group) -> Self { variant as _ From bbab423b1700dbe90dbaaf0bece9d7e3bcd07e06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Mon, 24 Aug 2020 23:09:02 +0200 Subject: [PATCH 11/12] Cleanup --- nrf-hal-common/src/pwm.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/nrf-hal-common/src/pwm.rs b/nrf-hal-common/src/pwm.rs index 1d2291ec..cb4a0c4c 100644 --- a/nrf-hal-common/src/pwm.rs +++ b/nrf-hal-common/src/pwm.rs @@ -339,10 +339,6 @@ where /// Sets duty cycle (15 bit) for a PWM group. pub fn set_duty_on_group(&self, group: Group, duty: u16) { compiler_fence(Ordering::SeqCst); - // unsafe { - // *(self.duty.as_ptr().offset(group.into()) as *mut u16) = - // duty.min(self.max_duty()) & 0x7FFF; - // } self.duty.borrow_mut()[usize::from(group)] = duty.min(self.max_duty()) & 0x7FFF; self.one_shot(); self.set_load_mode(LoadMode::Grouped); From 318500b3101105264940559cf8e93fac5dfdab80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 25 Aug 2020 21:39:31 +0200 Subject: [PATCH 12/12] Add docs about seq lifetime and set_duty behaviour --- nrf-hal-common/src/pwm.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/nrf-hal-common/src/pwm.rs b/nrf-hal-common/src/pwm.rs index cb4a0c4c..5e04d007 100644 --- a/nrf-hal-common/src/pwm.rs +++ b/nrf-hal-common/src/pwm.rs @@ -293,6 +293,7 @@ where } /// Sets duty cycle (15 bit) for all PWM channels. + /// Will replace any ongoing sequence playback. pub fn set_duty_on_common(&self, duty: u16) { compiler_fence(Ordering::SeqCst); self.duty @@ -308,7 +309,8 @@ where self.start_seq(Seq::Seq0); } - /// Sets inverted duty cycle (15 bit) for all PWM channels. + /// Sets inverted duty cycle (15 bit) for all PWM channels. + /// Will replace any ongoing sequence playback. pub fn set_duty_off_common(&self, duty: u16) { compiler_fence(Ordering::SeqCst); self.duty @@ -337,6 +339,7 @@ where } /// Sets duty cycle (15 bit) for a PWM group. + /// Will replace any ongoing sequence playback. pub fn set_duty_on_group(&self, group: Group, duty: u16) { compiler_fence(Ordering::SeqCst); self.duty.borrow_mut()[usize::from(group)] = duty.min(self.max_duty()) & 0x7FFF; @@ -351,6 +354,7 @@ where } /// Sets inverted duty cycle (15 bit) for a PWM group. + /// Will replace any ongoing sequence playback. pub fn set_duty_off_group(&self, group: Group, duty: u16) { compiler_fence(Ordering::SeqCst); self.duty.borrow_mut()[usize::from(group)] = duty.min(self.max_duty()) | 0x8000; @@ -376,7 +380,8 @@ where self.duty_off_value(usize::from(group)) } - /// Sets duty cycle (15 bit) for a PWM channel. + /// Sets duty cycle (15 bit) for a PWM channel. + /// Will replace any ongoing sequence playback and the other channels will return to their previously set value. pub fn set_duty_on(&self, channel: Channel, duty: u16) { compiler_fence(Ordering::SeqCst); self.duty.borrow_mut()[usize::from(channel)] = duty.min(self.max_duty()) & 0x7FFF; @@ -388,6 +393,7 @@ where } /// Sets inverted duty cycle (15 bit) for a PWM channel. + /// Will replace any ongoing sequence playback and the other channels will return to their previously set value. pub fn set_duty_off(&self, channel: Channel, duty: u16) { compiler_fence(Ordering::SeqCst); self.duty.borrow_mut()[usize::from(channel)] = duty.min(self.max_duty()) | 0x8000; @@ -466,7 +472,7 @@ where } /// Loads a sequence buffer. - /// NOTE: `buf` must live until the sequence is done playing. + /// NOTE: `buf` must live until the sequence is done playing, or it might play a corrupted sequence. pub fn load_seq(&self, seq: Seq, buf: &[u16]) -> Result<(), Error> { if (buf.as_ptr() as usize) < SRAM_LOWER || (buf.as_ptr() as usize) > SRAM_UPPER { return Err(Error::DMABufferNotInDataMemory);