506 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			506 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! Simple PWM driver.
 | |
| 
 | |
| use core::marker::PhantomData;
 | |
| 
 | |
| use embassy_hal_internal::{into_ref, PeripheralRef};
 | |
| 
 | |
| use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer};
 | |
| use super::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance4Channel};
 | |
| use crate::gpio::{AfType, AnyPin, OutputType, Speed};
 | |
| use crate::time::Hertz;
 | |
| use crate::Peripheral;
 | |
| 
 | |
| /// Channel 1 marker type.
 | |
| pub enum Ch1 {}
 | |
| /// Channel 2 marker type.
 | |
| pub enum Ch2 {}
 | |
| /// Channel 3 marker type.
 | |
| pub enum Ch3 {}
 | |
| /// Channel 4 marker type.
 | |
| pub enum Ch4 {}
 | |
| 
 | |
| /// PWM pin wrapper.
 | |
| ///
 | |
| /// This wraps a pin to make it usable with PWM.
 | |
| pub struct PwmPin<'d, T, C> {
 | |
|     _pin: PeripheralRef<'d, AnyPin>,
 | |
|     phantom: PhantomData<(T, C)>,
 | |
| }
 | |
| 
 | |
| macro_rules! channel_impl {
 | |
|     ($new_chx:ident, $channel:ident, $pin_trait:ident) => {
 | |
|         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);
 | |
|                 critical_section::with(|_| {
 | |
|                     pin.set_low();
 | |
|                     pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh));
 | |
|                 });
 | |
|                 PwmPin {
 | |
|                     _pin: pin.map_into(),
 | |
|                     phantom: PhantomData,
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     };
 | |
| }
 | |
| 
 | |
| channel_impl!(new_ch1, Ch1, Channel1Pin);
 | |
| channel_impl!(new_ch2, Ch2, Channel2Pin);
 | |
| channel_impl!(new_ch3, Ch3, Channel3Pin);
 | |
| channel_impl!(new_ch4, Ch4, Channel4Pin);
 | |
| 
 | |
| /// A single channel of a pwm, obtained from [`SimplePwm::split`],
 | |
| /// [`SimplePwm::channel`], [`SimplePwm::ch1`], etc.
 | |
| ///
 | |
| /// It is not possible to change the pwm frequency because
 | |
| /// the frequency configuration is shared with all four channels.
 | |
| pub struct SimplePwmChannel<'d, T: GeneralInstance4Channel> {
 | |
|     timer: &'d Timer<'d, T>,
 | |
|     channel: Channel,
 | |
| }
 | |
| 
 | |
| // TODO: check for RMW races
 | |
| impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> {
 | |
|     /// Enable the given channel.
 | |
|     pub fn enable(&mut self) {
 | |
|         self.timer.enable_channel(self.channel, true);
 | |
|     }
 | |
| 
 | |
|     /// Disable the given channel.
 | |
|     pub fn disable(&mut self) {
 | |
|         self.timer.enable_channel(self.channel, false);
 | |
|     }
 | |
| 
 | |
|     /// Check whether given channel is enabled
 | |
|     pub fn is_enabled(&self) -> bool {
 | |
|         self.timer.get_channel_enable_state(self.channel)
 | |
|     }
 | |
| 
 | |
|     /// Get max duty value.
 | |
|     ///
 | |
|     /// This value depends on the configured frequency and the timer's clock rate from RCC.
 | |
|     pub fn max_duty_cycle(&self) -> u16 {
 | |
|         let max = self.timer.get_max_compare_value();
 | |
|         assert!(max < u16::MAX as u32);
 | |
|         max as u16 + 1
 | |
|     }
 | |
| 
 | |
|     /// Set the duty for a given channel.
 | |
|     ///
 | |
|     /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included.
 | |
|     pub fn set_duty_cycle(&mut self, duty: u16) {
 | |
|         assert!(duty <= (*self).max_duty_cycle());
 | |
|         self.timer.set_compare_value(self.channel, duty.into())
 | |
|     }
 | |
| 
 | |
|     fn set_duty_cycle_fully_off(&mut self) {
 | |
|         self.set_duty_cycle(0);
 | |
|     }
 | |
| 
 | |
|     fn set_duty_cycle_fully_on(&mut self) {
 | |
|         self.set_duty_cycle((*self).max_duty_cycle());
 | |
|     }
 | |
| 
 | |
|     fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) {
 | |
|         assert!(denom != 0);
 | |
|         assert!(num <= denom);
 | |
|         let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom);
 | |
