Skip to content

Added support for using variable period PWM timers by updating ARR in stead of CCR. #4143

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

Open
wants to merge 3 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
33 changes: 32 additions & 1 deletion embassy-stm32/src/timer/low_level.rs
Original file line number Diff line number Diff line change
Expand Up @@ -585,10 +585,14 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
}

/// Set compare value for a channel.
/// This will panic if the value is too large for the timer.
pub fn set_compare_value(&self, channel: Channel, value: u32) {
match T::BITS {
TimerBits::Bits16 => {
let value = unwrap!(u16::try_from(value));
let value = match u16::try_from(value) {
Ok(v) => v,
Err(_) => panic!("Value to large for 16bit timer"),
};
self.regs_gp16().ccr(channel.index()).modify(|w| w.set_ccr(value));
}
#[cfg(not(stm32l0))]
Expand All @@ -607,6 +611,33 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
}
}

/// Set auto-reload value.
/// This will panic if the value is too large for the timer.
pub fn set_auto_reload_value(&self, value: u32) {
match T::BITS {
TimerBits::Bits16 => {
let value = match u16::try_from(value) {
Ok(v) => v,
Err(_) => panic!("Value to large for 16bit timer"),
};
self.regs_gp16().arr().modify(|w| w.set_arr(value));
}
#[cfg(not(stm32l0))]
TimerBits::Bits32 => {
self.regs_gp32_unchecked().arr().write_value(value);
}
}
}

/// Get auto-reload value.
pub fn get_auto_reload_value(&self) -> u32 {
match T::BITS {
TimerBits::Bits16 => self.regs_gp16().arr().read().arr() as u32,
#[cfg(not(stm32l0))]
TimerBits::Bits32 => self.regs_gp32_unchecked().arr().read(),
}
}

/// Get capture value for a channel.
pub fn get_capture_value(&self, channel: Channel) -> u32 {
self.get_compare_value(channel)
Expand Down
97 changes: 97 additions & 0 deletions embassy-stm32/src/timer/simple_pwm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,103 @@ impl_waveform_chx!(waveform_ch2, Ch2Dma, Ch2);
impl_waveform_chx!(waveform_ch3, Ch3Dma, Ch3);
impl_waveform_chx!(waveform_ch4, Ch4Dma, Ch4);

macro_rules! impl_waveform_variable_period_chx {
($fn_name:ident, $dma_ch:ident, $cc_ch:ident) => {
impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
/// Generate a sequence of PWM waveform
pub async fn $fn_name(&mut self, dma: Peri<'_, impl super::$dma_ch<T>>, duty: &[u32]) {
use crate::pac::timer::vals::Ccds;

#[allow(clippy::let_unit_value)] // eg. stm32f334
let req = dma.request();

let cc_channel = Channel::$cc_ch;

let original_auto_reload_value = self.inner.get_auto_reload_value();
let original_enable_state = self.channel(cc_channel).is_enabled();
let original_cc_dma_on_compare = self.inner.get_cc_dma_selection() == Ccds::ON_COMPARE;
let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel);

// redirect CC DMA request onto Compare Event
if !original_cc_dma_on_compare {
self.inner.set_cc_dma_selection(Ccds::ON_COMPARE)
}

if !original_cc_dma_enabled {
self.inner.set_cc_dma_enable_state(cc_channel, true);
}

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

unsafe {
#[cfg(not(any(bdma, gpdma)))]
use crate::dma::{Burst, FifoThreshold};
use crate::dma::{Transfer, TransferOptions};

let dma_transfer_option = TransferOptions {
#[cfg(not(any(bdma, gpdma)))]
fifo_threshold: Some(FifoThreshold::Full),
#[cfg(not(any(bdma, gpdma)))]
mburst: Burst::Single,
..Default::default()
};

match self.inner.bits() {
TimerBits::Bits16 => {
Transfer::new_write(
dma,
req,
duty,
self.inner.regs_gp16().arr().as_ptr() as *mut u16,
dma_transfer_option,
)
.await
}
#[cfg(not(any(stm32l0)))]
TimerBits::Bits32 => {
Transfer::new_write(
dma,
req,
duty,
self.inner.regs_gp16().arr().as_ptr() as *mut u32,
dma_transfer_option,
)
.await
}
};
};

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

self.inner.set_auto_reload_value(original_auto_reload_value);

// Since DMA is closed before timer Capture Compare Event trigger DMA is turn off,
// this can almost always trigger a DMA FIFO error.
//
// optional TODO:
// clean FEIF after disable UDE
if !original_cc_dma_enabled {
self.inner.set_cc_dma_enable_state(cc_channel, false);
}

if !original_cc_dma_on_compare {
self.inner.set_cc_dma_selection(Ccds::ON_UPDATE)
}
}
}
};
}

impl_waveform_variable_period_chx!(waveform_variable_period_ch1, Ch1Dma, Ch1);
impl_waveform_variable_period_chx!(waveform_variable_period_ch2, Ch2Dma, Ch2);
impl_waveform_variable_period_chx!(waveform_variable_period_ch3, Ch3Dma, Ch3);
impl_waveform_variable_period_chx!(waveform_variable_period_ch4, Ch4Dma, Ch4);

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