stm32/timer: docs.

This commit is contained in:
Dario Nieuwenhuis 2023-12-19 17:35:38 +01:00
parent 41c3c26beb
commit 189b15c426
8 changed files with 247 additions and 61 deletions

View File

@ -68,22 +68,22 @@ mod sealed {
pub trait AdvancedChannel<T: Instance>: sealed::AdvancedChannel<T> {} pub trait AdvancedChannel<T: Instance>: sealed::AdvancedChannel<T> {}
/// HRTIM PWM pin. /// HRTIM PWM pin.
pub struct PwmPin<'d, Perip, Channel> { pub struct PwmPin<'d, T, C> {
_pin: PeripheralRef<'d, AnyPin>, _pin: PeripheralRef<'d, AnyPin>,
phantom: PhantomData<(Perip, Channel)>, phantom: PhantomData<(T, C)>,
} }
/// HRTIM complementary PWM pin. /// HRTIM complementary PWM pin.
pub struct ComplementaryPwmPin<'d, Perip, Channel> { pub struct ComplementaryPwmPin<'d, T, C> {
_pin: PeripheralRef<'d, AnyPin>, _pin: PeripheralRef<'d, AnyPin>,
phantom: PhantomData<(Perip, Channel)>, phantom: PhantomData<(T, C)>,
} }
macro_rules! advanced_channel_impl { macro_rules! advanced_channel_impl {
($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => {
impl<'d, Perip: Instance> PwmPin<'d, Perip, $channel<Perip>> { impl<'d, T: Instance> PwmPin<'d, T, $channel<T>> {
#[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")]
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<Perip>> + 'd) -> Self { pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd) -> Self {
into_ref!(pin); into_ref!(pin);
critical_section::with(|_| { critical_section::with(|_| {
pin.set_low(); pin.set_low();
@ -98,9 +98,9 @@ macro_rules! advanced_channel_impl {
} }
} }
impl<'d, Perip: Instance> ComplementaryPwmPin<'d, Perip, $channel<Perip>> { impl<'d, T: Instance> ComplementaryPwmPin<'d, T, $channel<T>> {
#[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")] #[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")]
pub fn $new_chx(pin: impl Peripheral<P = impl $complementary_pin_trait<Perip>> + 'd) -> Self { pub fn $new_chx(pin: impl Peripheral<P = impl $complementary_pin_trait<T>> + 'd) -> Self {
into_ref!(pin); into_ref!(pin);
critical_section::with(|_| { critical_section::with(|_| {
pin.set_low(); pin.set_low();

View File

@ -79,6 +79,7 @@ pub(crate) mod _generated {
#![allow(dead_code)] #![allow(dead_code)]
#![allow(unused_imports)] #![allow(unused_imports)]
#![allow(non_snake_case)] #![allow(non_snake_case)]
#![allow(missing_docs)]
include!(concat!(env!("OUT_DIR"), "/_generated.rs")); include!(concat!(env!("OUT_DIR"), "/_generated.rs"));
} }
@ -149,15 +150,33 @@ use crate::interrupt::Priority;
pub use crate::pac::NVIC_PRIO_BITS; pub use crate::pac::NVIC_PRIO_BITS;
use crate::rcc::sealed::RccPeripheral; use crate::rcc::sealed::RccPeripheral;
/// `embassy-stm32` global configuration.
#[non_exhaustive] #[non_exhaustive]
pub struct Config { pub struct Config {
/// RCC config.
pub rcc: rcc::Config, pub rcc: rcc::Config,
/// Enable debug during sleep.
///
/// May incrase power consumption. Defaults to true.
#[cfg(dbgmcu)] #[cfg(dbgmcu)]
pub enable_debug_during_sleep: bool, pub enable_debug_during_sleep: bool,
/// BDMA interrupt priority.
///
/// Defaults to P0 (highest).
#[cfg(bdma)] #[cfg(bdma)]
pub bdma_interrupt_priority: Priority, pub bdma_interrupt_priority: Priority,
/// DMA interrupt priority.
///
/// Defaults to P0 (highest).
#[cfg(dma)] #[cfg(dma)]
pub dma_interrupt_priority: Priority, pub dma_interrupt_priority: Priority,
/// GPDMA interrupt priority.
///
/// Defaults to P0 (highest).
#[cfg(gpdma)] #[cfg(gpdma)]
pub gpdma_interrupt_priority: Priority, pub gpdma_interrupt_priority: Priority,
} }
@ -178,7 +197,11 @@ impl Default for Config {
} }
} }
/// Initialize embassy. /// Initialize the `embassy-stm32` HAL with the provided configuration.
///
/// This returns the peripheral singletons that can be used for creating drivers.
///
/// This should only be called once at startup, otherwise it panics.
pub fn init(config: Config) -> Peripherals { pub fn init(config: Config) -> Peripherals {
critical_section::with(|cs| { critical_section::with(|cs| {
let p = Peripherals::take_with_cs(cs); let p = Peripherals::take_with_cs(cs);

View File

@ -13,15 +13,19 @@ use crate::gpio::{AnyPin, OutputType};
use crate::time::Hertz; use crate::time::Hertz;
use crate::Peripheral; use crate::Peripheral;
pub struct ComplementaryPwmPin<'d, Perip, Channel> { /// Complementary PWM pin wrapper.
///
/// This wraps a pin to make it usable with PWM.
pub struct ComplementaryPwmPin<'d, T, C> {
_pin: PeripheralRef<'d, AnyPin>, _pin: PeripheralRef<'d, AnyPin>,
phantom: PhantomData<(Perip, Channel)>, phantom: PhantomData<(T, C)>,
} }
macro_rules! complementary_channel_impl { macro_rules! complementary_channel_impl {
($new_chx:ident, $channel:ident, $pin_trait:ident) => { ($new_chx:ident, $channel:ident, $pin_trait:ident) => {
impl<'d, Perip: CaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { impl<'d, T: CaptureCompare16bitInstance> ComplementaryPwmPin<'d, T, $channel> {
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<Perip>> + 'd, output_type: OutputType) -> Self { #[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); into_ref!(pin);
critical_section::with(|_| { critical_section::with(|_| {
pin.set_low(); pin.set_low();
@ -43,11 +47,13 @@ complementary_channel_impl!(new_ch2, Ch2, Channel2ComplementaryPin);
complementary_channel_impl!(new_ch3, Ch3, Channel3ComplementaryPin); complementary_channel_impl!(new_ch3, Ch3, Channel3ComplementaryPin);
complementary_channel_impl!(new_ch4, Ch4, Channel4ComplementaryPin); complementary_channel_impl!(new_ch4, Ch4, Channel4ComplementaryPin);
/// PWM driver with support for standard and complementary outputs.
pub struct ComplementaryPwm<'d, T> { pub struct ComplementaryPwm<'d, T> {
inner: PeripheralRef<'d, T>, inner: PeripheralRef<'d, T>,
} }
impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
/// Create a new complementary PWM driver.
pub fn new( pub fn new(
tim: impl Peripheral<P = T> + 'd, tim: impl Peripheral<P = T> + 'd,
_ch1: Option<PwmPin<'d, T, Ch1>>, _ch1: Option<PwmPin<'d, T, Ch1>>,
@ -72,7 +78,7 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
let mut this = Self { inner: tim }; let mut this = Self { inner: tim };
this.inner.set_counting_mode(counting_mode); this.inner.set_counting_mode(counting_mode);
this.set_freq(freq); this.set_frequency(freq);
this.inner.start(); this.inner.start();
this.inner.enable_outputs(); this.inner.enable_outputs();
@ -88,17 +94,23 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
this this
} }
/// Enable the given channel.
pub fn enable(&mut self, channel: Channel) { pub fn enable(&mut self, channel: Channel) {
self.inner.enable_channel(channel, true); self.inner.enable_channel(channel, true);
self.inner.enable_complementary_channel(channel, true); self.inner.enable_complementary_channel(channel, true);
} }
/// Disable the given channel.
pub fn disable(&mut self, channel: Channel) { pub fn disable(&mut self, channel: Channel) {
self.inner.enable_complementary_channel(channel, false); self.inner.enable_complementary_channel(channel, false);
self.inner.enable_channel(channel, false); self.inner.enable_channel(channel, false);
} }
pub fn set_freq(&mut self, freq: Hertz) { /// Set PWM frequency.
///
/// Note: when you call this, the max duty value changes, so you will have to
/// call `set_duty` on all channels with the duty calculated based on the new max duty.
pub fn set_frequency(&mut self, freq: Hertz) {
let multiplier = if self.inner.get_counting_mode().is_center_aligned() { let multiplier = if self.inner.get_counting_mode().is_center_aligned() {
2u8 2u8
} else { } else {
@ -107,15 +119,22 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
self.inner.set_frequency(freq * multiplier); self.inner.set_frequency(freq * multiplier);
} }
/// 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) -> u16 {
self.inner.get_max_compare_value() + 1 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: u16) {
assert!(duty <= self.get_max_duty()); assert!(duty <= self.get_max_duty());
self.inner.set_compare_value(channel, duty) self.inner.set_compare_value(channel, duty)
} }
/// Set the output polarity for a given channel.
pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
self.inner.set_output_polarity(channel, polarity); self.inner.set_output_polarity(channel, polarity);
self.inner.set_complementary_output_polarity(channel, polarity); self.inner.set_complementary_output_polarity(channel, polarity);

View File

@ -17,17 +17,27 @@ pub mod low_level {
} }
pub(crate) mod sealed { pub(crate) mod sealed {
use super::*; use super::*;
/// Basic 16-bit timer instance.
pub trait Basic16bitInstance: RccPeripheral { pub trait Basic16bitInstance: RccPeripheral {
/// Interrupt for this timer.
type Interrupt: interrupt::typelevel::Interrupt; type Interrupt: interrupt::typelevel::Interrupt;
/// Get access to the basic 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.
fn regs() -> crate::pac::timer::TimBasic; fn regs() -> crate::pac::timer::TimBasic;
/// Start the timer.
fn start(&mut self) { fn start(&mut self) {
Self::regs().cr1().modify(|r| r.set_cen(true)); Self::regs().cr1().modify(|r| r.set_cen(true));
} }
/// Stop the timer.
fn stop(&mut self) { fn stop(&mut self) {
Self::regs().cr1().modify(|r| r.set_cen(false)); Self::regs().cr1().modify(|r| r.set_cen(false));
} }
@ -63,6 +73,9 @@ pub(crate) mod sealed {
regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
} }
/// Clear update interrupt.
///
/// Returns whether the update interrupt flag was set.
fn clear_update_interrupt(&mut self) -> bool { fn clear_update_interrupt(&mut self) -> bool {
let regs = Self::regs(); let regs = Self::regs();
let sr = regs.sr().read(); let sr = regs.sr().read();
@ -76,14 +89,17 @@ pub(crate) mod sealed {
} }
} }
/// Enable/disable the update interrupt.
fn enable_update_interrupt(&mut self, enable: bool) { fn enable_update_interrupt(&mut self, enable: bool) {
Self::regs().dier().write(|r| r.set_uie(enable)); Self::regs().dier().write(|r| r.set_uie(enable));
} }
/// Enable/disable autoreload preload.
fn set_autoreload_preload(&mut self, enable: bool) { fn set_autoreload_preload(&mut self, enable: bool) {
Self::regs().cr1().modify(|r| r.set_arpe(enable)); Self::regs().cr1().modify(|r| r.set_arpe(enable));
} }
/// Get the timer frequency.
fn get_frequency(&self) -> Hertz { fn get_frequency(&self) -> Hertz {
let timer_f = Self::frequency(); let timer_f = Self::frequency();
@ -95,9 +111,17 @@ pub(crate) mod sealed {
} }
} }
/// Gneral-purpose 16-bit timer instance.
pub trait GeneralPurpose16bitInstance: Basic16bitInstance { pub trait GeneralPurpose16bitInstance: Basic16bitInstance {
/// 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.
fn regs_gp16() -> crate::pac::timer::TimGp16; fn regs_gp16() -> crate::pac::timer::TimGp16;
/// Set counting mode.
fn set_counting_mode(&mut self, mode: CountingMode) { fn set_counting_mode(&mut self, mode: CountingMode) {
let (cms, dir) = mode.into(); let (cms, dir) = mode.into();
@ -110,19 +134,29 @@ pub(crate) mod sealed {
Self::regs_gp16().cr1().modify(|r| r.set_cms(cms)) Self::regs_gp16().cr1().modify(|r| r.set_cms(cms))
} }
/// Get counting mode.
fn get_counting_mode(&self) -> CountingMode { fn get_counting_mode(&self) -> CountingMode {
let cr1 = Self::regs_gp16().cr1().read(); let cr1 = Self::regs_gp16().cr1().read();
(cr1.cms(), cr1.dir()).into() (cr1.cms(), cr1.dir()).into()
} }
/// Set clock divider.
fn set_clock_division(&mut self, ckd: vals::Ckd) { fn set_clock_division(&mut self, ckd: vals::Ckd) {
Self::regs_gp16().cr1().modify(|r| r.set_ckd(ckd)); Self::regs_gp16().cr1().modify(|r| r.set_ckd(ckd));
} }
} }
/// Gneral-purpose 32-bit timer instance.
pub trait GeneralPurpose32bitInstance: GeneralPurpose16bitInstance { pub trait GeneralPurpose32bitInstance: GeneralPurpose16bitInstance {
/// 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.
fn regs_gp32() -> crate::pac::timer::TimGp32; fn regs_gp32() -> crate::pac::timer::TimGp32;
/// Set timer frequency.
fn set_frequency(&mut self, frequency: Hertz) { fn set_frequency(&mut self, frequency: Hertz) {
let f = frequency.0; let f = frequency.0;
assert!(f > 0); assert!(f > 0);
@ -140,6 +174,7 @@ pub(crate) mod sealed {
regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
} }
/// Get timer frequency.
fn get_frequency(&self) -> Hertz { fn get_frequency(&self) -> Hertz {
let timer_f = Self::frequency(); let timer_f = Self::frequency();
@ -151,141 +186,177 @@ pub(crate) mod sealed {
} }
} }
/// Advanced control timer instance.
pub trait AdvancedControlInstance: GeneralPurpose16bitInstance { pub trait AdvancedControlInstance: GeneralPurpose16bitInstance {
/// Get access to the advanced timer registers.
fn regs_advanced() -> crate::pac::timer::TimAdv; fn regs_advanced() -> crate::pac::timer::TimAdv;
} }
/// Capture/Compare 16-bit timer instance.
pub trait CaptureCompare16bitInstance: GeneralPurpose16bitInstance { pub trait CaptureCompare16bitInstance: GeneralPurpose16bitInstance {
/// Set input capture filter.
fn set_input_capture_filter(&mut self, channel: Channel, icf: vals::Icf) { fn set_input_capture_filter(&mut self, channel: Channel, icf: vals::Icf) {
let raw_channel = channel.raw(); let raw_channel = channel.index();
Self::regs_gp16() Self::regs_gp16()
.ccmr_input(raw_channel / 2) .ccmr_input(raw_channel / 2)
.modify(|r| r.set_icf(raw_channel % 2, icf)); .modify(|r| r.set_icf(raw_channel % 2, icf));
} }
/// Clear input interrupt.
fn clear_input_interrupt(&mut self, channel: Channel) { fn clear_input_interrupt(&mut self, channel: Channel) {
Self::regs_gp16().sr().modify(|r| r.set_ccif(channel.raw(), false)); Self::regs_gp16().sr().modify(|r| r.set_ccif(channel.index(), false));
} }
/// Enable input interrupt.
fn enable_input_interrupt(&mut self, channel: Channel, enable: bool) { fn enable_input_interrupt(&mut self, channel: Channel, enable: bool) {
Self::regs_gp16().dier().modify(|r| r.set_ccie(channel.raw(), enable)); Self::regs_gp16().dier().modify(|r| r.set_ccie(channel.index(), enable));
} }
/// Set input capture prescaler.
fn set_input_capture_prescaler(&mut self, channel: Channel, factor: u8) { fn set_input_capture_prescaler(&mut self, channel: Channel, factor: u8) {
let raw_channel = channel.raw(); let raw_channel = channel.index();
Self::regs_gp16() Self::regs_gp16()
.ccmr_input(raw_channel / 2) .ccmr_input(raw_channel / 2)
.modify(|r| r.set_icpsc(raw_channel % 2, factor)); .modify(|r| r.set_icpsc(raw_channel % 2, factor));
} }
/// Set input TI selection.
fn set_input_ti_selection(&mut self, channel: Channel, tisel: InputTISelection) { fn set_input_ti_selection(&mut self, channel: Channel, tisel: InputTISelection) {
let raw_channel = channel.raw(); let raw_channel = channel.index();
Self::regs_gp16() Self::regs_gp16()
.ccmr_input(raw_channel / 2) .ccmr_input(raw_channel / 2)
.modify(|r| r.set_ccs(raw_channel % 2, tisel.into())); .modify(|r| r.set_ccs(raw_channel % 2, tisel.into()));
} }
/// Set input capture mode.
fn set_input_capture_mode(&mut self, channel: Channel, mode: InputCaptureMode) { fn set_input_capture_mode(&mut self, channel: Channel, mode: InputCaptureMode) {
Self::regs_gp16().ccer().modify(|r| match mode { Self::regs_gp16().ccer().modify(|r| match mode {
InputCaptureMode::Rising => { InputCaptureMode::Rising => {
r.set_ccnp(channel.raw(), false); r.set_ccnp(channel.index(), false);
r.set_ccp(channel.raw(), false); r.set_ccp(channel.index(), false);
} }
InputCaptureMode::Falling => { InputCaptureMode::Falling => {
r.set_ccnp(channel.raw(), false); r.set_ccnp(channel.index(), false);
r.set_ccp(channel.raw(), true); r.set_ccp(channel.index(), true);
} }
InputCaptureMode::BothEdges => { InputCaptureMode::BothEdges => {
r.set_ccnp(channel.raw(), true); r.set_ccnp(channel.index(), true);
r.set_ccp(channel.raw(), true); r.set_ccp(channel.index(), true);
} }
}); });
} }
/// Enable timer outputs.
fn enable_outputs(&mut self); fn enable_outputs(&mut self);
/// Set output compare mode.
fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode) { fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode) {
let r = Self::regs_gp16(); let r = Self::regs_gp16();
let raw_channel: usize = channel.raw(); let raw_channel: usize = channel.index();
r.ccmr_output(raw_channel / 2) r.ccmr_output(raw_channel / 2)
.modify(|w| w.set_ocm(raw_channel % 2, mode.into())); .modify(|w| w.set_ocm(raw_channel % 2, mode.into()));
} }
/// Set output polarity.
fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
Self::regs_gp16() Self::regs_gp16()
.ccer() .ccer()
.modify(|w| w.set_ccp(channel.raw(), polarity.into())); .modify(|w| w.set_ccp(channel.index(), polarity.into()));
} }
/// Enable/disable a channel.
fn enable_channel(&mut self, channel: Channel, enable: bool) { fn enable_channel(&mut self, channel: Channel, enable: bool) {
Self::regs_gp16().ccer().modify(|w| w.set_cce(channel.raw(), enable)); Self::regs_gp16().ccer().modify(|w| w.set_cce(channel.index(), enable));
} }
/// Set compare value for a channel.
fn set_compare_value(&mut self, channel: Channel, value: u16) { fn set_compare_value(&mut self, channel: Channel, value: u16) {
Self::regs_gp16().ccr(channel.raw()).modify(|w| w.set_ccr(value)); Self::regs_gp16().ccr(channel.index()).modify(|w| w.set_ccr(value));
} }
/// Get capture value for a channel.
fn get_capture_value(&mut self, channel: Channel) -> u16 { fn get_capture_value(&mut self, channel: Channel) -> u16 {
Self::regs_gp16().ccr(channel.raw()).read().ccr() Self::regs_gp16().ccr(channel.index()).read().ccr()
} }
/// Get max compare value. This depends on the timer frequency and the clock frequency from RCC.
fn get_max_compare_value(&self) -> u16 { fn get_max_compare_value(&self) -> u16 {
Self::regs_gp16().arr().read().arr() Self::regs_gp16().arr().read().arr()
} }
/// Get compare value for a channel.
fn get_compare_value(&self, channel: Channel) -> u16 { fn get_compare_value(&self, channel: Channel) -> u16 {
Self::regs_gp16().ccr(channel.raw()).read().ccr() Self::regs_gp16().ccr(channel.index()).read().ccr()
} }
} }
/// Capture/Compare 16-bit timer instance with complementary pin support.
pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance + AdvancedControlInstance { pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance + AdvancedControlInstance {
/// Set complementary output polarity.
fn set_complementary_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { fn set_complementary_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
Self::regs_advanced() Self::regs_advanced()
.ccer() .ccer()
.modify(|w| w.set_ccnp(channel.raw(), polarity.into())); .modify(|w| w.set_ccnp(channel.index(), polarity.into()));
} }
/// Set clock divider for the dead time.
fn set_dead_time_clock_division(&mut self, value: vals::Ckd) { fn set_dead_time_clock_division(&mut self, value: vals::Ckd) {
Self::regs_advanced().cr1().modify(|w| w.set_ckd(value)); Self::regs_advanced().cr1().modify(|w| w.set_ckd(value));
} }
/// Set dead time, as a fraction of the max duty value.
fn set_dead_time_value(&mut self, value: u8) { fn set_dead_time_value(&mut self, value: u8) {
Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value)); Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value));
} }
/// Enable/disable a complementary channel.
fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) { fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) {
Self::regs_advanced() Self::regs_advanced()
.ccer() .ccer()
.modify(|w| w.set_ccne(channel.raw(), enable)); .modify(|w| w.set_ccne(channel.index(), enable));
} }
} }
/// Capture/Compare 32-bit timer instance.
pub trait CaptureCompare32bitInstance: GeneralPurpose32bitInstance + CaptureCompare16bitInstance { pub trait CaptureCompare32bitInstance: GeneralPurpose32bitInstance + CaptureCompare16bitInstance {
/// Set comapre value for a channel.
fn set_compare_value(&mut self, channel: Channel, value: u32) { fn set_compare_value(&mut self, channel: Channel, value: u32) {
Self::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(value)); Self::regs_gp32().ccr(channel.index()).modify(|w| w.set_ccr(value));
} }
/// Get capture value for a channel.
fn get_capture_value(&mut self, channel: Channel) -> u32 { fn get_capture_value(&mut self, channel: Channel) -> u32 {
Self::regs_gp32().ccr(channel.raw()).read().ccr() Self::regs_gp32().ccr(channel.index()).read().ccr()
} }
/// Get max compare value. This depends on the timer frequency and the clock frequency from RCC.
fn get_max_compare_value(&self) -> u32 { fn get_max_compare_value(&self) -> u32 {
Self::regs_gp32().arr().read().arr() Self::regs_gp32().arr().read().arr()
} }
/// Get compare value for a channel.
fn get_compare_value(&self, channel: Channel) -> u32 { fn get_compare_value(&self, channel: Channel) -> u32 {
Self::regs_gp32().ccr(channel.raw()).read().ccr() Self::regs_gp32().ccr(channel.index()).read().ccr()
} }
} }
} }
/// Timer channel.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum Channel { pub enum Channel {
/// Channel 1.
Ch1, Ch1,
/// Channel 2.
Ch2, Ch2,
/// Channel 3.
Ch3, Ch3,
/// Channel 4.
Ch4, Ch4,
} }
impl Channel { impl Channel {
pub fn raw(&self) -> usize { /// Get the channel index (0..3)
pub fn index(&self) -> usize {
match self { match self {
Channel::Ch1 => 0, Channel::Ch1 => 0,
Channel::Ch2 => 1, Channel::Ch2 => 1,
@ -295,17 +366,25 @@ impl Channel {
} }
} }
/// Input capture mode.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum InputCaptureMode { pub enum InputCaptureMode {
/// Rising edge only.
Rising, Rising,
/// Falling edge only.
Falling, Falling,
/// Both rising or falling edges.
BothEdges, BothEdges,
} }
/// Input TI selection.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum InputTISelection { pub enum InputTISelection {
/// Normal
Normal, Normal,
/// Alternate
Alternate, Alternate,
/// TRC
TRC, TRC,
} }
@ -319,6 +398,7 @@ impl From<InputTISelection> for stm32_metapac::timer::vals::CcmrInputCcs {
} }
} }
/// Timer counting mode.
#[repr(u8)] #[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum CountingMode { pub enum CountingMode {
@ -345,6 +425,7 @@ pub enum CountingMode {
} }
impl CountingMode { impl CountingMode {
/// Return whether this mode is edge-aligned (up or down).
pub fn is_edge_aligned(&self) -> bool { pub fn is_edge_aligned(&self) -> bool {
match self { match self {
CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown => true, CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown => true,
@ -352,6 +433,7 @@ impl CountingMode {
} }
} }
/// Return whether this mode is center-aligned.
pub fn is_center_aligned(&self) -> bool { pub fn is_center_aligned(&self) -> bool {
match self { match self {
CountingMode::CenterAlignedDownInterrupts CountingMode::CenterAlignedDownInterrupts
@ -386,16 +468,34 @@ impl From<(vals::Cms, vals::Dir)> for CountingMode {
} }
} }
/// Output compare mode.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum OutputCompareMode { 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, 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, 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, InactiveOnMatch,
/// Toggle - OCxREF toggles when TIMx_CNT=TIMx_CCRx.
Toggle, Toggle,
/// Force inactive level - OCxREF is forced low.
ForceInactive, ForceInactive,
/// Force active level - OCxREF is forced high.
ForceActive, 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, 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, PwmMode2,
// TODO: there's more modes here depending on the chip family.
} }
impl From<OutputCompareMode> for stm32_metapac::timer::vals::Ocm { impl From<OutputCompareMode> for stm32_metapac::timer::vals::Ocm {
@ -413,9 +513,12 @@ impl From<OutputCompareMode> for stm32_metapac::timer::vals::Ocm {
} }
} }
/// Timer output pin polarity.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum OutputPolarity { pub enum OutputPolarity {
/// Active high (higher duty value makes the pin spend more time high).
ActiveHigh, ActiveHigh,
/// Active low (higher duty value makes the pin spend more time low).
ActiveLow, ActiveLow,
} }
@ -428,24 +531,31 @@ impl From<OutputPolarity> for bool {
} }
} }
/// Basic 16-bit timer instance.
pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {} pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {}
/// Gneral-purpose 16-bit timer instance.
pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + 'static {} pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + 'static {}
/// Gneral-purpose 32-bit timer instance.
pub trait GeneralPurpose32bitInstance: sealed::GeneralPurpose32bitInstance + 'static {} pub trait GeneralPurpose32bitInstance: sealed::GeneralPurpose32bitInstance + 'static {}
/// Advanced control timer instance.
pub trait AdvancedControlInstance: sealed::AdvancedControlInstance + 'static {} pub trait AdvancedControlInstance: sealed::AdvancedControlInstance + 'static {}
/// Capture/Compare 16-bit timer instance.
pub trait CaptureCompare16bitInstance: pub trait CaptureCompare16bitInstance:
sealed::CaptureCompare16bitInstance + GeneralPurpose16bitInstance + 'static sealed::CaptureCompare16bitInstance + GeneralPurpose16bitInstance + 'static
{ {
} }
/// Capture/Compare 16-bit timer instance with complementary pin support.
pub trait ComplementaryCaptureCompare16bitInstance: pub trait ComplementaryCaptureCompare16bitInstance:
sealed::ComplementaryCaptureCompare16bitInstance + AdvancedControlInstance + 'static sealed::ComplementaryCaptureCompare16bitInstance + AdvancedControlInstance + 'static
{ {
} }
/// Capture/Compare 32-bit timer instance.
pub trait CaptureCompare32bitInstance: pub trait CaptureCompare32bitInstance:
sealed::CaptureCompare32bitInstance + CaptureCompare16bitInstance + GeneralPurpose32bitInstance + 'static sealed::CaptureCompare32bitInstance + CaptureCompare16bitInstance + GeneralPurpose32bitInstance + 'static
{ {

View File

@ -9,23 +9,30 @@ use crate::gpio::sealed::AFType;
use crate::gpio::AnyPin; use crate::gpio::AnyPin;
use crate::Peripheral; use crate::Peripheral;
/// Counting direction
pub enum Direction { pub enum Direction {
/// Counting up.
Upcounting, Upcounting,
/// Counting down.
Downcounting, Downcounting,
} }
pub struct Ch1; /// Channel 1 marker type.
pub struct Ch2; pub enum Ch1 {}
/// Channel 2 marker type.
pub enum Ch2 {}
pub struct QeiPin<'d, Perip, Channel> { /// Wrapper for using a pin with QEI.
pub struct QeiPin<'d, T, Channel> {
_pin: PeripheralRef<'d, AnyPin>, _pin: PeripheralRef<'d, AnyPin>,
phantom: PhantomData<(Perip, Channel)>, phantom: PhantomData<(T, Channel)>,
} }
macro_rules! channel_impl { macro_rules! channel_impl {
($new_chx:ident, $channel:ident, $pin_trait:ident) => { ($new_chx:ident, $channel:ident, $pin_trait:ident) => {
impl<'d, Perip: CaptureCompare16bitInstance> QeiPin<'d, Perip, $channel> { impl<'d, T: CaptureCompare16bitInstance> QeiPin<'d, T, $channel> {
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<Perip>> + 'd) -> Self { #[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); into_ref!(pin);
critical_section::with(|_| { critical_section::with(|_| {
pin.set_low(); pin.set_low();
@ -45,11 +52,13 @@ macro_rules! channel_impl {
channel_impl!(new_ch1, Ch1, Channel1Pin); channel_impl!(new_ch1, Ch1, Channel1Pin);
channel_impl!(new_ch2, Ch2, Channel2Pin); channel_impl!(new_ch2, Ch2, Channel2Pin);
/// Quadrature decoder driver.
pub struct Qei<'d, T> { pub struct Qei<'d, T> {
_inner: PeripheralRef<'d, T>, _inner: PeripheralRef<'d, T>,
} }
impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> { impl<'d, T: CaptureCompare16bitInstance> 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 { pub fn new(tim: impl Peripheral<P = T> + 'd, _ch1: QeiPin<'d, T, Ch1>, _ch2: QeiPin<'d, T, Ch2>) -> Self {
Self::new_inner(tim) Self::new_inner(tim)
} }
@ -84,6 +93,7 @@ impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> {
Self { _inner: tim } Self { _inner: tim }
} }
/// Get direction.
pub fn read_direction(&self) -> Direction { pub fn read_direction(&self) -> Direction {
match T::regs_gp16().cr1().read().dir() { match T::regs_gp16().cr1().read().dir() {
vals::Dir::DOWN => Direction::Downcounting, vals::Dir::DOWN => Direction::Downcounting,
@ -91,6 +101,7 @@ impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> {
} }
} }
/// Get count.
pub fn count(&self) -> u16 { pub fn count(&self) -> u16 {
T::regs_gp16().cnt().read().cnt() T::regs_gp16().cnt().read().cnt()
} }

View File

@ -11,20 +11,28 @@ use crate::gpio::{AnyPin, OutputType};
use crate::time::Hertz; use crate::time::Hertz;
use crate::Peripheral; use crate::Peripheral;
pub struct Ch1; /// Channel 1 marker type.
pub struct Ch2; pub enum Ch1 {}
pub struct Ch3; /// Channel 2 marker type.
pub struct Ch4; pub enum Ch2 {}
/// Channel 3 marker type.
pub enum Ch3 {}
/// Channel 4 marker type.
pub enum Ch4 {}
pub struct PwmPin<'d, Perip, Channel> { /// PWM pin wrapper.
///
/// This wraps a pin to make it usable with PWM.
pub struct PwmPin<'d, T, C> {
_pin: PeripheralRef<'d, AnyPin>, _pin: PeripheralRef<'d, AnyPin>,
phantom: PhantomData<(Perip, Channel)>, phantom: PhantomData<(T, C)>,
} }
macro_rules! channel_impl { macro_rules! channel_impl {
($new_chx:ident, $channel:ident, $pin_trait:ident) => { ($new_chx:ident, $channel:ident, $pin_trait:ident) => {
impl<'d, Perip: CaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { impl<'d, T: CaptureCompare16bitInstance> PwmPin<'d, T, $channel> {
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<Perip>> + 'd, output_type: OutputType) -> Self { #[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); into_ref!(pin);
critical_section::with(|_| { critical_section::with(|_| {
pin.set_low(); pin.set_low();
@ -46,11 +54,13 @@ channel_impl!(new_ch2, Ch2, Channel2Pin);
channel_impl!(new_ch3, Ch3, Channel3Pin); channel_impl!(new_ch3, Ch3, Channel3Pin);
channel_impl!(new_ch4, Ch4, Channel4Pin); channel_impl!(new_ch4, Ch4, Channel4Pin);
/// Simple PWM driver.
pub struct SimplePwm<'d, T> { pub struct SimplePwm<'d, T> {
inner: PeripheralRef<'d, T>, inner: PeripheralRef<'d, T>,
} }
impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
/// Create a new simple PWM driver.
pub fn new( pub fn new(
tim: impl Peripheral<P = T> + 'd, tim: impl Peripheral<P = T> + 'd,
_ch1: Option<PwmPin<'d, T, Ch1>>, _ch1: Option<PwmPin<'d, T, Ch1>>,
@ -71,7 +81,7 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
let mut this = Self { inner: tim }; let mut this = Self { inner: tim };
this.inner.set_counting_mode(counting_mode); this.inner.set_counting_mode(counting_mode);
this.set_freq(freq); this.set_frequency(freq);
this.inner.start(); this.inner.start();
this.inner.enable_outputs(); this.inner.enable_outputs();
@ -87,15 +97,21 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
this this
} }
/// Enable the given channel.
pub fn enable(&mut self, channel: Channel) { pub fn enable(&mut self, channel: Channel) {
self.inner.enable_channel(channel, true); self.inner.enable_channel(channel, true);
} }
/// Disable the given channel.
pub fn disable(&mut self, channel: Channel) { pub fn disable(&mut self, channel: Channel) {
self.inner.enable_channel(channel, false); self.inner.enable_channel(channel, false);
} }
pub fn set_freq(&mut self, freq: Hertz) { /// Set PWM frequency.
///
/// Note: when you call this, the max duty value changes, so you will have to
/// call `set_duty` on all channels with the duty calculated based on the new max duty.
pub fn set_frequency(&mut self, freq: Hertz) {
let multiplier = if self.inner.get_counting_mode().is_center_aligned() { let multiplier = if self.inner.get_counting_mode().is_center_aligned() {
2u8 2u8
} else { } else {
@ -104,15 +120,22 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
self.inner.set_frequency(freq * multiplier); self.inner.set_frequency(freq * multiplier);
} }
/// 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) -> u16 {
self.inner.get_max_compare_value() + 1 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: u16) {
assert!(duty <= self.get_max_duty()); assert!(duty <= self.get_max_duty());
self.inner.set_compare_value(channel, duty) self.inner.set_compare_value(channel, duty)
} }
/// Set the output polarity for a given channel.
pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
self.inner.set_output_polarity(channel, polarity); self.inner.set_output_polarity(channel, polarity);
} }

View File

@ -110,7 +110,7 @@ async fn main(_spawner: Spawner) {
&mut dp.DMA1_CH2, &mut dp.DMA1_CH2,
5, 5,
color_list[color_list_index], color_list[color_list_index],
pac::TIM3.ccr(pwm_channel.raw()).as_ptr() as *mut _, pac::TIM3.ccr(pwm_channel.index()).as_ptr() as *mut _,
dma_transfer_option, dma_transfer_option,
) )
.await; .await;

View File

@ -85,7 +85,7 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> {
let mut this = Self { inner: tim }; let mut this = Self { inner: tim };
this.set_freq(freq); this.set_frequency(freq);
this.inner.start(); this.inner.start();
let r = T::regs_gp32(); let r = T::regs_gp32();
@ -102,14 +102,14 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> {
} }
pub fn enable(&mut self, channel: Channel) { pub fn enable(&mut self, channel: Channel) {
T::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), true)); T::regs_gp32().ccer().modify(|w| w.set_cce(channel.index(), true));
} }
pub fn disable(&mut self, channel: Channel) { pub fn disable(&mut self, channel: Channel) {
T::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), false)); T::regs_gp32().ccer().modify(|w| w.set_cce(channel.index(), false));
} }
pub fn set_freq(&mut self, freq: Hertz) { pub fn set_frequency(&mut self, freq: Hertz) {
<T as embassy_stm32::timer::low_level::GeneralPurpose32bitInstance>::set_frequency(&mut self.inner, freq); <T as embassy_stm32::timer::low_level::GeneralPurpose32bitInstance>::set_frequency(&mut self.inner, freq);
} }
@ -119,6 +119,6 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> {
pub fn set_duty(&mut self, channel: Channel, duty: u32) { pub fn set_duty(&mut self, channel: Channel, duty: u32) {
defmt::assert!(duty < self.get_max_duty()); defmt::assert!(duty < self.get_max_duty());
T::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(duty)) T::regs_gp32().ccr(channel.index()).modify(|w| w.set_ccr(duty))
} }
} }