Skip to content

Commit

Permalink
wip: PWM API
Browse files Browse the repository at this point in the history
  • Loading branch information
cylewitruk committed Apr 27, 2024
1 parent 11997eb commit d2493e3
Show file tree
Hide file tree
Showing 4 changed files with 271 additions and 143 deletions.
64 changes: 31 additions & 33 deletions embassy-rp/src/pwm/builder.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use core::marker::PhantomData;

use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
use embedded_hal_1::pwm::ErrorKind;
use rp_pac::pwm::vals::Divmode;

use crate::{
Expand All @@ -11,7 +12,7 @@ use crate::{
};

use super::{
v2::{EdgeSensitivity, Frequency, PwmFreeRunningSlice, PwmInputOutputSlice},
v2::{Channel, EdgeSensitivity, Frequency, PwmError, PwmFreeRunningSlice, PwmInputOutputSlice},
ChannelAPin, ChannelBPin, InputMode, Slice,
};

Expand Down Expand Up @@ -216,7 +217,6 @@ pub struct PwmFreeRunningSliceBuilder<'a, T: Slice> {
inner: PeripheralRef<'a, T>,
builder_a: Option<PwmFreeRunningChannelBuilder<'a, T>>,
builder_b: Option<PwmFreeRunningChannelBuilder<'a, T>>,
frequency: Frequency,
frequency_hz: u32,
phase_correct: bool,
}
Expand All @@ -227,7 +227,6 @@ impl<'a, T: Slice> PwmFreeRunningSliceBuilder<'a, T> {
inner: slice,
builder_a: None,
builder_b: None,
frequency: Frequency::Hz(clk_sys_freq()),
frequency_hz: clk_sys_freq(),
phase_correct: false,
}
Expand All @@ -248,7 +247,7 @@ impl<'a, T: Slice> PwmFreeRunningSliceBuilder<'a, T> {
panic!("Frequency must be less than the system clock frequency");
}

self.frequency = freq;
self.frequency_hz = freq_hz;
self
}

Expand Down Expand Up @@ -307,53 +306,52 @@ impl<'a, T: Slice> PwmFreeRunningSliceBuilder<'a, T> {
/// Note that this will not enable the slice, only configure it. You must
/// call [`PwmInputOutputSlice::enable`], or alternatively use the
/// [`enable_pwm_slices`] function, to start the slice.
pub fn apply(mut self) -> PwmFreeRunningSlice<'a, T> {
pub fn apply(mut self) -> Result<PwmFreeRunningSlice<'a, T>, PwmError> {
// Require that at least one of A or B is configured.
if self.builder_a.is_none() && self.builder_b.is_none() {
panic!("At least one channel must be configured");
}

// Get an instance of the registers for this slice.
let regs = self.inner.regs();

let slice: PwmFreeRunningSlice<'_, T> = PwmFreeRunningSlice::new(
self.inner,
self.frequency_hz,
self.phase_correct,
self.builder_a.as_ref().map(|a| a.duty_percent),
self.builder_b.as_ref().map(|b| b.duty_percent),
self.builder_a.take().map(|b| b.pin),
self.builder_b.take().map(|b| b.pin),
);
// Grab our pins.
let pin_a = self.builder_a.take();
let pin_b = self.builder_b.take();

// If channel A is configured, set the pin function to PWM.
if let Some(pin) = &slice.pin_a {
pin.gpio().ctrl().write(|w| w.set_funcsel(4));
if let Some(a) = &pin_a {
a.pin.gpio().ctrl().write(|w| w.set_funcsel(4));
regs.csr().modify(|w| w.set_a_inv(a.invert));
}

// If channel B is configured, set the pin function to PWM.
if let Some(pin) = &slice.pin_b {
pin.gpio().ctrl().write(|w| w.set_funcsel(4));
if let Some(b) = &pin_b {
b.pin.gpio().ctrl().write(|w| w.set_funcsel(4));
regs.csr().modify(|w| w.set_b_inv(b.invert));
}

// Set the control and status register values.
// (CH0_CSR..CH7_CSR).
regs.csr().write_set(|w| {
// TODO: Calculate div/top for slice
let pin_a_duty = pin_a.as_ref().map(|a| a.duty_percent);
let pin_b_duty = pin_b.as_ref().map(|b| b.duty_percent);

if let Some(a) = self.builder_a {
w.set_a_inv(a.invert);
// Create our PWM slice instance.
let mut slice: PwmFreeRunningSlice<'_, T> = PwmFreeRunningSlice::new(
self.inner,
pin_a.map(|b| b.pin),
pin_b.map(|b| b.pin),
);

// TODO: Calculate compare value for A
}
if let Some(b) = self.builder_b {
w.set_b_inv(b.invert);
// If channel A is configured, configure PWM for the A pin.
if let Some(duty) = pin_a_duty {
slice.reconfigure(Channel::A, self.frequency_hz, duty, self.phase_correct)?;
}

// TODO: Calculate compare value for B
}
w.set_ph_correct(self.phase_correct);
});
// If channel B is configured, configure PWM for the B pin.
if let Some(duty) = pin_b_duty {
slice.reconfigure(Channel::B, self.frequency_hz, duty, self.phase_correct)?;
}

slice
Ok(slice)
}
}

