aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.vscode/settings.json1
-rwxr-xr-xci.sh1
-rw-r--r--embassy-stm32/build.rs10
-rw-r--r--embassy-stm32/src/hrtim/mod.rs409
-rw-r--r--embassy-stm32/src/hrtim/traits.rs193
-rw-r--r--embassy-stm32/src/lib.rs9
-rw-r--r--embassy-stm32/src/rcc/f3.rs1
-rw-r--r--examples/stm32f334/.cargo/config.toml9
-rw-r--r--examples/stm32f334/Cargo.toml26
-rw-r--r--examples/stm32f334/build.rs5
-rw-r--r--examples/stm32f334/src/bin/button.rs27
-rw-r--r--examples/stm32f334/src/bin/hello.rs23
-rw-r--r--examples/stm32f334/src/bin/pwm.rs71
13 files changed, 781 insertions, 4 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 725fb69d0..29e8812e3 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -25,6 +25,7 @@
25 // "examples/stm32f1/Cargo.toml", 25 // "examples/stm32f1/Cargo.toml",
26 // "examples/stm32f2/Cargo.toml", 26 // "examples/stm32f2/Cargo.toml",
27 // "examples/stm32f3/Cargo.toml", 27 // "examples/stm32f3/Cargo.toml",
28 // "examples/stm32f334/Cargo.toml",
28 // "examples/stm32f4/Cargo.toml", 29 // "examples/stm32f4/Cargo.toml",
29 // "examples/stm32f7/Cargo.toml", 30 // "examples/stm32f7/Cargo.toml",
30 // "examples/stm32g0/Cargo.toml", 31 // "examples/stm32g0/Cargo.toml",
diff --git a/ci.sh b/ci.sh
index 19628b50e..32ae7855e 100755
--- a/ci.sh
+++ b/ci.sh
@@ -117,6 +117,7 @@ cargo batch \
117 --- build --release --manifest-path examples/stm32f1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f1 \ 117 --- build --release --manifest-path examples/stm32f1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f1 \
118 --- build --release --manifest-path examples/stm32f2/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f2 \ 118 --- build --release --manifest-path examples/stm32f2/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f2 \
119 --- build --release --manifest-path examples/stm32f3/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f3 \ 119 --- build --release --manifest-path examples/stm32f3/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f3 \
120 --- build --release --manifest-path examples/stm32f334/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f334 \
120 --- build --release --manifest-path examples/stm32f4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f4 \ 121 --- build --release --manifest-path examples/stm32f4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f4 \
121 --- build --release --manifest-path examples/stm32f7/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f7 \ 122 --- build --release --manifest-path examples/stm32f7/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f7 \
122 --- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32c0 \ 123 --- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32c0 \
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 409a943d2..9b3caefd5 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -587,6 +587,16 @@ fn main() {
587 (("timer", "BKIN2"), quote!(crate::timer::BreakInput2Pin)), 587 (("timer", "BKIN2"), quote!(crate::timer::BreakInput2Pin)),
588 (("timer", "BKIN2_COMP1"), quote!(crate::timer::BreakInput2Comparator1Pin)), 588 (("timer", "BKIN2_COMP1"), quote!(crate::timer::BreakInput2Comparator1Pin)),
589 (("timer", "BKIN2_COMP2"), quote!(crate::timer::BreakInput2Comparator2Pin)), 589 (("timer", "BKIN2_COMP2"), quote!(crate::timer::BreakInput2Comparator2Pin)),
590 (("hrtim", "CHA1"), quote!(crate::hrtim::ChannelAPin)),
591 (("hrtim", "CHA2"), quote!(crate::hrtim::ChannelAComplementaryPin)),
592 (("hrtim", "CHB1"), quote!(crate::hrtim::ChannelBPin)),
593 (("hrtim", "CHB2"), quote!(crate::hrtim::ChannelBComplementaryPin)),
594 (("hrtim", "CHC1"), quote!(crate::hrtim::ChannelCPin)),
595 (("hrtim", "CHC2"), quote!(crate::hrtim::ChannelCComplementaryPin)),
596 (("hrtim", "CHD1"), quote!(crate::hrtim::ChannelDPin)),
597 (("hrtim", "CHD2"), quote!(crate::hrtim::ChannelDComplementaryPin)),
598 (("hrtim", "CHE1"), quote!(crate::hrtim::ChannelEPin)),
599 (("hrtim", "CHE2"), quote!(crate::hrtim::ChannelEComplementaryPin)),
590 (("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)), 600 (("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)),
591 (("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)), 601 (("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)),
592 (("sdmmc", "D0"), quote!(crate::sdmmc::D0Pin)), 602 (("sdmmc", "D0"), quote!(crate::sdmmc::D0Pin)),
diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs
new file mode 100644
index 000000000..a930ff73f
--- /dev/null
+++ b/embassy-stm32/src/hrtim/mod.rs
@@ -0,0 +1,409 @@
1mod traits;
2
3use core::marker::PhantomData;
4
5use embassy_hal_internal::{into_ref, PeripheralRef};
6pub use traits::Instance;
7
8#[allow(unused_imports)]
9use crate::gpio::sealed::{AFType, Pin};
10use crate::gpio::AnyPin;
11use crate::time::Hertz;
12use crate::Peripheral;
13
14pub enum Source {
15 Master,
16 ChA,
17 ChB,
18 ChC,
19 ChD,
20 ChE,
21}
22
23pub struct BurstController<T: Instance> {
24 phantom: PhantomData<T>,
25}
26pub struct Master<T: Instance> {
27 phantom: PhantomData<T>,
28}
29pub struct ChA<T: Instance> {
30 phantom: PhantomData<T>,
31}
32pub struct ChB<T: Instance> {
33 phantom: PhantomData<T>,
34}
35pub struct ChC<T: Instance> {
36 phantom: PhantomData<T>,
37}
38pub struct ChD<T: Instance> {
39 phantom: PhantomData<T>,
40}
41pub struct ChE<T: Instance> {
42 phantom: PhantomData<T>,
43}
44
45mod sealed {
46 use super::Instance;
47
48 pub trait AdvancedChannel<T: Instance> {
49 fn raw() -> usize;
50 }
51}
52
53pub trait AdvancedChannel<T: Instance>: sealed::AdvancedChannel<T> {}
54
55pub struct PwmPin<'d, Perip, Channel> {
56 _pin: PeripheralRef<'d, AnyPin>,
57 phantom: PhantomData<(Perip, Channel)>,
58}
59
60pub struct ComplementaryPwmPin<'d, Perip, Channel> {
61 _pin: PeripheralRef<'d, AnyPin>,
62 phantom: PhantomData<(Perip, Channel)>,
63}
64
65macro_rules! advanced_channel_impl {
66 ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => {
67 impl<'d, Perip: Instance> PwmPin<'d, Perip, $channel<Perip>> {
68 pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<Perip>> + 'd) -> Self {
69 into_ref!(pin);
70 critical_section::with(|_| {
71 pin.set_low();
72 pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
73 #[cfg(gpio_v2)]
74 pin.set_speed(crate::gpio::Speed::VeryHigh);
75 });
76 PwmPin {
77 _pin: pin.map_into(),
78 phantom: PhantomData,
79 }
80 }
81 }
82
83 impl<'d, Perip: Instance> ComplementaryPwmPin<'d, Perip, $channel<Perip>> {
84 pub fn $new_chx(pin: impl Peripheral<P = impl $complementary_pin_trait<Perip>> + 'd) -> Self {
85 into_ref!(pin);
86 critical_section::with(|_| {
87 pin.set_low();
88 pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
89 #[cfg(gpio_v2)]
90 pin.set_speed(crate::gpio::Speed::VeryHigh);
91 });
92 ComplementaryPwmPin {
93 _pin: pin.map_into(),
94 phantom: PhantomData,
95 }
96 }
97 }
98
99 impl<T: Instance> sealed::AdvancedChannel<T> for $channel<T> {
100 fn raw() -> usize {
101 $ch_num
102 }
103 }
104 impl<T: Instance> AdvancedChannel<T> for $channel<T> {}
105 };
106}
107
108advanced_channel_impl!(new_cha, ChA, 0, ChannelAPin, ChannelAComplementaryPin);
109advanced_channel_impl!(new_chb, ChB, 1, ChannelBPin, ChannelBComplementaryPin);
110advanced_channel_impl!(new_chc, ChC, 2, ChannelCPin, ChannelCComplementaryPin);
111advanced_channel_impl!(new_chd, ChD, 3, ChannelDPin, ChannelDComplementaryPin);
112advanced_channel_impl!(new_che, ChE, 4, ChannelEPin, ChannelEComplementaryPin);
113
114/// Struct used to divide a high resolution timer into multiple channels
115pub struct AdvancedPwm<'d, T: Instance> {
116 _inner: PeripheralRef<'d, T>,
117 pub master: Master<T>,
118 pub burst_controller: BurstController<T>,
119 pub ch_a: ChA<T>,
120 pub ch_b: ChB<T>,
121 pub ch_c: ChC<T>,
122 pub ch_d: ChD<T>,
123 pub ch_e: ChE<T>,
124}
125
126impl<'d, T: Instance> AdvancedPwm<'d, T> {
127 pub fn new(
128 tim: impl Peripheral<P = T> + 'd,
129 _cha: Option<PwmPin<'d, T, ChA<T>>>,
130 _chan: Option<ComplementaryPwmPin<'d, T, ChA<T>>>,
131 _chb: Option<PwmPin<'d, T, ChB<T>>>,
132 _chbn: Option<ComplementaryPwmPin<'d, T, ChB<T>>>,
133 _chc: Option<PwmPin<'d, T, ChC<T>>>,
134 _chcn: Option<ComplementaryPwmPin<'d, T, ChC<T>>>,
135 _chd: Option<PwmPin<'d, T, ChD<T>>>,
136 _chdn: Option<ComplementaryPwmPin<'d, T, ChD<T>>>,
137 _che: Option<PwmPin<'d, T, ChE<T>>>,
138 _chen: Option<ComplementaryPwmPin<'d, T, ChE<T>>>,
139 ) -> Self {
140 Self::new_inner(tim)
141 }
142
143 fn new_inner(tim: impl Peripheral<P = T> + 'd) -> Self {
144 into_ref!(tim);
145
146 T::enable();
147 <T as crate::rcc::sealed::RccPeripheral>::reset();
148
149 // // Enable and and stabilize the DLL
150 // T::regs().dllcr().modify(|w| {
151 // // w.set_calen(true);
152 // // w.set_calrte(11);
153 // w.set_cal(true);
154 // });
155 //
156 // debug!("wait for dll calibration");
157 // while !T::regs().isr().read().dllrdy() {}
158 //
159 // debug!("dll calibration complete");
160
161 Self {
162 _inner: tim,
163 master: Master { phantom: PhantomData },
164 burst_controller: BurstController { phantom: PhantomData },
165 ch_a: ChA { phantom: PhantomData },
166 ch_b: ChB { phantom: PhantomData },
167 ch_c: ChC { phantom: PhantomData },
168 ch_d: ChD { phantom: PhantomData },
169 ch_e: ChE { phantom: PhantomData },
170 }
171 }
172}
173
174impl<T: Instance> BurstController<T> {
175 pub fn set_source(&mut self, _source: Source) {
176 todo!("burst mode control registers not implemented")
177 }
178}
179
180/// Represents a fixed-frequency bridge converter
181///
182/// Our implementation of the bridge converter uses a single channel and three compare registers,
183/// allowing implementation of a synchronous buck or boost converter in continuous or discontinuous
184/// conduction mode.
185///
186/// It is important to remember that in synchronous topologies, energy can flow in reverse during
187/// light loading conditions, and that the low-side switch must be active for a short time to drive
188/// a bootstrapped high-side switch.
189pub struct BridgeConverter<T: Instance, C: AdvancedChannel<T>> {
190 timer: PhantomData<T>,
191 channel: PhantomData<C>,
192 dead_time: u16,
193 primary_duty: u16,
194 min_secondary_duty: u16,
195 max_secondary_duty: u16,
196}
197
198impl<T: Instance, C: AdvancedChannel<T>> BridgeConverter<T, C> {
199 pub fn new(_channel: C, frequency: Hertz) -> Self {
200 use crate::pac::hrtim::vals::{Activeeffect, Inactiveeffect};
201
202 T::set_channel_frequency(C::raw(), frequency);
203
204 // Always enable preload
205 T::regs().tim(C::raw()).cr().modify(|w| {
206 w.set_preen(true);
207 w.set_repu(true);
208 w.set_cont(true);
209 });
210
211 // Enable timer outputs
212 T::regs().oenr().modify(|w| {
213 w.set_t1oen(C::raw(), true);
214 w.set_t2oen(C::raw(), true);
215 });
216
217 // The dead-time generation unit cannot be used because it forces the other output
218 // to be completely complementary to the first output, which restricts certain waveforms
219 // Therefore, software-implemented dead time must be used when setting the duty cycles
220
221 // Set output 1 to active on a period event
222 T::regs()
223 .tim(C::raw())
224 .setr(0)
225 .modify(|w| w.set_per(Activeeffect::SETACTIVE));
226
227 // Set output 1 to inactive on a compare 1 event
228 T::regs()
229 .tim(C::raw())
230 .rstr(0)
231 .modify(|w| w.set_cmp(0, Inactiveeffect::SETINACTIVE));
232
233 // Set output 2 to active on a compare 2 event
234 T::regs()
235 .tim(C::raw())
236 .setr(1)
237 .modify(|w| w.set_cmp(1, Activeeffect::SETACTIVE));
238
239 // Set output 2 to inactive on a compare 3 event
240 T::regs()
241 .tim(C::raw())
242 .rstr(1)
243 .modify(|w| w.set_cmp(2, Inactiveeffect::SETINACTIVE));
244
245 Self {
246 timer: PhantomData,
247 channel: PhantomData,
248 dead_time: 0,
249 primary_duty: 0,
250 min_secondary_duty: 0,
251 max_secondary_duty: 0,
252 }
253 }
254
255 pub fn start(&mut self) {
256 T::regs().mcr().modify(|w| w.set_tcen(C::raw(), true));
257 }
258
259 pub fn stop(&mut self) {
260 T::regs().mcr().modify(|w| w.set_tcen(C::raw(), false));
261 }
262
263 pub fn enable_burst_mode(&mut self) {
264 T::regs().tim(C::raw()).outr().modify(|w| {
265 // Enable Burst Mode
266 w.set_idlem(0, true);
267 w.set_idlem(1, true);
268
269 // Set output to active during the burst
270 w.set_idles(0, true);
271 w.set_idles(1, true);
272 })
273 }
274
275 pub fn disable_burst_mode(&mut self) {
276 T::regs().tim(C::raw()).outr().modify(|w| {
277 // Disable Burst Mode
278 w.set_idlem(0, false);
279 w.set_idlem(1, false);
280 })
281 }
282
283 fn update_primary_duty_or_dead_time(&mut self) {
284 self.min_secondary_duty = self.primary_duty + self.dead_time;
285
286 T::regs().tim(C::raw()).cmp(0).modify(|w| w.set_cmp(self.primary_duty));
287 T::regs()
288 .tim(C::raw())
289 .cmp(1)
290 .modify(|w| w.set_cmp(self.min_secondary_duty));
291 }
292
293 /// Set the dead time as a proportion of the maximum compare value
294 pub fn set_dead_time(&mut self, dead_time: u16) {
295 self.dead_time = dead_time;
296 self.max_secondary_duty = self.get_max_compare_value() - dead_time;
297 self.update_primary_duty_or_dead_time();
298 }
299
300 /// Get the maximum compare value of a duty cycle
301 pub fn get_max_compare_value(&mut self) -> u16 {
302 T::regs().tim(C::raw()).per().read().per()
303 }
304
305 /// The primary duty is the period in which the primary switch is active
306 ///
307 /// In the case of a buck converter, this is the high-side switch
308 /// In the case of a boost converter, this is the low-side switch
309 pub fn set_primary_duty(&mut self, primary_duty: u16) {
310 self.primary_duty = primary_duty;
311 self.update_primary_duty_or_dead_time();
312 }
313
314 /// The secondary duty is the period in any switch is active
315 ///
316 /// If less than or equal to the primary duty, the secondary switch will be active for one tick
317 /// If a fully complementary output is desired, the secondary duty can be set to the max compare
318 pub fn set_secondary_duty(&mut self, secondary_duty: u16) {
319 let secondary_duty = if secondary_duty > self.max_secondary_duty {
320 self.max_secondary_duty
321 } else if secondary_duty <= self.min_secondary_duty {
322 self.min_secondary_duty + 1
323 } else {
324 secondary_duty
325 };
326
327 T::regs().tim(C::raw()).cmp(2).modify(|w| w.set_cmp(secondary_duty));
328 }
329}
330
331/// Represents a variable-frequency resonant converter
332///
333/// This implementation of a resonsant converter is appropriate for a half or full bridge,
334/// but does not include secondary rectification, which is appropriate for applications
335/// with a low-voltage on the secondary side.
336pub struct ResonantConverter<T: Instance, C: AdvancedChannel<T>> {
337 timer: PhantomData<T>,
338 channel: PhantomData<C>,
339 min_period: u16,
340 max_period: u16,
341}
342
343impl<T: Instance, C: AdvancedChannel<T>> ResonantConverter<T, C> {
344 pub fn new(_channel: C, min_frequency: Hertz, max_frequency: Hertz) -> Self {
345 T::set_channel_frequency(C::raw(), min_frequency);
346
347 // Always enable preload
348 T::regs().tim(C::raw()).cr().modify(|w| {
349 w.set_preen(true);
350 w.set_repu(true);
351
352 w.set_cont(true);
353 w.set_half(true);
354 });
355
356 // Enable timer outputs
357 T::regs().oenr().modify(|w| {
358 w.set_t1oen(C::raw(), true);
359 w.set_t2oen(C::raw(), true);
360 });
361
362 // Dead-time generator can be used in this case because the primary fets
363 // of a resonant converter are always complementary
364 T::regs().tim(C::raw()).outr().modify(|w| w.set_dten(true));
365
366 let max_period = T::regs().tim(C::raw()).per().read().per();
367 let min_period = max_period * (min_frequency.0 / max_frequency.0) as u16;
368
369 Self {
370 timer: PhantomData,
371 channel: PhantomData,
372 min_period: min_period,
373 max_period: max_period,
374 }
375 }
376
377 /// Set the dead time as a proportion of the maximum compare value
378 pub fn set_dead_time(&mut self, value: u16) {
379 T::set_channel_dead_time(C::raw(), value);
380 }
381
382 pub fn set_period(&mut self, period: u16) {
383 assert!(period < self.max_period);
384 assert!(period > self.min_period);
385
386 T::regs().tim(C::raw()).per().modify(|w| w.set_per(period));
387 }
388
389 /// Get the minimum compare value of a duty cycle
390 pub fn get_min_period(&mut self) -> u16 {
391 self.min_period
392 }
393
394 /// Get the maximum compare value of a duty cycle
395 pub fn get_max_period(&mut self) -> u16 {
396 self.max_period
397 }
398}
399
400pin_trait!(ChannelAPin, Instance);
401pin_trait!(ChannelAComplementaryPin, Instance);
402pin_trait!(ChannelBPin, Instance);
403pin_trait!(ChannelBComplementaryPin, Instance);
404pin_trait!(ChannelCPin, Instance);
405pin_trait!(ChannelCComplementaryPin, Instance);
406pin_trait!(ChannelDPin, Instance);
407pin_trait!(ChannelDComplementaryPin, Instance);
408pin_trait!(ChannelEPin, Instance);
409pin_trait!(ChannelEComplementaryPin, Instance);
diff --git a/embassy-stm32/src/hrtim/traits.rs b/embassy-stm32/src/hrtim/traits.rs
new file mode 100644
index 000000000..158a68862
--- /dev/null
+++ b/embassy-stm32/src/hrtim/traits.rs
@@ -0,0 +1,193 @@
1use crate::rcc::sealed::RccPeripheral;
2use crate::time::Hertz;
3
4#[derive(Clone, Copy)]
5pub(crate) enum Prescaler {
6 Div1,
7 Div2,
8 Div4,
9 Div8,
10 Div16,
11 Div32,
12 Div64,
13 Div128,
14}
15
16impl From<Prescaler> for u32 {
17 fn from(val: Prescaler) -> Self {
18 match val {
19 Prescaler::Div1 => 1,
20 Prescaler::Div2 => 2,
21 Prescaler::Div4 => 4,
22 Prescaler::Div8 => 8,
23 Prescaler::Div16 => 16,
24 Prescaler::Div32 => 32,
25 Prescaler::Div64 => 64,
26 Prescaler::Div128 => 128,
27 }
28 }
29}
30
31impl From<Prescaler> for u8 {
32 fn from(val: Prescaler) -> Self {
33 match val {
34 Prescaler::Div1 => 0b000,
35 Prescaler::Div2 => 0b001,
36 Prescaler::Div4 => 0b010,
37 Prescaler::Div8 => 0b011,
38 Prescaler::Div16 => 0b100,
39 Prescaler::Div32 => 0b101,
40 Prescaler::Div64 => 0b110,
41 Prescaler::Div128 => 0b111,
42 }
43 }
44}
45
46impl From<u8> for Prescaler {
47 fn from(val: u8) -> Self {
48 match val {
49 0b000 => Prescaler::Div1,
50 0b001 => Prescaler::Div2,
51 0b010 => Prescaler::Div4,
52 0b011 => Prescaler::Div8,
53 0b100 => Prescaler::Div16,
54 0b101 => Prescaler::Div32,
55 0b110 => Prescaler::Div64,
56 0b111 => Prescaler::Div128,
57 _ => unreachable!(),
58 }
59 }
60}
61
62impl Prescaler {
63 pub fn compute_min_high_res(val: u32) -> Self {
64 *[
65 Prescaler::Div1,
66 Prescaler::Div2,
67 Prescaler::Div4,
68 Prescaler::Div8,
69 Prescaler::Div16,
70 Prescaler::Div32,
71 Prescaler::Div64,
72 Prescaler::Div128,
73 ]
74 .iter()
75 .skip_while(|psc| <Prescaler as Into<u32>>::into(**psc) <= val)
76 .next()
77 .unwrap()
78 }
79
80 pub fn compute_min_low_res(val: u32) -> Self {
81 *[Prescaler::Div32, Prescaler::Div64, Prescaler::Div128]
82 .iter()
83 .skip_while(|psc| <Prescaler as Into<u32>>::into(**psc) <= val)
84 .next()
85 .unwrap()
86 }
87}
88
89pub(crate) mod sealed {
90 use super::*;
91
92 pub trait Instance: RccPeripheral {
93 fn regs() -> crate::pac::hrtim::Hrtim;
94
95 fn set_master_frequency(frequency: Hertz);
96
97 fn set_channel_frequency(channnel: usize, frequency: Hertz);
98
99 /// Set the dead time as a proportion of max_duty
100 fn set_channel_dead_time(channnel: usize, dead_time: u16);
101
102 // fn enable_outputs(enable: bool);
103 //
104 // fn enable_channel(&mut self, channel: usize, enable: bool);
105 }
106}
107
108pub trait Instance: sealed::Instance + 'static {}
109
110foreach_interrupt! {
111 ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => {
112 impl sealed::Instance for crate::peripherals::$inst {
113 fn regs() -> crate::pac::hrtim::Hrtim {
114 crate::pac::$inst
115 }
116
117 fn set_master_frequency(frequency: Hertz) {
118 use crate::rcc::sealed::RccPeripheral;
119
120 let f = frequency.0;
121 let timer_f = Self::frequency().0;
122 let psc_min = (timer_f / f) / (u16::MAX as u32 / 32);
123 let psc = if Self::regs().isr().read().dllrdy() {
124 Prescaler::compute_min_high_res(psc_min)
125 } else {
126 Prescaler::compute_min_low_res(psc_min)
127 };
128
129 let psc_val: u32 = psc.into();
130 let timer_f = 32 * (timer_f / psc_val);
131 let per: u16 = (timer_f / f) as u16;
132
133 let regs = Self::regs();
134
135 regs.mcr().modify(|w| w.set_ckpsc(psc.into()));
136 regs.mper().modify(|w| w.set_mper(per));
137 }
138
139 fn set_channel_frequency(channel: usize, frequency: Hertz) {
140 use crate::rcc::sealed::RccPeripheral;
141
142 let f = frequency.0;
143 let timer_f = Self::frequency().0;
144 let psc_min = (timer_f / f) / (u16::MAX as u32 / 32);
145 let psc = if Self::regs().isr().read().dllrdy() {
146 Prescaler::compute_min_high_res(psc_min)
147 } else {
148 Prescaler::compute_min_low_res(psc_min)
149 };
150
151 let psc_val: u32 = psc.into();
152 let timer_f = 32 * (timer_f / psc_val);
153 let per: u16 = (timer_f / f) as u16;
154
155 let regs = Self::regs();
156
157 regs.tim(channel).cr().modify(|w| w.set_ckpsc(psc.into()));
158 regs.tim(channel).per().modify(|w| w.set_per(per));
159 }
160
161 fn set_channel_dead_time(channel: usize, dead_time: u16) {
162
163 let regs = Self::regs();
164
165 let channel_psc: Prescaler = regs.tim(channel).cr().read().ckpsc().into();
166 let psc_val: u32 = channel_psc.into();
167
168
169 // The dead-time base clock runs 4 times slower than the hrtim base clock
170 // u9::MAX = 511
171 let psc_min = (psc_val * dead_time as u32) / (4 * 511);
172 let psc = if Self::regs().isr().read().dllrdy() {
173 Prescaler::compute_min_high_res(psc_min)
174 } else {
175 Prescaler::compute_min_low_res(psc_min)
176 };
177
178 let dt_psc_val: u32 = psc.into();
179 let dt_val = (dt_psc_val * dead_time as u32) / (4 * psc_val);
180
181 regs.tim(channel).dt().modify(|w| {
182 w.set_dtprsc(psc.into());
183 w.set_dtf(dt_val as u16);
184 w.set_dtr(dt_val as u16);
185 });
186 }
187 }
188
189 impl Instance for crate::peripherals::$inst {
190
191 }
192 };
193}
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 9e67596b0..34220fbf5 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -26,6 +26,8 @@ pub mod timer;
26pub mod adc; 26pub mod adc;
27#[cfg(can)] 27#[cfg(can)]
28pub mod can; 28pub mod can;
29#[cfg(crc)]
30pub mod crc;
29#[cfg(dac)] 31#[cfg(dac)]
30pub mod dac; 32pub mod dac;
31#[cfg(dcmi)] 33#[cfg(dcmi)]
@@ -34,14 +36,13 @@ pub mod dcmi;
34pub mod eth; 36pub mod eth;
35#[cfg(feature = "exti")] 37#[cfg(feature = "exti")]
36pub mod exti; 38pub mod exti;
39pub mod flash;
37#[cfg(fmc)] 40#[cfg(fmc)]
38pub mod fmc; 41pub mod fmc;
42#[cfg(hrtim_v1)]
43pub mod hrtim;
39#[cfg(i2c)] 44#[cfg(i2c)]
40pub mod i2c; 45pub mod i2c;
41
42#[cfg(crc)]
43pub mod crc;
44pub mod flash;
45#[cfg(all(spi_v1, rcc_f4))] 46#[cfg(all(spi_v1, rcc_f4))]
46pub mod i2s; 47pub mod i2s;
47#[cfg(stm32wb)] 48#[cfg(stm32wb)]
diff --git a/embassy-stm32/src/rcc/f3.rs b/embassy-stm32/src/rcc/f3.rs
index 2deee80d6..321270a70 100644
--- a/embassy-stm32/src/rcc/f3.rs
+++ b/embassy-stm32/src/rcc/f3.rs
@@ -264,6 +264,7 @@ fn calc_pll(config: &Config, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) {
264} 264}
265 265
266#[inline] 266#[inline]
267#[allow(unused_variables)]
267fn get_usb_pre(config: &Config, sysclk: u32, pclk1: u32, pll_config: &Option<PllConfig>) -> Usbpre { 268fn get_usb_pre(config: &Config, sysclk: u32, pclk1: u32, pll_config: &Option<PllConfig>) -> Usbpre {
268 cfg_if::cfg_if! { 269 cfg_if::cfg_if! {
269 // Some chips do not have USB 270 // Some chips do not have USB
diff --git a/examples/stm32f334/.cargo/config.toml b/examples/stm32f334/.cargo/config.toml
new file mode 100644
index 000000000..caf947be6
--- /dev/null
+++ b/examples/stm32f334/.cargo/config.toml
@@ -0,0 +1,9 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list`
3runner = "probe-run --chip STM32F334R8"
4
5[build]
6target = "thumbv7em-none-eabihf"
7
8[env]
9DEFMT_LOG = "trace"
diff --git a/examples/stm32f334/Cargo.toml b/examples/stm32f334/Cargo.toml
new file mode 100644
index 000000000..6410891a1
--- /dev/null
+++ b/examples/stm32f334/Cargo.toml
@@ -0,0 +1,26 @@
1[package]
2edition = "2021"
3name = "embassy-stm32f3-examples"
4version = "0.1.0"
5license = "MIT OR Apache-2.0"
6
7[dependencies]
8embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f334r8", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
13embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
14
15defmt = "0.3"
16defmt-rtt = "0.4"
17
18cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
19cortex-m-rt = "0.7.0"
20embedded-hal = "0.2.6"
21panic-probe = { version = "0.3", features = ["print-defmt"] }
22futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
23heapless = { version = "0.7.5", default-features = false }
24nb = "1.0.0"
25embedded-storage = "0.3.0"
26static_cell = { version = "1.1", features = ["nightly"]}
diff --git a/examples/stm32f334/build.rs b/examples/stm32f334/build.rs
new file mode 100644
index 000000000..8cd32d7ed
--- /dev/null
+++ b/examples/stm32f334/build.rs
@@ -0,0 +1,5 @@
1fn main() {
2 println!("cargo:rustc-link-arg-bins=--nmagic");
3 println!("cargo:rustc-link-arg-bins=-Tlink.x");
4 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
5}
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/hello.rs b/examples/stm32f334/src/bin/hello.rs
new file mode 100644
index 000000000..65773210d
--- /dev/null
+++ b/examples/stm32f334/src/bin/hello.rs
@@ -0,0 +1,23 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::info;
6use embassy_executor::Spawner;
7use embassy_stm32::time::Hertz;
8use embassy_stm32::Config;
9use embassy_time::{Duration, Timer};
10use {defmt_rtt as _, panic_probe as _};
11
12#[embassy_executor::main]
13async fn main(_spawner: Spawner) -> ! {
14 let mut config = Config::default();
15 config.rcc.hse = Some(Hertz(8_000_000));
16 config.rcc.sysclk = Some(Hertz(16_000_000));
17 let _p = embassy_stm32::init(config);
18
19 loop {
20 info!("Hello World!");
21 Timer::after(Duration::from_secs(1)).await;
22 }
23}
diff --git a/examples/stm32f334/src/bin/pwm.rs b/examples/stm32f334/src/bin/pwm.rs
new file mode 100644
index 000000000..2660b10c5
--- /dev/null
+++ b/examples/stm32f334/src/bin/pwm.rs
@@ -0,0 +1,71 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_stm32::hrtim::*;
8use embassy_stm32::time::{khz, mhz};
9use embassy_stm32::Config;
10use embassy_time::{Duration, Timer};
11use {defmt_rtt as _, panic_probe as _};
12
13#[embassy_executor::main]
14async fn main(_spawner: Spawner) {
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);
22 info!("Hello World!");
23
24 let ch1 = PwmPin::new_cha(p.PA8);
25 let ch1n = ComplementaryPwmPin::new_cha(p.PA9);
26 let pwm = AdvancedPwm::new(
27 p.HRTIM1,
28 Some(ch1),
29 Some(ch1n),
30 None,
31 None,
32 None,
33 None,
34 None,
35 None,
36 None,
37 None,
38 );
39
40 info!("pwm constructed");
41
42 let mut buck_converter = BridgeConverter::new(pwm.ch_a, khz(5));
43
44 // embassy_stm32::pac::HRTIM1
45 // .tim(0)
46 // .setr(0)
47 // .modify(|w| w.set_sst(Activeeffect::SETACTIVE));
48 //
49 // Timer::after(Duration::from_millis(500)).await;
50 //
51 // embassy_stm32::pac::HRTIM1
52 // .tim(0)
53 // .rstr(0)
54 // .modify(|w| w.set_srt(Inactiveeffect::SETINACTIVE));
55
56 let max_duty = buck_converter.get_max_compare_value();
57
58 info!("max compare value: {}", max_duty);
59
60 buck_converter.set_dead_time(max_duty / 20);
61 buck_converter.set_primary_duty(max_duty / 2);
62 buck_converter.set_secondary_duty(3 * max_duty / 4);
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();
71}