stm32/timer: simplify traits, convert from trait methods to struct.

This commit is contained in:
Dario Nieuwenhuis 2024-03-23 00:35:06 +01:00
parent c2aa95016a
commit 389cbc0a77
11 changed files with 885 additions and 1007 deletions

1
ci.sh
View File

@ -124,6 +124,7 @@ cargo batch \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h725re,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7b3ai,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7b3ai,defmt,exti,time-driver-tim1,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l431cb,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l422cb,defmt,exti,time-driver-any,time \

View File

@ -8,7 +8,7 @@ use critical_section::CriticalSection;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::blocking_mutex::Mutex;
use embassy_time_driver::{AlarmHandle, Driver, TICK_HZ};
use stm32_metapac::timer::regs;
use stm32_metapac::timer::{regs, TimGp16};
use crate::interrupt::typelevel::Interrupt;
use crate::pac::timer::vals;
@ -16,8 +16,8 @@ use crate::rcc::sealed::RccPeripheral;
#[cfg(feature = "low-power")]
use crate::rtc::Rtc;
#[cfg(any(time_driver_tim1, time_driver_tim8, time_driver_tim20))]
use crate::timer::sealed::AdvancedControlInstance;
use crate::timer::sealed::{CoreInstance, GeneralPurpose16bitInstance as Instance};
use crate::timer::AdvancedInstance1Channel;
use crate::timer::CoreInstance;
use crate::{interrupt, peripherals};
// NOTE regarding ALARM_COUNT:
@ -207,6 +207,10 @@ foreach_interrupt! {
};
}
fn regs_gp16() -> TimGp16 {
unsafe { TimGp16::from_ptr(T::regs()) }
}
// Clock timekeeping works with something we call "periods", which are time intervals
// of 2^15 ticks. The Clock counter value is 16 bits, so one "overflow cycle" is 2 periods.
//
@ -271,7 +275,7 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
impl RtcDriver {
fn init(&'static self, cs: critical_section::CriticalSection) {
let r = T::regs_gp16();
let r = regs_gp16();
<T as RccPeripheral>::enable_and_reset_with_cs(cs);
@ -308,9 +312,9 @@ impl RtcDriver {
#[cfg(any(time_driver_tim1, time_driver_tim8, time_driver_tim20))]
{
<T as AdvancedControlInstance>::CaptureCompareInterrupt::unpend();
<T as AdvancedInstance1Channel>::CaptureCompareInterrupt::unpend();
unsafe {
<T as AdvancedControlInstance>::CaptureCompareInterrupt::enable();
<T as AdvancedInstance1Channel>::CaptureCompareInterrupt::enable();
}
}
@ -318,7 +322,7 @@ impl RtcDriver {
}
fn on_interrupt(&self) {
let r = T::regs_gp16();
let r = regs_gp16();
// XXX: reduce the size of this critical section ?
critical_section::with(|cs| {
@ -349,7 +353,7 @@ impl RtcDriver {
}
fn next_period(&self) {
let r = T::regs_gp16();
let r = regs_gp16();
// We only modify the period from the timer interrupt, so we know this can't race.
let period = self.period.load(Ordering::Relaxed) + 1;
@ -413,7 +417,7 @@ impl RtcDriver {
/// Add the given offset to the current time
fn add_time(&self, offset: embassy_time::Duration, cs: CriticalSection) {
let offset = offset.as_ticks();
let cnt = T::regs_gp16().cnt().read().cnt() as u32;
let cnt = regs_gp16().cnt().read().cnt() as u32;
let period = self.period.load(Ordering::SeqCst);
// Correct the race, if it exists
@ -439,7 +443,7 @@ impl RtcDriver {
let period = if cnt > u16::MAX as u32 / 2 { period + 1 } else { period };
self.period.store(period, Ordering::SeqCst);
T::regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16));
regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16));
// Now, recompute all alarms
for i in 0..ALARM_COUNT {
@ -496,7 +500,7 @@ impl RtcDriver {
.unwrap()
.start_wakeup_alarm(time_until_next_alarm, cs);
T::regs_gp16().cr1().modify(|w| w.set_cen(false));
regs_gp16().cr1().modify(|w| w.set_cen(false));
Ok(())
}
@ -506,7 +510,7 @@ impl RtcDriver {
#[cfg(feature = "low-power")]
/// Resume the timer with the given offset
pub(crate) fn resume_time(&self) {
if T::regs_gp16().cr1().read().cen() {
if regs_gp16().cr1().read().cen() {
// Time isn't currently stopped
return;
@ -515,14 +519,14 @@ impl RtcDriver {
critical_section::with(|cs| {
self.stop_wakeup_alarm(cs);
T::regs_gp16().cr1().modify(|w| w.set_cen(true));
regs_gp16().cr1().modify(|w| w.set_cen(true));
})
}
}
impl Driver for RtcDriver {
fn now(&self) -> u64 {
let r = T::regs_gp16();
let r = regs_gp16();
let period = self.period.load(Ordering::Relaxed);
compiler_fence(Ordering::Acquire);
@ -553,7 +557,7 @@ impl Driver for RtcDriver {
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
critical_section::with(|cs| {
let r = T::regs_gp16();
let r = regs_gp16();
let n = alarm.id() as usize;
let alarm = self.get_alarm(cs, alarm);

View File

@ -5,11 +5,15 @@ use core::marker::PhantomData;
use embassy_hal_internal::{into_ref, PeripheralRef};
use stm32_metapac::timer::vals::Ckd;
use super::simple_pwm::*;
use super::*;
#[allow(unused_imports)]
use crate::gpio::sealed::{AFType, Pin};
use super::low_level::{CountingMode, OutputPolarity, Timer};
use super::simple_pwm::{Ch1, Ch2, Ch3, Ch4, PwmPin};
use super::{
AdvancedInstance4Channel, Channel, Channel1ComplementaryPin, Channel2ComplementaryPin, Channel3ComplementaryPin,
Channel4ComplementaryPin,
};
use crate::gpio::{AnyPin, OutputType};
use crate::time::Hertz;
use crate::timer::low_level::OutputCompareMode;
use crate::Peripheral;
/// Complementary PWM pin wrapper.
@ -22,7 +26,7 @@ pub struct ComplementaryPwmPin<'d, T, C> {
macro_rules! complementary_channel_impl {
($new_chx:ident, $channel:ident, $pin_trait:ident) => {
impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwmPin<'d, T, $channel> {
impl<'d, T: AdvancedInstance4Channel> ComplementaryPwmPin<'d, T, $channel> {
#[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")]
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, output_type: OutputType) -> Self {
into_ref!(pin);
@ -47,11 +51,11 @@ complementary_channel_impl!(new_ch3, Ch3, Channel3ComplementaryPin);
complementary_channel_impl!(new_ch4, Ch4, Channel4ComplementaryPin);
/// PWM driver with support for standard and complementary outputs.
pub struct ComplementaryPwm<'d, T> {
inner: PeripheralRef<'d, T>,
pub struct ComplementaryPwm<'d, T: AdvancedInstance4Channel> {
inner: Timer<'d, T>,
}
impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
/// Create a new complementary PWM driver.
#[allow(clippy::too_many_arguments)]
pub fn new(
@ -71,11 +75,7 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
}
fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, counting_mode: CountingMode) -> Self {
into_ref!(tim);
T::enable_and_reset();
let mut this = Self { inner: tim };
let mut this = Self { inner: Timer::new(tim) };
this.inner.set_counting_mode(counting_mode);
this.set_frequency(freq);
@ -122,7 +122,7 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
///
/// This value depends on the configured frequency and the timer's clock rate from RCC.
pub fn get_max_duty(&self) -> u16 {
self.inner.get_max_compare_value() + 1
self.inner.get_max_compare_value() as u16 + 1
}
/// Set the duty for a given channel.
@ -130,7 +130,7 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
/// 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: u16) {
assert!(duty <= self.get_max_duty());
self.inner.set_compare_value(channel, duty)
self.inner.set_compare_value(channel, duty as _)
}
/// Set the output polarity for a given channel.
@ -148,7 +148,7 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
}
}
impl<'d, T: ComplementaryCaptureCompare16bitInstance> embedded_hal_02::Pwm for ComplementaryPwm<'d, T> {
impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm<'d, T> {
type Channel = Channel;
type Time = Hertz;
type Duty = u16;
@ -168,16 +168,16 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> embedded_hal_02::Pwm for C
}
fn get_duty(&self, channel: Self::Channel) -> Self::Duty {
self.inner.get_compare_value(channel)
self.inner.get_compare_value(channel) as u16
}
fn get_max_duty(&self) -> Self::Duty {
self.inner.get_max_compare_value() + 1
self.inner.get_max_compare_value() as u16 + 1
}
fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) {
assert!(duty <= self.get_max_duty());
self.inner.set_compare_value(channel, duty)
self.inner.set_compare_value(channel, duty as u32)
}
fn set_period<P>(&mut self, period: P)

View File

@ -0,0 +1,638 @@
//! Low-level timer driver.
//!
//! This is an unopinionated, very low-level driver for all STM32 timers. It allows direct register
//! manipulation with the `regs_*()` methods, and has utility functions that are thin wrappers
//! over the registers.
//!
//! The available functionality depends on the timer type.
use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
use super::*;
use crate::pac::timer::vals;
use crate::time::Hertz;
/// Input capture mode.
#[derive(Clone, Copy)]
pub enum InputCaptureMode {
/// Rising edge only.
Rising,
/// Falling edge only.
Falling,
/// Both rising or falling edges.
BothEdges,
}
/// Input TI selection.
#[derive(Clone, Copy)]
pub enum InputTISelection {
/// Normal
Normal,
/// Alternate
Alternate,
/// TRC
TRC,
}
impl From<InputTISelection> for stm32_metapac::timer::vals::CcmrInputCcs {
fn from(tisel: InputTISelection) -> Self {
match tisel {
InputTISelection::Normal => stm32_metapac::timer::vals::CcmrInputCcs::TI4,
InputTISelection::Alternate => stm32_metapac::timer::vals::CcmrInputCcs::TI3,
InputTISelection::TRC => stm32_metapac::timer::vals::CcmrInputCcs::TRC,
}
}
}
/// Timer counting mode.
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum CountingMode {
#[default]
/// The timer counts up to the reload value and then resets back to 0.
EdgeAlignedUp,
/// The timer counts down to 0 and then resets back to the reload value.
EdgeAlignedDown,
/// The timer counts up to the reload value and then counts back to 0.
///
/// The output compare interrupt flags of channels configured in output are
/// set when the counter is counting down.
CenterAlignedDownInterrupts,
/// The timer counts up to the reload value and then counts back to 0.
///
/// The output compare interrupt flags of channels configured in output are
/// set when the counter is counting up.
CenterAlignedUpInterrupts,
/// The timer counts up to the reload value and then counts back to 0.
///
/// The output compare interrupt flags of channels configured in output are
/// set when the counter is counting both up or down.
CenterAlignedBothInterrupts,
}
impl CountingMode {
/// Return whether this mode is edge-aligned (up or down).
pub fn is_edge_aligned(&self) -> bool {
matches!(self, CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown)
}
/// Return whether this mode is center-aligned.
pub fn is_center_aligned(&self) -> bool {
matches!(
self,
CountingMode::CenterAlignedDownInterrupts
| CountingMode::CenterAlignedUpInterrupts
| CountingMode::CenterAlignedBothInterrupts
)
}
}
impl From<CountingMode> for (vals::Cms, vals::Dir) {
fn from(value: CountingMode) -> Self {
match value {
CountingMode::EdgeAlignedUp => (vals::Cms::EDGEALIGNED, vals::Dir::UP),
CountingMode::EdgeAlignedDown => (vals::Cms::EDGEALIGNED, vals::Dir::DOWN),
CountingMode::CenterAlignedDownInterrupts => (vals::Cms::CENTERALIGNED1, vals::Dir::UP),
CountingMode::CenterAlignedUpInterrupts => (vals::Cms::CENTERALIGNED2, vals::Dir::UP),
CountingMode::CenterAlignedBothInterrupts => (vals::Cms::CENTERALIGNED3, vals::Dir::UP),
}
}
}
impl From<(vals::Cms, vals::Dir)> for CountingMode {
fn from(value: (vals::Cms, vals::Dir)) -> Self {
match value {
(vals::Cms::EDGEALIGNED, vals::Dir::UP) => CountingMode::EdgeAlignedUp,
(vals::Cms::EDGEALIGNED, vals::Dir::DOWN) => CountingMode::EdgeAlignedDown,
(vals::Cms::CENTERALIGNED1, _) => CountingMode::CenterAlignedDownInterrupts,
(vals::Cms::CENTERALIGNED2, _) => CountingMode::CenterAlignedUpInterrupts,
(vals::Cms::CENTERALIGNED3, _) => CountingMode::CenterAlignedBothInterrupts,
}
}
}
/// Output compare mode.
#[derive(Clone, Copy)]
pub enum OutputCompareMode {
/// The comparison between the output compare register TIMx_CCRx and
/// the counter TIMx_CNT has no effect on the outputs.
/// (this mode is used to generate a timing base).
Frozen,
/// Set channel to active level on match. OCxREF signal is forced high when the
/// counter TIMx_CNT matches the capture/compare register x (TIMx_CCRx).
ActiveOnMatch,
/// Set channel to inactive level on match. OCxREF signal is forced low when the
/// counter TIMx_CNT matches the capture/compare register x (TIMx_CCRx).
InactiveOnMatch,
/// Toggle - OCxREF toggles when TIMx_CNT=TIMx_CCRx.
Toggle,
/// Force inactive level - OCxREF is forced low.
ForceInactive,
/// Force active level - OCxREF is forced high.
ForceActive,
/// PWM mode 1 - In upcounting, channel is active as long as TIMx_CNT<TIMx_CCRx
/// else inactive. In downcounting, channel is inactive (OCxREF=0) as long as
/// TIMx_CNT>TIMx_CCRx else active (OCxREF=1).
PwmMode1,
/// PWM mode 2 - In upcounting, channel is inactive as long as
/// TIMx_CNT<TIMx_CCRx else active. In downcounting, channel is active as long as
/// TIMx_CNT>TIMx_CCRx else inactive.
PwmMode2,
// TODO: there's more modes here depending on the chip family.
}
impl From<OutputCompareMode> for stm32_metapac::timer::vals::Ocm {
fn from(mode: OutputCompareMode) -> Self {
match mode {
OutputCompareMode::Frozen => stm32_metapac::timer::vals::Ocm::FROZEN,
OutputCompareMode::ActiveOnMatch => stm32_metapac::timer::vals::Ocm::ACTIVEONMATCH,
OutputCompareMode::InactiveOnMatch => stm32_metapac::timer::vals::Ocm::INACTIVEONMATCH,
OutputCompareMode::Toggle => stm32_metapac::timer::vals::Ocm::TOGGLE,
OutputCompareMode::ForceInactive => stm32_metapac::timer::vals::Ocm::FORCEINACTIVE,
OutputCompareMode::ForceActive => stm32_metapac::timer::vals::Ocm::FORCEACTIVE,
OutputCompareMode::PwmMode1 => stm32_metapac::timer::vals::Ocm::PWMMODE1,
OutputCompareMode::PwmMode2 => stm32_metapac::timer::vals::Ocm::PWMMODE2,
}
}
}
/// Timer output pin polarity.
#[derive(Clone, Copy)]
pub enum OutputPolarity {
/// Active high (higher duty value makes the pin spend more time high).
ActiveHigh,
/// Active low (higher duty value makes the pin spend more time low).
ActiveLow,
}
impl From<OutputPolarity> for bool {
fn from(mode: OutputPolarity) -> Self {
match mode {
OutputPolarity::ActiveHigh => false,
OutputPolarity::ActiveLow => true,
}
}
}
/// Low-level timer driver.
pub struct Timer<'d, T: CoreInstance> {
tim: PeripheralRef<'d, T>,
}
impl<'d, T: CoreInstance> Drop for Timer<'d, T> {
fn drop(&mut self) {
T::disable()
}
}
impl<'d, T: CoreInstance> Timer<'d, T> {
/// Create a new timer driver.
pub fn new(tim: impl Peripheral<P = T> + 'd) -> Self {
into_ref!(tim);
T::enable_and_reset();
Self { tim }
}
/// Get access to the virutal core 16bit timer registers.
///
/// Note: This works even if the timer is more capable, because registers
/// for the less capable timers are a subset. This allows writing a driver
/// for a given set of capabilities, and having it transparently work with
/// more capable timers.
pub fn regs_core(&self) -> crate::pac::timer::TimCore {
unsafe { crate::pac::timer::TimCore::from_ptr(T::regs()) }
}
#[cfg(not(stm32l0))]
fn regs_gp32_unchecked(&self) -> crate::pac::timer::TimGp32 {
unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) }
}
/// Start the timer.
pub fn start(&self) {
self.regs_core().cr1().modify(|r| r.set_cen(true));
}
/// Stop the timer.
pub fn stop(&self) {
self.regs_core().cr1().modify(|r| r.set_cen(false));
}
/// Reset the counter value to 0
pub fn reset(&self) {
self.regs_core().cnt().write(|r| r.set_cnt(0));
}
/// Set the frequency of how many times per second the timer counts up to the max value or down to 0.
///
/// This means that in the default edge-aligned mode,
/// the timer counter will wrap around at the same frequency as is being set.
/// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved
/// because it needs to count up and down.
pub fn set_frequency(&self, frequency: Hertz) {
let f = frequency.0;
assert!(f > 0);
let timer_f = T::frequency().0;
match T::BITS {
TimerBits::Bits16 => {
let pclk_ticks_per_timer_period = timer_f / f;
let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 16)).try_into());
let divide_by = pclk_ticks_per_timer_period / (u32::from(psc) + 1);
// the timer counts `0..=arr`, we want it to count `0..divide_by`
let arr = unwrap!(u16::try_from(divide_by - 1));
let regs = self.regs_core();
regs.psc().write_value(psc);
regs.arr().write(|r| r.set_arr(arr));
regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY));
regs.egr().write(|r| r.set_ug(true));
regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
}
#[cfg(not(stm32l0))]
TimerBits::Bits32 => {
let pclk_ticks_per_timer_period = (timer_f / f) as u64;
let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 32)).try_into());
let arr: u32 = unwrap!((pclk_ticks_per_timer_period / (psc as u64 + 1)).try_into());
let regs = self.regs_gp32_unchecked();
regs.psc().write_value(psc);
regs.arr().write_value(arr);
regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY));
regs.egr().write(|r| r.set_ug(true));
regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
}
}
}
/// Clear update interrupt.
///
/// Returns whether the update interrupt flag was set.
pub fn clear_update_interrupt(&self) -> bool {
let regs = self.regs_core();
let sr = regs.sr().read();
if sr.uif() {
regs.sr().modify(|r| {
r.set_uif(false);
});
true
} else {
false
}
}
/// Enable/disable the update interrupt.
pub fn enable_update_interrupt(&self, enable: bool) {
self.regs_core().dier().modify(|r| r.set_uie(enable));
}
/// Enable/disable autoreload preload.
pub fn set_autoreload_preload(&self, enable: bool) {
self.regs_core().cr1().modify(|r| r.set_arpe(enable));
}
/// Get the timer frequency.
pub fn get_frequency(&self) -> Hertz {
let timer_f = T::frequency();
match T::BITS {
TimerBits::Bits16 => {
let regs = self.regs_core();
let arr = regs.arr().read().arr();
let psc = regs.psc().read();
timer_f / arr / (psc + 1)
}
#[cfg(not(stm32l0))]
TimerBits::Bits32 => {
let regs = self.regs_gp32_unchecked();
let arr = regs.arr().read();
let psc = regs.psc().read();
timer_f / arr / (psc + 1)
}
}
}
}
impl<'d, T: BasicNoCr2Instance> Timer<'d, T> {
/// Get access to the Baisc 16bit timer registers.
///
/// Note: This works even if the timer is more capable, because registers
/// for the less capable timers are a subset. This allows writing a driver
/// for a given set of capabilities, and having it transparently work with
/// more capable timers.
pub fn regs_basic_no_cr2(&self) -> crate::pac::timer::TimBasicNoCr2 {
unsafe { crate::pac::timer::TimBasicNoCr2::from_ptr(T::regs()) }
}
/// Enable/disable the update dma.
pub fn enable_update_dma(&self, enable: bool) {
self.regs_basic_no_cr2().dier().modify(|r| r.set_ude(enable));
}
/// Get the update dma enable/disable state.
pub fn get_update_dma_state(&self) -> bool {
self.regs_basic_no_cr2().dier().read().ude()
}
}
impl<'d, T: BasicInstance> Timer<'d, T> {
/// Get access to the Baisc 16bit timer registers.
///
/// Note: This works even if the timer is more capable, because registers
/// for the less capable timers are a subset. This allows writing a driver
/// for a given set of capabilities, and having it transparently work with
/// more capable timers.
pub fn regs_basic(&self) -> crate::pac::timer::TimBasic {
unsafe { crate::pac::timer::TimBasic::from_ptr(T::regs()) }
}
}
impl<'d, T: GeneralInstance1Channel> Timer<'d, T> {
/// Get access to the general purpose 1 channel 16bit timer registers.
///
/// Note: This works even if the timer is more capable, because registers
/// for the less capable timers are a subset. This allows writing a driver
/// for a given set of capabilities, and having it transparently work with
/// more capable timers.
pub fn regs_1ch(&self) -> crate::pac::timer::Tim1ch {
unsafe { crate::pac::timer::Tim1ch::from_ptr(T::regs()) }
}
/// Set clock divider.
pub fn set_clock_division(&self, ckd: vals::Ckd) {
self.regs_1ch().cr1().modify(|r| r.set_ckd(ckd));
}
/// Get max compare value. This depends on the timer frequency and the clock frequency from RCC.
pub fn get_max_compare_value(&self) -> u32 {
match T::BITS {
TimerBits::Bits16 => self.regs_1ch().arr().read().arr() as u32,
#[cfg(not(stm32l0))]
TimerBits::Bits32 => self.regs_gp32_unchecked().arr().read(),
}
}
}
impl<'d, T: GeneralInstance2Channel> Timer<'d, T> {
/// Get access to the general purpose 2 channel 16bit timer registers.
///
/// Note: This works even if the timer is more capable, because registers
/// for the less capable timers are a subset. This allows writing a driver
/// for a given set of capabilities, and having it transparently work with
/// more capable timers.
pub fn regs_2ch(&self) -> crate::pac::timer::Tim2ch {
unsafe { crate::pac::timer::Tim2ch::from_ptr(T::regs()) }
}
}
impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
/// Get access to the general purpose 16bit timer registers.
///
/// Note: This works even if the timer is more capable, because registers
/// for the less capable timers are a subset. This allows writing a driver
/// for a given set of capabilities, and having it transparently work with
/// more capable timers.
pub fn regs_gp16(&self) -> crate::pac::timer::TimGp16 {
unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }
}
/// Enable timer outputs.
pub fn enable_outputs(&self) {
self.tim.enable_outputs()
}
/// Set counting mode.
pub fn set_counting_mode(&self, mode: CountingMode) {
let (cms, dir) = mode.into();
let timer_enabled = self.regs_core().cr1().read().cen();
// Changing from edge aligned to center aligned (and vice versa) is not allowed while the timer is running.
// Changing direction is discouraged while the timer is running.
assert!(!timer_enabled);
self.regs_gp16().cr1().modify(|r| r.set_dir(dir));
self.regs_gp16().cr1().modify(|r| r.set_cms(cms))
}
/// Get counting mode.
pub fn get_counting_mode(&self) -> CountingMode {
let cr1 = self.regs_gp16().cr1().read();
(cr1.cms(), cr1.dir()).into()
}
/// Set input capture filter.
pub fn set_input_capture_filter(&self, channel: Channel, icf: vals::FilterValue) {
let raw_channel = channel.index();
self.regs_gp16()
.ccmr_input(raw_channel / 2)
.modify(|r| r.set_icf(raw_channel % 2, icf));
}
/// Clear input interrupt.
pub fn clear_input_interrupt(&self, channel: Channel) {
self.regs_gp16().sr().modify(|r| r.set_ccif(channel.index(), false));
}
/// Enable input interrupt.
pub fn enable_input_interrupt(&self, channel: Channel, enable: bool) {
self.regs_gp16().dier().modify(|r| r.set_ccie(channel.index(), enable));
}
/// Set input capture prescaler.
pub fn set_input_capture_prescaler(&self, channel: Channel, factor: u8) {
let raw_channel = channel.index();
self.regs_gp16()
.ccmr_input(raw_channel / 2)
.modify(|r| r.set_icpsc(raw_channel % 2, factor));
}
/// Set input TI selection.
pub fn set_input_ti_selection(&self, channel: Channel, tisel: InputTISelection) {
let raw_channel = channel.index();
self.regs_gp16()
.ccmr_input(raw_channel / 2)
.modify(|r| r.set_ccs(raw_channel % 2, tisel.into()));
}
/// Set input capture mode.
pub fn set_input_capture_mode(&self, channel: Channel, mode: InputCaptureMode) {
self.regs_gp16().ccer().modify(|r| match mode {
InputCaptureMode::Rising => {
r.set_ccnp(channel.index(), false);
r.set_ccp(channel.index(), false);
}
InputCaptureMode::Falling => {
r.set_ccnp(channel.index(), false);
r.set_ccp(channel.index(), true);
}
InputCaptureMode::BothEdges => {
r.set_ccnp(channel.index(), true);
r.set_ccp(channel.index(), true);
}
});
}
/// Set output compare mode.
pub fn set_output_compare_mode(&self, channel: Channel, mode: OutputCompareMode) {
let raw_channel: usize = channel.index();
self.regs_gp16()
.ccmr_output(raw_channel / 2)
.modify(|w| w.set_ocm(raw_channel % 2, mode.into()));
}
/// Set output polarity.
pub fn set_output_polarity(&self, channel: Channel, polarity: OutputPolarity) {
self.regs_gp16()
.ccer()
.modify(|w| w.set_ccp(channel.index(), polarity.into()));
}
/// Enable/disable a channel.
pub fn enable_channel(&self, channel: Channel, enable: bool) {
self.regs_gp16().ccer().modify(|w| w.set_cce(channel.index(), enable));
}
/// Get enable/disable state of a channel
pub fn get_channel_enable_state(&self, channel: Channel) -> bool {
self.regs_gp16().ccer().read().cce(channel.index())
}
/// Set compare value for a channel.
pub fn set_compare_value(&self, channel: Channel, value: u32) {
match T::BITS {
TimerBits::Bits16 => {
let value = unwrap!(u16::try_from(value));
self.regs_gp16().ccr(channel.index()).modify(|w| w.set_ccr(value));
}
#[cfg(not(stm32l0))]
TimerBits::Bits32 => {
self.regs_gp32_unchecked().ccr(channel.index()).write_value(value);
}
}
}
/// Get compare value for a channel.
pub fn get_compare_value(&self, channel: Channel) -> u32 {
match T::BITS {
TimerBits::Bits16 => self.regs_gp16().ccr(channel.index()).read().ccr() as u32,
#[cfg(not(stm32l0))]
TimerBits::Bits32 => self.regs_gp32_unchecked().ccr(channel.index()).read(),
}
}
/// Get capture value for a channel.
pub fn get_capture_value(&self, channel: Channel) -> u32 {
self.get_compare_value(channel)
}
/// Set output compare preload.
pub fn set_output_compare_preload(&self, channel: Channel, preload: bool) {
let channel_index = channel.index();
self.regs_gp16()
.ccmr_output(channel_index / 2)
.modify(|w| w.set_ocpe(channel_index % 2, preload));
}
/// Get capture compare DMA selection
pub fn get_cc_dma_selection(&self) -> vals::Ccds {
self.regs_gp16().cr2().read().ccds()
}
/// Set capture compare DMA selection
pub fn set_cc_dma_selection(&self, ccds: vals::Ccds) {
self.regs_gp16().cr2().modify(|w| w.set_ccds(ccds))
}
/// Get capture compare DMA enable state
pub fn get_cc_dma_enable_state(&self, channel: Channel) -> bool {
self.regs_gp16().dier().read().ccde(channel.index())
}
/// Set capture compare DMA enable state
pub fn set_cc_dma_enable_state(&self, channel: Channel, ccde: bool) {
self.regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde))
}
}
#[cfg(not(stm32l0))]
impl<'d, T: GeneralInstance32bit4Channel> Timer<'d, T> {
/// Get access to the general purpose 32bit timer registers.
///
/// Note: This works even if the timer is more capable, because registers
/// for the less capable timers are a subset. This allows writing a driver
/// for a given set of capabilities, and having it transparently work with
/// more capable timers.
pub fn regs_gp32(&self) -> crate::pac::timer::TimGp32 {
unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) }
}
}
#[cfg(not(stm32l0))]
impl<'d, T: AdvancedInstance1Channel> Timer<'d, T> {
/// Get access to the general purpose 1 channel with one complementary 16bit timer registers.
///
/// Note: This works even if the timer is more capable, because registers
/// for the less capable timers are a subset. This allows writing a driver
/// for a given set of capabilities, and having it transparently work with
/// more capable timers.
pub fn regs_1ch_cmp(&self) -> crate::pac::timer::Tim1chCmp {
unsafe { crate::pac::timer::Tim1chCmp::from_ptr(T::regs()) }
}
/// Set clock divider for the dead time.
pub fn set_dead_time_clock_division(&self, value: vals::Ckd) {
self.regs_1ch_cmp().cr1().modify(|w| w.set_ckd(value));
}
/// Set dead time, as a fraction of the max duty value.
pub fn set_dead_time_value(&self, value: u8) {
self.regs_1ch_cmp().bdtr().modify(|w| w.set_dtg(value));
}
/// Set state of MOE-bit in BDTR register to en-/disable output
pub fn set_moe(&self, enable: bool) {
self.regs_1ch_cmp().bdtr().modify(|w| w.set_moe(enable));
}
}
#[cfg(not(stm32l0))]
impl<'d, T: AdvancedInstance2Channel> Timer<'d, T> {
/// Get access to the general purpose 2 channel with one complementary 16bit timer registers.
///
/// Note: This works even if the timer is more capable, because registers
/// for the less capable timers are a subset. This allows writing a driver
/// for a given set of capabilities, and having it transparently work with
/// more capable timers.
pub fn regs_2ch_cmp(&self) -> crate::pac::timer::Tim2chCmp {
unsafe { crate::pac::timer::Tim2chCmp::from_ptr(T::regs()) }
}
}
#[cfg(not(stm32l0))]
impl<'d, T: AdvancedInstance4Channel> Timer<'d, T> {
/// Get access to the advanced timer registers.
pub fn regs_advanced(&self) -> crate::pac::timer::TimAdv {
unsafe { crate::pac::timer::TimAdv::from_ptr(T::regs()) }
}
/// Set complementary output polarity.
pub fn set_complementary_output_polarity(&self, channel: Channel, polarity: OutputPolarity) {
self.regs_advanced()
.ccer()
.modify(|w| w.set_ccnp(channel.index(), polarity.into()));
}
/// Enable/disable a complementary channel.
pub fn enable_complementary_channel(&self, channel: Channel, enable: bool) {
self.regs_advanced()
.ccer()
.modify(|w| w.set_ccne(channel.index(), enable));
}
}