| 
 | |
|         // This is safe because we know that `num <= denom`, so `duty <= self.max_duty_cycle()` (u16)
 | |
|         #[allow(clippy::cast_possible_truncation)]
 | |
|         self.set_duty_cycle(duty as u16);
 | |
|     }
 | |
| 
 | |
|     fn set_duty_cycle_percent(&mut self, percent: u8) {
 | |
|         self.set_duty_cycle_fraction(u16::from(percent), 100)
 | |
|     }
 | |
| 
 | |
|     /// Get the duty for a given channel.
 | |
|     ///
 | |
|     /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included.
 | |
|     pub fn get_duty(&self) -> u16 {
 | |
|         unwrap!(self.timer.get_compare_value(self.channel).try_into())
 | |
|     }
 | |
| 
 | |
|     /// Set the output polarity for a given channel.
 | |
|     pub fn set_polarity(&mut self, polarity: OutputPolarity) {
 | |
|         self.timer.set_output_polarity(self.channel, polarity);
 | |
|     }
 | |
| 
 | |
|     /// Set the output compare mode for a given channel.
 | |
|     pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) {
 | |
|         self.timer.set_output_compare_mode(self.channel, mode);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`].
 | |
| pub struct SimplePwmChannels<'d, T: GeneralInstance4Channel> {
 | |
|     /// Channel 1
 | |
|     pub ch1: SimplePwmChannel<'d, T>,
 | |
|     /// Channel 2
 | |
|     pub ch2: SimplePwmChannel<'d, T>,
 | |
|     /// Channel 3
 | |
|     pub ch3: SimplePwmChannel<'d, T>,
 | |
|     /// Channel 4
 | |
|     pub ch4: SimplePwmChannel<'d, T>,
 | |
| }
 | |
| 
 | |
| /// Simple PWM driver.
 | |
| pub struct SimplePwm<'d, T: GeneralInstance4Channel> {
 | |
|     inner: Timer<'d, T>,
 | |
| }
 | |
| 
 | |
| impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
 | |
|     /// Create a new simple PWM driver.
 | |
|     pub fn new(
 | |
|         tim: impl Peripheral<P = T> + 'd,
 | |
|         _ch1: Option<PwmPin<'d, T, Ch1>>,
 | |
|         _ch2: Option<PwmPin<'d, T, Ch2>>,
 | |
|         _ch3: Option<PwmPin<'d, T, Ch3>>,
 | |
|         _ch4: Option<PwmPin<'d, T, Ch4>>,
 | |
|         freq: Hertz,
 | |
|         counting_mode: CountingMode,
 | |
|     ) -> Self {
 | |
|         Self::new_inner(tim, freq, counting_mode)
 | |
|     }
 | |
| 
 | |
|     fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, counting_mode: CountingMode) -> Self {
 | |
|         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 GeneralInstance4Channel for details
 | |
|         this.inner.start();
 | |
| 
 | |
|         [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
 | |
|             .iter()
 | |
|             .for_each(|&channel| {
 | |
|                 this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1);
 | |
| 
 | |
|                 this.inner.set_output_compare_preload(channel, true);
 | |
|             });
 | |
| 
 | |
|         this
 | |
|     }
 | |
| 
 | |
|     /// Get a single channel
 | |
|     ///
 | |
|     /// If you need to use multiple channels, use [`Self::split`].
 | |
|     pub fn channel(&mut self, channel: Channel) -> SimplePwmChannel<'_, T> {
 | |
|         SimplePwmChannel {
 | |
|             timer: &self.inner,
 | |
|             channel,
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// Channel 1
 | |
|     ///
 | |
|     /// This is just a convenience wrapper around [`Self::channel`].
 | |
|     ///
 | |
|     /// If you need to use multiple channels, use [`Self::split`].
 | |
|     pub fn ch1(&mut self) -> SimplePwmChannel<'_, T> {
 | |
|         self.channel(Channel::Ch1)
 | |
|     }
 | |
| 
 | |
|     /// Channel 2
 | |
|     ///
 | |
|     /// This is just a convenience wrapper around [`Self::channel`].
 | |
|     ///
 | |
|     /// If you need to use multiple channels, use [`Self::split`].
 | |
|     pub fn ch2(&mut self) -> SimplePwmChannel<'_, T> {
 | |
|         self.channel(Channel::Ch2)
 | |
|     }
 | |
| 
 | |
|     /// Channel 3
 | |
|     ///
 | |
|     /// This is just a convenience wrapper around [`Self::channel`].
 | |
|     ///
 | |
|     /// If you need to use multiple channels, use [`Self::split`].
 | |
|     pub fn ch3(&mut self) -> SimplePwmChannel<'_, T> {
 | |
|         self.channel(Channel::Ch3)
 | |
|     }
 | |
| 
 | |
|     /// Channel 4
 | |
|     ///
 | |
|     /// This is just a convenience wrapper around [`Self::channel`].
 | |
|     ///
 | |
|     /// If you need to use multiple channels, use [`Self::split`].
 | |
|     pub fn ch4(&mut self) -> SimplePwmChannel<'_, T> {
 | |
|         self.channel(Channel::Ch4)
 | |
|     }
 | |
| 
 | |
|     /// Splits a [`SimplePwm`] into four pwm channels.
 | |
|     ///
 | |
|     /// This returns all four channels, including channels that
 | |
|     /// aren't configured with a [`PwmPin`].
 | |
|     // TODO: I hate the name "split"
 | |
|     pub fn split(&mut self) -> SimplePwmChannels<'_, T> {
 | |
|         // TODO: pre-enable channels?
 | |
| 
 | |
|         // we can't use self.channel() because that takes &mut self
 | |
|         let ch = |channel| SimplePwmChannel {
 | |
|             timer: &self.inner,
 | |
|             channel,
 | |
|         };
 | |
| 
 | |
|         SimplePwmChannels {
 | |
|             ch1: ch(Channel::Ch1),
 | |
|             ch2: ch(Channel::Ch2),
 | |
|             ch3: ch(Channel::Ch3),
 | |
|             ch4: ch(Channel::Ch4),
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// 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) {
 | |
|         // TODO: prevent ARR = u16::MAX?
 | |
|         let multiplier = if self.inner.get_counting_mode().is_center_aligned() {
 | |
|             2u8
 | |
|         } else {
 | |
|             1u8
 | |
|         };
 | |
|         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 max_duty_cycle(&self) -> u16 {
 | |
|         let max = self.inner.get_max_compare_value();
 | |
|         assert!(max < u16::MAX as u32);
 | |
|         max as u16 + 1
 | |
|     }
 | |
| 
 | |
|     /// Generate a sequence of PWM waveform
 | |
|     ///
 | |
|     /// Note:  
 | |
|     /// you will need to provide corresponding TIMx_UP DMA channel to use this method.
 | |
|     pub async fn waveform_up(
 | |
|         &mut self,
 | |
|         dma: impl Peripheral<P = impl super::UpDma<T>>,
 | |
|         channel: Channel,
 | |
|         duty: &[u16],
 | |
|     ) {
 | |
|         into_ref!(dma);
 | |
| 
 | |
|         #[allow(clippy::let_unit_value)] // eg. stm32f334
 | |
|         let req = dma.request();
 | |
| 
 | |
|         let original_duty_state = self.channel(channel).get_duty();
 | |
|         let original_enable_state = self.channel(channel).is_enabled();
 | |
|         let original_update_dma_state = self.inner.get_update_dma_state();
 | |
| 
 | |
|         if !original_update_dma_state {
 | |
|             self.inner.enable_update_dma(true);
 | |
|         }
 | |
| 
 | |
|         if !original_enable_state {
 | |
|             self.channel(channel).enable();
 | |
|         }
 | |
| 
 | |
|         unsafe {
 | |
|             #[cfg(not(any(bdma, gpdma)))]
 | |
|             use crate::dma::{Burst, FifoThreshold};
 | |
|             use crate::dma::{Transfer, TransferOptions};
 | |
| 
 | |
|             let dma_transfer_option = TransferOptions {
 | |
|                 #[cfg(not(any(bdma, gpdma)))]
 | |
|                 fifo_threshold: Some(FifoThreshold::Full),
 | |
|                 #[cfg(not(any(bdma, gpdma)))]
 | |
|                 mburst: Burst::Incr8,
 | |
|                 ..Default::default()
 | |
|             };
 | |
| 
 | |
|             Transfer::new_write(
 | |
|                 &mut dma,
 | |
|                 req,
 | |
|                 duty,
 | |
|                 self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut _,
 | |
|                 dma_transfer_option,
 | |
|             )
 | |
|             .await
 | |
|         };
 | |
| 
 | |
|         // restore output compare state
 | |
|         if !original_enable_state {
 | |
|             self.channel(channel).disable();
 | |
|         }
 | |
| 
 | |
|         self.channel(channel).set_duty_cycle(original_duty_state);
 | |
| 
 | |
|         // Since DMA is closed before timer update event trigger DMA is turn off,
 | |
|         // this can almost always trigger a DMA FIFO error.
 | |
|         //
 | |
|         // optional TODO:
 | |
|         // clean FEIF after disable UDE
 | |
|         if !original_update_dma_state {
 | |
|             self.inner.enable_update_dma(false);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| macro_rules! impl_waveform_chx {
 | |
|     ($fn_name:ident, $dma_ch:ident, $cc_ch:ident) => {
 | |
|         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 crate::pac::timer::vals::Ccds;
 | |
| 
 | |
|                 into_ref!(dma);
 | |
| 
 | |
|                 #[allow(clippy::let_unit_value)] // eg. stm32f334
 | |
|                 let req = dma.request();
 | |
| 
 | |
|                 let cc_channel = Channel::$cc_ch;
 | |
| 
 | |
|                 let original_duty_state = self.channel(cc_channel).get_duty();
 | |
|                 let original_enable_state = self.channel(cc_channel).is_enabled();
 | |
|                 let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ONUPDATE;
 | |
|                 let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel);
 | |
| 
 | |
|                 // redirect CC DMA request onto Update Event
 | |
|                 if !original_cc_dma_on_update {
 | |
|                     self.inner.set_cc_dma_selection(Ccds::ONUPDATE)
 | |
|                 }
 | |
| 
 | |
|                 if !original_cc_dma_enabled {
 | |
|                     self.inner.set_cc_dma_enable_state(cc_channel, true);
 | |
|                 }
 | |
| 
 | |
|                 if !original_enable_state {
 | |
|                     self.channel(cc_channel).enable();
 | |
|                 }
 | |
| 
 | |
|                 unsafe {
 | |
|                     #[cfg(not(any(bdma, gpdma)))]
 | |
|                     use crate::dma::{Burst, FifoThreshold};
 | |
|                     use crate::dma::{Transfer, TransferOptions};
 | |
| 
 | |
|                     let dma_transfer_option = TransferOptions {
 | |
|                         #[cfg(not(any(bdma, gpdma)))]
 | |
|                         fifo_threshold: Some(FifoThreshold::Full),
 | |
|                         #[cfg(not(any(bdma, gpdma)))]
 | |
|                         mburst: Burst::Incr8,
 | |
|                         ..Default::default()
 | |
|                     };
 | |
| 
 | |
|                     Transfer::new_write(
 | |
|                         &mut dma,
 | |
|                         req,
 | |
|                         duty,
 | |
|                         self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut _,
 | |
|                         dma_transfer_option,
 | |
|                     )
 | |
|                     .await
 | |
|                 };
 | |
| 
 | |
|                 // restore output compare state
 | |
|                 if !original_enable_state {
 | |
|                     self.channel(cc_channel).disable();
 | |
|                 }
 | |
| 
 | |
|                 self.channel(cc_channel).set_duty_cycle(original_duty_state);
 | |
| 
 | |
|                 // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off,
 | |
|                 // this can almost always trigger a DMA FIFO error.
 | |
|                 //
 | |
|                 // optional TODO:
 | |
|                 // clean FEIF after disable UDE
 | |
|                 if !original_cc_dma_enabled {
 | |
|                     self.inner.set_cc_dma_enable_state(cc_channel, false);
 | |
|                 }
 | |
| 
 | |
|                 if !original_cc_dma_on_update {
 | |
|                     self.inner.set_cc_dma_selection(Ccds::ONCOMPARE)
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     };
 | |
| }
 | |
| 
 | |
| impl_waveform_chx!(waveform_ch1, Ch1Dma, Ch1);
 | |
| impl_waveform_chx!(waveform_ch2, Ch2Dma, Ch2);
 | |
| impl_waveform_chx!(waveform_ch3, Ch3Dma, Ch3);
 | |
| impl_waveform_chx!(waveform_ch4, Ch4Dma, Ch4);
 | |
| 
 | |
| impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> {
 | |
|     type Error = core::convert::Infallible;
 | |
| }
 | |
| 
 | |
| impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> {
 | |
|     fn max_duty_cycle(&self) -> u16 {
 | |
|         self.max_duty_cycle()
 | |
|     }
 | |
| 
 | |
|     fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
 | |
|         self.set_duty_cycle(duty);
 | |
|         Ok(())
 | |
|     }
 | |
| 
 | |
|     fn set_duty_cycle_fully_off(&mut self) -> Result<(), Self::Error> {
 | |
|         self.set_duty_cycle_fully_off();
 | |
|         Ok(())
 | |
|     }
 | |
| 
 | |
|     fn set_duty_cycle_fully_on(&mut self) -> Result<(), Self::Error> {
 | |
|         self.set_duty_cycle_fully_on();
 | |
|         Ok(())
 | |
|     }
 | |
| 
 | |
|     fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> {
 | |
|         self.set_duty_cycle_fraction(num, denom);
 | |
|         Ok(())
 | |
|     }
 | |
| 
 | |
|     fn set_duty_cycle_percent(&mut self, percent: u8) -> Result<(), Self::Error> {
 | |
|         self.set_duty_cycle_percent(percent);
 | |
|         Ok(())
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> {
 | |
|     type Channel = Channel;
 | |
|     type Time = Hertz;
 | |
|     type Duty = u32;
 | |
| 
 | |
|     fn disable(&mut self, channel: Self::Channel) {
 | |
|         self.inner.enable_channel(channel, false);
 | |
|     }
 | |
| 
 | |
|     fn enable(&mut self, channel: Self::Channel) {
 | |
|         self.inner.enable_channel(channel, true);
 | |
|     }
 | |
| 
 | |
|     fn get_period(&self) -> Self::Time {
 | |
|         self.inner.get_frequency()
 | |
|     }
 | |
| 
 | |
|     fn get_duty(&self, channel: Self::Channel) -> Self::Duty {
 | |
|         self.inner.get_compare_value(channel)
 | |
|     }
 | |
| 
 | |
|     fn get_max_duty(&self) -> Self::Duty {
 | |
|         self.inner.get_max_compare_value() + 1
 | |
|     }
 | |
| 
 | |
|     fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) {
 | |
|         assert!(duty <= self.max_duty_cycle() as u32);
 | |
|         self.inner.set_compare_value(channel, duty)
 | |
|     }
 | |
| 
 | |
|     fn set_period<P>(&mut self, period: P)
 | |
|     where
 | |
|         P: Into<Self::Time>,
 | |
|     {
 | |
|         self.inner.set_frequency(period.into());
 | |
|     }
 | |
| }
 |