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 | |