Expand Down
26 changes: 13 additions & 13 deletions embassy-rp/src/pwm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::clocks::clk_sys_freq;
use crate::gpio::{AnyPin, Pin as GpioPin, Pull, SealedPin as _};
use crate::{pac, peripherals, RegExt};

use self::v2::Channel;
use self::v2::{Channel, PwmError};

pub mod builder;
pub mod v2;
Expand Down Expand Up @@ -254,14 +254,14 @@ impl<'d, T: Slice> Pwm<'d, T> {
///
/// TODO: Add support for fractional hz
#[inline]
pub fn set_freq(&mut self, freq: u32) -> Result<(), ErrorKind> {
pub fn set_freq(&mut self, freq: u32) -> Result<(), PwmError> {
let clock_hz = clk_sys_freq() / 1_000_000;
let min_hz = clock_hz / 125;
let max_hz = clock_hz / 2;

// The frequency must be between 8 and the clock frequency
if freq < MIN_PWM_FREQ as u32 || freq > max_hz as u32 {
return Err(ErrorKind::Other);
return Err(PwmError::Other(ErrorKind::Other));
}

// Only perform recalculations if the frequency has changed
Expand Down Expand Up @@ -292,9 +292,9 @@ impl<'d, T: Slice> Pwm<'d, T> {
/// Set the PWM duty cycle for channel A. Returns an error if the slice
/// does not have a channel A configured.
#[inline]
pub fn set_duty_a(&mut self, duty: u16) -> Result<(), ErrorKind> {
pub fn set_duty_a(&mut self, duty: u16) -> Result<(), PwmError> {
if self.pin_a.is_none() {
return Err(ErrorKind::Other);
return Err(PwmError::Other(ErrorKind::Other));
}

self.recalculate_duty(Channel::A, duty)?;
Expand All @@ -305,9 +305,9 @@ impl<'d, T: Slice> Pwm<'d, T> {
/// Set the PWM duty cycle for channel B. Returns an error if the slice
/// does not have a channel B configured.
#[inline]
pub fn set_duty_b(&mut self, duty: u16) -> Result<(), ErrorKind> {
pub fn set_duty_b(&mut self, duty: u16) -> Result<(), PwmError> {
if self.pin_b.is_none() {
return Err(ErrorKind::Other);
return Err(PwmError::Other(ErrorKind::Other));
}

self.recalculate_duty(Channel::B, duty)?;
Expand All @@ -318,9 +318,9 @@ impl<'d, T: Slice> Pwm<'d, T> {
/// Set the PWM duty cycle for both channels A and B. Returns an error if
/// the slice does not have both A & B channels configured.
#[inline]
pub fn set_duty_ab(&mut self, duty: u16) -> Result<(), ErrorKind> {
pub fn set_duty_ab(&mut self, duty: u16) -> Result<(), PwmError> {
if self.pin_a.is_none() || self.pin_b.is_none() {
return Err(ErrorKind::Other);
return Err(PwmError::Other(ErrorKind::Other));
}

self.set_duty_a(duty)?;
Expand Down Expand Up @@ -401,7 +401,7 @@ impl<'d, T: Slice> Pwm<'d, T> {

/// Recalculates the TOP and DIV values and updates the PWM slice registers.
#[inline]
fn recalculate_div_wrap(&self, freq: u32) -> Result<(), ErrorKind> {
fn recalculate_div_wrap(&self, freq: u32) -> Result<(), PwmError> {
let mut clk_divider = 0;
let mut wrap = 0;
let mut clock_div;
Expand All @@ -425,14 +425,14 @@ impl<'d, T: Slice> Pwm<'d, T> {
self.inner.regs().top().write(|w| w.set_top(wrap as u16));
Ok(())
} else {
Err(ErrorKind::Other)
Err(PwmError::Other(ErrorKind::Other))
}
}

/// Recalculates the duty cycle for a channel and updates the PWM slice CC
/// register with the new values for both A and B channels.
#[inline]
fn recalculate_duty(&self, channel: Channel, duty: u16) -> Result<(), ErrorKind> {
fn recalculate_duty(&self, channel: Channel, duty: u16) -> Result<(), PwmError> {
// Get the current `DIV` register value for this slice (only the
// integer part is used)
let wrap = self.inner.regs().div().read().int();
Expand All @@ -448,7 +448,7 @@ impl<'d, T: Slice> Pwm<'d, T> {
} else {
// We shouldn't have been able to get here if the `Pwm`
// constructors are doing their job.
return Err(ErrorKind::Other);
return Err(PwmError::Other(ErrorKind::Other));
}
Ok(())
})
Expand Down

0 comments on commit d2493e3

Please sign in to comment.