aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/src/pwm/advanced_pwm.rs87
-rw-r--r--embassy-stm32/src/pwm/mod.rs37
-rw-r--r--examples/stm32f334/src/bin/button.rs27
-rw-r--r--examples/stm32f334/src/bin/pwm.rs65
4 files changed, 160 insertions, 56 deletions
diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/pwm/advanced_pwm.rs
index 65f4e7ca7..fa34b2e18 100644
--- a/embassy-stm32/src/pwm/advanced_pwm.rs
+++ b/embassy-stm32/src/pwm/advanced_pwm.rs
@@ -144,6 +144,18 @@ impl<'d, T: HighResolutionCaptureCompare16bitInstance> AdvancedPwm<'d, T> {
144 T::enable(); 144 T::enable();
145 <T as crate::rcc::sealed::RccPeripheral>::reset(); 145 <T as crate::rcc::sealed::RccPeripheral>::reset();
146 146
147 // // Enable and and stabilize the DLL
148 // T::regs().dllcr().modify(|w| {
149 // // w.set_calen(true);
150 // // w.set_calrte(11);
151 // w.set_cal(true);
152 // });
153 //
154 // debug!("wait for dll calibration");
155 // while !T::regs().isr().read().dllrdy() {}
156 //
157 // debug!("dll calibration complete");
158
147 Self { 159 Self {
148 _inner: tim, 160 _inner: tim,
149 master: Master { phantom: PhantomData }, 161 master: Master { phantom: PhantomData },
@@ -173,12 +185,14 @@ impl<T: HighResolutionCaptureCompare16bitInstance> BurstController<T> {
173/// light loading conditions, and that the low-side switch must be active for a short time to drive 185/// light loading conditions, and that the low-side switch must be active for a short time to drive
174/// a bootstrapped high-side switch. 186/// a bootstrapped high-side switch.
175pub struct BridgeConverter<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> { 187pub struct BridgeConverter<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> {
176 phantom: PhantomData<T>, 188 timer: PhantomData<T>,
177 pub ch: C, 189 channel: PhantomData<C>,
190 dead_time: u16,
191 primary_duty: u16,
178} 192}
179 193
180impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> BridgeConverter<T, C> { 194impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> BridgeConverter<T, C> {
181 pub fn new(channel: C, frequency: Hertz) -> Self { 195 pub fn new(_channel: C, frequency: Hertz) -> Self {
182 use crate::pac::hrtim::vals::{Activeeffect, Cont, Inactiveeffect}; 196 use crate::pac::hrtim::vals::{Activeeffect, Cont, Inactiveeffect};
183 197
184 T::set_channel_frequency(C::raw(), frequency); 198 T::set_channel_frequency(C::raw(), frequency);
@@ -186,15 +200,21 @@ impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> Bridge
186 // Always enable preload 200 // Always enable preload
187 T::regs().tim(C::raw()).cr().modify(|w| { 201 T::regs().tim(C::raw()).cr().modify(|w| {
188 w.set_preen(true); 202 w.set_preen(true);
203 w.set_repu(true);
189 204
190 w.set_cont(Cont::CONTINUOUS); 205 w.set_cont(Cont::CONTINUOUS);
191 }); 206 });
192 207
208 // Enable timer outputs
193 T::regs().oenr().modify(|w| { 209 T::regs().oenr().modify(|w| {
194 w.set_t1oen(C::raw(), true); 210 w.set_t1oen(C::raw(), true);
195 w.set_t2oen(C::raw(), true); 211 w.set_t2oen(C::raw(), true);
196 }); 212 });
197 213
214 // The dead-time generation unit cannot be used because it forces the other output
215 // to be completely complementary to the first output, which restricts certain waveforms
216 // Therefore, software-implemented dead time must be used when setting the duty cycles
217
198 // Set output 1 to active on a period event 218 // Set output 1 to active on a period event
199 T::regs() 219 T::regs()
200 .tim(C::raw()) 220 .tim(C::raw())
@@ -207,21 +227,23 @@ impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> Bridge
207 .rstr(0) 227 .rstr(0)
208 .modify(|w| w.set_cmp(0, Inactiveeffect::SETINACTIVE)); 228 .modify(|w| w.set_cmp(0, Inactiveeffect::SETINACTIVE));
209 229
210 // Set output 2 to active on a compare 1 event 230 // Set output 2 to active on a compare 2 event
211 T::regs() 231 T::regs()
212 .tim(C::raw()) 232 .tim(C::raw())
213 .setr(1) 233 .setr(1)
214 .modify(|w| w.set_cmp(0, Activeeffect::SETACTIVE)); 234 .modify(|w| w.set_cmp(1, Activeeffect::SETACTIVE));
215 235
216 // Set output 2 to inactive on a compare 2 event 236 // Set output 2 to inactive on a compare 3 event
217 T::regs() 237 T::regs()
218 .tim(C::raw()) 238 .tim(C::raw())
219 .rstr(1) 239 .rstr(1)
220 .modify(|w| w.set_cmp(1, Inactiveeffect::SETINACTIVE)); 240 .modify(|w| w.set_cmp(2, Inactiveeffect::SETINACTIVE));
221 241
222 Self { 242 Self {
223 phantom: PhantomData, 243 timer: PhantomData,
224 ch: channel, 244 channel: PhantomData,
245 dead_time: 0,
246 primary_duty: 0,
225 } 247 }
226 } 248 }
227 249
@@ -236,7 +258,6 @@ impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> Bridge
236 pub fn enable_burst_mode(&mut self) { 258 pub fn enable_burst_mode(&mut self) {
237 use crate::pac::hrtim::vals::{Idlem, Idles}; 259 use crate::pac::hrtim::vals::{Idlem, Idles};
238 260
239 // TODO: fix metapac
240 T::regs().tim(C::raw()).outr().modify(|w| { 261 T::regs().tim(C::raw()).outr().modify(|w| {
241 w.set_idlem(0, Idlem::SETIDLE); 262 w.set_idlem(0, Idlem::SETIDLE);
242 w.set_idlem(1, Idlem::SETIDLE); 263 w.set_idlem(1, Idlem::SETIDLE);
@@ -258,9 +279,18 @@ impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> Bridge
258 }) 279 })
259 } 280 }
260 281
282 fn update_primary_duty_or_dead_time(&mut self) {
283 T::regs().tim(C::raw()).cmp(0).modify(|w| w.set_cmp(self.primary_duty));
284 T::regs()
285 .tim(C::raw())
286 .cmp(1)
287 .modify(|w| w.set_cmp(self.primary_duty + self.dead_time));
288 }
289
261 /// Set the dead time as a proportion of the maximum compare value 290 /// Set the dead time as a proportion of the maximum compare value
262 pub fn set_dead_time(&mut self, value: u16) { 291 pub fn set_dead_time(&mut self, dead_time: u16) {
263 T::set_channel_dead_time(C::raw(), value); 292 self.dead_time = dead_time;
293 self.update_primary_duty_or_dead_time();
264 } 294 }
265 295
266 /// Get the maximum compare value of a duty cycle 296 /// Get the maximum compare value of a duty cycle
@@ -272,15 +302,17 @@ impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> Bridge
272 /// 302 ///
273 /// In the case of a buck converter, this is the high-side switch 303 /// In the case of a buck converter, this is the high-side switch
274 /// In the case of a boost converter, this is the low-side switch 304 /// In the case of a boost converter, this is the low-side switch
275 pub fn set_primary_duty(&mut self, primary: u16) { 305 pub fn set_primary_duty(&mut self, primary_duty: u16) {
276 T::regs().tim(C::raw()).cmp(0).modify(|w| w.set_cmp(primary)); 306 self.primary_duty = primary_duty;
307 self.update_primary_duty_or_dead_time();
277 } 308 }
278 309
279 /// The primary duty is the period in any switch is active 310 /// The secondary duty is the period in any switch is active
280 /// 311 ///
281 /// If less than or equal to the primary duty, the secondary switch will never be active 312 /// If less than or equal to the primary duty, the secondary switch will never be active
282 pub fn set_secondary_duty(&mut self, secondary: u16) { 313 /// If a fully complementary output is desired, the secondary duty can be set to the max compare
283 T::regs().tim(C::raw()).cmp(1).modify(|w| w.set_cmp(secondary)); 314 pub fn set_secondary_duty(&mut self, secondary_duty: u16) {
315 T::regs().tim(C::raw()).cmp(2).modify(|w| w.set_cmp(secondary_duty));
284 } 316 }
285} 317}
286 318
@@ -290,14 +322,14 @@ impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> Bridge
290/// but does not include secondary rectification, which is appropriate for applications 322/// but does not include secondary rectification, which is appropriate for applications
291/// with a low-voltage on the secondary side. 323/// with a low-voltage on the secondary side.
292pub struct ResonantConverter<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> { 324pub struct ResonantConverter<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> {
293 phantom: PhantomData<T>, 325 timer: PhantomData<T>,
326 channel: PhantomData<C>,
294 min_period: u16, 327 min_period: u16,
295 max_period: u16, 328 max_period: u16,
296 pub ch: C,
297} 329}
298 330
299impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> ResonantConverter<T, C> { 331impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> ResonantConverter<T, C> {
300 pub fn new(channel: C, min_frequency: Hertz, max_frequency: Hertz) -> Self { 332 pub fn new(_channel: C, min_frequency: Hertz, max_frequency: Hertz) -> Self {
301 use crate::pac::hrtim::vals::Cont; 333 use crate::pac::hrtim::vals::Cont;
302 334
303 T::set_channel_frequency(C::raw(), min_frequency); 335 T::set_channel_frequency(C::raw(), min_frequency);
@@ -305,19 +337,30 @@ impl<T: HighResolutionCaptureCompare16bitInstance, C: AdvancedChannel<T>> Resona
305 // Always enable preload 337 // Always enable preload
306 T::regs().tim(C::raw()).cr().modify(|w| { 338 T::regs().tim(C::raw()).cr().modify(|w| {
307 w.set_preen(true); 339 w.set_preen(true);
340 w.set_repu(true);
308 341
309 w.set_cont(Cont::CONTINUOUS); 342 w.set_cont(Cont::CONTINUOUS);
310 w.set_half(true); 343 w.set_half(true);
311 }); 344 });
312 345
346 // Enable timer outputs
347 T::regs().oenr().modify(|w| {
348 w.set_t1oen(C::raw(), true);
349 w.set_t2oen(C::raw(), true);
350 });
351
352 // Dead-time generator can be used in this case because the primary fets
353 // of a resonant converter are always complementary
354 T::regs().tim(C::raw()).outr().modify(|w| w.set_dten(true));
355
313 let max_period = T::regs().tim(C::raw()).per().read().per(); 356 let max_period = T::regs().tim(C::raw()).per().read().per();
314 let min_period = max_period * (min_frequency.0 / max_frequency.0) as u16; 357 let min_period = max_period * (min_frequency.0 / max_frequency.0) as u16;
315 358
316 Self { 359 Self {
360 timer: PhantomData,
361 channel: PhantomData,
317 min_period: min_period, 362 min_period: min_period,
318 max_period: max_period, 363 max_period: max_period,
319 phantom: PhantomData,
320 ch: channel,
321 } 364 }
322 } 365 }
323 366
diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs
index d09e38d0e..429a290ee 100644
--- a/embassy-stm32/src/pwm/mod.rs
+++ b/embassy-stm32/src/pwm/mod.rs
@@ -123,7 +123,7 @@ impl From<u8> for HighResolutionControlPrescaler {
123 123
124#[cfg(hrtim_v1)] 124#[cfg(hrtim_v1)]
125impl HighResolutionControlPrescaler { 125impl HighResolutionControlPrescaler {
126 pub fn compute_min(val: u32) -> Self { 126 pub fn compute_min_high_res(val: u32) -> Self {
127 *[ 127 *[
128 HighResolutionControlPrescaler::Div1, 128 HighResolutionControlPrescaler::Div1,
129 HighResolutionControlPrescaler::Div2, 129 HighResolutionControlPrescaler::Div2,
@@ -139,6 +139,18 @@ impl HighResolutionControlPrescaler {
139 .next() 139 .next()
140 .unwrap() 140 .unwrap()
141 } 141 }
142
143 pub fn compute_min_low_res(val: u32) -> Self {
144 *[
145 HighResolutionControlPrescaler::Div32,
146 HighResolutionControlPrescaler::Div64,
147 HighResolutionControlPrescaler::Div128,
148 ]
149 .iter()
150 .skip_while(|psc| <HighResolutionControlPrescaler as Into<u32>>::into(**psc) <= val)
151 .next()
152 .unwrap()
153 }
142} 154}
143 155
144pub(crate) mod sealed { 156pub(crate) mod sealed {
@@ -367,10 +379,14 @@ foreach_interrupt! {
367 let f = frequency.0; 379 let f = frequency.0;
368 let timer_f = Self::frequency().0; 380 let timer_f = Self::frequency().0;
369 let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); 381 let psc_min = (timer_f / f) / (u16::MAX as u32 / 32);
370 let psc = HighResolutionControlPrescaler::compute_min(psc_min); 382 let psc = if Self::regs().isr().read().dllrdy() {
383 HighResolutionControlPrescaler::compute_min_high_res(psc_min)
384 } else {
385 HighResolutionControlPrescaler::compute_min_low_res(psc_min)
386 };
371 387
372 let psc_val: u32 = psc.into(); 388 let psc_val: u32 = psc.into();
373 let timer_f = timer_f / psc_val; 389 let timer_f = 32 * (timer_f / psc_val);
374 let per: u16 = (timer_f / f) as u16; 390 let per: u16 = (timer_f / f) as u16;
375 391
376 let regs = Self::regs(); 392 let regs = Self::regs();
@@ -386,10 +402,14 @@ foreach_interrupt! {
386 let f = frequency.0; 402 let f = frequency.0;
387 let timer_f = Self::frequency().0; 403 let timer_f = Self::frequency().0;
388 let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); 404 let psc_min = (timer_f / f) / (u16::MAX as u32 / 32);
389 let psc = HighResolutionControlPrescaler::compute_min(psc_min); 405 let psc = if Self::regs().isr().read().dllrdy() {
406 HighResolutionControlPrescaler::compute_min_high_res(psc_min)
407 } else {
408 HighResolutionControlPrescaler::compute_min_low_res(psc_min)
409 };
390 410
391 let psc_val: u32 = psc.into(); 411 let psc_val: u32 = psc.into();
392 let timer_f = timer_f / psc_val; 412 let timer_f = 32 * (timer_f / psc_val);
393 let per: u16 = (timer_f / f) as u16; 413 let per: u16 = (timer_f / f) as u16;
394 414
395 let regs = Self::regs(); 415 let regs = Self::regs();
@@ -410,7 +430,12 @@ foreach_interrupt! {
410 // The dead-time base clock runs 4 times slower than the hrtim base clock 430 // The dead-time base clock runs 4 times slower than the hrtim base clock
411 // u9::MAX = 511 431 // u9::MAX = 511
412 let psc_min = (psc_val * dead_time as u32) / (4 * 511); 432 let psc_min = (psc_val * dead_time as u32) / (4 * 511);
413 let psc = HighResolutionControlPrescaler::compute_min(psc_min); 433 let psc = if Self::regs().isr().read().dllrdy() {
434 HighResolutionControlPrescaler::compute_min_high_res(psc_min)
435 } else {
436 HighResolutionControlPrescaler::compute_min_low_res(psc_min)
437 };
438
414 let dt_psc_val: u32 = psc.into(); 439 let dt_psc_val: u32 = psc.into();
415 let dt_val = (dt_psc_val * dead_time as u32) / (4 * psc_val); 440 let dt_val = (dt_psc_val * dead_time as u32) / (4 * psc_val);
416 441
diff --git a/examples/stm32f334/src/bin/button.rs b/examples/stm32f334/src/bin/button.rs
new file mode 100644
index 000000000..599c0f27d
--- /dev/null
+++ b/examples/stm32f334/src/bin/button.rs
@@ -0,0 +1,27 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_time::{Duration, Timer};
9use {defmt_rtt as _, panic_probe as _};
10
11#[embassy_executor::main]
12async fn main(_spawner: Spawner) {
13 info!("Hello World!");
14
15 let p = embassy_stm32::init(Default::default());
16
17 let mut out1 = Output::new(p.PA8, Level::Low, Speed::High);
18
19 out1.set_high();
20 Timer::after(Duration::from_millis(500)).await;
21 out1.set_low();
22
23 Timer::after(Duration::from_millis(500)).await;
24 info!("end program");
25
26 cortex_m::asm::bkpt();
27}
diff --git a/examples/stm32f334/src/bin/pwm.rs b/examples/stm32f334/src/bin/pwm.rs
index 1b5d509e7..364119744 100644
--- a/examples/stm32f334/src/bin/pwm.rs
+++ b/examples/stm32f334/src/bin/pwm.rs
@@ -5,12 +5,20 @@
5use defmt::*; 5use defmt::*;
6use embassy_executor::Spawner; 6use embassy_executor::Spawner;
7use embassy_stm32::pwm::advanced_pwm::*; 7use embassy_stm32::pwm::advanced_pwm::*;
8use embassy_stm32::time::khz; 8use embassy_stm32::time::{khz, mhz};
9use embassy_stm32::Config;
10use embassy_time::{Duration, Timer};
9use {defmt_rtt as _, panic_probe as _}; 11use {defmt_rtt as _, panic_probe as _};
10 12
11#[embassy_executor::main] 13#[embassy_executor::main]
12async fn main(_spawner: Spawner) { 14async fn main(_spawner: Spawner) {
13 let p = embassy_stm32::init(Default::default()); 15 let mut config: Config = Default::default();
16 config.rcc.sysclk = Some(mhz(64));
17 config.rcc.hclk = Some(mhz(64));
18 config.rcc.pclk1 = Some(mhz(32));
19 config.rcc.pclk2 = Some(mhz(64));
20
21 let p = embassy_stm32::init(config);
14 info!("Hello World!"); 22 info!("Hello World!");
15 23
16 let ch1 = PwmPin::new_cha(p.PA8); 24 let ch1 = PwmPin::new_cha(p.PA8);
@@ -29,34 +37,35 @@ async fn main(_spawner: Spawner) {
29 None, 37 None,
30 ); 38 );
31 39
32 let mut buck_converter = BridgeConverter::new(pwm.ch_a, khz(100)); 40 info!("pwm constructed");
33
34 buck_converter.set_primary_duty(0);
35 buck_converter.set_secondary_duty(0);
36 buck_converter.set_dead_time(0);
37
38 // note: if the pins are not passed into the advanced pwm struct, they will not be output
39 let mut boost_converter = BridgeConverter::new(pwm.ch_b, khz(100));
40 41
41 boost_converter.set_primary_duty(0); 42 let mut buck_converter = BridgeConverter::new(pwm.ch_a, khz(5));
42 boost_converter.set_secondary_duty(0);
43 43
44 // let max = pwm.get_max_duty(); 44 // embassy_stm32::pac::HRTIM1
45 // pwm.set_dead_time(max / 1024); 45 // .tim(0)
46 // .setr(0)
47 // .modify(|w| w.set_sst(Activeeffect::SETACTIVE));
46 // 48 //
47 // pwm.enable(Channel::Ch1); 49 // Timer::after(Duration::from_millis(500)).await;
48 // 50 //
49 // info!("PWM initialized"); 51 // embassy_stm32::pac::HRTIM1
50 // info!("PWM max duty {}", max); 52 // .tim(0)
51 // 53 // .rstr(0)
52 // loop { 54 // .modify(|w| w.set_srt(Inactiveeffect::SETINACTIVE));
53 // pwm.set_duty(Channel::Ch1, 0); 55
54 // Timer::after(Duration::from_millis(300)).await; 56 let max_duty = buck_converter.get_max_compare_value();
55 // pwm.set_duty(Channel::Ch1, max / 4); 57
56 // Timer::after(Duration::from_millis(300)).await; 58 info!("max compare value: {}", max_duty);
57 // pwm.set_duty(Channel::Ch1, max / 2); 59
58 // Timer::after(Duration::from_millis(300)).await; 60 buck_converter.set_dead_time(max_duty / 20);
59 // pwm.set_duty(Channel::Ch1, max - 1); 61 buck_converter.set_primary_duty(max_duty / 2);
60 // Timer::after(Duration::from_millis(300)).await; 62 buck_converter.set_secondary_duty(3 * max_duty / 4);
61 // } 63
64 buck_converter.start();
65
66 Timer::after(Duration::from_millis(500)).await;
67
68 info!("end program");
69
70 cortex_m::asm::bkpt();
62} 71}