Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

embassy-stm32: Add SimplePwmChannel #3317

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions embassy-stm32/src/timer/low_level.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
//!
//! The available functionality depends on the timer type.

use core::mem::ManuallyDrop;

use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
// Re-export useful enums
pub use stm32_metapac::timer::vals::{FilterValue, Sms as SlaveMode, Ts as TriggerSource};
Expand Down Expand Up @@ -198,6 +200,13 @@ impl<'d, T: CoreInstance> Timer<'d, T> {
Self { tim }
}

pub(crate) unsafe fn clone_unchecked(&self) -> ManuallyDrop<Self> {
// this doesn't work for some reason
// let tim = unsafe { self.tim.clone_unchecked() };
let tim = todo!();
ManuallyDrop::new(Self { tim })
}

/// Get access to the virutal core 16bit timer registers.
///
/// Note: This works even if the timer is more capable, because registers
Expand Down
1 change: 1 addition & 0 deletions embassy-stm32/src/timer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use core::marker::PhantomData;

use embassy_hal_internal::Peripheral;
use embassy_sync::waitqueue::AtomicWaker;

#[cfg(not(stm32l0))]
Expand Down
270 changes: 223 additions & 47 deletions embassy-stm32/src/timer/simple_pwm.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Simple PWM driver.

use core::marker::PhantomData;
use core::mem::ManuallyDrop;

use embassy_hal_internal::{into_ref, PeripheralRef};

Expand Down Expand Up @@ -51,6 +52,111 @@ channel_impl!(new_ch2, Ch2, Channel2Pin);
channel_impl!(new_ch3, Ch3, Channel3Pin);
channel_impl!(new_ch4, Ch4, Channel4Pin);

/// A single channel of a pwm, obtained from [`SimplePwm::split`],
/// [`SimplePwm::channel`], [`SimplePwm::ch1`], etc.
///
/// It is not possible to change the pwm frequency because
/// the frequency configuration is shared with all four channels.
pub struct SimplePwmChannel<'d, T: GeneralInstance4Channel> {
timer: ManuallyDrop<Timer<'d, T>>,
channel: Channel,
}

// TODO: check for RMW races
impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> {
/// Enable the given channel.
pub fn enable(&mut self) {
self.timer.enable_channel(self.channel, true);
}

/// Disable the given channel.
pub fn disable(&mut self) {
self.timer.enable_channel(self.channel, false);
}

/// Check whether given channel is enabled
pub fn is_enabled(&self) -> bool {
self.timer.get_channel_enable_state(self.channel)
}

/// Get max duty value.
///
/// This value depends on the configured frequency and the timer's clock rate from RCC.
pub fn max_duty_cycle(&self) -> u16 {
let max = self.timer.get_max_compare_value();
assert!(max < u16::MAX as u32);
max as u16 + 1
}

/// Set the duty for a given channel.
///
/// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included.
pub fn set_duty_cycle(&mut self, duty: u16) {
assert!(duty <= (*self).max_duty_cycle());
self.timer.set_compare_value(self.channel, duty.into())
}

/// Set the duty cycle to 0%, or always inactive.
pub fn set_duty_cycle_fully_off(&mut self) {
self.set_duty_cycle(0);
}

/// Set the duty cycle to 100%, or always active.
pub fn set_duty_cycle_fully_on(&mut self) {
self.set_duty_cycle((*self).max_duty_cycle());
}

/// Set the duty cycle to `num / denom`.
///
/// The caller is responsible for ensuring that `num` is less than or equal to `denom`,
/// and that `denom` is not zero.
pub fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) {
assert!(denom != 0);
assert!(num <= denom);
let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom);

// This is safe because we know that `num <= denom`, so `duty <= self.max_duty_cycle()` (u16)
#[allow(clippy::cast_possible_truncation)]
self.set_duty_cycle(duty as u16);
}

/// Set the duty cycle to `percent / 100`
///
/// The caller is responsible for ensuring that `percent` is less than or equal to 100.
pub fn set_duty_cycle_percent(&mut self, percent: u8) {
self.set_duty_cycle_fraction(u16::from(percent), 100)
}

/// Get the duty for a given channel.
///
/// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included.
pub fn current_duty_cycle(&self) -> u16 {
unwrap!(self.timer.get_compare_value(self.channel).try_into())
}

