aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2023-04-06 21:33:17 +0000
committerGitHub <[email protected]>2023-04-06 21:33:17 +0000
commitda8258b7673a52a4639818f9c3f3b7ea241b4799 (patch)
tree074787c16ce64c2f6033a4d79487ad8c0321a210
parent60809edf2ce8f3cb91d31398dc347e4193e2e5f2 (diff)
parent31ef783ac1add908c4c4506d3ce4e5f6ad9bea56 (diff)
Merge #1330
1330: stm32/pwm: add complementary pwm r=Dirbaio a=xoviat This implements complementary PWM with dead time on many supported targets. The specific dead-time programming functions are passed through directly to the user, which is a bit ugly but the best compromise I could reach for now. Co-authored-by: xoviat <[email protected]>
-rw-r--r--embassy-stm32/src/pwm/complementary_pwm.rs124
-rw-r--r--embassy-stm32/src/pwm/mod.rs40
-rw-r--r--examples/stm32f4/src/bin/pwm_complementary.rs77
3 files changed, 241 insertions, 0 deletions
diff --git a/embassy-stm32/src/pwm/complementary_pwm.rs b/embassy-stm32/src/pwm/complementary_pwm.rs
new file mode 100644
index 000000000..13edfbaa3
--- /dev/null
+++ b/embassy-stm32/src/pwm/complementary_pwm.rs
@@ -0,0 +1,124 @@
1use core::marker::PhantomData;
2
3use embassy_hal_common::{into_ref, PeripheralRef};
4pub use stm32_metapac::timer::vals::Ckd;
5
6use super::simple_pwm::*;
7use super::*;
8#[allow(unused_imports)]
9use crate::gpio::sealed::{AFType, Pin};
10use crate::gpio::AnyPin;
11use crate::time::Hertz;
12use crate::Peripheral;
13
14pub struct ComplementaryPwmPin<'d, Perip, Channel> {
15 _pin: PeripheralRef<'d, AnyPin>,
16 phantom: PhantomData<(Perip, Channel)>,
17}
18
19macro_rules! complementary_channel_impl {
20 ($new_chx:ident, $channel:ident, $pin_trait:ident, $complementary_pin_trait:ident) => {
21 impl<'d, Perip: CaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> {
22 pub fn $new_chx(pin: impl Peripheral<P = impl $complementary_pin_trait<Perip>> + 'd) -> Self {
23 into_ref!(pin);
24 critical_section::with(|_| unsafe {
25 pin.set_low();
26 pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
27 #[cfg(gpio_v2)]
28 pin.set_speed(crate::gpio::Speed::VeryHigh);
29 });
30 ComplementaryPwmPin {
31 _pin: pin.map_into(),
32 phantom: PhantomData,
33 }
34 }
35 }
36 };
37}
38
39complementary_channel_impl!(new_ch1, Ch1, Channel1Pin, Channel1ComplementaryPin);
40complementary_channel_impl!(new_ch2, Ch2, Channel2Pin, Channel2ComplementaryPin);
41complementary_channel_impl!(new_ch3, Ch3, Channel3Pin, Channel3ComplementaryPin);
42complementary_channel_impl!(new_ch4, Ch4, Channel4Pin, Channel4ComplementaryPin);
43
44pub struct ComplementaryPwm<'d, T> {
45 inner: PeripheralRef<'d, T>,
46}
47
48impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
49 pub fn new(
50 tim: impl Peripheral<P = T> + 'd,
51 _ch1: Option<PwmPin<'d, T, Ch1>>,
52 _ch1n: Option<ComplementaryPwmPin<'d, T, Ch1>>,
53 _ch2: Option<PwmPin<'d, T, Ch2>>,
54 _ch2n: Option<ComplementaryPwmPin<'d, T, Ch2>>,
55 _ch3: Option<PwmPin<'d, T, Ch3>>,
56 _ch3n: Option<ComplementaryPwmPin<'d, T, Ch3>>,
57 _ch4: Option<PwmPin<'d, T, Ch4>>,
58 _ch4n: Option<ComplementaryPwmPin<'d, T, Ch4>>,
59 freq: Hertz,
60 ) -> Self {
61 Self::new_inner(tim, freq)
62 }
63
64 fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz) -> Self {
65 into_ref!(tim);
66
67 T::enable();
68 <T as crate::rcc::sealed::RccPeripheral>::reset();
69
70 let mut this = Self { inner: tim };
71
72 this.inner.set_frequency(freq);
73 this.inner.start();
74
75 unsafe {
76 this.inner.enable_outputs(true);
77
78 this.inner
79 .set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1);
80 this.inner
81 .set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1);
82 this.inner
83 .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1);
84 this.inner
85 .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1);
86 }
87 this
88 }
89
90 pub fn enable(&mut self, channel: Channel) {
91 unsafe {
92 self.inner.enable_channel(channel, true);
93 self.inner.enable_complementary_channel(channel, true);
94 }
95 }
96
97 pub fn disable(&mut self, channel: Channel) {
98 unsafe {
99 self.inner.enable_complementary_channel(channel, false);
100 self.inner.enable_channel(channel, false);
101 }
102 }
103
104 pub fn set_freq(&mut self, freq: Hertz) {
105 self.inner.set_frequency(freq);
106 }
107
108 pub fn get_max_duty(&self) -> u16 {
109 unsafe { self.inner.get_max_compare_value() }
110 }
111
112 pub fn set_duty(&mut self, channel: Channel, duty: u16) {
113 assert!(duty < self.get_max_duty());
114 unsafe { self.inner.set_compare_value(channel, duty) }
115 }
116
117 pub fn set_dead_time_clock_division(&mut self, value: Ckd) {
118 unsafe { self.inner.set_dead_time_clock_division(value) }
119 }
120
121 pub fn set_dead_time_value(&mut self, value: u8) {
122 unsafe { self.inner.set_dead_time_value(value) }
123 }
124}
diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs
index d3713391c..0bef07089 100644
--- a/embassy-stm32/src/pwm/mod.rs
+++ b/embassy-stm32/src/pwm/mod.rs
@@ -1,5 +1,8 @@
1pub mod complementary_pwm;
1pub mod simple_pwm; 2pub mod simple_pwm;
2 3
4use stm32_metapac::timer::vals::Ckd;
5
3#[cfg(feature = "unstable-pac")] 6#[cfg(feature = "unstable-pac")]
4pub mod low_level { 7pub mod low_level {
5 pub use super::sealed::*; 8 pub use super::sealed::*;
@@ -67,6 +70,14 @@ pub(crate) mod sealed {
67 unsafe fn get_max_compare_value(&self) -> u16; 70 unsafe fn get_max_compare_value(&self) -> u16;
68 } 71 }
69 72
73 pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance {
74 unsafe fn set_dead_time_clock_division(&mut self, value: Ckd);
75
76 unsafe fn set_dead_time_value(&mut self, value: u8);
77
78 unsafe fn enable_complementary_channel(&mut self, channel: Channel, enable: bool);
79 }
80
70 pub trait CaptureCompare32bitInstance: crate::timer::sealed::GeneralPurpose32bitInstance { 81 pub trait CaptureCompare32bitInstance: crate::timer::sealed::GeneralPurpose32bitInstance {
71 unsafe fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); 82 unsafe fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode);
72 83
@@ -82,6 +93,12 @@ pub trait CaptureCompare16bitInstance:
82 sealed::CaptureCompare16bitInstance + crate::timer::GeneralPurpose16bitInstance + 'static 93 sealed::CaptureCompare16bitInstance + crate::timer::GeneralPurpose16bitInstance + 'static
83{ 94{
84} 95}
96
97pub trait ComplementaryCaptureCompare16bitInstance:
98 sealed::ComplementaryCaptureCompare16bitInstance + crate::timer::AdvancedControlInstance + 'static
99{
100}
101
85pub trait CaptureCompare32bitInstance: 102pub trait CaptureCompare32bitInstance:
86 sealed::CaptureCompare32bitInstance + CaptureCompare16bitInstance + crate::timer::GeneralPurpose32bitInstance + 'static 103 sealed::CaptureCompare32bitInstance + CaptureCompare16bitInstance + crate::timer::GeneralPurpose32bitInstance + 'static
87{ 104{
@@ -209,6 +226,29 @@ foreach_interrupt! {
209 impl CaptureCompare16bitInstance for crate::peripherals::$inst { 226 impl CaptureCompare16bitInstance for crate::peripherals::$inst {
210 227
211 } 228 }
229
230 impl crate::pwm::sealed::ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {
231 unsafe fn set_dead_time_clock_division(&mut self, value: Ckd) {
232 use crate::timer::sealed::AdvancedControlInstance;
233 Self::regs_advanced().cr1().modify(|w| w.set_ckd(value));
234 }
235
236 unsafe fn set_dead_time_value(&mut self, value: u8) {
237 use crate::timer::sealed::AdvancedControlInstance;
238 Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value));
239 }
240
241 unsafe fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) {
242 use crate::timer::sealed::AdvancedControlInstance;
243 Self::regs_advanced()
244 .ccer()
245 .modify(|w| w.set_ccne(channel.raw(), enable));
246 }
247 }
248
249 impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {
250
251 }
212 }; 252 };
213} 253}
214 254
diff --git a/examples/stm32f4/src/bin/pwm_complementary.rs b/examples/stm32f4/src/bin/pwm_complementary.rs
new file mode 100644
index 000000000..6e17f3fd3
--- /dev/null
+++ b/examples/stm32f4/src/bin/pwm_complementary.rs
@@ -0,0 +1,77 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_stm32::pwm::complementary_pwm::{Ckd, ComplementaryPwm, ComplementaryPwmPin};
8use embassy_stm32::pwm::simple_pwm::PwmPin;
9use embassy_stm32::pwm::Channel;
10use embassy_stm32::time::khz;
11use embassy_time::{Duration, Timer};
12use {defmt_rtt as _, panic_probe as _};
13
14#[embassy_executor::main]
15async fn main(_spawner: Spawner) {
16 let p = embassy_stm32::init(Default::default());
17 info!("Hello World!");
18
19 let ch1 = PwmPin::new_ch1(p.PE9);
20 let ch1n = ComplementaryPwmPin::new_ch1(p.PA7);
21 let mut pwm = ComplementaryPwm::new(
22 p.TIM1,
23 Some(ch1),
24 Some(ch1n),
25 None,
26 None,
27 None,
28 None,
29 None,
30 None,
31 khz(10),
32 );
33
34 /*
35 Dead-time = T_clk * T_dts * T_dtg
36
37 T_dts:
38 This bit-field indicates the division ratio between the timer clock (CK_INT) frequency and the
39 dead-time and sampling clock (tDTS)used by the dead-time generators and the digital filters
40 (ETR, TIx),
41 00: tDTS=tCK_INT
42 01: tDTS=2*tCK_INT
43 10: tDTS=4*tCK_INT
44
45 T_dtg:
46 This bit-field defines the duration of the dead-time inserted between the complementary
47 outputs. DT correspond to this duration.
48 DTG[7:5]=0xx => DT=DTG[7:0]x tdtg with tdtg=tDTS.
49 DTG[7:5]=10x => DT=(64+DTG[5:0])xtdtg with Tdtg=2xtDTS.
50 DTG[7:5]=110 => DT=(32+DTG[4:0])xtdtg with Tdtg=8xtDTS.
51 DTG[7:5]=111 => DT=(32+DTG[4:0])xtdtg with Tdtg=16xtDTS.
52 Example if TDTS=125ns (8MHz), dead-time possible values are:
53 0 to 15875 ns by 125 ns steps,
54 16 us to 31750 ns by 250 ns steps,
55 32 us to 63us by 1 us steps,
56 64 us to 126 us by 2 us steps
57 */
58 pwm.set_dead_time_clock_division(Ckd::DIV1);
59 pwm.set_dead_time_value(0);
60
61 let max = pwm.get_max_duty();
62 pwm.enable(Channel::Ch1);
63
64 info!("PWM initialized");
65 info!("PWM max duty {}", max);
66
67 loop {
68 pwm.set_duty(Channel::Ch1, 0);
69 Timer::after(Duration::from_millis(300)).await;
70 pwm.set_duty(Channel::Ch1, max / 4);
71 Timer::after(Duration::from_millis(300)).await;
72 pwm.set_duty(Channel::Ch1, max / 2);
73 Timer::after(Duration::from_millis(300)).await;
74 pwm.set_duty(Channel::Ch1, max - 1);
75 Timer::after(Duration::from_millis(300)).await;
76 }
77}