File diff suppressed because it is too large Load Diff

View File

@ -3,8 +3,10 @@
use core::marker::PhantomData;
use embassy_hal_internal::{into_ref, PeripheralRef};
use stm32_metapac::timer::vals;
use super::*;
use super::low_level::Timer;
use super::{Channel1Pin, Channel2Pin, GeneralInstance4Channel};
use crate::gpio::sealed::AFType;
use crate::gpio::AnyPin;
use crate::Peripheral;
@ -30,7 +32,7 @@ pub struct QeiPin<'d, T, Channel> {
macro_rules! channel_impl {
($new_chx:ident, $channel:ident, $pin_trait:ident) => {
impl<'d, T: CaptureCompare16bitInstance> QeiPin<'d, T, $channel> {
impl<'d, T: GeneralInstance4Channel> QeiPin<'d, T, $channel> {
#[doc = concat!("Create a new ", stringify!($channel), " QEI pin instance.")]
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd) -> Self {
into_ref!(pin);
@ -53,29 +55,28 @@ channel_impl!(new_ch1, Ch1, Channel1Pin);
channel_impl!(new_ch2, Ch2, Channel2Pin);
/// Quadrature decoder driver.
pub struct Qei<'d, T> {
_inner: PeripheralRef<'d, T>,
pub struct Qei<'d, T: GeneralInstance4Channel> {
inner: Timer<'d, T>,
}
impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> {
impl<'d, T: GeneralInstance4Channel> Qei<'d, T> {
/// Create a new quadrature decoder driver.
pub fn new(tim: impl Peripheral<P = T> + 'd, _ch1: QeiPin<'d, T, Ch1>, _ch2: QeiPin<'d, T, Ch2>) -> Self {
Self::new_inner(tim)
}
fn new_inner(tim: impl Peripheral<P = T> + 'd) -> Self {
into_ref!(tim);
T::enable_and_reset();
let inner = Timer::new(tim);
let r = inner.regs_gp16();
// Configure TxC1 and TxC2 as captures
T::regs_gp16().ccmr_input(0).modify(|w| {
r.ccmr_input(0).modify(|w| {
w.set_ccs(0, vals::CcmrInputCcs::TI4);
w.set_ccs(1, vals::CcmrInputCcs::TI4);
});
// enable and configure to capture on rising edge
T::regs_gp16().ccer().modify(|w| {
r.ccer().modify(|w| {
w.set_cce(0, true);
w.set_cce(1, true);
@ -83,19 +84,19 @@ impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> {
w.set_ccp(1, false);
});
T::regs_gp16().smcr().modify(|w| {
r.smcr().modify(|w| {
w.set_sms(vals::Sms::ENCODER_MODE_3);
});
T::regs_gp16().arr().modify(|w| w.set_arr(u16::MAX));
T::regs_gp16().cr1().modify(|w| w.set_cen(true));
r.arr().modify(|w| w.set_arr(u16::MAX));
r.cr1().modify(|w| w.set_cen(true));
Self { _inner: tim }
Self { inner }
}
/// Get direction.
pub fn read_direction(&self) -> Direction {
match T::regs_gp16().cr1().read().dir() {
match self.inner.regs_gp16().cr1().read().dir() {
vals::Dir::DOWN => Direction::Downcounting,
vals::Dir::UP => Direction::Upcounting,
}
@ -103,6 +104,6 @@ impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> {
/// Get count.
pub fn count(&self) -> u16 {
T::regs_gp16().cnt().read().cnt()
self.inner.regs_gp16().cnt().read().cnt()
}
}

View File

@ -4,10 +4,10 @@ use core::marker::PhantomData;
use embassy_hal_internal::{into_ref, PeripheralRef};
use super::*;
#[allow(unused_imports)]
use crate::gpio::sealed::{AFType, Pin};
use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer};
use super::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance4Channel};
use crate::gpio::{AnyPin, OutputType};
use crate::time::Hertz;
use crate::Peripheral;
/// Channel 1 marker type.
@ -29,7 +29,7 @@ pub struct PwmPin<'d, T, C> {
macro_rules! channel_impl {
($new_chx:ident, $channel:ident, $pin_trait:ident) => {
impl<'d, T: CaptureCompare16bitInstance> PwmPin<'d, T, $channel> {
impl<'d, T: GeneralInstance4Channel> PwmPin<'d, T, $channel> {
#[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")]
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, output_type: OutputType) -> Self {
into_ref!(pin);
@ -54,11 +54,11 @@ channel_impl!(new_ch3, Ch3, Channel3Pin);
channel_impl!(new_ch4, Ch4, Channel4Pin);
/// Simple PWM driver.
pub struct SimplePwm<'d, T> {
inner: PeripheralRef<'d, T>,
pub struct SimplePwm<'d, T: GeneralInstance4Channel> {
inner: Timer<'d, T>,
}
impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
/// Create a new simple PWM driver.
pub fn new(
tim: impl Peripheral<P = T> + 'd,
@ -73,15 +73,11 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
}
fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, counting_mode: CountingMode) -> Self {
into_ref!(tim);
T::enable_and_reset();
let mut this = Self { inner: tim };
let mut this = Self { inner: Timer::new(tim) };
this.inner.set_counting_mode(counting_mode);
this.set_frequency(freq);
this.inner.enable_outputs(); // Required for advanced timers, see CaptureCompare16bitInstance for details
this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details
this.inner.start();
[Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
@ -126,14 +122,14 @@ impl<'d, T: CaptureCompare16bitInstance> 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) -> u16 {
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: u16) {
pub fn set_duty(&mut self, channel: Channel, duty: u32) {
assert!(duty <= self.get_max_duty());
self.inner.set_compare_value(channel, duty)
}
@ -141,7 +137,7 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
/// 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) -> u16 {
pub fn get_duty(&self, channel: Channel) -> u32 {
self.inner.get_compare_value(channel)
}
@ -165,8 +161,6 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
channel: Channel,
duty: &[u16],
) {
assert!(duty.iter().all(|v| *v <= self.get_max_duty()));
into_ref!(dma);
#[allow(clippy::let_unit_value)] // eg. stm32f334
@ -201,7 +195,7 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
&mut dma,
req,
duty,
T::regs_1ch().ccr(channel.index()).as_ptr() as *mut _,
self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut _,
dma_transfer_option,
)
.await
@ -227,22 +221,20 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
macro_rules! impl_waveform_chx {
($fn_name:ident, $dma_ch:ident, $cc_ch:ident) => {
impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
/// Generate a sequence of PWM waveform
///
/// Note:
/// you will need to provide corresponding TIMx_CHy DMA channel to use this method.
pub async fn $fn_name(&mut self, dma: impl Peripheral<P = impl super::$dma_ch<T>>, duty: &[u16]) {
use super::vals::Ccds;
assert!(duty.iter().all(|v| *v <= self.get_max_duty()));
use crate::pac::timer::vals::Ccds;
into_ref!(dma);
#[allow(clippy::let_unit_value)] // eg. stm32f334
let req = dma.request();
let cc_channel = super::Channel::$cc_ch;
let cc_channel = Channel::$cc_ch;
let original_duty_state = self.get_duty(cc_channel);
let original_enable_state = self.is_enabled(cc_channel);
@ -279,7 +271,7 @@ macro_rules! impl_waveform_chx {
&mut dma,
req,
duty,
T::regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut _,
self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut _,
dma_transfer_option,
)
.await
@ -314,10 +306,10 @@ impl_waveform_chx!(waveform_ch2, Ch2Dma, Ch2);
impl_waveform_chx!(waveform_ch3, Ch3Dma, Ch3);
impl_waveform_chx!(waveform_ch4, Ch4Dma, Ch4);
impl<'d, T: CaptureCompare16bitInstance> embedded_hal_02::Pwm for SimplePwm<'d, T> {
impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> {
type Channel = Channel;
type Time = Hertz;
type Duty = u16;
type Duty = u32;
fn disable(&mut self, channel: Self::Channel) {
self.inner.enable_channel(channel, false);

View File

@ -15,8 +15,9 @@
use embassy_executor::Spawner;
use embassy_stm32::gpio::OutputType;
use embassy_stm32::time::khz;
use embassy_stm32::timer::low_level::CountingMode;
use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
use embassy_stm32::timer::{Channel, CountingMode};
use embassy_stm32::timer::Channel;
use embassy_time::{Duration, Ticker, Timer};
use {defmt_rtt as _, panic_probe as _};
@ -60,7 +61,7 @@ async fn main(_spawner: Spawner) {
// construct ws2812 non-return-to-zero (NRZ) code bit by bit
// ws2812 only need 24 bits for each LED, but we add one bit more to keep PWM output low
let max_duty = ws2812_pwm.get_max_duty();
let max_duty = ws2812_pwm.get_max_duty() as u16;
let n0 = 8 * max_duty / 25; // ws2812 Bit 0 high level timing
let n1 = 2 * n0; // ws2812 Bit 1 high level timing

View File

@ -8,7 +8,7 @@ use embassy_stm32::pac::timer::vals::Mms;
use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7};
use embassy_stm32::rcc::low_level::RccPeripheral;
use embassy_stm32::time::Hertz;
use embassy_stm32::timer::low_level::BasicInstance;
use embassy_stm32::timer::low_level::Timer;
use micromath::F32Ext;
use {defmt_rtt as _, panic_probe as _};
@ -51,12 +51,12 @@ async fn main(spawner: Spawner) {
// Obtain two independent channels (p.DAC1 can only be consumed once, though!)
let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split();
spawner.spawn(dac_task1(dac_ch1)).ok();
spawner.spawn(dac_task2(dac_ch2)).ok();
spawner.spawn(dac_task1(p.TIM6, dac_ch1)).ok();
spawner.spawn(dac_task2(p.TIM7, dac_ch2)).ok();
}
#[embassy_executor::task]
async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
async fn dac_task1(tim: TIM6, mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
let data: &[u8; 256] = &calculate_array::<256>();
info!("TIM6 frequency is {}", TIM6::frequency());
@ -74,10 +74,10 @@ async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
dac.set_triggering(true);
dac.enable();
TIM6::enable_and_reset();
TIM6::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
TIM6::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
TIM6::regs_basic().cr1().modify(|w| {
let tim = Timer::new(tim);
tim.regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
tim.regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
tim.regs_basic().cr1().modify(|w| {
w.set_opm(false);
w.set_cen(true);
});
@ -99,7 +99,7 @@ async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
}
#[embassy_executor::task]
async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
async fn dac_task2(tim: TIM7, mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
let data: &[u8; 256] = &calculate_array::<256>();
info!("TIM7 frequency is {}", TIM7::frequency());
@ -111,10 +111,10 @@ async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
error!("Reload value {} below threshold!", reload);
}
TIM7::enable_and_reset();
TIM7::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
TIM7::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
TIM7::regs_basic().cr1().modify(|w| {
let tim = Timer::new(tim);
tim.regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
tim.regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
tim.regs_basic().cr1().modify(|w| {
w.set_opm(false);
w.set_cen(true);
});

View File

@ -6,8 +6,9 @@ use embassy_executor::Spawner;
use embassy_stm32::gpio::low_level::AFType;
use embassy_stm32::gpio::Speed;
use embassy_stm32::time::{khz, Hertz};
use embassy_stm32::timer::*;
use embassy_stm32::{into_ref, Config, Peripheral, PeripheralRef};
use embassy_stm32::timer::low_level::{OutputCompareMode, Timer as LLTimer};
use embassy_stm32::timer::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance32bit4Channel};
use embassy_stm32::{into_ref, Config, Peripheral};
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
@ -56,11 +57,11 @@ async fn main(_spawner: Spawner) {
Timer::after_millis(300).await;
}
}
pub struct SimplePwm32<'d, T: CaptureCompare32bitInstance> {
inner: PeripheralRef<'d, T>,
pub struct SimplePwm32<'d, T: GeneralInstance32bit4Channel> {
tim: LLTimer<'d, T>,
}
impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> {
impl<'d, T: GeneralInstance32bit4Channel> SimplePwm32<'d, T> {
pub fn new(
tim: impl Peripheral<P = T> + 'd,
ch1: impl Peripheral<P = impl Channel1Pin<T>> + 'd,
@ -69,9 +70,7 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> {
ch4: impl Peripheral<P = impl Channel4Pin<T>> + 'd,
freq: Hertz,
) -> Self {
into_ref!(tim, ch1, ch2, ch3, ch4);
T::enable_and_reset();
into_ref!(ch1, ch2, ch3, ch4);
ch1.set_speed(Speed::VeryHigh);
ch1.set_as_af(ch1.af_num(), AFType::OutputPushPull);
@ -82,12 +81,12 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> {
ch4.set_speed(Speed::VeryHigh);
ch4.set_as_af(ch1.af_num(), AFType::OutputPushPull);
let mut this = Self { inner: tim };
let mut this = Self { tim: LLTimer::new(tim) };
this.set_frequency(freq);
this.inner.start();
this.tim.start();
let r = T::regs_gp32();
let r = this.tim.regs_gp32();
r.ccmr_output(0)
.modify(|w| w.set_ocm(0, OutputCompareMode::PwmMode1.into()));
r.ccmr_output(0)
@ -101,23 +100,26 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> {
}
pub fn enable(&mut self, channel: Channel) {
T::regs_gp32().ccer().modify(|w| w.set_cce(channel.index(), true));
self.tim.regs_gp32().ccer().modify(|w| w.set_cce(channel.index(), true));
}
pub fn disable(&mut self, channel: Channel) {
T::regs_gp32().ccer().modify(|w| w.set_cce(channel.index(), false));
self.tim
.regs_gp32()
.ccer()
.modify(|w| w.set_cce(channel.index(), false));
}
pub fn set_frequency(&mut self, freq: Hertz) {
<T as embassy_stm32::timer::low_level::GeneralPurpose32bitInstance>::set_frequency(&mut self.inner, freq);
self.tim.set_frequency(freq);
}
pub fn get_max_duty(&self) -> u32 {
T::regs_gp32().arr().read()
self.tim.regs_gp32().arr().read()
}
pub fn set_duty(&mut self, channel: Channel, duty: u32) {
defmt::assert!(duty < self.get_max_duty());
T::regs_gp32().ccr(channel.index()).write_value(duty)
self.tim.regs_gp32().ccr(channel.index()).write_value(duty)
}
}

View File

@ -8,7 +8,7 @@ use embassy_stm32::pac::timer::vals::Mms;
use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7};
use embassy_stm32::rcc::low_level::RccPeripheral;
use embassy_stm32::time::Hertz;
use embassy_stm32::timer::low_level::BasicInstance;
use embassy_stm32::timer::low_level::Timer;
use micromath::F32Ext;
use {defmt_rtt as _, panic_probe as _};
@ -22,12 +22,12 @@ async fn main(spawner: Spawner) {
// Obtain two independent channels (p.DAC1 can only be consumed once, though!)
let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split();
spawner.spawn(dac_task1(dac_ch1)).ok();
spawner.spawn(dac_task2(dac_ch2)).ok();
spawner.spawn(dac_task1(p.TIM6, dac_ch1)).ok();
spawner.spawn(dac_task2(p.TIM7, dac_ch2)).ok();
}
#[embassy_executor::task]
async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
async fn dac_task1(tim: TIM6, mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
let data: &[u8; 256] = &calculate_array::<256>();
info!("TIM6 frequency is {}", TIM6::frequency());
@ -45,10 +45,10 @@ async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
dac.set_triggering(true);
dac.enable();
TIM6::enable_and_reset();
TIM6::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
TIM6::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
TIM6::regs_basic().cr1().modify(|w| {
let tim = Timer::new(tim);
tim.regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
tim.regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
tim.regs_basic().cr1().modify(|w| {
w.set_opm(false);
w.set_cen(true);
});
@ -70,7 +70,7 @@ async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
}
#[embassy_executor::task]
async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
async fn dac_task2(tim: TIM7, mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
let data: &[u8; 256] = &calculate_array::<256>();
info!("TIM7 frequency is {}", TIM7::frequency());
@ -82,10 +82,10 @@ async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
error!("Reload value {} below threshold!", reload);
}
TIM7::enable_and_reset();
TIM7::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
TIM7::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
TIM7::regs_basic().cr1().modify(|w| {
let tim = Timer::new(tim);
tim.regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
tim.regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
tim.regs_basic().cr1().modify(|w| {
w.set_opm(false);
w.set_cen(true);
});