/// Set the output polarity for a given channel.
pub fn set_polarity(&mut self, polarity: OutputPolarity) {
self.timer.set_output_polarity(self.channel, polarity);
}

/// Set the output compare mode for a given channel.
pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) {
self.timer.set_output_compare_mode(self.channel, mode);
}
}

/// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`].
pub struct SimplePwmChannels<'d, T: GeneralInstance4Channel> {
/// Channel 1
pub ch1: SimplePwmChannel<'d, T>,
/// Channel 2
pub ch2: SimplePwmChannel<'d, T>,
/// Channel 3
pub ch3: SimplePwmChannel<'d, T>,
/// Channel 4
pub ch4: SimplePwmChannel<'d, T>,
}

/// Simple PWM driver.
pub struct SimplePwm<'d, T: GeneralInstance4Channel> {
inner: Timer<'d, T>,
Expand Down Expand Up @@ -89,26 +195,84 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
this
}

/// Enable the given channel.
pub fn enable(&mut self, channel: Channel) {
self.inner.enable_channel(channel, true);
/// Get a single channel
///
/// If you need to use multiple channels, use [`Self::split`].
pub fn channel(&mut self, channel: Channel) -> SimplePwmChannel<'_, T> {
SimplePwmChannel {
timer: unsafe { self.inner.clone_unchecked() },
channel,
}
}

/// Disable the given channel.
pub fn disable(&mut self, channel: Channel) {
self.inner.enable_channel(channel, false);
/// Channel 1
///
/// This is just a convenience wrapper around [`Self::channel`].
///
/// If you need to use multiple channels, use [`Self::split`].
pub fn ch1(&mut self) -> SimplePwmChannel<'_, T> {
self.channel(Channel::Ch1)
}

/// Check whether given channel is enabled
pub fn is_enabled(&self, channel: Channel) -> bool {
self.inner.get_channel_enable_state(channel)
/// Channel 2
///
/// This is just a convenience wrapper around [`Self::channel`].
///
/// If you need to use multiple channels, use [`Self::split`].
pub fn ch2(&mut self) -> SimplePwmChannel<'_, T> {
self.channel(Channel::Ch2)
}

/// Channel 3
///
/// This is just a convenience wrapper around [`Self::channel`].
///
/// If you need to use multiple channels, use [`Self::split`].
pub fn ch3(&mut self) -> SimplePwmChannel<'_, T> {
self.channel(Channel::Ch3)
}

/// Channel 4
///
/// This is just a convenience wrapper around [`Self::channel`].
///
/// If you need to use multiple channels, use [`Self::split`].
pub fn ch4(&mut self) -> SimplePwmChannel<'_, T> {
self.channel(Channel::Ch4)
}

/// Splits a [`SimplePwm`] into four pwm channels.
///
/// This returns all four channels, including channels that
/// aren't configured with a [`PwmPin`].
// TODO: I hate the name "split"
pub fn split(self) -> SimplePwmChannels<'static, T>
where
// must be static because the timer will never be dropped/disabled
'd: 'static,
{
// without this, the timer would be disabled at the end of this function
let timer = ManuallyDrop::new(self.inner);

let ch = |channel| SimplePwmChannel {
timer: unsafe { timer.clone_unchecked() },
channel,
};

SimplePwmChannels {
ch1: ch(Channel::Ch1),
ch2: ch(Channel::Ch2),
ch3: ch(Channel::Ch3),
ch4: ch(Channel::Ch4),
}
}

