Skip to content

Commit 7ffe7bf

Browse files
authored
lptim: add interface to set the PWM frequency
1 parent 67de71a commit 7ffe7bf

File tree

1 file changed

+72
-5
lines changed

1 file changed

+72
-5
lines changed

hal/src/lptim/mod.rs

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ use crate::{
4141
},
4242
pac, Ratio,
4343
};
44+
use core::cmp::min;
4445
use paste::paste;
4546
use void::Void;
4647

@@ -104,6 +105,7 @@ pub(crate) mod sealed {
104105
fn set_cr(&mut self, cr: Cr);
105106
fn cmp(&self) -> u16;
106107
fn set_cmp(&mut self, cmp: u16);
108+
fn autoreload(&self) -> u16;
107109
fn set_autoreload(&mut self, ar: u16);
108110
unsafe fn cnt() -> u16;
109111
fn set_or(&mut self, or: u32);
@@ -177,6 +179,11 @@ macro_rules! impl_lptim_base_for {
177179
self.cmp.write(|w| w.cmp().bits(cmp));
178180
}
179181

182+
#[inline(always)]
183+
fn autoreload(&self) -> u16 {
184+
self.arr.read().arr().bits()
185+
}
186+
180187
#[inline(always)]
181188
fn set_autoreload(&mut self, ar: u16) {
182189
self.arr.write(|w| w.arr().bits(ar));
@@ -497,6 +504,68 @@ pub trait LpTim: sealed::LpTim {
497504
.set_trg_filter(filter)
498505
})
499506
}
507+
508+
/// Set the maximum duty cycle (autoreload value).
509+
///
510+
/// This is used to control the frequency of the PWM output.
511+
///
512+
/// This function does not poll for completion, use [`isr`](Self::isr)
513+
/// and [`ARROK`](irq::ARROK) to determine when the value has been updated.
514+
///
515+
/// # Example
516+
///
517+
/// Set the frequency to 50Hz (20ms period) for servo motor control.
518+
///
519+
/// ```no_run
520+
/// use stm32wlxx_hal::{
521+
/// gpio,
522+
/// lptim::{self, LpTim, LpTim1},
523+
/// pac,
524+
/// };
525+
///
526+
/// let mut dp: pac::Peripherals = pac::Peripherals::take().unwrap();
527+
///
528+
/// // enable the HSI16 source clock
529+
/// dp.RCC.cr.modify(|_, w| w.hsion().set_bit());
530+
/// while dp.RCC.cr.read().hsirdy().is_not_ready() {}
531+
///
532+
/// let pc: gpio::PortC = gpio::PortC::split(dp.GPIOC, &mut dp.RCC);
533+
///
534+
/// let mut lptim1: LpTim1 = LpTim1::new(
535+
/// dp.LPTIM1,
536+
/// lptim::Clk::Hsi16,
537+
/// lptim::Prescaler::Div8,
538+
/// &mut dp.RCC,
539+
/// );
540+
/// cortex_m::interrupt::free(|cs| lptim1.new_output_pin(pc.c1, cs));
541+
///
542+
/// // 20ms period to control PWM servo motors
543+
/// const SERVO_FREQ_HZ: u32 = 50;
544+
///
545+
/// // source_freq / prescaler = 16_000_000 MHz / 8 = 2 Mhz
546+
/// let lptim1_freq_hz = lptim1.hz().to_integer();
547+
///
548+
/// // 2MHz / 50Hz = 40_000
549+
/// let max_duty: u32 = lptim1_freq_hz / SERVO_FREQ_HZ;
550+
///
551+
/// lptim1.set_max_duty(max_duty.try_into().unwrap());
552+
/// ```
553+
#[inline]
554+
fn set_max_duty(&mut self, duty: u16) {
555+
if !self.is_enabled() {
556+
const CR: Cr = Cr::RESET.enable();
557+
self.as_mut_tim().set_cr(CR);
558+
559+
// RM0461 Rev 4 "Timer enable":
560+
// After setting the ENABLE bit, a delay of two counter
561+
// clock is needed before the LPTIM is actually enabled.
562+
const MAX_SYS_FREQ: u32 = 48_000_000;
563+
let delay: u32 = (MAX_SYS_FREQ * 2) / self.hz().to_integer();
564+
cortex_m::asm::delay(delay);
565+
}
566+
567+
self.as_mut_tim().set_autoreload(duty);
568+
}
500569
}
501570

502571
impl LpTim for LpTim1 {
@@ -712,7 +781,7 @@ macro_rules! impl_eh_pwmpin_for {
712781
}
713782

714783
fn get_max_duty(&self) -> Self::Duty {
715-
u16::MAX
784+
self.as_tim().autoreload()
716785
}
717786

718787
fn set_duty(&mut self, duty: Self::Duty) {
@@ -729,10 +798,8 @@ macro_rules! impl_eh_pwmpin_for {
729798
}
730799

731800
// can only be modified when enabled
732-
self.as_mut_tim().set_autoreload(u16::MAX);
733-
self.as_mut_tim().set_cmp(duty);
734-
while Self::isr() & (irq::ARROK | irq::CMPOK) == 0 {}
735-
unsafe { self.set_icr(irq::ARROK | irq::CMPOK) };
801+
let max_duty: u16 = self.get_max_duty();
802+
self.as_mut_tim().set_cmp(min(max_duty, duty));
736803
}
737804
}
738805
};

0 commit comments

Comments
 (0)