1 | //! Simple PWM driver. |
2 | |
3 | use core::marker::PhantomData; |
4 | use core::mem::ManuallyDrop; |
5 | |
6 | use embassy_hal_internal::{into_ref, PeripheralRef}; |
7 | |
8 | use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; |
9 | use super::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance4Channel}; |
10 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; |
11 | use crate::time::Hertz; |
12 | use crate::Peripheral; |
13 | |
14 | /// Channel 1 marker type. |
15 | pub enum Ch1 {} |
16 | /// Channel 2 marker type. |
17 | pub enum Ch2 {} |
18 | /// Channel 3 marker type. |
19 | pub enum Ch3 {} |
20 | /// Channel 4 marker type. |
21 | pub enum Ch4 {} |
22 | |
23 | /// PWM pin wrapper. |
24 | /// |
25 | /// This wraps a pin to make it usable with PWM. |
26 | pub struct PwmPin<'d, T, C> { |
27 | _pin: PeripheralRef<'d, AnyPin>, |
28 | phantom: PhantomData<(T, C)>, |
29 | } |
30 | |
31 | macro_rules! channel_impl { |
32 | ($new_chx:ident, $channel:ident, $pin_trait:ident) => { |
33 | impl<'d, T: GeneralInstance4Channel> PwmPin<'d, T, $channel> { |
34 | #[doc = concat!("Create a new " , stringify!($channel), " PWM pin instance." )] |
35 | pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, output_type: OutputType) -> Self { |
36 | into_ref!(pin); |
37 | critical_section::with(|_| { |
38 | pin.set_low(); |
39 | pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh)); |
40 | }); |
41 | PwmPin { |
42 | _pin: pin.map_into(), |
43 | phantom: PhantomData, |
44 | } |
45 | } |
46 | } |
47 | }; |
48 | } |
49 | |
50 | channel_impl!(new_ch1, Ch1, Channel1Pin); |
51 | channel_impl!(new_ch2, Ch2, Channel2Pin); |
52 | channel_impl!(new_ch3, Ch3, Channel3Pin); |
53 | channel_impl!(new_ch4, Ch4, Channel4Pin); |
54 | |
55 | /// A single channel of a pwm, obtained from [`SimplePwm::split`], |
56 | /// [`SimplePwm::channel`], [`SimplePwm::ch1`], etc. |
57 | /// |
58 | /// It is not possible to change the pwm frequency because |
59 | /// the frequency configuration is shared with all four channels. |
60 | pub struct SimplePwmChannel<'d, T: GeneralInstance4Channel> { |
61 | timer: ManuallyDrop<Timer<'d, T>>, |
62 | channel: Channel, |
63 | } |
64 | |
65 | // TODO: check for RMW races |
66 | impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { |
67 | /// Enable the given channel. |
68 | pub fn enable(&mut self) { |
69 | self.timer.enable_channel(self.channel, true); |
70 | } |
71 | |
72 | /// Disable the given channel. |
73 | pub fn disable(&mut self) { |
74 | self.timer.enable_channel(self.channel, false); |
75 | } |
76 | |
77 | /// Check whether given channel is enabled |
78 | pub fn is_enabled(&self) -> bool { |
79 | self.timer.get_channel_enable_state(self.channel) |
80 | } |
81 | |
82 | /// Get max duty value. |
83 | /// |
84 | /// This value depends on the configured frequency and the timer's clock rate from RCC. |
85 | pub fn max_duty_cycle(&self) -> u16 { |
86 | let max = self.timer.get_max_compare_value(); |
87 | assert!(max < u16::MAX as u32); |
88 | max as u16 + 1 |
89 | } |
90 | |
91 | /// Set the duty for a given channel. |
92 | /// |
93 | /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included. |
94 | pub fn set_duty_cycle(&mut self, duty: u16) { |
95 | assert!(duty <= (*self).max_duty_cycle()); |
96 | self.timer.set_compare_value(self.channel, duty.into()) |
97 | } |
98 | |
99 | /// Set the duty cycle to 0%, or always inactive. |
100 | pub fn set_duty_cycle_fully_off(&mut self) { |
101 | self.set_duty_cycle(0); |
102 | } |
103 | |
104 | /// Set the duty cycle to 100%, or always active. |
105 | pub fn set_duty_cycle_fully_on(&mut self) { |
106 | self.set_duty_cycle((*self).max_duty_cycle()); |
107 | } |
108 | |
109 | /// Set the duty cycle to `num / denom`. |
110 | /// |
111 | /// The caller is responsible for ensuring that `num` is less than or equal to `denom`, |
112 | /// and that `denom` is not zero. |
113 | pub fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) { |
114 | assert!(denom != 0); |
115 | assert!(num <= denom); |
116 | let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom); |
117 | |
118 | // This is safe because we know that `num <= denom`, so `duty <= self.max_duty_cycle()` (u16) |
119 | #[allow (clippy::cast_possible_truncation)] |
120 | self.set_duty_cycle(duty as u16); |
121 | } |
122 | |
123 | /// Set the duty cycle to `percent / 100` |
124 | /// |
125 | /// The caller is responsible for ensuring that `percent` is less than or equal to 100. |
126 | pub fn set_duty_cycle_percent(&mut self, percent: u8) { |
127 | self.set_duty_cycle_fraction(u16::from(percent), 100) |
128 | } |
129 | |
130 | /// Get the duty for a given channel. |
131 | /// |
132 | /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included. |
133 | pub fn current_duty_cycle(&self) -> u16 { |
134 | unwrap!(self.timer.get_compare_value(self.channel).try_into()) |
135 | } |
136 | |
137 | /// Set the output polarity for a given channel. |
138 | pub fn set_polarity(&mut self, polarity: OutputPolarity) { |
139 | self.timer.set_output_polarity(self.channel, polarity); |
140 | } |
141 | |
142 | /// Set the output compare mode for a given channel. |
143 | pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) { |
144 | self.timer.set_output_compare_mode(self.channel, mode); |
145 | } |
146 | } |
147 | |
148 | /// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`]. |
149 | pub struct SimplePwmChannels<'d, T: GeneralInstance4Channel> { |
150 | /// Channel 1 |
151 | pub ch1: SimplePwmChannel<'d, T>, |
152 | /// Channel 2 |
153 | pub ch2: SimplePwmChannel<'d, T>, |
154 | /// Channel 3 |
155 | pub ch3: SimplePwmChannel<'d, T>, |
156 | /// Channel 4 |
157 | pub ch4: SimplePwmChannel<'d, T>, |
158 | } |
159 | |
160 | /// Simple PWM driver. |
161 | pub struct SimplePwm<'d, T: GeneralInstance4Channel> { |
162 | inner: Timer<'d, T>, |
163 | } |
164 | |
165 | impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { |
166 | /// Create a new simple PWM driver. |
167 | pub fn new( |
168 | tim: impl Peripheral<P = T> + 'd, |
169 | _ch1: Option<PwmPin<'d, T, Ch1>>, |
170 | _ch2: Option<PwmPin<'d, T, Ch2>>, |
171 | _ch3: Option<PwmPin<'d, T, Ch3>>, |
172 | _ch4: Option<PwmPin<'d, T, Ch4>>, |
173 | freq: Hertz, |
174 | counting_mode: CountingMode, |
175 | ) -> Self { |
176 | Self::new_inner(tim, freq, counting_mode) |
177 | } |
178 | |
179 | fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, counting_mode: CountingMode) -> Self { |
180 | let mut this = Self { inner: Timer::new(tim) }; |
181 | |
182 | this.inner.set_counting_mode(counting_mode); |
183 | this.set_frequency(freq); |
184 | this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details |
185 | this.inner.start(); |
186 | |
187 | [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] |
188 | .iter() |
189 | .for_each(|&channel| { |
190 | this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1); |
191 | |
192 | this.inner.set_output_compare_preload(channel, true); |
193 | }); |
194 | |
195 | this |
196 | } |
197 | |
198 | /// Get a single channel |
199 | /// |
200 | /// If you need to use multiple channels, use [`Self::split`]. |
201 | pub fn channel(&mut self, channel: Channel) -> SimplePwmChannel<'_, T> { |
202 | SimplePwmChannel { |
203 | timer: unsafe { self.inner.clone_unchecked() }, |
204 | channel, |
205 | } |
206 | } |
207 | |
208 | /// Channel 1 |
209 | /// |
210 | /// This is just a convenience wrapper around [`Self::channel`]. |
211 | /// |
212 | /// If you need to use multiple channels, use [`Self::split`]. |
213 | pub fn ch1(&mut self) -> SimplePwmChannel<'_, T> { |
214 | self.channel(Channel::Ch1) |
215 | } |
216 | |
217 | /// Channel 2 |
218 | /// |
219 | /// This is just a convenience wrapper around [`Self::channel`]. |
220 | /// |
221 | /// If you need to use multiple channels, use [`Self::split`]. |
222 | pub fn ch2(&mut self) -> SimplePwmChannel<'_, T> { |
223 | self.channel(Channel::Ch2) |
224 | } |
225 | |
226 | /// Channel 3 |
227 | /// |
228 | /// This is just a convenience wrapper around [`Self::channel`]. |
229 | /// |
230 | /// If you need to use multiple channels, use [`Self::split`]. |
231 | pub fn ch3(&mut self) -> SimplePwmChannel<'_, T> { |
232 | self.channel(Channel::Ch3) |
233 | } |
234 | |
235 | /// Channel 4 |
236 | /// |
237 | /// This is just a convenience wrapper around [`Self::channel`]. |
238 | /// |
239 | /// If you need to use multiple channels, use [`Self::split`]. |
240 | pub fn ch4(&mut self) -> SimplePwmChannel<'_, T> { |
241 | self.channel(Channel::Ch4) |
242 | } |
243 | |
244 | /// Splits a [`SimplePwm`] into four pwm channels. |
245 | /// |
246 | /// This returns all four channels, including channels that |
247 | /// aren't configured with a [`PwmPin`]. |
248 | // TODO: I hate the name "split" |
249 | pub fn split(self) -> SimplePwmChannels<'static, T> |
250 | where |
251 | // must be static because the timer will never be dropped/disabled |
252 | 'd: 'static, |
253 | { |
254 | // without this, the timer would be disabled at the end of this function |
255 | let timer = ManuallyDrop::new(self.inner); |
256 | |
257 | let ch = |channel| SimplePwmChannel { |
258 | timer: unsafe { timer.clone_unchecked() }, |
259 | channel, |
260 | }; |
261 | |
262 | SimplePwmChannels { |
263 | ch1: ch(Channel::Ch1), |
264 | ch2: ch(Channel::Ch2), |
265 | ch3: ch(Channel::Ch3), |
266 | ch4: ch(Channel::Ch4), |
267 | } |
268 | } |
269 | |
270 | /// Set PWM frequency. |
271 | /// |
272 | /// Note: when you call this, the max duty value changes, so you will have to |
273 | /// call `set_duty` on all channels with the duty calculated based on the new max duty. |
274 | pub fn set_frequency(&mut self, freq: Hertz) { |
275 | // TODO: prevent ARR = u16::MAX? |
276 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { |
277 | 2u8 |
278 | } else { |
279 | 1u8 |
280 | }; |
281 | self.inner.set_frequency_internal(freq * multiplier, 16); |
282 | } |
283 | |
284 | /// Get max duty value. |
285 | /// |
286 | /// This value depends on the configured frequency and the timer's clock rate from RCC. |
287 | pub fn max_duty_cycle(&self) -> u16 { |
288 | let max = self.inner.get_max_compare_value(); |
289 | assert!(max < u16::MAX as u32); |
290 | max as u16 + 1 |
291 | } |
292 | |
293 | /// Generate a sequence of PWM waveform |
294 | /// |
295 | /// Note: |
296 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. |
297 | pub async fn waveform_up( |
298 | &mut self, |
299 | dma: impl Peripheral<P = impl super::UpDma<T>>, |
300 | channel: Channel, |
301 | duty: &[u16], |
302 | ) { |
303 | into_ref!(dma); |
304 | |
305 | #[allow (clippy::let_unit_value)] // eg. stm32f334 |
306 | let req = dma.request(); |
307 | |
308 | let original_duty_state = self.channel(channel).current_duty_cycle(); |
309 | let original_enable_state = self.channel(channel).is_enabled(); |
310 | let original_update_dma_state = self.inner.get_update_dma_state(); |
311 | |
312 | if !original_update_dma_state { |
313 | self.inner.enable_update_dma(true); |
314 | } |
315 | |
316 | if !original_enable_state { |
317 | self.channel(channel).enable(); |
318 | } |
319 | |
320 | unsafe { |
321 | #[cfg (not(any(bdma, gpdma)))] |
322 | use crate::dma::{Burst, FifoThreshold}; |
323 | use crate::dma::{Transfer, TransferOptions}; |
324 | |
325 | let dma_transfer_option = TransferOptions { |
326 | #[cfg (not(any(bdma, gpdma)))] |
327 | fifo_threshold: Some(FifoThreshold::Full), |
328 | #[cfg (not(any(bdma, gpdma)))] |
329 | mburst: Burst::Incr8, |
330 | ..Default::default() |
331 | }; |
332 | |
333 | Transfer::new_write( |
334 | &mut dma, |
335 | req, |
336 | duty, |
337 | self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut _, |
338 | dma_transfer_option, |
339 | ) |
340 | .await |
341 | }; |
342 | |
343 | // restore output compare state |
344 | if !original_enable_state { |
345 | self.channel(channel).disable(); |
346 | } |
347 | |
348 | self.channel(channel).set_duty_cycle(original_duty_state); |
349 | |
350 | // Since DMA is closed before timer update event trigger DMA is turn off, |
351 | // this can almost always trigger a DMA FIFO error. |
352 | // |
353 | // optional TODO: |
354 | // clean FEIF after disable UDE |
355 | if !original_update_dma_state { |
356 | self.inner.enable_update_dma(false); |
357 | } |
358 | } |
359 | } |
360 | |
361 | macro_rules! impl_waveform_chx { |
362 | ($fn_name:ident, $dma_ch:ident, $cc_ch:ident) => { |
363 | impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { |
364 | /// Generate a sequence of PWM waveform |
365 | /// |
366 | /// Note: |
367 | /// you will need to provide corresponding TIMx_CHy DMA channel to use this method. |
368 | pub async fn $fn_name(&mut self, dma: impl Peripheral<P = impl super::$dma_ch<T>>, duty: &[u16]) { |
369 | use crate::pac::timer::vals::Ccds; |
370 | |
371 | into_ref!(dma); |
372 | |
373 | #[allow(clippy::let_unit_value)] // eg. stm32f334 |
374 | let req = dma.request(); |
375 | |
376 | let cc_channel = Channel::$cc_ch; |
377 | |
378 | let original_duty_state = self.channel(cc_channel).current_duty_cycle(); |
379 | let original_enable_state = self.channel(cc_channel).is_enabled(); |
380 | let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE; |
381 | let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel); |
382 | |
383 | // redirect CC DMA request onto Update Event |
384 | if !original_cc_dma_on_update { |
385 | self.inner.set_cc_dma_selection(Ccds::ON_UPDATE) |
386 | } |
387 | |
388 | if !original_cc_dma_enabled { |
389 | self.inner.set_cc_dma_enable_state(cc_channel, true); |
390 | } |
391 | |
392 | if !original_enable_state { |
393 | self.channel(cc_channel).enable(); |
394 | } |
395 | |
396 | unsafe { |
397 | #[cfg(not(any(bdma, gpdma)))] |
398 | use crate::dma::{Burst, FifoThreshold}; |
399 | use crate::dma::{Transfer, TransferOptions}; |
400 | |
401 | let dma_transfer_option = TransferOptions { |
402 | #[cfg(not(any(bdma, gpdma)))] |
403 | fifo_threshold: Some(FifoThreshold::Full), |
404 | #[cfg(not(any(bdma, gpdma)))] |
405 | mburst: Burst::Incr8, |
406 | ..Default::default() |
407 | }; |
408 | |
409 | Transfer::new_write( |
410 | &mut dma, |
411 | req, |
412 | duty, |
413 | self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut _, |
414 | dma_transfer_option, |
415 | ) |
416 | .await |
417 | }; |
418 | |
419 | // restore output compare state |
420 | if !original_enable_state { |
421 | self.channel(cc_channel).disable(); |
422 | } |
423 | |
424 | self.channel(cc_channel).set_duty_cycle(original_duty_state); |
425 | |
426 | // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, |
427 | // this can almost always trigger a DMA FIFO error. |
428 | // |
429 | // optional TODO: |
430 | // clean FEIF after disable UDE |
431 | if !original_cc_dma_enabled { |
432 | self.inner.set_cc_dma_enable_state(cc_channel, false); |
433 | } |
434 | |
435 | if !original_cc_dma_on_update { |
436 | self.inner.set_cc_dma_selection(Ccds::ON_COMPARE) |
437 | } |
438 | } |
439 | } |
440 | }; |
441 | } |
442 | |
443 | impl_waveform_chx!(waveform_ch1, Ch1Dma, Ch1); |
444 | impl_waveform_chx!(waveform_ch2, Ch2Dma, Ch2); |
445 | impl_waveform_chx!(waveform_ch3, Ch3Dma, Ch3); |
446 | impl_waveform_chx!(waveform_ch4, Ch4Dma, Ch4); |
447 | |
448 | impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> { |
449 | type Error = core::convert::Infallible; |
450 | } |
451 | |
452 | impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> { |
453 | fn max_duty_cycle(&self) -> u16 { |
454 | self.max_duty_cycle() |
455 | } |
456 | |
457 | fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { |
458 | self.set_duty_cycle(duty); |
459 | Ok(()) |
460 | } |
461 | |
462 | fn set_duty_cycle_fully_off(&mut self) -> Result<(), Self::Error> { |
463 | self.set_duty_cycle_fully_off(); |
464 | Ok(()) |
465 | } |
466 | |
467 | fn set_duty_cycle_fully_on(&mut self) -> Result<(), Self::Error> { |
468 | self.set_duty_cycle_fully_on(); |
469 | Ok(()) |
470 | } |
471 | |
472 | fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { |
473 | self.set_duty_cycle_fraction(num, denom); |
474 | Ok(()) |
475 | } |
476 | |
477 | fn set_duty_cycle_percent(&mut self, percent: u8) -> Result<(), Self::Error> { |
478 | self.set_duty_cycle_percent(percent); |
479 | Ok(()) |
480 | } |
481 | } |
482 | |
483 | impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> { |
484 | type Channel = Channel; |
485 | type Time = Hertz; |
486 | type Duty = u32; |
487 | |
488 | fn disable(&mut self, channel: Self::Channel) { |
489 | self.inner.enable_channel(channel, false); |
490 | } |
491 | |
492 | fn enable(&mut self, channel: Self::Channel) { |
493 | self.inner.enable_channel(channel, true); |
494 | } |
495 | |
496 | fn get_period(&self) -> Self::Time { |
497 | self.inner.get_frequency() |
498 | } |
499 | |
500 | fn get_duty(&self, channel: Self::Channel) -> Self::Duty { |
501 | self.inner.get_compare_value(channel) |
502 | } |
503 | |
504 | fn get_max_duty(&self) -> Self::Duty { |
505 | self.inner.get_max_compare_value() + 1 |
506 | } |
507 | |
508 | fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { |
509 | assert!(duty <= self.max_duty_cycle() as u32); |
510 | self.inner.set_compare_value(channel, duty) |
511 | } |
512 | |
513 | fn set_period<P>(&mut self, period: P) |
514 | where |
515 | P: Into<Self::Time>, |
516 | { |
517 | self.inner.set_frequency(period.into()); |
518 | } |
519 | } |
520 | |