mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-25 08:12:30 +00:00
stm32/timer: simplify traits, convert from trait methods to struct.
This commit is contained in:
parent
c2aa95016a
commit
389cbc0a77
1
ci.sh
1
ci.sh
@ -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 \
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
638
embassy-stm32/src/timer/low_level.rs
Normal file
638
embassy-stm32/src/timer/low_level.rs
Normal 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
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user