/// Set PWM frequency.
///
/// Note: when you call this, the max duty value changes, so you will have to
/// call `set_duty` on all channels with the duty calculated based on the new max duty.
pub fn set_frequency(&mut self, freq: Hertz) {
// TODO: prevent ARR = u16::MAX?
let multiplier = if self.inner.get_counting_mode().is_center_aligned() {
2u8
} else {
Expand All @@ -120,33 +284,10 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
/// Get max duty value.
///
/// This value depends on the configured frequency and the timer's clock rate from RCC.
pub fn get_max_duty(&self) -> u32 {
self.inner.get_max_compare_value() + 1
}

/// Set the duty for a given channel.
///
/// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included.
pub fn set_duty(&mut self, channel: Channel, duty: u32) {
assert!(duty <= self.get_max_duty());
self.inner.set_compare_value(channel, duty)
}

/// Get the duty for a given channel.
///
/// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included.
pub fn get_duty(&self, channel: Channel) -> u32 {
self.inner.get_compare_value(channel)
}

/// Set the output polarity for a given channel.
pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
self.inner.set_output_polarity(channel, polarity);
}

/// Set the output compare mode for a given channel.
pub fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode) {
self.inner.set_output_compare_mode(channel, mode);
pub fn max_duty_cycle(&self) -> u16 {
let max = self.inner.get_max_compare_value();
assert!(max < u16::MAX as u32);
max as u16 + 1
}

/// Generate a sequence of PWM waveform
Expand All @@ -164,16 +305,16 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
#[allow(clippy::let_unit_value)] // eg. stm32f334
let req = dma.request();

let original_duty_state = self.get_duty(channel);
let original_enable_state = self.is_enabled(channel);
let original_duty_state = self.channel(channel).current_duty_cycle();
let original_enable_state = self.channel(channel).is_enabled();
let original_update_dma_state = self.inner.get_update_dma_state();

if !original_update_dma_state {
self.inner.enable_update_dma(true);
}

if !original_enable_state {
self.enable(channel);
self.channel(channel).enable();
}

unsafe {
Expand Down Expand Up @@ -201,10 +342,10 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {

// restore output compare state
if !original_enable_state {
self.disable(channel);
self.channel(channel).disable();
}

self.set_duty(channel, original_duty_state);
self.channel(channel).set_duty_cycle(original_duty_state);

// Since DMA is closed before timer update event trigger DMA is turn off,
// this can almost always trigger a DMA FIFO error.
Expand Down Expand Up @@ -234,8 +375,8 @@ macro_rules! impl_waveform_chx {

let cc_channel = Channel::$cc_ch;

let original_duty_state = self.get_duty(cc_channel);
let original_enable_state = self.is_enabled(cc_channel);
let original_duty_state = self.channel(cc_channel).current_duty_cycle();
let original_enable_state = self.channel(cc_channel).is_enabled();
let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ONUPDATE;
let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel);

Expand All @@ -249,7 +390,7 @@ macro_rules! impl_waveform_chx {
}

if !original_enable_state {
self.enable(cc_channel);
self.channel(cc_channel).enable();
}

unsafe {
Expand Down Expand Up @@ -277,10 +418,10 @@ macro_rules! impl_waveform_chx {

// restore output compare state
if !original_enable_state {
self.disable(cc_channel);
self.channel(cc_channel).disable();
}

self.set_duty(cc_channel, original_duty_state);
self.channel(cc_channel).set_duty_cycle(original_duty_state);

// Since DMA is closed before timer Capture Compare Event trigger DMA is turn off,
// this can almost always trigger a DMA FIFO error.
Expand All @@ -304,6 +445,41 @@ impl_waveform_chx!(waveform_ch2, Ch2Dma, Ch2);
impl_waveform_chx!(waveform_ch3, Ch3Dma, Ch3);
impl_waveform_chx!(waveform_ch4, Ch4Dma, Ch4);

impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> {
type Error = core::convert::Infallible;
}

impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> {
fn max_duty_cycle(&self) -> u16 {
self.max_duty_cycle()
}

fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
self.set_duty_cycle(duty);
Ok(())
}

fn set_duty_cycle_fully_off(&mut self) -> Result<(), Self::Error> {
self.set_duty_cycle_fully_off();
Ok(())
}

fn set_duty_cycle_fully_on(&mut self) -> Result<(), Self::Error> {
self.set_duty_cycle_fully_on();
Ok(())
}

fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> {
self.set_duty_cycle_fraction(num, denom);
Ok(())
}

fn set_duty_cycle_percent(&mut self, percent: u8) -> Result<(), Self::Error> {
self.set_duty_cycle_percent(percent);
Ok(())
}
}

impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> {
type Channel = Channel;
type Time = Hertz;
Expand All @@ -330,7 +506,7 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> {
}

fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) {
assert!(duty <= self.get_max_duty());
assert!(duty <= self.max_duty_cycle() as u32);
self.inner.set_compare_value(channel, duty)
}

Expand Down
Loading