Skip to content

Commit b8b94e6

Browse files
Merge #585
585: Example dead time2 r=burrbull a=janschiefer Advanced PWM dead time insertion example. Co-authored-by: Jan Schiefer <[email protected]>
2 parents 19880a7 + c3ccf9b commit b8b94e6

File tree

3 files changed

+84
-3
lines changed

3 files changed

+84
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
88
## [Unreleased]
99

1010
- Improve SPI::new* docs [#587]
11+
- Add advanced timer dead time insertion example [#585]
1112

1213
## [v0.15.0] - 2023-03-13
1314

examples/pwm-dead-time.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#![deny(unsafe_code)]
2+
#![deny(warnings)]
3+
#![no_main]
4+
#![no_std]
5+
6+
// Halt on panic
7+
use panic_halt as _; // panic handler
8+
9+
use cortex_m_rt::entry;
10+
use stm32f4xx_hal as hal;
11+
12+
use crate::hal::{pac, prelude::*, timer::Channel, timer::Polarity};
13+
14+
#[entry]
15+
fn main() -> ! {
16+
if let Some(dp) = pac::Peripherals::take() {
17+
// Set up the system clock. We want to run at 84MHz for this one.
18+
let rcc = dp.RCC.constrain();
19+
let clocks = rcc.cfgr.sysclk(25.MHz()).freeze();
20+
21+
let gpioa = dp.GPIOA.split();
22+
23+
let channels = (gpioa.pa8.into_alternate(), gpioa.pa7.into_alternate());
24+
25+
let mut pwm = dp.TIM1.pwm_hz(channels, 20.kHz(), &clocks);
26+
27+
let max_duty: u16 = pwm.get_max_duty();
28+
29+
pwm.set_polarity(Channel::C1, Polarity::ActiveHigh);
30+
pwm.set_complementary_polarity(Channel::C1, Polarity::ActiveHigh);
31+
32+
pwm.set_duty(Channel::C1, max_duty / 2);
33+
34+
pwm.set_dead_time(200);
35+
36+
pwm.enable(Channel::C1);
37+
pwm.enable_complementary(Channel::C1);
38+
}
39+
40+
loop {
41+
cortex_m::asm::nop();
42+
}
43+
}

src/timer/pwm.rs

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,59 +217,71 @@ impl<TIM: Instance + WithPwm, const C: u8> PwmChannel<TIM, C> {
217217
}
218218

219219
impl<TIM: Instance + WithPwm, const C: u8, const COMP: bool> PwmChannel<TIM, C, COMP> {
220+
/// Disable PWM channel
220221
#[inline]
221222
pub fn disable(&mut self) {
222223
TIM::enable_channel(C, false);
223224
}
224225

226+
/// Enable PWM channel
225227
#[inline]
226228
pub fn enable(&mut self) {
227229
TIM::enable_channel(C, true);
228230
}
229231

232+
/// Set PWM channel polarity
230233
#[inline]
231234
pub fn set_polarity(&mut self, p: Polarity) {
232235
TIM::set_channel_polarity(C, p);
233236
}
234237

238+
/// Get PWM channel duty cycle
235239
#[inline]
236240
pub fn get_duty(&self) -> u16 {
237241
TIM::read_cc_value(C) as u16
238242
}
239243

244+
/// Get the maximum duty cycle value of the PWM channel
245+
///
240246
/// If `0` returned means max_duty is 2^16
241247
#[inline]
242248
pub fn get_max_duty(&self) -> u16 {
243249
(TIM::read_auto_reload() as u16).wrapping_add(1)
244250
}
245251

252+
/// Set PWM channel duty cycle
246253
#[inline]
247254
pub fn set_duty(&mut self, duty: u16) {
248255
TIM::set_cc_value(C, duty as u32)
249256
}
250257

258+
/// Set complementary PWM channel polarity
251259
#[inline]
252260
pub fn set_complementary_polarity(&mut self, p: Polarity) {
253261
TIM::set_nchannel_polarity(C, p);
254262
}
255263
}
256264

257265
impl<TIM: Instance + WithPwm + Advanced, const C: u8> PwmChannel<TIM, C, true> {
266+
/// Disable complementary PWM channel
258267
#[inline]
259268
pub fn disable_complementary(&mut self) {
260269
TIM::enable_nchannel(C, false);
261270
}
262271

272+
/// Enable complementary PWM channel
263273
#[inline]
264274
pub fn enable_complementary(&mut self) {
265275
TIM::enable_nchannel(C, true);
266276
}
267277

278+
/// Set PWM channel idle state
268279
#[inline]
269280
pub fn set_idle_state(&mut self, s: IdleState) {
270281
TIM::idle_state(C, false, s);
271282
}
272283

284+
/// Set complementary PWM channel idle state
273285
#[inline]
274286
pub fn set_complementary_idle_state(&mut self, s: IdleState) {
275287
TIM::idle_state(C, true, s);
@@ -592,55 +604,66 @@ where
592604
TIM: Instance + WithPwm,
593605
PINS: Pins<TIM, P>,
594606
{
607+
/// Enable PWM output of the timer on channel `channel`
595608
#[inline]
596609
pub fn enable(&mut self, channel: Channel) {
597610
TIM::enable_channel(PINS::check_used(channel) as u8, true)
598611
}
599612

613+
/// Disable PWM output of the timer on channel `channel`
600614
#[inline]
601615
pub fn disable(&mut self, channel: Channel) {
602616
TIM::enable_channel(PINS::check_used(channel) as u8, false)
603617
}
604618

619+
/// Set the polarity of the active state for the primary PWM output of the timer on channel `channel`
605620
#[inline]
606621
pub fn set_polarity(&mut self, channel: Channel, p: Polarity) {
607622
TIM::set_channel_polarity(PINS::check_used(channel) as u8, p);
608623
}
609624

625+
/// Get the current duty cycle of the timer on channel `channel`
610626
#[inline]
611627
pub fn get_duty(&self, channel: Channel) -> u16 {
612628
TIM::read_cc_value(PINS::check_used(channel) as u8) as u16
613629
}
614-
630+
/// Get the current duty cycle of the timer on channel `channel` and convert to a duration
615631
#[inline]
616632
pub fn get_duty_time(&self, channel: Channel) -> TimerDurationU32<FREQ> {
617633
TimerDurationU32::from_ticks(TIM::read_cc_value(PINS::check_used(channel) as u8))
618634
}
619635

636+
/// Set the duty cycle of the timer on channel `channel`
620637
#[inline]
621638
pub fn set_duty(&mut self, channel: Channel, duty: u16) {
622639
TIM::set_cc_value(PINS::check_used(channel) as u8, duty.into())
623640
}
624641

642+
/// Set the duty cycle of the timer on channel `channel` from a duration
625643
#[inline]
626644
pub fn set_duty_time(&mut self, channel: Channel, duty: TimerDurationU32<FREQ>) {
627645
TIM::set_cc_value(PINS::check_used(channel) as u8, duty.ticks())
628646
}
629647

648+
/// Get the maximum duty cycle value of the timer
649+
///
630650
/// If `0` returned means max_duty is 2^16
631651
pub fn get_max_duty(&self) -> u16 {
632652
(TIM::read_auto_reload() as u16).wrapping_add(1)
633653
}
634654

655+
/// Get the PWM frequency of the timer as a duration
635656
pub fn get_period(&self) -> TimerDurationU32<FREQ> {
636657
TimerDurationU32::from_ticks(TIM::read_auto_reload() + 1)
637658
}
638659

660+
/// Set the PWM frequency for the timer from a duration
639661
pub fn set_period(&mut self, period: TimerDurationU32<FREQ>) {
640662
self.tim.set_auto_reload(period.ticks() - 1).unwrap();
641663
self.tim.cnt_reset();
642664
}
643665

666+
/// Set the polarity of the active state for the complementary PWM output of the advanced timer on channel `channel`
644667
#[inline]
645668
pub fn set_complementary_polarity(&mut self, channel: Channel, p: Polarity) {
646669
TIM::set_channel_polarity(PINS::check_complementary_used(channel) as u8, p);
@@ -652,30 +675,39 @@ where
652675
TIM: Instance + WithPwm + Advanced,
653676
PINS: Pins<TIM, P>,
654677
{
678+
/// Enable complementary PWM output of the timer on channel `channel`
655679
#[inline]
656680
pub fn enable_complementary(&mut self, channel: Channel) {
657681
TIM::enable_nchannel(PINS::check_complementary_used(channel) as u8, true)
658682
}
659683

684+
/// Disable complementary PWM output of the timer on channel `channel`
660685
#[inline]
661686
pub fn disable_complementary(&mut self, channel: Channel) {
662687
TIM::enable_nchannel(PINS::check_complementary_used(channel) as u8, false)
663688
}
664689

665-
/// Set number DTS ticks during that complementary pin is `dead`
690+
/// Set number DTS ticks during that the primary and complementary PWM pins are simultaneously forced to their inactive states
691+
/// ( see [`Polarity`] setting ) when changing PWM state. This duration when both channels are in an 'off' state is called 'dead time'.
692+
///
693+
/// This is necessary in applications like motor control or power converters to prevent the destruction of the switching elements by
694+
/// short circuit in the moment of switching.
666695
#[inline]
667696
pub fn set_dead_time(&mut self, dts_ticks: u16) {
668697
let bits = pack_ceil_dead_time(dts_ticks);
669698
TIM::set_dtg_value(bits);
670699
}
671700

672701
/// Set raw dead time (DTG) bits
702+
///
703+
/// The dead time generation is nonlinear and constrained by the DTS tick duration. DTG register configuration and calculation of
704+
/// the actual resulting dead time is described in the application note RM0368 from ST Microelectronics
673705
#[inline]
674706
pub fn set_dead_time_bits(&mut self, bits: u8) {
675707
TIM::set_dtg_value(bits);
676708
}
677709

678-
/// Return dead time for complementary pins in DTS ticks
710+
/// Return dead time for complementary pins in the unit of DTS ticks
679711
#[inline]
680712
pub fn get_dead_time(&self) -> u16 {
681713
unpack_dead_time(TIM::read_dtg_value())
@@ -687,17 +719,21 @@ where
687719
TIM::read_dtg_value()
688720
}
689721

722+
/// Set the pin idle state
690723
#[inline]
691724
pub fn set_idle_state(&mut self, channel: Channel, s: IdleState) {
692725
TIM::idle_state(PINS::check_used(channel) as u8, false, s);
693726
}
694727

728+
/// Set the complementary pin idle state
695729
#[inline]
696730
pub fn set_complementary_idle_state(&mut self, channel: Channel, s: IdleState) {
697731
TIM::idle_state(PINS::check_complementary_used(channel) as u8, true, s);
698732
}
699733
}
700734

735+
/// Convert number dead time ticks to raw DTG register bits.
736+
/// Values greater than 1009 result in maximum dead time of 126 us
701737
const fn pack_ceil_dead_time(dts_ticks: u16) -> u8 {
702738
match dts_ticks {
703739
0..=127 => dts_ticks as u8,
@@ -708,6 +744,7 @@ const fn pack_ceil_dead_time(dts_ticks: u16) -> u8 {
708744
}
709745
}
710746

747+
/// Convert raw DTG register bits value to number of dead time ticks
711748
const fn unpack_dead_time(bits: u8) -> u16 {
712749
if bits & 0b_1000_0000 == 0 {
713750
bits as u16

0 commit comments

Comments
 (0)