diff options
Diffstat (limited to 'embassy-stm32/src/timer/low_level.rs')
| -rw-r--r-- | embassy-stm32/src/timer/low_level.rs | 430 |
1 files changed, 321 insertions, 109 deletions
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index ac039bb0d..82e936f3a 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs | |||
| @@ -10,9 +10,10 @@ use core::mem::ManuallyDrop; | |||
| 10 | 10 | ||
| 11 | use embassy_hal_internal::Peri; | 11 | use embassy_hal_internal::Peri; |
| 12 | // Re-export useful enums | 12 | // Re-export useful enums |
| 13 | pub use stm32_metapac::timer::vals::{FilterValue, Sms as SlaveMode, Ts as TriggerSource}; | 13 | pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource}; |
| 14 | 14 | ||
| 15 | use super::*; | 15 | use super::*; |
| 16 | use crate::dma::{self, Transfer, WritableRingBuffer}; | ||
| 16 | use crate::pac::timer::vals; | 17 | use crate::pac::timer::vals; |
| 17 | use crate::rcc; | 18 | use crate::rcc; |
| 18 | use crate::time::Hertz; | 19 | use crate::time::Hertz; |
| @@ -143,20 +144,69 @@ pub enum OutputCompareMode { | |||
| 143 | /// TIMx_CNT<TIMx_CCRx else active. In downcounting, channel is active as long as | 144 | /// TIMx_CNT<TIMx_CCRx else active. In downcounting, channel is active as long as |
| 144 | /// TIMx_CNT>TIMx_CCRx else inactive. | 145 | /// TIMx_CNT>TIMx_CCRx else inactive. |
| 145 | PwmMode2, | 146 | PwmMode2, |
| 146 | // TODO: there's more modes here depending on the chip family. | 147 | |
| 148 | #[cfg(timer_v2)] | ||
| 149 | /// In up-counting mode, the channel is active until a trigger | ||
| 150 | /// event is detected (on tim_trgi signal). Then, a comparison is performed as in PWM | ||
| 151 | /// mode 1 and the channels becomes active again at the next update. In down-counting | ||
| 152 | /// mode, the channel is inactive until a trigger event is detected (on tim_trgi signal). | ||
| 153 | /// Then, a comparison is performed as in PWM mode 1 and the channels becomes | ||
| 154 | /// inactive again at the next update. | ||
| 155 | OnePulseMode1, | ||
| 156 | |||
| 157 | #[cfg(timer_v2)] | ||
| 158 | /// In up-counting mode, the channel is inactive until a | ||
| 159 | /// trigger event is detected (on tim_trgi signal). Then, a comparison is performed as in | ||
| 160 | /// PWM mode 2 and the channels becomes inactive again at the next update. In down | ||
| 161 | /// counting mode, the channel is active until a trigger event is detected (on tim_trgi | ||
| 162 | /// signal). Then, a comparison is performed as in PWM mode 1 and the channels | ||
| 163 | /// becomes active again at the next update. | ||
| 164 | OnePulseMode2, | ||
| 165 | |||
| 166 | #[cfg(timer_v2)] | ||
| 167 | /// Combined PWM mode 1 - tim_oc1ref has the same behavior as in PWM mode 1. | ||
| 168 | /// tim_oc1refc is the logical OR between tim_oc1ref and tim_oc2ref. | ||
| 169 | CombinedPwmMode1, | ||
| 170 | |||
| 171 | #[cfg(timer_v2)] | ||
| 172 | /// Combined PWM mode 2 - tim_oc1ref has the same behavior as in PWM mode 2. | ||
| 173 | /// tim_oc1refc is the logical AND between tim_oc1ref and tim_oc2ref. | ||
| 174 | CombinedPwmMode2, | ||
| 175 | |||
| 176 | #[cfg(timer_v2)] | ||
| 177 | /// tim_oc1ref has the same behavior as in PWM mode 1. tim_oc1refc outputs tim_oc1ref | ||
| 178 | /// when the counter is counting up, tim_oc2ref when it is counting down. | ||
| 179 | AsymmetricPwmMode1, | ||
| 180 | |||
| 181 | #[cfg(timer_v2)] | ||
| 182 | /// tim_oc1ref has the same behavior as in PWM mode 2. tim_oc1refc outputs tim_oc1ref | ||
| 183 | /// when the counter is counting up, tim_oc2ref when it is counting down. | ||
| 184 | AsymmetricPwmMode2, | ||
| 147 | } | 185 | } |
| 148 | 186 | ||
| 149 | impl From<OutputCompareMode> for stm32_metapac::timer::vals::Ocm { | 187 | impl From<OutputCompareMode> for crate::pac::timer::vals::Ocm { |
| 150 | fn from(mode: OutputCompareMode) -> Self { | 188 | fn from(mode: OutputCompareMode) -> Self { |
| 151 | match mode { | 189 | match mode { |
| 152 | OutputCompareMode::Frozen => stm32_metapac::timer::vals::Ocm::FROZEN, | 190 | OutputCompareMode::Frozen => crate::pac::timer::vals::Ocm::FROZEN, |
| 153 | OutputCompareMode::ActiveOnMatch => stm32_metapac::timer::vals::Ocm::ACTIVE_ON_MATCH, | 191 | OutputCompareMode::ActiveOnMatch => crate::pac::timer::vals::Ocm::ACTIVE_ON_MATCH, |
| 154 | OutputCompareMode::InactiveOnMatch => stm32_metapac::timer::vals::Ocm::INACTIVE_ON_MATCH, | 192 | OutputCompareMode::InactiveOnMatch => crate::pac::timer::vals::Ocm::INACTIVE_ON_MATCH, |
| 155 | OutputCompareMode::Toggle => stm32_metapac::timer::vals::Ocm::TOGGLE, | 193 | OutputCompareMode::Toggle => crate::pac::timer::vals::Ocm::TOGGLE, |
| 156 | OutputCompareMode::ForceInactive => stm32_metapac::timer::vals::Ocm::FORCE_INACTIVE, | 194 | OutputCompareMode::ForceInactive => crate::pac::timer::vals::Ocm::FORCE_INACTIVE, |
| 157 | OutputCompareMode::ForceActive => stm32_metapac::timer::vals::Ocm::FORCE_ACTIVE, | 195 | OutputCompareMode::ForceActive => crate::pac::timer::vals::Ocm::FORCE_ACTIVE, |
| 158 | OutputCompareMode::PwmMode1 => stm32_metapac::timer::vals::Ocm::PWM_MODE1, | 196 | OutputCompareMode::PwmMode1 => crate::pac::timer::vals::Ocm::PWM_MODE1, |
| 159 | OutputCompareMode::PwmMode2 => stm32_metapac::timer::vals::Ocm::PWM_MODE2, | 197 | OutputCompareMode::PwmMode2 => crate::pac::timer::vals::Ocm::PWM_MODE2, |
| 198 | #[cfg(timer_v2)] | ||
| 199 | OutputCompareMode::OnePulseMode1 => crate::pac::timer::vals::Ocm::RETRIGERRABLE_OPM_MODE_1, | ||
| 200 | #[cfg(timer_v2)] | ||
| 201 | OutputCompareMode::OnePulseMode2 => crate::pac::timer::vals::Ocm::RETRIGERRABLE_OPM_MODE_2, | ||
| 202 | #[cfg(timer_v2)] | ||
| 203 | OutputCompareMode::CombinedPwmMode1 => crate::pac::timer::vals::Ocm::COMBINED_PWM_MODE_1, | ||
| 204 | #[cfg(timer_v2)] | ||
| 205 | OutputCompareMode::CombinedPwmMode2 => crate::pac::timer::vals::Ocm::COMBINED_PWM_MODE_2, | ||
| 206 | #[cfg(timer_v2)] | ||
| 207 | OutputCompareMode::AsymmetricPwmMode1 => crate::pac::timer::vals::Ocm::ASYMMETRIC_PWM_MODE_1, | ||
| 208 | #[cfg(timer_v2)] | ||
| 209 | OutputCompareMode::AsymmetricPwmMode2 => crate::pac::timer::vals::Ocm::ASYMMETRIC_PWM_MODE_2, | ||
| 160 | } | 210 | } |
| 161 | } | 211 | } |
| 162 | } | 212 | } |
| @@ -218,11 +268,27 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 218 | unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) } | 268 | unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) } |
| 219 | } | 269 | } |
| 220 | 270 | ||
| 271 | #[cfg(stm32l0)] | ||
| 272 | fn regs_gp32_unchecked(&self) -> crate::pac::timer::TimGp16 { | ||
| 273 | unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) } | ||
| 274 | } | ||
| 275 | |||
| 221 | /// Start the timer. | 276 | /// Start the timer. |
| 222 | pub fn start(&self) { | 277 | pub fn start(&self) { |
| 223 | self.regs_core().cr1().modify(|r| r.set_cen(true)); | 278 | self.regs_core().cr1().modify(|r| r.set_cen(true)); |
| 224 | } | 279 | } |
| 225 | 280 | ||
| 281 | /// Generate timer update event from software. | ||
| 282 | /// | ||
| 283 | /// Set URS to avoid generating interrupt or DMA request. This update event is only | ||
| 284 | /// used to load value from pre-load registers. If called when the timer is running, | ||
| 285 | /// it may disrupt the output waveform. | ||
| 286 | pub fn generate_update_event(&self) { | ||
| 287 | self.regs_core().cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | ||
| 288 | self.regs_core().egr().write(|r| r.set_ug(true)); | ||
| 289 | self.regs_core().cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | ||
| 290 | } | ||
| 291 | |||
| 226 | /// Stop the timer. | 292 | /// Stop the timer. |
| 227 | pub fn stop(&self) { | 293 | pub fn stop(&self) { |
| 228 | self.regs_core().cr1().modify(|r| r.set_cen(false)); | 294 | self.regs_core().cr1().modify(|r| r.set_cen(false)); |
| @@ -235,7 +301,12 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 235 | 301 | ||
| 236 | /// get the capability of the timer | 302 | /// get the capability of the timer |
| 237 | pub fn bits(&self) -> TimerBits { | 303 | pub fn bits(&self) -> TimerBits { |
| 238 | T::BITS | 304 | match T::Word::bits() { |
| 305 | 16 => TimerBits::Bits16, | ||
| 306 | #[cfg(not(stm32l0))] | ||
| 307 | 32 => TimerBits::Bits32, | ||
| 308 | _ => unreachable!(), | ||
| 309 | } | ||
| 239 | } | 310 | } |
| 240 | 311 | ||
| 241 | /// Set the frequency of how many times per second the timer counts up to the max value or down to 0. | 312 | /// Set the frequency of how many times per second the timer counts up to the max value or down to 0. |
| @@ -245,18 +316,10 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 245 | /// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved | 316 | /// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved |
| 246 | /// because it needs to count up and down. | 317 | /// because it needs to count up and down. |
| 247 | pub fn set_frequency(&self, frequency: Hertz) { | 318 | pub fn set_frequency(&self, frequency: Hertz) { |
| 248 | match T::BITS { | 319 | self.set_frequency_internal(frequency, T::Word::bits()); |
| 249 | TimerBits::Bits16 => { | ||
| 250 | self.set_frequency_internal(frequency, 16); | ||
| 251 | } | ||
| 252 | #[cfg(not(stm32l0))] | ||
| 253 | TimerBits::Bits32 => { | ||
| 254 | self.set_frequency_internal(frequency, 32); | ||
| 255 | } | ||
| 256 | } | ||
| 257 | } | 320 | } |
| 258 | 321 | ||
| 259 | pub(crate) fn set_frequency_internal(&self, frequency: Hertz, max_divide_by_bits: u8) { | 322 | pub(crate) fn set_frequency_internal(&self, frequency: Hertz, max_divide_by_bits: usize) { |
| 260 | let f = frequency.0; | 323 | let f = frequency.0; |
| 261 | assert!(f > 0); | 324 | assert!(f > 0); |
| 262 | let timer_f = T::frequency().0; | 325 | let timer_f = T::frequency().0; |
| @@ -265,33 +328,15 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 265 | let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << max_divide_by_bits)).try_into()); | 328 | let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << max_divide_by_bits)).try_into()); |
| 266 | let divide_by = pclk_ticks_per_timer_period / (u64::from(psc) + 1); | 329 | let divide_by = pclk_ticks_per_timer_period / (u64::from(psc) + 1); |
| 267 | 330 | ||
| 268 | match T::BITS { | 331 | // the timer counts `0..=arr`, we want it to count `0..divide_by` |
| 269 | TimerBits::Bits16 => { | 332 | let arr: T::Word = unwrap!(T::Word::try_from(divide_by - 1)); |
| 270 | // the timer counts `0..=arr`, we want it to count `0..divide_by` | ||
| 271 | let arr = unwrap!(u16::try_from(divide_by - 1)); | ||
| 272 | |||
| 273 | let regs = self.regs_core(); | ||
| 274 | regs.psc().write_value(psc); | ||
| 275 | regs.arr().write(|r| r.set_arr(arr)); | ||
| 276 | |||
| 277 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | ||
| 278 | regs.egr().write(|r| r.set_ug(true)); | ||
| 279 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | ||
| 280 | } | ||
| 281 | #[cfg(not(stm32l0))] | ||
| 282 | TimerBits::Bits32 => { | ||
| 283 | // the timer counts `0..=arr`, we want it to count `0..divide_by` | ||
| 284 | let arr: u32 = unwrap!(u32::try_from(divide_by - 1)); | ||
| 285 | |||
| 286 | let regs = self.regs_gp32_unchecked(); | ||
| 287 | regs.psc().write_value(psc); | ||
| 288 | regs.arr().write_value(arr); | ||
| 289 | 333 | ||
| 290 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | 334 | let regs = self.regs_gp32_unchecked(); |
| 291 | regs.egr().write(|r| r.set_ug(true)); | 335 | regs.psc().write_value(psc); |
| 292 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | 336 | #[cfg(stm32l0)] |
| 293 | } | 337 | regs.arr().write(|r| r.set_arr(unwrap!(arr.try_into()))); |
| 294 | } | 338 | #[cfg(not(stm32l0))] |
| 339 | regs.arr().write_value(arr.into()); | ||
| 295 | } | 340 | } |
| 296 | 341 | ||
| 297 | /// Set tick frequency. | 342 | /// Set tick frequency. |
| @@ -340,23 +385,14 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 340 | pub fn get_frequency(&self) -> Hertz { | 385 | pub fn get_frequency(&self) -> Hertz { |
| 341 | let timer_f = T::frequency(); | 386 | let timer_f = T::frequency(); |
| 342 | 387 | ||
| 343 | match T::BITS { | 388 | let regs = self.regs_gp32_unchecked(); |
| 344 | TimerBits::Bits16 => { | 389 | #[cfg(not(stm32l0))] |
| 345 | let regs = self.regs_core(); | 390 | let arr = regs.arr().read(); |
| 346 | let arr = regs.arr().read().arr(); | 391 | #[cfg(stm32l0)] |
| 347 | let psc = regs.psc().read(); | 392 | let arr = regs.arr().read().arr(); |
| 348 | 393 | let psc = regs.psc().read(); | |
| 349 | timer_f / arr / (psc + 1) | ||
| 350 | } | ||
| 351 | #[cfg(not(stm32l0))] | ||
| 352 | TimerBits::Bits32 => { | ||
| 353 | let regs = self.regs_gp32_unchecked(); | ||
| 354 | let arr = regs.arr().read(); | ||
| 355 | let psc = regs.psc().read(); | ||
| 356 | 394 | ||
| 357 | timer_f / arr / (psc + 1) | 395 | timer_f / arr / (psc + 1) |
| 358 | } | ||
| 359 | } | ||
| 360 | } | 396 | } |
| 361 | 397 | ||
| 362 | /// Get the clock frequency of the timer (before prescaler is applied). | 398 | /// Get the clock frequency of the timer (before prescaler is applied). |
| @@ -416,42 +452,29 @@ impl<'d, T: GeneralInstance1Channel> Timer<'d, T> { | |||
| 416 | } | 452 | } |
| 417 | 453 | ||
| 418 | /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. | 454 | /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. |
| 419 | pub fn get_max_compare_value(&self) -> u32 { | 455 | pub fn get_max_compare_value(&self) -> T::Word { |
| 420 | match T::BITS { | 456 | #[cfg(not(stm32l0))] |
| 421 | TimerBits::Bits16 => self.regs_1ch().arr().read().arr() as u32, | 457 | return unwrap!(self.regs_gp32_unchecked().arr().read().try_into()); |
| 422 | #[cfg(not(stm32l0))] | 458 | #[cfg(stm32l0)] |
| 423 | TimerBits::Bits32 => self.regs_gp32_unchecked().arr().read(), | 459 | return unwrap!(self.regs_gp32_unchecked().arr().read().arr().try_into()); |
| 424 | } | ||
| 425 | } | 460 | } |
| 426 | 461 | ||
| 427 | /// Set the max compare value. | 462 | /// Set the max compare value. |
| 428 | /// | 463 | /// |
| 429 | /// An update event is generated to load the new value. The update event is | 464 | /// An update event is generated to load the new value. The update event is |
| 430 | /// generated such that it will not cause an interrupt or DMA request. | 465 | /// generated such that it will not cause an interrupt or DMA request. |
| 431 | pub fn set_max_compare_value(&self, ticks: u32) { | 466 | pub fn set_max_compare_value(&self, ticks: T::Word) { |
| 432 | match T::BITS { | 467 | let arr = ticks; |
| 433 | TimerBits::Bits16 => { | ||
| 434 | let arr = unwrap!(u16::try_from(ticks)); | ||
| 435 | |||
| 436 | let regs = self.regs_1ch(); | ||
| 437 | regs.arr().write(|r| r.set_arr(arr)); | ||
| 438 | |||
| 439 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | ||
| 440 | regs.egr().write(|r| r.set_ug(true)); | ||
| 441 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | ||
| 442 | } | ||
| 443 | #[cfg(not(stm32l0))] | ||
| 444 | TimerBits::Bits32 => { | ||
| 445 | let arr = ticks; | ||
| 446 | 468 | ||
| 447 | let regs = self.regs_gp32_unchecked(); | 469 | let regs = self.regs_gp32_unchecked(); |
| 448 | regs.arr().write_value(arr); | 470 | #[cfg(not(stm32l0))] |
| 471 | regs.arr().write_value(arr.into()); | ||
| 472 | #[cfg(stm32l0)] | ||
| 473 | regs.arr().write(|r| r.set_arr(unwrap!(arr.try_into()))); | ||
| 449 | 474 | ||
| 450 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | 475 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); |
| 451 | regs.egr().write(|r| r.set_ug(true)); | 476 | regs.egr().write(|r| r.set_ug(true)); |
| 452 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | 477 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); |
| 453 | } | ||
| 454 | } | ||
| 455 | } | 478 | } |
| 456 | } | 479 | } |
| 457 | 480 | ||
| @@ -585,30 +608,204 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { | |||
| 585 | } | 608 | } |
| 586 | 609 | ||
| 587 | /// Set compare value for a channel. | 610 | /// Set compare value for a channel. |
| 588 | pub fn set_compare_value(&self, channel: Channel, value: u32) { | 611 | pub fn set_compare_value(&self, channel: Channel, value: T::Word) { |
| 589 | match T::BITS { | 612 | #[cfg(not(stm32l0))] |
| 590 | TimerBits::Bits16 => { | 613 | self.regs_gp32_unchecked() |
| 591 | let value = unwrap!(u16::try_from(value)); | 614 | .ccr(channel.index()) |
| 592 | self.regs_gp16().ccr(channel.index()).modify(|w| w.set_ccr(value)); | 615 | .write_value(value.into()); |
| 593 | } | 616 | #[cfg(stm32l0)] |
| 594 | #[cfg(not(stm32l0))] | 617 | self.regs_gp16() |
| 595 | TimerBits::Bits32 => { | 618 | .ccr(channel.index()) |
| 596 | self.regs_gp32_unchecked().ccr(channel.index()).write_value(value); | 619 | .modify(|w| w.set_ccr(unwrap!(value.try_into()))); |
| 597 | } | ||
| 598 | } | ||
| 599 | } | 620 | } |
| 600 | 621 | ||
| 601 | /// Get compare value for a channel. | 622 | /// Get compare value for a channel. |
| 602 | pub fn get_compare_value(&self, channel: Channel) -> u32 { | 623 | pub fn get_compare_value(&self, channel: Channel) -> T::Word { |
| 603 | match T::BITS { | 624 | #[cfg(not(stm32l0))] |
| 604 | TimerBits::Bits16 => self.regs_gp16().ccr(channel.index()).read().ccr() as u32, | 625 | return unwrap!(self.regs_gp32_unchecked().ccr(channel.index()).read().try_into()); |
| 605 | #[cfg(not(stm32l0))] | 626 | #[cfg(stm32l0)] |
| 606 | TimerBits::Bits32 => self.regs_gp32_unchecked().ccr(channel.index()).read(), | 627 | return unwrap!(self.regs_gp32_unchecked().ccr(channel.index()).read().ccr().try_into()); |
| 628 | } | ||
| 629 | |||
| 630 | pub(crate) fn clamp_compare_value<W: Word>(&mut self, channel: Channel) { | ||
| 631 | self.set_compare_value( | ||
| 632 | channel, | ||
| 633 | unwrap!( | ||
| 634 | self.get_compare_value(channel) | ||
| 635 | .into() | ||
| 636 | .clamp(0, W::max() as u32) | ||
| 637 | .try_into() | ||
| 638 | ), | ||
| 639 | ); | ||
| 640 | } | ||
| 641 | |||
| 642 | /// Setup a ring buffer for the channel | ||
| 643 | pub fn setup_ring_buffer<'a, W: Word + Into<T::Word>>( | ||
| 644 | &mut self, | ||
| 645 | dma: Peri<'a, impl super::UpDma<T>>, | ||
| 646 | channel: Channel, | ||
| 647 | dma_buf: &'a mut [W], | ||
| 648 | ) -> WritableRingBuffer<'a, W> { | ||
| 649 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 650 | let req = dma.request(); | ||
| 651 | |||
| 652 | unsafe { | ||
| 653 | use crate::dma::TransferOptions; | ||
| 654 | #[cfg(not(any(bdma, gpdma)))] | ||
| 655 | use crate::dma::{Burst, FifoThreshold}; | ||
| 656 | |||
| 657 | let dma_transfer_option = TransferOptions { | ||
| 658 | #[cfg(not(any(bdma, gpdma)))] | ||
| 659 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 660 | #[cfg(not(any(bdma, gpdma)))] | ||
| 661 | mburst: Burst::Incr8, | ||
| 662 | ..Default::default() | ||
| 663 | }; | ||
| 664 | |||
| 665 | WritableRingBuffer::new( | ||
| 666 | dma, | ||
| 667 | req, | ||
| 668 | self.regs_1ch().ccr(channel.index()).as_ptr() as *mut W, | ||
| 669 | dma_buf, | ||
| 670 | dma_transfer_option, | ||
| 671 | ) | ||
| 672 | } | ||
| 673 | } | ||
| 674 | |||
| 675 | /// Generate a sequence of PWM waveform | ||
| 676 | /// | ||
| 677 | /// Note: | ||
| 678 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | ||
| 679 | pub fn setup_update_dma<'a, W: Word + Into<T::Word>>( | ||
| 680 | &mut self, | ||
| 681 | dma: Peri<'a, impl super::UpDma<T>>, | ||
| 682 | channel: Channel, | ||
| 683 | duty: &'a [W], | ||
| 684 | ) -> Transfer<'a> { | ||
| 685 | self.setup_update_dma_inner(dma.request(), dma, channel, duty) | ||
| 686 | } | ||
| 687 | |||
| 688 | /// Generate a sequence of PWM waveform | ||
| 689 | /// | ||
| 690 | /// Note: | ||
| 691 | /// The DMA channel provided does not need to correspond to the requested channel. | ||
| 692 | pub fn setup_channel_update_dma<'a, C: TimerChannel, W: Word + Into<T::Word>>( | ||
| 693 | &mut self, | ||
| 694 | dma: Peri<'a, impl super::Dma<T, C>>, | ||
| 695 | channel: Channel, | ||
| 696 | duty: &'a [W], | ||
| 697 | ) -> Transfer<'a> { | ||
| 698 | self.setup_update_dma_inner(dma.request(), dma, channel, duty) | ||
| 699 | } | ||
| 700 | |||
| 701 | fn setup_update_dma_inner<'a, W: Word + Into<T::Word>>( | ||
| 702 | &mut self, | ||
| 703 | request: dma::Request, | ||
| 704 | dma: Peri<'a, impl dma::Channel>, | ||
| 705 | channel: Channel, | ||
| 706 | duty: &'a [W], | ||
| 707 | ) -> Transfer<'a> { | ||
| 708 | unsafe { | ||
| 709 | #[cfg(not(any(bdma, gpdma)))] | ||
| 710 | use crate::dma::{Burst, FifoThreshold}; | ||
| 711 | use crate::dma::{Transfer, TransferOptions}; | ||
| 712 | |||
| 713 | let dma_transfer_option = TransferOptions { | ||
| 714 | #[cfg(not(any(bdma, gpdma)))] | ||
| 715 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 716 | #[cfg(not(any(bdma, gpdma)))] | ||
| 717 | mburst: Burst::Incr8, | ||
| 718 | ..Default::default() | ||
| 719 | }; | ||
| 720 | |||
| 721 | Transfer::new_write( | ||
| 722 | dma, | ||
| 723 | request, | ||
| 724 | duty, | ||
| 725 | self.regs_gp16().ccr(channel.index()).as_ptr() as *mut W, | ||
| 726 | dma_transfer_option, | ||
| 727 | ) | ||
| 728 | } | ||
| 729 | } | ||
| 730 | |||
| 731 | /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. | ||
| 732 | /// | ||
| 733 | /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers | ||
| 734 | /// in sequence on each update event (UEV). The data is written via the DMAR register using the | ||
| 735 | /// DMA base address (DBA) and burst length (DBL) configured in the DCR register. | ||
| 736 | /// | ||
| 737 | /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row | ||
| 738 | /// represents a single update event and each column corresponds to a specific timer channel (starting | ||
| 739 | /// from `starting_channel` up to and including `ending_channel`). | ||
| 740 | /// | ||
| 741 | /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: | ||
| 742 | /// | ||
| 743 | /// ```rust,ignore | ||
| 744 | /// let dma_buf: [u16; 16] = [ | ||
| 745 | /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 | ||
| 746 | /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 | ||
| 747 | /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 | ||
| 748 | /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 | ||
| 749 | /// ]; | ||
| 750 | /// ``` | ||
| 751 | /// | ||
| 752 | /// Each group of `N` values (where `N` is number of channels) is transferred on one update event, | ||
| 753 | /// updating the duty cycles of all selected channels simultaneously. | ||
| 754 | /// | ||
| 755 | /// Note: | ||
| 756 | /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. | ||
| 757 | /// Also be aware that embassy timers use one of timers internally. It is possible to | ||
| 758 | /// switch this timer by using `time-driver-timX` feature. | ||
| 759 | /// | ||
| 760 | pub fn setup_update_dma_burst<'a, W: Word + Into<T::Word>>( | ||
| 761 | &mut self, | ||
| 762 | dma: Peri<'a, impl super::UpDma<T>>, | ||
| 763 | starting_channel: Channel, | ||
| 764 | ending_channel: Channel, | ||
| 765 | duty: &'a [W], | ||
| 766 | ) -> Transfer<'a> { | ||
| 767 | let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32; | ||
| 768 | let start_ch_index = starting_channel.index(); | ||
| 769 | let end_ch_index = ending_channel.index(); | ||
| 770 | |||
| 771 | assert!(start_ch_index <= end_ch_index); | ||
| 772 | |||
| 773 | let ccrx_addr = self.regs_gp16().ccr(start_ch_index).as_ptr() as u32; | ||
| 774 | self.regs_gp16() | ||
| 775 | .dcr() | ||
| 776 | .modify(|w| w.set_dba(((ccrx_addr - cr1_addr) / 4) as u8)); | ||
| 777 | self.regs_gp16() | ||
| 778 | .dcr() | ||
| 779 | .modify(|w| w.set_dbl((end_ch_index - start_ch_index) as u8)); | ||
| 780 | |||
| 781 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 782 | let req = dma.request(); | ||
| 783 | |||
| 784 | unsafe { | ||
| 785 | #[cfg(not(any(bdma, gpdma)))] | ||
| 786 | use crate::dma::{Burst, FifoThreshold}; | ||
| 787 | use crate::dma::{Transfer, TransferOptions}; | ||
| 788 | |||
| 789 | let dma_transfer_option = TransferOptions { | ||
| 790 | #[cfg(not(any(bdma, gpdma)))] | ||
| 791 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 792 | #[cfg(not(any(bdma, gpdma)))] | ||
| 793 | mburst: Burst::Incr4, | ||
| 794 | ..Default::default() | ||
| 795 | }; | ||
| 796 | |||
| 797 | Transfer::new_write( | ||
| 798 | dma, | ||
| 799 | req, | ||
| 800 | duty, | ||
| 801 | self.regs_gp16().dmar().as_ptr() as *mut W, | ||
| 802 | dma_transfer_option, | ||
| 803 | ) | ||
| 607 | } | 804 | } |
| 608 | } | 805 | } |
| 609 | 806 | ||
| 610 | /// Get capture value for a channel. | 807 | /// Get capture value for a channel. |
| 611 | pub fn get_capture_value(&self, channel: Channel) -> u32 { | 808 | pub fn get_capture_value(&self, channel: Channel) -> T::Word { |
| 612 | self.get_compare_value(channel) | 809 | self.get_compare_value(channel) |
| 613 | } | 810 | } |
| 614 | 811 | ||
| @@ -640,6 +837,11 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { | |||
| 640 | self.regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde)) | 837 | self.regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde)) |
| 641 | } | 838 | } |
| 642 | 839 | ||
| 840 | /// Set Timer Master Mode | ||
| 841 | pub fn set_master_mode(&self, mms: MasterMode) { | ||
| 842 | self.regs_gp16().cr2().modify(|w| w.set_mms(mms)); | ||
| 843 | } | ||
| 844 | |||
| 643 | /// Set Timer Slave Mode | 845 | /// Set Timer Slave Mode |
| 644 | pub fn set_slave_mode(&self, sms: SlaveMode) { | 846 | pub fn set_slave_mode(&self, sms: SlaveMode) { |
| 645 | self.regs_gp16().smcr().modify(|r| r.set_sms(sms)); | 847 | self.regs_gp16().smcr().modify(|r| r.set_sms(sms)); |
| @@ -760,6 +962,16 @@ impl<'d, T: AdvancedInstance4Channel> Timer<'d, T> { | |||
| 760 | self.regs_advanced().cr2().modify(|w| w.set_oisn(channel.index(), val)); | 962 | self.regs_advanced().cr2().modify(|w| w.set_oisn(channel.index(), val)); |
| 761 | } | 963 | } |
| 762 | 964 | ||
| 965 | /// Set master mode selection 2 | ||
| 966 | pub fn set_mms2_selection(&self, mms2: vals::Mms2) { | ||
| 967 | self.regs_advanced().cr2().modify(|w| w.set_mms2(mms2)); | ||
| 968 | } | ||
| 969 | |||
| 970 | /// Set repetition counter | ||
| 971 | pub fn set_repetition_counter(&self, val: u16) { | ||
| 972 | self.regs_advanced().rcr().modify(|w| w.set_rep(val)); | ||
| 973 | } | ||
| 974 | |||
| 763 | /// Trigger software break 1 or 2 | 975 | /// Trigger software break 1 or 2 |
| 764 | /// Setting this bit generates a break event. This bit is automatically cleared by the hardware. | 976 | /// Setting this bit generates a break event. This bit is automatically cleared by the hardware. |
| 765 | pub fn trigger_software_break(&self, n: usize) { | 977 | pub fn trigger_software_break(&self, n: usize) { |
