| 1 | //! Low-level timer driver. |
| 2 | //! |
| 3 | //! This is an unopinionated, very low-level driver for all STM32 timers. It allows direct register |
| 4 | //! manipulation with the `regs_*()` methods, and has utility functions that are thin wrappers |
| 5 | //! over the registers. |
| 6 | //! |
| 7 | //! The available functionality depends on the timer type. |
| 8 | |
| 9 | use core::mem::ManuallyDrop; |
| 10 | |
| 11 | use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; |
| 12 | // Re-export useful enums |
| 13 | pub use stm32_metapac::timer::vals::{FilterValue, Sms as SlaveMode, Ts as TriggerSource}; |
| 14 | |
| 15 | use super::*; |
| 16 | use crate::pac::timer::vals; |
| 17 | use crate::rcc; |
| 18 | use crate::time::Hertz; |
| 19 | |
| 20 | /// Input capture mode. |
| 21 | #[derive (Clone, Copy)] |
| 22 | pub enum InputCaptureMode { |
| 23 | /// Rising edge only. |
| 24 | Rising, |
| 25 | /// Falling edge only. |
| 26 | Falling, |
| 27 | /// Both rising or falling edges. |
| 28 | BothEdges, |
| 29 | } |
| 30 | |
| 31 | /// Input TI selection. |
| 32 | #[derive (Clone, Copy)] |
| 33 | pub enum InputTISelection { |
| 34 | /// Normal |
| 35 | Normal, |
| 36 | /// Alternate |
| 37 | Alternate, |
| 38 | /// TRC |
| 39 | TRC, |
| 40 | } |
| 41 | |
| 42 | impl From<InputTISelection> for stm32_metapac::timer::vals::CcmrInputCcs { |
| 43 | fn from(tisel: InputTISelection) -> Self { |
| 44 | match tisel { |
| 45 | InputTISelection::Normal => stm32_metapac::timer::vals::CcmrInputCcs::TI4, |
| 46 | InputTISelection::Alternate => stm32_metapac::timer::vals::CcmrInputCcs::TI3, |
| 47 | InputTISelection::TRC => stm32_metapac::timer::vals::CcmrInputCcs::TRC, |
| 48 | } |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | /// Timer counting mode. |
| 53 | #[repr (u8)] |
| 54 | #[derive (Debug, Clone, Copy, PartialEq, Eq, Default)] |
| 55 | pub enum CountingMode { |
| 56 | #[default] |
| 57 | /// The timer counts up to the reload value and then resets back to 0. |
| 58 | EdgeAlignedUp, |
| 59 | /// The timer counts down to 0 and then resets back to the reload value. |
| 60 | EdgeAlignedDown, |
| 61 | /// The timer counts up to the reload value and then counts back to 0. |
| 62 | /// |
| 63 | /// The output compare interrupt flags of channels configured in output are |
| 64 | /// set when the counter is counting down. |
| 65 | CenterAlignedDownInterrupts, |
| 66 | /// The timer counts up to the reload value and then counts back to 0. |
| 67 | /// |
| 68 | /// The output compare interrupt flags of channels configured in output are |
| 69 | /// set when the counter is counting up. |
| 70 | CenterAlignedUpInterrupts, |
| 71 | /// The timer counts up to the reload value and then counts back to 0. |
| 72 | /// |
| 73 | /// The output compare interrupt flags of channels configured in output are |
| 74 | /// set when the counter is counting both up or down. |
| 75 | CenterAlignedBothInterrupts, |
| 76 | } |
| 77 | |
| 78 | impl CountingMode { |
| 79 | /// Return whether this mode is edge-aligned (up or down). |
| 80 | pub fn is_edge_aligned(&self) -> bool { |
| 81 | matches!(self, CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown) |
| 82 | } |
| 83 | |
| 84 | /// Return whether this mode is center-aligned. |
| 85 | pub fn is_center_aligned(&self) -> bool { |
| 86 | matches!( |
| 87 | self, |
| 88 | CountingMode::CenterAlignedDownInterrupts |
| 89 | | CountingMode::CenterAlignedUpInterrupts |
| 90 | | CountingMode::CenterAlignedBothInterrupts |
| 91 | ) |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | impl From<CountingMode> for (vals::Cms, vals::Dir) { |
| 96 | fn from(value: CountingMode) -> Self { |
| 97 | match value { |
| 98 | CountingMode::EdgeAlignedUp => (vals::Cms::EDGE_ALIGNED, vals::Dir::UP), |
| 99 | CountingMode::EdgeAlignedDown => (vals::Cms::EDGE_ALIGNED, vals::Dir::DOWN), |
| 100 | CountingMode::CenterAlignedDownInterrupts => (vals::Cms::CENTER_ALIGNED1, vals::Dir::UP), |
| 101 | CountingMode::CenterAlignedUpInterrupts => (vals::Cms::CENTER_ALIGNED2, vals::Dir::UP), |
| 102 | CountingMode::CenterAlignedBothInterrupts => (vals::Cms::CENTER_ALIGNED3, vals::Dir::UP), |
| 103 | } |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | impl From<(vals::Cms, vals::Dir)> for CountingMode { |
| 108 | fn from(value: (vals::Cms, vals::Dir)) -> Self { |
| 109 | match value { |
| 110 | (vals::Cms::EDGE_ALIGNED, vals::Dir::UP) => CountingMode::EdgeAlignedUp, |
| 111 | (vals::Cms::EDGE_ALIGNED, vals::Dir::DOWN) => CountingMode::EdgeAlignedDown, |
| 112 | (vals::Cms::CENTER_ALIGNED1, _) => CountingMode::CenterAlignedDownInterrupts, |
| 113 | (vals::Cms::CENTER_ALIGNED2, _) => CountingMode::CenterAlignedUpInterrupts, |
| 114 | (vals::Cms::CENTER_ALIGNED3, _) => CountingMode::CenterAlignedBothInterrupts, |
| 115 | } |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | /// Output compare mode. |
| 120 | #[derive (Clone, Copy)] |
| 121 | pub enum OutputCompareMode { |
| 122 | /// The comparison between the output compare register TIMx_CCRx and |
| 123 | /// the counter TIMx_CNT has no effect on the outputs. |
| 124 | /// (this mode is used to generate a timing base). |
| 125 | Frozen, |
| 126 | /// Set channel to active level on match. OCxREF signal is forced high when the |
| 127 | /// counter TIMx_CNT matches the capture/compare register x (TIMx_CCRx). |
| 128 | ActiveOnMatch, |
| 129 | /// Set channel to inactive level on match. OCxREF signal is forced low when the |
| 130 | /// counter TIMx_CNT matches the capture/compare register x (TIMx_CCRx). |
| 131 | InactiveOnMatch, |
| 132 | /// Toggle - OCxREF toggles when TIMx_CNT=TIMx_CCRx. |
| 133 | Toggle, |
| 134 | /// Force inactive level - OCxREF is forced low. |
| 135 | ForceInactive, |
| 136 | /// Force active level - OCxREF is forced high. |
| 137 | ForceActive, |
| 138 | /// PWM mode 1 - In upcounting, channel is active as long as TIMx_CNT<TIMx_CCRx |
| 139 | /// else inactive. In downcounting, channel is inactive (OCxREF=0) as long as |
| 140 | /// TIMx_CNT>TIMx_CCRx else active (OCxREF=1). |
| 141 | PwmMode1, |
| 142 | /// PWM mode 2 - In upcounting, channel is inactive as long as |
| 143 | /// TIMx_CNT<TIMx_CCRx else active. In downcounting, channel is active as long as |
| 144 | /// TIMx_CNT>TIMx_CCRx else inactive. |
| 145 | PwmMode2, |
| 146 | // TODO: there's more modes here depending on the chip family. |
| 147 | } |
| 148 | |
| 149 | impl From<OutputCompareMode> for stm32_metapac::timer::vals::Ocm { |
| 150 | fn from(mode: OutputCompareMode) -> Self { |
| 151 | match mode { |
| 152 | OutputCompareMode::Frozen => stm32_metapac::timer::vals::Ocm::FROZEN, |
| 153 | OutputCompareMode::ActiveOnMatch => stm32_metapac::timer::vals::Ocm::ACTIVE_ON_MATCH, |
| 154 | OutputCompareMode::InactiveOnMatch => stm32_metapac::timer::vals::Ocm::INACTIVE_ON_MATCH, |
| 155 | OutputCompareMode::Toggle => stm32_metapac::timer::vals::Ocm::TOGGLE, |
| 156 | OutputCompareMode::ForceInactive => stm32_metapac::timer::vals::Ocm::FORCE_INACTIVE, |
| 157 | OutputCompareMode::ForceActive => stm32_metapac::timer::vals::Ocm::FORCE_ACTIVE, |
| 158 | OutputCompareMode::PwmMode1 => stm32_metapac::timer::vals::Ocm::PWM_MODE1, |
| 159 | OutputCompareMode::PwmMode2 => stm32_metapac::timer::vals::Ocm::PWM_MODE2, |
| 160 | } |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | /// Timer output pin polarity. |
| 165 | #[derive (Clone, Copy)] |
| 166 | pub enum OutputPolarity { |
| 167 | /// Active high (higher duty value makes the pin spend more time high). |
| 168 | ActiveHigh, |
| 169 | /// Active low (higher duty value makes the pin spend more time low). |
| 170 | ActiveLow, |
| 171 | } |
| 172 | |
| 173 | impl From<OutputPolarity> for bool { |
| 174 | fn from(mode: OutputPolarity) -> Self { |
| 175 | match mode { |
| 176 | OutputPolarity::ActiveHigh => false, |
| 177 | OutputPolarity::ActiveLow => true, |
| 178 | } |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | /// Low-level timer driver. |
| 183 | pub struct Timer<'d, T: CoreInstance> { |
| 184 | tim: PeripheralRef<'d, T>, |
| 185 | } |
| 186 | |
| 187 | impl<'d, T: CoreInstance> Drop for Timer<'d, T> { |
| 188 | fn drop(&mut self) { |
| 189 | rcc::disable::<T>(); |
| 190 | } |
| 191 | } |
| 192 | |
| 193 | impl<'d, T: CoreInstance> Timer<'d, T> { |
| 194 | /// Create a new timer driver. |
| 195 | pub fn new(tim: impl Peripheral<P = T> + 'd) -> Self { |
| 196 | into_ref!(tim); |
| 197 | |
| 198 | rcc::enable_and_reset::<T>(); |
| 199 | |
| 200 | Self { tim } |
| 201 | } |
| 202 | |
| 203 | pub(crate) unsafe fn clone_unchecked(&self) -> ManuallyDrop<Self> { |
| 204 | let tim = unsafe { self.tim.clone_unchecked() }; |
| 205 | ManuallyDrop::new(Self { tim }) |
| 206 | } |
| 207 | |
| 208 | /// Get access to the virutal core 16bit timer registers. |
| 209 | /// |
| 210 | /// Note: This works even if the timer is more capable, because registers |
| 211 | /// for the less capable timers are a subset. This allows writing a driver |
| 212 | /// for a given set of capabilities, and having it transparently work with |
| 213 | /// more capable timers. |
| 214 | pub fn regs_core(&self) -> crate::pac::timer::TimCore { |
| 215 | unsafe { crate::pac::timer::TimCore::from_ptr(T::regs()) } |
| 216 | } |
| 217 | |
| 218 | #[cfg (not(stm32l0))] |
| 219 | fn regs_gp32_unchecked(&self) -> crate::pac::timer::TimGp32 { |
| 220 | unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) } |
| 221 | } |
| 222 | |
| 223 | /// Start the timer. |
| 224 | pub fn start(&self) { |
| 225 | self.regs_core().cr1().modify(|r| r.set_cen(true)); |
| 226 | } |
| 227 | |
| 228 | /// Stop the timer. |
| 229 | pub fn stop(&self) { |
| 230 | self.regs_core().cr1().modify(|r| r.set_cen(false)); |
| 231 | } |
| 232 | |
| 233 | /// Reset the counter value to 0 |
| 234 | pub fn reset(&self) { |
| 235 | self.regs_core().cnt().write(|r| r.set_cnt(0)); |
| 236 | } |
| 237 | |
| 238 | /// Set the frequency of how many times per second the timer counts up to the max value or down to 0. |
| 239 | /// |
| 240 | /// This means that in the default edge-aligned mode, |
| 241 | /// the timer counter will wrap around at the same frequency as is being set. |
| 242 | /// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved |
| 243 | /// because it needs to count up and down. |
| 244 | pub fn set_frequency(&self, frequency: Hertz) { |
| 245 | match T::BITS { |
| 246 | TimerBits::Bits16 => { |
| 247 | self.set_frequency_internal(frequency, 16); |
| 248 | } |
| 249 | #[cfg (not(stm32l0))] |
| 250 | TimerBits::Bits32 => { |
| 251 | self.set_frequency_internal(frequency, 32); |
| 252 | } |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | pub(crate) fn set_frequency_internal(&self, frequency: Hertz, max_divide_by_bits: u8) { |
| 257 | let f = frequency.0; |
| 258 | assert!(f > 0); |
| 259 | let timer_f = T::frequency().0; |
| 260 | |
| 261 | let pclk_ticks_per_timer_period = (timer_f / f) as u64; |
| 262 | let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << max_divide_by_bits)).try_into()); |
| 263 | let divide_by = pclk_ticks_per_timer_period / (u64::from(psc) + 1); |
| 264 | |
| 265 | match T::BITS { |
| 266 | TimerBits::Bits16 => { |
| 267 | // the timer counts `0..=arr`, we want it to count `0..divide_by` |
| 268 | let arr = unwrap!(u16::try_from(divide_by - 1)); |
| 269 | |
| 270 | let regs = self.regs_core(); |
| 271 | regs.psc().write_value(psc); |
| 272 | regs.arr().write(|r| r.set_arr(arr)); |
| 273 | |
| 274 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); |
| 275 | regs.egr().write(|r| r.set_ug(true)); |
| 276 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); |
| 277 | } |
| 278 | #[cfg (not(stm32l0))] |
| 279 | TimerBits::Bits32 => { |
| 280 | // the timer counts `0..=arr`, we want it to count `0..divide_by` |
| 281 | let arr: u32 = unwrap!(u32::try_from(divide_by - 1)); |
| 282 | |
| 283 | let regs = self.regs_gp32_unchecked(); |
| 284 | regs.psc().write_value(psc); |
| 285 | regs.arr().write_value(arr); |
| 286 | |
| 287 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); |
| 288 | regs.egr().write(|r| r.set_ug(true)); |
| 289 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); |
| 290 | } |
| 291 | } |
| 292 | } |
| 293 | |
| 294 | /// Set tick frequency. |
| 295 | pub fn set_tick_freq(&mut self, freq: Hertz) { |
| 296 | let f = freq; |
| 297 | assert!(f.0 > 0); |
| 298 | let timer_f = self.get_clock_frequency(); |
| 299 | |
| 300 | let pclk_ticks_per_timer_period = timer_f / f; |
| 301 | let psc: u16 = unwrap!((pclk_ticks_per_timer_period - 1).try_into()); |
| 302 | |
| 303 | let regs = self.regs_core(); |
| 304 | regs.psc().write_value(psc); |
| 305 | |
| 306 | // Generate an Update Request |
| 307 | regs.egr().write(|r| r.set_ug(true)); |
| 308 | } |
| 309 | |
| 310 | /// Clear update interrupt. |
| 311 | /// |
| 312 | /// Returns whether the update interrupt flag was set. |
| 313 | pub fn clear_update_interrupt(&self) -> bool { |
| 314 | let regs = self.regs_core(); |
| 315 | let sr = regs.sr().read(); |
| 316 | if sr.uif() { |
| 317 | regs.sr().modify(|r| { |
| 318 | r.set_uif(false); |
| 319 | }); |
| 320 | true |
| 321 | } else { |
| 322 | false |
| 323 | } |
| 324 | } |
| 325 | |
| 326 | /// Enable/disable the update interrupt. |
| 327 | pub fn enable_update_interrupt(&self, enable: bool) { |
| 328 | self.regs_core().dier().modify(|r| r.set_uie(enable)); |
| 329 | } |
| 330 | |
| 331 | /// Enable/disable autoreload preload. |
| 332 | pub fn set_autoreload_preload(&self, enable: bool) { |
| 333 | self.regs_core().cr1().modify(|r| r.set_arpe(enable)); |
| 334 | } |
| 335 | |
| 336 | /// Get the timer frequency. |
| 337 | pub fn get_frequency(&self) -> Hertz { |
| 338 | let timer_f = T::frequency(); |
| 339 | |
| 340 | match T::BITS { |
| 341 | TimerBits::Bits16 => { |
| 342 | let regs = self.regs_core(); |
| 343 | let arr = regs.arr().read().arr(); |
| 344 | let psc = regs.psc().read(); |
| 345 | |
| 346 | timer_f / arr / (psc + 1) |
| 347 | } |
| 348 | #[cfg (not(stm32l0))] |
| 349 | TimerBits::Bits32 => { |
| 350 | let regs = self.regs_gp32_unchecked(); |
| 351 | let arr = regs.arr().read(); |
| 352 | let psc = regs.psc().read(); |
| 353 | |
| 354 | timer_f / arr / (psc + 1) |
| 355 | } |
| 356 | } |
| 357 | } |
| 358 | |
| 359 | /// Get the clock frequency of the timer (before prescaler is applied). |
| 360 | pub fn get_clock_frequency(&self) -> Hertz { |
| 361 | T::frequency() |
| 362 | } |
| 363 | } |
| 364 | |
| 365 | impl<'d, T: BasicNoCr2Instance> Timer<'d, T> { |
| 366 | /// Get access to the Baisc 16bit timer registers. |
| 367 | /// |
| 368 | /// Note: This works even if the timer is more capable, because registers |
| 369 | /// for the less capable timers are a subset. This allows writing a driver |
| 370 | /// for a given set of capabilities, and having it transparently work with |
| 371 | /// more capable timers. |
| 372 | pub fn regs_basic_no_cr2(&self) -> crate::pac::timer::TimBasicNoCr2 { |
| 373 | unsafe { crate::pac::timer::TimBasicNoCr2::from_ptr(T::regs()) } |
| 374 | } |
| 375 | |
| 376 | /// Enable/disable the update dma. |
| 377 | pub fn enable_update_dma(&self, enable: bool) { |
| 378 | self.regs_basic_no_cr2().dier().modify(|r: &mut DierBasicNoCr2| r.set_ude(val:enable)); |
| 379 | } |
| 380 | |
| 381 | /// Get the update dma enable/disable state. |
| 382 | pub fn get_update_dma_state(&self) -> bool { |
| 383 | self.regs_basic_no_cr2().dier().read().ude() |
| 384 | } |
| 385 | } |
| 386 | |
| 387 | impl<'d, T: BasicInstance> Timer<'d, T> { |
| 388 | /// Get access to the Baisc 16bit timer registers. |
| 389 | /// |
| 390 | /// Note: This works even if the timer is more capable, because registers |
| 391 | /// for the less capable timers are a subset. This allows writing a driver |
| 392 | /// for a given set of capabilities, and having it transparently work with |
| 393 | /// more capable timers. |
| 394 | pub fn regs_basic(&self) -> crate::pac::timer::TimBasic { |
| 395 | unsafe { crate::pac::timer::TimBasic::from_ptr(T::regs()) } |
| 396 | } |
| 397 | } |
| 398 | |
| 399 | impl<'d, T: GeneralInstance1Channel> Timer<'d, T> { |
| 400 | /// Get access to the general purpose 1 channel 16bit timer registers. |
| 401 | /// |
| 402 | /// Note: This works even if the timer is more capable, because registers |
| 403 | /// for the less capable timers are a subset. This allows writing a driver |
| 404 | /// for a given set of capabilities, and having it transparently work with |
| 405 | /// more capable timers. |
| 406 | pub fn regs_1ch(&self) -> crate::pac::timer::Tim1ch { |
| 407 | unsafe { crate::pac::timer::Tim1ch::from_ptr(T::regs()) } |
| 408 | } |
| 409 | |
| 410 | /// Set clock divider. |
| 411 | pub fn set_clock_division(&self, ckd: vals::Ckd) { |
| 412 | self.regs_1ch().cr1().modify(|r| r.set_ckd(ckd)); |
| 413 | } |
| 414 | |
| 415 | /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. |
| 416 | pub fn get_max_compare_value(&self) -> u32 { |
| 417 | match T::BITS { |
| 418 | TimerBits::Bits16 => self.regs_1ch().arr().read().arr() as u32, |
| 419 | #[cfg (not(stm32l0))] |
| 420 | TimerBits::Bits32 => self.regs_gp32_unchecked().arr().read(), |
| 421 | } |
| 422 | } |
| 423 | } |
| 424 | |
| 425 | impl<'d, T: GeneralInstance2Channel> Timer<'d, T> { |
| 426 | /// Get access to the general purpose 2 channel 16bit timer registers. |
| 427 | /// |
| 428 | /// Note: This works even if the timer is more capable, because registers |
| 429 | /// for the less capable timers are a subset. This allows writing a driver |
| 430 | /// for a given set of capabilities, and having it transparently work with |
| 431 | /// more capable timers. |
| 432 | pub fn regs_2ch(&self) -> crate::pac::timer::Tim2ch { |
| 433 | unsafe { crate::pac::timer::Tim2ch::from_ptr(T::regs()) } |
| 434 | } |
| 435 | } |
| 436 | |
| 437 | impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { |
| 438 | /// Get access to the general purpose 16bit timer registers. |
| 439 | /// |
| 440 | /// Note: This works even if the timer is more capable, because registers |
| 441 | /// for the less capable timers are a subset. This allows writing a driver |
| 442 | /// for a given set of capabilities, and having it transparently work with |
| 443 | /// more capable timers. |
| 444 | pub fn regs_gp16(&self) -> crate::pac::timer::TimGp16 { |
| 445 | unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) } |
| 446 | } |
| 447 | |
| 448 | /// Enable timer outputs. |
| 449 | pub fn enable_outputs(&self) { |
| 450 | self.tim.enable_outputs() |
| 451 | } |
| 452 | |
| 453 | /// Set counting mode. |
| 454 | pub fn set_counting_mode(&self, mode: CountingMode) { |
| 455 | let (cms, dir) = mode.into(); |
| 456 | |
| 457 | let timer_enabled = self.regs_core().cr1().read().cen(); |
| 458 | // Changing from edge aligned to center aligned (and vice versa) is not allowed while the timer is running. |
| 459 | // Changing direction is discouraged while the timer is running. |
| 460 | assert!(!timer_enabled); |
| 461 | |
| 462 | self.regs_gp16().cr1().modify(|r| r.set_dir(dir)); |
| 463 | self.regs_gp16().cr1().modify(|r| r.set_cms(cms)) |
| 464 | } |
| 465 | |
| 466 | /// Get counting mode. |
| 467 | pub fn get_counting_mode(&self) -> CountingMode { |
| 468 | let cr1 = self.regs_gp16().cr1().read(); |
| 469 | (cr1.cms(), cr1.dir()).into() |
| 470 | } |
| 471 | |
| 472 | /// Set input capture filter. |
| 473 | pub fn set_input_capture_filter(&self, channel: Channel, icf: vals::FilterValue) { |
| 474 | let raw_channel = channel.index(); |
| 475 | self.regs_gp16() |
| 476 | .ccmr_input(raw_channel / 2) |
| 477 | .modify(|r| r.set_icf(raw_channel % 2, icf)); |
| 478 | } |
| 479 | |
| 480 | /// Clear input interrupt. |
| 481 | pub fn clear_input_interrupt(&self, channel: Channel) { |
| 482 | self.regs_gp16().sr().modify(|r| r.set_ccif(channel.index(), false)); |
| 483 | } |
| 484 | |
| 485 | /// Get input interrupt. |
| 486 | pub fn get_input_interrupt(&self, channel: Channel) -> bool { |
| 487 | self.regs_gp16().sr().read().ccif(channel.index()) |
| 488 | } |
| 489 | |
| 490 | /// Enable input interrupt. |
| 491 | pub fn enable_input_interrupt(&self, channel: Channel, enable: bool) { |
| 492 | self.regs_gp16().dier().modify(|r| r.set_ccie(channel.index(), enable)); |
| 493 | } |
| 494 | |
| 495 | /// Set input capture prescaler. |
| 496 | pub fn set_input_capture_prescaler(&self, channel: Channel, factor: u8) { |
| 497 | let raw_channel = channel.index(); |
| 498 | self.regs_gp16() |
| 499 | .ccmr_input(raw_channel / 2) |
| 500 | .modify(|r| r.set_icpsc(raw_channel % 2, factor)); |
| 501 | } |
| 502 | |
| 503 | /// Set input TI selection. |
| 504 | pub fn set_input_ti_selection(&self, channel: Channel, tisel: InputTISelection) { |
| 505 | let raw_channel = channel.index(); |
| 506 | self.regs_gp16() |
| 507 | .ccmr_input(raw_channel / 2) |
| 508 | .modify(|r| r.set_ccs(raw_channel % 2, tisel.into())); |
| 509 | } |
| 510 | |
| 511 | /// Set input capture mode. |
| 512 | pub fn set_input_capture_mode(&self, channel: Channel, mode: InputCaptureMode) { |
| 513 | self.regs_gp16().ccer().modify(|r| match mode { |
| 514 | InputCaptureMode::Rising => { |
| 515 | r.set_ccnp(channel.index(), false); |
| 516 | r.set_ccp(channel.index(), false); |
| 517 | } |
| 518 | InputCaptureMode::Falling => { |
| 519 | r.set_ccnp(channel.index(), false); |
| 520 | r.set_ccp(channel.index(), true); |
| 521 | } |
| 522 | InputCaptureMode::BothEdges => { |
| 523 | r.set_ccnp(channel.index(), true); |
| 524 | r.set_ccp(channel.index(), true); |
| 525 | } |
| 526 | }); |
| 527 | } |
| 528 | |
| 529 | /// Set output compare mode. |
| 530 | pub fn set_output_compare_mode(&self, channel: Channel, mode: OutputCompareMode) { |
| 531 | let raw_channel: usize = channel.index(); |
| 532 | self.regs_gp16() |
| 533 | .ccmr_output(raw_channel / 2) |
| 534 | .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); |
| 535 | } |
| 536 | |
| 537 | /// Set output polarity. |
| 538 | pub fn set_output_polarity(&self, channel: Channel, polarity: OutputPolarity) { |
| 539 | self.regs_gp16() |
| 540 | .ccer() |
| 541 | .modify(|w| w.set_ccp(channel.index(), polarity.into())); |
| 542 | } |
| 543 | |
| 544 | /// Enable/disable a channel. |
| 545 | pub fn enable_channel(&self, channel: Channel, enable: bool) { |
| 546 | self.regs_gp16().ccer().modify(|w| w.set_cce(channel.index(), enable)); |
| 547 | } |
| 548 | |
| 549 | /// Get enable/disable state of a channel |
| 550 | pub fn get_channel_enable_state(&self, channel: Channel) -> bool { |
| 551 | self.regs_gp16().ccer().read().cce(channel.index()) |
| 552 | } |
| 553 | |
| 554 | /// Set compare value for a channel. |
| 555 | pub fn set_compare_value(&self, channel: Channel, value: u32) { |
| 556 | match T::BITS { |
| 557 | TimerBits::Bits16 => { |
| 558 | let value = unwrap!(u16::try_from(value)); |
| 559 | self.regs_gp16().ccr(channel.index()).modify(|w| w.set_ccr(value)); |
| 560 | } |
| 561 | #[cfg (not(stm32l0))] |
| 562 | TimerBits::Bits32 => { |
| 563 | self.regs_gp32_unchecked().ccr(channel.index()).write_value(value); |
| 564 | } |
| 565 | } |
| 566 | } |
| 567 | |
| 568 | /// Get compare value for a channel. |
| 569 | pub fn get_compare_value(&self, channel: Channel) -> u32 { |
| 570 | match T::BITS { |
| 571 | TimerBits::Bits16 => self.regs_gp16().ccr(channel.index()).read().ccr() as u32, |
| 572 | #[cfg (not(stm32l0))] |
| 573 | TimerBits::Bits32 => self.regs_gp32_unchecked().ccr(channel.index()).read(), |
| 574 | } |
| 575 | } |
| 576 | |
| 577 | /// Get capture value for a channel. |
| 578 | pub fn get_capture_value(&self, channel: Channel) -> u32 { |
| 579 | self.get_compare_value(channel) |
| 580 | } |
| 581 | |
| 582 | /// Set output compare preload. |
| 583 | pub fn set_output_compare_preload(&self, channel: Channel, preload: bool) { |
| 584 | let channel_index = channel.index(); |
| 585 | self.regs_gp16() |
| 586 | .ccmr_output(channel_index / 2) |
| 587 | .modify(|w| w.set_ocpe(channel_index % 2, preload)); |
| 588 | } |
| 589 | |
| 590 | /// Get capture compare DMA selection |
| 591 | pub fn get_cc_dma_selection(&self) -> vals::Ccds { |
| 592 | self.regs_gp16().cr2().read().ccds() |
| 593 | } |
| 594 | |
| 595 | /// Set capture compare DMA selection |
| 596 | pub fn set_cc_dma_selection(&self, ccds: vals::Ccds) { |
| 597 | self.regs_gp16().cr2().modify(|w| w.set_ccds(ccds)) |
| 598 | } |
| 599 | |
| 600 | /// Get capture compare DMA enable state |
| 601 | pub fn get_cc_dma_enable_state(&self, channel: Channel) -> bool { |
| 602 | self.regs_gp16().dier().read().ccde(channel.index()) |
| 603 | } |
| 604 | |
| 605 | /// Set capture compare DMA enable state |
| 606 | pub fn set_cc_dma_enable_state(&self, channel: Channel, ccde: bool) { |
| 607 | self.regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde)) |
| 608 | } |
| 609 | |
| 610 | /// Set Timer Slave Mode |
| 611 | pub fn set_slave_mode(&self, sms: SlaveMode) { |
| 612 | self.regs_gp16().smcr().modify(|r| r.set_sms(sms)); |
| 613 | } |
| 614 | |
| 615 | /// Set Timer Trigger Source |
| 616 | pub fn set_trigger_source(&self, ts: TriggerSource) { |
| 617 | self.regs_gp16().smcr().modify(|r| r.set_ts(ts)); |
| 618 | } |
| 619 | } |
| 620 | |
| 621 | #[cfg (not(stm32l0))] |
| 622 | impl<'d, T: GeneralInstance32bit4Channel> Timer<'d, T> { |
| 623 | /// Get access to the general purpose 32bit timer registers. |
| 624 | /// |
| 625 | /// Note: This works even if the timer is more capable, because registers |
| 626 | /// for the less capable timers are a subset. This allows writing a driver |
| 627 | /// for a given set of capabilities, and having it transparently work with |
| 628 | /// more capable timers. |
| 629 | pub fn regs_gp32(&self) -> crate::pac::timer::TimGp32 { |
| 630 | unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) } |
| 631 | } |
| 632 | } |
| 633 | |
| 634 | #[cfg (not(stm32l0))] |
| 635 | impl<'d, T: AdvancedInstance1Channel> Timer<'d, T> { |
| 636 | /// Get access to the general purpose 1 channel with one complementary 16bit timer registers. |
| 637 | /// |
| 638 | /// Note: This works even if the timer is more capable, because registers |
| 639 | /// for the less capable timers are a subset. This allows writing a driver |
| 640 | /// for a given set of capabilities, and having it transparently work with |
| 641 | /// more capable timers. |
| 642 | pub fn regs_1ch_cmp(&self) -> crate::pac::timer::Tim1chCmp { |
| 643 | unsafe { crate::pac::timer::Tim1chCmp::from_ptr(T::regs()) } |
| 644 | } |
| 645 | |
| 646 | /// Set clock divider for the dead time. |
| 647 | pub fn set_dead_time_clock_division(&self, value: vals::Ckd) { |
| 648 | self.regs_1ch_cmp().cr1().modify(|w| w.set_ckd(value)); |
| 649 | } |
| 650 | |
| 651 | /// Set dead time, as a fraction of the max duty value. |
| 652 | pub fn set_dead_time_value(&self, value: u8) { |
| 653 | self.regs_1ch_cmp().bdtr().modify(|w| w.set_dtg(value)); |
| 654 | } |
| 655 | |
| 656 | /// Set state of MOE-bit in BDTR register to en-/disable output |
| 657 | pub fn set_moe(&self, enable: bool) { |
| 658 | self.regs_1ch_cmp().bdtr().modify(|w| w.set_moe(enable)); |
| 659 | } |
| 660 | } |
| 661 | |
| 662 | #[cfg (not(stm32l0))] |
| 663 | impl<'d, T: AdvancedInstance2Channel> Timer<'d, T> { |
| 664 | /// Get access to the general purpose 2 channel with one complementary 16bit timer registers. |
| 665 | /// |
| 666 | /// Note: This works even if the timer is more capable, because registers |
| 667 | /// for the less capable timers are a subset. This allows writing a driver |
| 668 | /// for a given set of capabilities, and having it transparently work with |
| 669 | /// more capable timers. |
| 670 | pub fn regs_2ch_cmp(&self) -> crate::pac::timer::Tim2chCmp { |
| 671 | unsafe { crate::pac::timer::Tim2chCmp::from_ptr(T::regs()) } |
| 672 | } |
| 673 | } |
| 674 | |
| 675 | #[cfg (not(stm32l0))] |
| 676 | impl<'d, T: AdvancedInstance4Channel> Timer<'d, T> { |
| 677 | /// Get access to the advanced timer registers. |
| 678 | pub fn regs_advanced(&self) -> crate::pac::timer::TimAdv { |
| 679 | unsafe { crate::pac::timer::TimAdv::from_ptr(T::regs()) } |
| 680 | } |
| 681 | |
| 682 | /// Set complementary output polarity. |
| 683 | pub fn set_complementary_output_polarity(&self, channel: Channel, polarity: OutputPolarity) { |
| 684 | self.regs_advanced() |
| 685 | .ccer() |
| 686 | .modify(|w: &mut CcerAdv| w.set_ccnp(n:channel.index(), val:polarity.into())); |
| 687 | } |
| 688 | |
| 689 | /// Enable/disable a complementary channel. |
| 690 | pub fn enable_complementary_channel(&self, channel: Channel, enable: bool) { |
| 691 | self.regs_advanced() |
| 692 | .ccer() |
| 693 | .modify(|w: &mut CcerAdv| w.set_ccne(n:channel.index(), val:enable)); |
| 694 | } |
| 695 | } |
| 696 | |