aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2022-01-13 16:09:20 +0000
committerGitHub <[email protected]>2022-01-13 16:09:20 +0000
commitd07f6828ef605cd27dcebfcd13703b8f98194fb0 (patch)
tree3a86254c58afa1ab790475b32b53debce7dcaec4
parent0d67ceb066f7b6d3b0163eaa3688b0499439a21c (diff)
parente056bedd553e7fbc7d28f8f516c87fcd12859aef (diff)
Merge #508
508: STM32 timers + pwm improvements r=matoushybl a=matoushybl Co-authored-by: Matous Hybl <[email protected]>
-rw-r--r--embassy-stm32/src/lib.rs1
-rw-r--r--embassy-stm32/src/pwm/mod.rs354
-rw-r--r--embassy-stm32/src/pwm/pins.rs138
-rw-r--r--embassy-stm32/src/pwm/simple_pwm.rs80
-rw-r--r--embassy-stm32/src/rcc/mod.rs5
-rw-r--r--embassy-stm32/src/timer/mod.rs224
-rw-r--r--examples/stm32g4/src/bin/pwm.rs4
-rw-r--r--examples/stm32h7/Cargo.toml3
-rw-r--r--examples/stm32h7/src/bin/low_level_timer_api.rs147
-rw-r--r--examples/stm32h7/src/bin/pwm.rs48
10 files changed, 857 insertions, 147 deletions
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 1ed48df3f..dfc027733 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -20,6 +20,7 @@ pub mod gpio;
20pub mod rcc; 20pub mod rcc;
21#[cfg(feature = "_time-driver")] 21#[cfg(feature = "_time-driver")]
22mod time_driver; 22mod time_driver;
23pub mod timer;
23 24
24// Sometimes-present hardware 25// Sometimes-present hardware
25 26
diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs
index 8357b6cdc..b62a14668 100644
--- a/embassy-stm32/src/pwm/mod.rs
+++ b/embassy-stm32/src/pwm/mod.rs
@@ -1,21 +1,17 @@
1use crate::gpio; 1#[cfg(feature = "unstable-pac")]
2use crate::rcc::RccPeripheral; 2#[macro_use]
3use crate::time::Hertz; 3pub mod pins;
4use core::marker::PhantomData; 4
5use embassy::util::Unborrow; 5#[cfg(not(feature = "unstable-pac"))]
6use embassy_hal_common::unborrow; 6#[macro_use]
7use stm32_metapac::timer::vals::Ocm; 7pub(crate) mod pins;
8
9pub struct Pwm<'d, T: Instance> {
10 phantom: PhantomData<&'d mut T>,
11}
12 8
13// TIM2 9pub mod simple_pwm;
14 10
15pub struct Ch1 {} 11#[cfg(feature = "unstable-pac")]
16pub struct Ch2 {} 12pub mod low_level {
17pub struct Ch3 {} 13 pub use super::sealed::*;
18pub struct Ch4 {} 14}
19 15
20#[derive(Clone, Copy)] 16#[derive(Clone, Copy)]
21pub enum Channel { 17pub enum Channel {
@@ -25,171 +21,241 @@ pub enum Channel {
25 Ch4, 21 Ch4,
26} 22}
27 23
28impl<'d, T: Instance> Pwm<'d, T> { 24impl Channel {
29 pub fn new<F: Into<Hertz>>( 25 pub fn raw(&self) -> usize {
30 _tim: impl Unborrow<Target = T> + 'd, 26 match self {
31 ch1: impl Unborrow<Target = impl PwmPin<T, Ch1>> + 'd, 27 Channel::Ch1 => 0,
32 ch2: impl Unborrow<Target = impl PwmPin<T, Ch2>> + 'd, 28 Channel::Ch2 => 1,
33 ch3: impl Unborrow<Target = impl PwmPin<T, Ch3>> + 'd, 29 Channel::Ch3 => 2,
34 ch4: impl Unborrow<Target = impl PwmPin<T, Ch4>> + 'd, 30 Channel::Ch4 => 3,
35 freq: F,
36 ) -> Self {
37 unborrow!(ch1, ch2, ch3, ch4);
38
39 T::enable();
40 T::reset();
41 let r = T::regs();
42
43 let mut this = Pwm {
44 phantom: PhantomData,
45 };
46 unsafe {
47 ch1.configure();
48 ch2.configure();
49 ch3.configure();
50 ch4.configure();
51 }
52
53 unsafe {
54 use stm32_metapac::timer::vals::Dir;
55 this.set_freq(freq);
56 r.cr1().write(|w| {
57 w.set_cen(true);
58 w.set_dir(Dir::UP)
59 });
60
61 this.set_ocm(Channel::Ch1, Ocm::PWMMODE1);
62 this.set_ocm(Channel::Ch2, Ocm::PWMMODE1);
63 this.set_ocm(Channel::Ch3, Ocm::PWMMODE1);
64 this.set_ocm(Channel::Ch4, Ocm::PWMMODE1);
65 } 31 }
66 this
67 } 32 }
33}
68 34
69 unsafe fn set_ocm(&mut self, channel: Channel, mode: Ocm) { 35#[derive(Clone, Copy)]
70 let r = T::regs(); 36pub enum OutputCompareMode {
71 match channel { 37 Frozen,
72 Channel::Ch1 => r.ccmr_output(0).modify(|w| w.set_ocm(0, mode)), 38 ActiveOnMatch,
73 Channel::Ch2 => r.ccmr_output(0).modify(|w| w.set_ocm(1, mode)), 39 InactiveOnMatch,
74 Channel::Ch3 => r.ccmr_output(1).modify(|w| w.set_ocm(0, mode)), 40 Toggle,
75 Channel::Ch4 => r.ccmr_output(1).modify(|w| w.set_ocm(1, mode)), 41 ForceInactive,
76 } 42 ForceActive,
77 } 43 PwmMode1,
44 PwmMode2,
45}
78 46
79 unsafe fn set_enable(&mut self, channel: Channel, enable: bool) { 47impl From<OutputCompareMode> for stm32_metapac::timer::vals::Ocm {
80 let r = T::regs(); 48 fn from(mode: OutputCompareMode) -> Self {
81 match channel { 49 match mode {
82 Channel::Ch1 => r.ccer().modify(|w| w.set_cce(0, enable)), 50 OutputCompareMode::Frozen => stm32_metapac::timer::vals::Ocm::FROZEN,
83 Channel::Ch2 => r.ccer().modify(|w| w.set_cce(1, enable)), 51 OutputCompareMode::ActiveOnMatch => stm32_metapac::timer::vals::Ocm::ACTIVEONMATCH,
84 Channel::Ch3 => r.ccer().modify(|w| w.set_cce(2, enable)), 52 OutputCompareMode::InactiveOnMatch => stm32_metapac::timer::vals::Ocm::INACTIVEONMATCH,
85 Channel::Ch4 => r.ccer().modify(|w| w.set_cce(3, enable)), 53 OutputCompareMode::Toggle => stm32_metapac::timer::vals::Ocm::TOGGLE,
54 OutputCompareMode::ForceInactive => stm32_metapac::timer::vals::Ocm::FORCEINACTIVE,
55 OutputCompareMode::ForceActive => stm32_metapac::timer::vals::Ocm::FORCEACTIVE,
56 OutputCompareMode::PwmMode1 => stm32_metapac::timer::vals::Ocm::PWMMODE1,
57 OutputCompareMode::PwmMode2 => stm32_metapac::timer::vals::Ocm::PWMMODE2,
86 } 58 }
87 } 59 }
60}
88 61
89 pub fn enable(&mut self, channel: Channel) { 62pub(crate) mod sealed {
90 unsafe { self.set_enable(channel, true) } 63 use super::*;
91 }
92 64
93 pub fn disable(&mut self, channel: Channel) { 65 pub trait CaptureCompareCapable16bitInstance:
94 unsafe { self.set_enable(channel, false) } 66 crate::timer::sealed::GeneralPurpose16bitInstance
95 } 67 {
68 unsafe fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode);
96 69
97 pub fn set_freq<F: Into<Hertz>>(&mut self, freq: F) { 70 unsafe fn enable_channel(&mut self, channel: Channel, enable: bool);
98 use core::convert::TryInto;
99 let clk = T::frequency();
100 let r = T::regs();
101 let freq: Hertz = freq.into();
102 let ticks: u32 = clk.0 / freq.0;
103 let psc: u16 = (ticks / (1 << 16)).try_into().unwrap();
104 let arr: u16 = (ticks / (u32::from(psc) + 1)).try_into().unwrap();
105 unsafe {
106 r.psc().write(|w| w.set_psc(psc));
107 r.arr().write(|w| w.set_arr(arr));
108 }
109 }
110 71
111 pub fn get_max_duty(&self) -> u32 { 72 unsafe fn set_compare_value(&mut self, channel: Channel, value: u16);
112 let r = T::regs();
113 unsafe { r.arr().read().arr() as u32 }
114 }
115 73
116 pub fn set_duty(&mut self, channel: Channel, duty: u32) { 74 unsafe fn get_max_compare_value(&self) -> u16;
117 use core::convert::TryInto;
118 assert!(duty < self.get_max_duty());
119 let duty: u16 = duty.try_into().unwrap();
120 let r = T::regs();
121 unsafe {
122 match channel {
123 Channel::Ch1 => r.ccr(0).modify(|w| w.set_ccr(duty)),
124 Channel::Ch2 => r.ccr(1).modify(|w| w.set_ccr(duty)),
125 Channel::Ch3 => r.ccr(2).modify(|w| w.set_ccr(duty)),
126 Channel::Ch4 => r.ccr(3).modify(|w| w.set_ccr(duty)),
127 }
128 }
129 } 75 }
130}
131 76
132pub(crate) mod sealed { 77 pub trait CaptureCompareCapable32bitInstance:
133 pub trait Instance { 78 crate::timer::sealed::GeneralPurpose32bitInstance
134 fn regs() -> crate::pac::timer::TimGp16; 79 {
80 unsafe fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode);
81
82 unsafe fn enable_channel(&mut self, channel: Channel, enable: bool);
83
84 unsafe fn set_compare_value(&mut self, channel: Channel, value: u32);
85
86 unsafe fn get_max_compare_value(&self) -> u32;
135 } 87 }
136} 88}
137 89
138pub trait Instance: sealed::Instance + Sized + RccPeripheral + 'static {} 90pub trait CaptureCompareCapable16bitInstance:
91 sealed::CaptureCompareCapable16bitInstance + crate::timer::GeneralPurpose16bitInstance + 'static
92{
93}
94pub trait CaptureCompareCapable32bitInstance:
95 sealed::CaptureCompareCapable32bitInstance + crate::timer::GeneralPurpose32bitInstance + 'static
96{
97}
139 98
140#[allow(unused)] 99#[allow(unused)]
141macro_rules! impl_timer { 100macro_rules! impl_compare_capable_16bit {
142 ($inst:ident) => { 101 ($inst:ident) => {
143 impl crate::pwm::sealed::Instance for crate::peripherals::$inst { 102 impl crate::pwm::sealed::CaptureCompareCapable16bitInstance for crate::peripherals::$inst {
144 fn regs() -> crate::pac::timer::TimGp16 { 103 unsafe fn set_output_compare_mode(
145 crate::pac::timer::TimGp16(crate::pac::$inst.0) 104 &mut self,
105 channel: crate::pwm::Channel,
106 mode: OutputCompareMode,
107 ) {
108 use crate::timer::sealed::GeneralPurpose16bitInstance;
109 let r = self.regs_gp16();
110 let raw_channel: usize = channel.raw();
111 r.ccmr_output(raw_channel / 2)
112 .modify(|w| w.set_ocm(raw_channel % 2, mode.into()));
146 } 113 }
147 }
148 114
149 impl crate::pwm::Instance for crate::peripherals::$inst {} 115 unsafe fn enable_channel(&mut self, channel: Channel, enable: bool) {
116 use crate::timer::sealed::GeneralPurpose16bitInstance;
117 self.regs_gp16()
118 .ccer()
119 .modify(|w| w.set_cce(channel.raw(), enable));
120 }
121
122 unsafe fn set_compare_value(&mut self, channel: Channel, value: u16) {
123 use crate::timer::sealed::GeneralPurpose16bitInstance;
124 self.regs_gp16()
125 .ccr(channel.raw())
126 .modify(|w| w.set_ccr(value));
127 }
128
129 unsafe fn get_max_compare_value(&self) -> u16 {
130 use crate::timer::sealed::GeneralPurpose16bitInstance;
131 self.regs_gp16().arr().read().arr()
132 }
133 }
150 }; 134 };
151} 135}
152 136
153pub trait PwmPin<Timer, Channel>: gpio::OptionalPin { 137crate::pac::interrupts! {
154 unsafe fn configure(&mut self); 138 ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => {
155} 139 impl_compare_capable_16bit!($inst);
156 140
157impl<Timer, Channel> PwmPin<Timer, Channel> for gpio::NoPin { 141 impl CaptureCompareCapable16bitInstance for crate::peripherals::$inst {
158 unsafe fn configure(&mut self) {}
159}
160 142
161#[allow(unused)] 143 }
162macro_rules! impl_pwm_pin { 144 };
163 ($timer:ident, $channel:ident, $pin:ident, $af:expr) => { 145
164 impl crate::pwm::PwmPin<crate::peripherals::$timer, crate::pwm::$channel> 146 ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => {
165 for crate::peripherals::$pin 147 impl_compare_capable_16bit!($inst);
166 { 148 impl crate::pwm::sealed::CaptureCompareCapable32bitInstance for crate::peripherals::$inst {
167 unsafe fn configure(&mut self) { 149 unsafe fn set_output_compare_mode(
168 use crate::gpio::sealed::{AFType, Pin}; 150 &mut self,
169 use crate::gpio::Speed; 151 channel: crate::pwm::Channel,
170 self.set_low(); 152 mode: OutputCompareMode,
171 self.set_speed(Speed::VeryHigh); 153 ) {
172 self.set_as_af($af, AFType::OutputPushPull); 154 use crate::timer::sealed::GeneralPurpose32bitInstance;
155 let raw_channel = channel.raw();
156 self.regs_gp32().ccmr_output(raw_channel / 2).modify(|w| w.set_ocm(raw_channel % 2, mode.into()));
173 } 157 }
158
159 unsafe fn enable_channel(&mut self, channel: Channel, enable: bool) {
160 use crate::timer::sealed::GeneralPurpose32bitInstance;
161 self.regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), enable));
162 }
163
164 unsafe fn set_compare_value(&mut self, channel: Channel, value: u32) {
165 use crate::timer::sealed::GeneralPurpose32bitInstance;
166 self.regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(value));
167 }
168
169 unsafe fn get_max_compare_value(&self) -> u32 {
170 use crate::timer::sealed::GeneralPurpose32bitInstance;
171 self.regs_gp32().arr().read().arr() as u32
172 }
173 }
174 impl CaptureCompareCapable16bitInstance for crate::peripherals::$inst {
175
176 }
177 impl CaptureCompareCapable32bitInstance for crate::peripherals::$inst {
178
179 }
180 };
181
182 ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => {
183 impl_compare_capable_16bit!($inst);
184 impl CaptureCompareCapable16bitInstance for crate::peripherals::$inst {
185
174 } 186 }
175 }; 187 };
176} 188}
177 189
190#[allow(unused)]
191macro_rules! impl_pwm_nopin {
192 ($inst:ident) => {
193 impl_no_pin!($inst, Channel1Pin);
194 impl_no_pin!($inst, Channel1ComplementaryPin);
195 impl_no_pin!($inst, Channel2Pin);
196 impl_no_pin!($inst, Channel2ComplementaryPin);
197 impl_no_pin!($inst, Channel3Pin);
198 impl_no_pin!($inst, Channel3ComplementaryPin);
199 impl_no_pin!($inst, Channel4Pin);
200 impl_no_pin!($inst, Channel4ComplementaryPin);
201 impl_no_pin!($inst, ExternalTriggerPin);
202 impl_no_pin!($inst, BreakInputPin);
203 impl_no_pin!($inst, BreakInputComparator1Pin);
204 impl_no_pin!($inst, BreakInputComparator2Pin);
205 impl_no_pin!($inst, BreakInput2Pin);
206 impl_no_pin!($inst, BreakInput2Comparator1Pin);
207 impl_no_pin!($inst, BreakInput2Comparator2Pin);
208 };
209}
210
178crate::pac::peripherals!( 211crate::pac::peripherals!(
179 (timer, $inst:ident) => { impl_timer!($inst); }; 212 (timer, $inst:ident) => { impl_pwm_nopin!($inst); };
180); 213);
181 214
182crate::pac::peripheral_pins!( 215crate::pac::peripheral_pins!(
183 ($inst:ident, timer,TIM_GP16, $pin:ident, CH1, $af:expr) => { 216 ($inst:ident, timer, $block:ident, $pin:ident, CH1, $af:expr) => {
184 impl_pwm_pin!($inst, Ch1, $pin, $af); 217 impl_pin!($inst, Channel1Pin, $pin, $af);
218 };
219 ($inst:ident, timer, $block:ident, $pin:ident, CH1N, $af:expr) => {
220 impl_pin!($inst, Channel1ComplementaryPin, $pin, $af);
221 };
222 ($inst:ident, timer, $block:ident, $pin:ident, CH2, $af:expr) => {
223 impl_pin!($inst, Channel2Pin, $pin, $af);
224 };
225 ($inst:ident, timer, $block:ident, $pin:ident, CH2N, $af:expr) => {
226 impl_pin!($inst, Channel2ComplementaryPin, $pin, $af);
227 };
228 ($inst:ident, timer, $block:ident, $pin:ident, CH3, $af:expr) => {
229 impl_pin!($inst, Channel3Pin, $pin, $af);
230 };
231 ($inst:ident, timer, $block:ident, $pin:ident, CH3N, $af:expr) => {
232 impl_pin!($inst, Channel3ComplementaryPin, $pin, $af);
233 };
234 ($inst:ident, timer, $block:ident, $pin:ident, CH4, $af:expr) => {
235 impl_pin!($inst, Channel4Pin, $pin, $af);
236 };
237 ($inst:ident, timer, $block:ident, $pin:ident, CH4N, $af:expr) => {
238 impl_pin!($inst, Channel4ComplementaryPin, $pin, $af);
239 };
240 ($inst:ident, timer, $block:ident, $pin:ident, ETR, $af:expr) => {
241 impl_pin!($inst, ExternalTriggerPin, $pin, $af);
242 };
243 ($inst:ident, timer, $block:ident, $pin:ident, BKIN, $af:expr) => {
244 impl_pin!($inst, BreakInputPin, $pin, $af);
245 };
246 ($inst:ident, timer, $block:ident, $pin:ident, BKIN_COMP1, $af:expr) => {
247 impl_pin!($inst, BreakInputComparator1Pin, $pin, $af);
248 };
249 ($inst:ident, timer, $block:ident, $pin:ident, BKIN_COMP2, $af:expr) => {
250 impl_pin!($inst, BreakInputComparator2Pin, $pin, $af);
185 }; 251 };
186 ($inst:ident, timer,TIM_GP16, $pin:ident, CH2, $af:expr) => { 252 ($inst:ident, timer, $block:ident, $pin:ident, BKIN2, $af:expr) => {
187 impl_pwm_pin!($inst, Ch2, $pin, $af); 253 impl_pin!($inst, BreakInput2Pin, $pin, $af);
188 }; 254 };
189 ($inst:ident, timer,TIM_GP16, $pin:ident, CH3, $af:expr) => { 255 ($inst:ident, timer, $block:ident, $pin:ident, BKIN2_COMP1, $af:expr) => {
190 impl_pwm_pin!($inst, Ch3, $pin, $af); 256 impl_pin!($inst, BreakInput2Comparator1Pin, $pin, $af);
191 }; 257 };
192 ($inst:ident, timer,TIM_GP16, $pin:ident, CH4, $af:expr) => { 258 ($inst:ident, timer, $block:ident, $pin:ident, BKIN2_COMP2, $af:expr) => {
193 impl_pwm_pin!($inst, Ch4, $pin, $af); 259 impl_pin!($inst, BreakInput2Comparator2Pin, $pin, $af);
194 }; 260 };
195); 261);
diff --git a/embassy-stm32/src/pwm/pins.rs b/embassy-stm32/src/pwm/pins.rs
new file mode 100644
index 000000000..77ba1f6d5
--- /dev/null
+++ b/embassy-stm32/src/pwm/pins.rs
@@ -0,0 +1,138 @@
1use crate::gpio::OptionalPin;
2
3#[cfg(feature = "unstable-pac")]
4pub mod low_level {
5 pub use super::sealed::*;
6}
7
8pub(crate) mod sealed {
9 use crate::gpio::sealed::OptionalPin;
10
11 pub trait Channel1Pin<Timer>: OptionalPin {
12 unsafe fn configure(&mut self);
13 }
14 pub trait Channel1ComplementaryPin<Timer>: OptionalPin {
15 unsafe fn configure(&mut self);
16 }
17
18 pub trait Channel2Pin<Timer>: OptionalPin {
19 unsafe fn configure(&mut self);
20 }
21 pub trait Channel2ComplementaryPin<Timer>: OptionalPin {
22 unsafe fn configure(&mut self);
23 }
24
25 pub trait Channel3Pin<Timer>: OptionalPin {
26 unsafe fn configure(&mut self);
27 }
28 pub trait Channel3ComplementaryPin<Timer>: OptionalPin {
29 unsafe fn configure(&mut self);
30 }
31
32 pub trait Channel4Pin<Timer>: OptionalPin {
33 unsafe fn configure(&mut self);
34 }
35 pub trait Channel4ComplementaryPin<Timer>: OptionalPin {
36 unsafe fn configure(&mut self);
37 }
38
39 pub trait ExternalTriggerPin<Timer>: OptionalPin {
40 unsafe fn configure(&mut self);
41 }
42
43 pub trait BreakInputPin<Timer>: OptionalPin {
44 unsafe fn configure(&mut self);
45 }
46 pub trait BreakInputComparator1Pin<Timer>: OptionalPin {
47 unsafe fn configure(&mut self);
48 }
49 pub trait BreakInputComparator2Pin<Timer>: OptionalPin {
50 unsafe fn configure(&mut self);
51 }
52
53 pub trait BreakInput2Pin<Timer>: OptionalPin {
54 unsafe fn configure(&mut self);
55 }
56 pub trait BreakInput2Comparator1Pin<Timer>: OptionalPin {
57 unsafe fn configure(&mut self);
58 }
59 pub trait BreakInput2Comparator2Pin<Timer>: OptionalPin {
60 unsafe fn configure(&mut self);
61 }
62}
63pub trait Channel1Pin<Timer>: sealed::Channel1Pin<Timer> + OptionalPin + 'static {}
64pub trait Channel1ComplementaryPin<Timer>:
65 sealed::Channel1ComplementaryPin<Timer> + OptionalPin + 'static
66{
67}
68
69pub trait Channel2Pin<Timer>: sealed::Channel2Pin<Timer> + 'static {}
70pub trait Channel2ComplementaryPin<Timer>:
71 sealed::Channel2ComplementaryPin<Timer> + OptionalPin + 'static
72{
73}
74
75pub trait Channel3Pin<Timer>: sealed::Channel3Pin<Timer> + 'static {}
76pub trait Channel3ComplementaryPin<Timer>:
77 sealed::Channel3ComplementaryPin<Timer> + OptionalPin + 'static
78{
79}
80
81pub trait Channel4Pin<Timer>: sealed::Channel4Pin<Timer> + 'static {}
82pub trait Channel4ComplementaryPin<Timer>:
83 sealed::Channel4ComplementaryPin<Timer> + OptionalPin + 'static
84{
85}
86
87pub trait ExternalTriggerPin<Timer>:
88 sealed::ExternalTriggerPin<Timer> + OptionalPin + 'static
89{
90}
91
92pub trait BreakInputPin<Timer>: sealed::BreakInputPin<Timer> + OptionalPin + 'static {}
93pub trait BreakInputComparator1Pin<Timer>:
94 sealed::BreakInputComparator1Pin<Timer> + OptionalPin + 'static
95{
96}
97pub trait BreakInputComparator2Pin<Timer>:
98 sealed::BreakInputComparator2Pin<Timer> + OptionalPin + 'static
99{
100}
101
102pub trait BreakInput2Pin<Timer>: sealed::BreakInput2Pin<Timer> + OptionalPin + 'static {}
103pub trait BreakInput2Comparator1Pin<Timer>:
104 sealed::BreakInput2Comparator1Pin<Timer> + OptionalPin + 'static
105{
106}
107pub trait BreakInput2Comparator2Pin<Timer>:
108 sealed::BreakInput2Comparator2Pin<Timer> + OptionalPin + 'static
109{
110}
111
112macro_rules! impl_no_pin {
113 ($timer:ident, $signal:ident) => {
114 impl crate::pwm::pins::sealed::$signal<crate::peripherals::$timer> for crate::gpio::NoPin {
115 unsafe fn configure(&mut self) {}
116 }
117 impl crate::pwm::pins::$signal<crate::peripherals::$timer> for crate::gpio::NoPin {}
118 };
119}
120
121#[allow(unused)]
122macro_rules! impl_pin {
123 ($timer:ident, $signal:ident, $pin:ident, $af:expr) => {
124 impl crate::pwm::pins::sealed::$signal<crate::peripherals::$timer>
125 for crate::peripherals::$pin
126 {
127 unsafe fn configure(&mut self) {
128 use crate::gpio::sealed::{AFType, Pin};
129 use crate::gpio::Speed;
130 self.set_low();
131 self.set_speed(Speed::VeryHigh);
132 self.set_as_af($af, AFType::OutputPushPull);
133 }
134 }
135
136 impl crate::pwm::pins::$signal<crate::peripherals::$timer> for crate::peripherals::$pin {}
137 };
138}
diff --git a/embassy-stm32/src/pwm/simple_pwm.rs b/embassy-stm32/src/pwm/simple_pwm.rs
new file mode 100644
index 000000000..3dc46e6e8
--- /dev/null
+++ b/embassy-stm32/src/pwm/simple_pwm.rs
@@ -0,0 +1,80 @@
1use crate::{
2 pwm::{pins::*, CaptureCompareCapable16bitInstance, Channel, OutputCompareMode},
3 time::Hertz,
4};
5use core::marker::PhantomData;
6use embassy::util::Unborrow;
7use embassy_hal_common::unborrow;
8
9pub struct SimplePwm<'d, T> {
10 phantom: PhantomData<&'d mut T>,
11 inner: T,
12}
13
14impl<'d, T: CaptureCompareCapable16bitInstance> SimplePwm<'d, T> {
15 pub fn new<F: Into<Hertz>>(
16 tim: impl Unborrow<Target = T> + 'd,
17 ch1: impl Unborrow<Target = impl Channel1Pin<T>> + 'd,
18 ch2: impl Unborrow<Target = impl Channel2Pin<T>> + 'd,
19 ch3: impl Unborrow<Target = impl Channel3Pin<T>> + 'd,
20 ch4: impl Unborrow<Target = impl Channel4Pin<T>> + 'd,
21 freq: F,
22 ) -> Self {
23 unborrow!(tim, ch1, ch2, ch3, ch4);
24
25 T::enable();
26 <T as crate::rcc::sealed::RccPeripheral>::reset();
27
28 unsafe {
29 ch1.configure();
30 ch2.configure();
31 ch3.configure();
32 ch4.configure();
33 }
34
35 let mut this = Self {
36 inner: tim,
37 phantom: PhantomData,
38 };
39
40 this.inner.set_frequency(freq);
41 this.inner.start();
42
43 unsafe {
44 this.inner
45 .set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1);
46 this.inner
47 .set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1);
48 this.inner
49 .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1);
50 this.inner
51 .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1);
52 }
53 this
54 }
55
56 pub fn enable(&mut self, channel: Channel) {
57 unsafe {
58 self.inner.enable_channel(channel, true);
59 }
60 }
61
62 pub fn disable(&mut self, channel: Channel) {
63 unsafe {
64 self.inner.enable_channel(channel, false);
65 }
66 }
67
68 pub fn set_freq<F: Into<Hertz>>(&mut self, freq: F) {
69 self.inner.set_frequency(freq);
70 }
71
72 pub fn get_max_duty(&self) -> u16 {
73 unsafe { self.inner.get_max_compare_value() }
74 }
75
76 pub fn set_duty(&mut self, channel: Channel, duty: u16) {
77 assert!(duty < self.get_max_duty());
78 unsafe { self.inner.set_compare_value(channel, duty) }
79 }
80}
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index d0b6e5a18..e5bf15c86 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -85,6 +85,11 @@ pub(crate) unsafe fn get_freqs() -> &'static Clocks {
85 &*CLOCK_FREQS.as_ptr() 85 &*CLOCK_FREQS.as_ptr()
86} 86}
87 87
88#[cfg(feature = "unstable-pac")]
89pub mod low_level {
90 pub use super::sealed::*;
91}
92
88pub(crate) mod sealed { 93pub(crate) mod sealed {
89 pub trait RccPeripheral { 94 pub trait RccPeripheral {
90 fn frequency() -> crate::time::Hertz; 95 fn frequency() -> crate::time::Hertz;
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs
new file mode 100644
index 000000000..ed2cb2d97
--- /dev/null
+++ b/embassy-stm32/src/timer/mod.rs
@@ -0,0 +1,224 @@
1use embassy::interrupt::Interrupt;
2
3use crate::rcc::{sealed::RccPeripheral as __RccPeri, RccPeripheral};
4use crate::time::Hertz;
5use stm32_metapac::timer::vals;
6
7#[cfg(feature = "unstable-pac")]
8pub mod low_level {
9 pub use super::sealed::*;
10}
11
12pub(crate) mod sealed {
13 use super::*;
14 pub trait Basic16bitInstance: RccPeripheral {
15 type Interrupt: Interrupt;
16
17 fn regs(&self) -> crate::pac::timer::TimBasic;
18
19 fn start(&mut self);
20
21 fn stop(&mut self);
22
23 fn reset(&mut self);
24
25 fn set_frequency<F: Into<Hertz>>(&mut self, frequency: F);
26
27 fn clear_update_interrupt(&mut self) -> bool;
28
29 fn enable_update_interrupt(&mut self, enable: bool);
30 }
31
32 pub trait GeneralPurpose16bitInstance: Basic16bitInstance {
33 fn regs_gp16(&self) -> crate::pac::timer::TimGp16;
34 }
35
36 pub trait GeneralPurpose32bitInstance: GeneralPurpose16bitInstance {
37 fn regs_gp32(&self) -> crate::pac::timer::TimGp32;
38
39 fn set_frequency<F: Into<Hertz>>(&mut self, frequency: F);
40 }
41
42 pub trait AdvancedControlInstance: GeneralPurpose16bitInstance {
43 fn regs_advanced(&self) -> crate::pac::timer::TimAdv;
44 }
45}
46
47pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + 'static {}
48
49pub trait GeneralPurpose32bitInstance: sealed::GeneralPurpose32bitInstance + 'static {}
50
51pub trait AdvancedControlInstance: sealed::AdvancedControlInstance + 'static {}
52
53pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {}
54
55#[allow(unused)]
56macro_rules! impl_basic_16bit_timer {
57 ($inst:ident, $irq:ident) => {
58 impl sealed::Basic16bitInstance for crate::peripherals::$inst {
59 type Interrupt = crate::interrupt::$irq;
60
61 fn regs(&self) -> crate::pac::timer::TimBasic {
62 crate::pac::timer::TimBasic(crate::pac::$inst.0)
63 }
64
65 fn start(&mut self) {
66 unsafe {
67 self.regs().cr1().modify(|r| r.set_cen(true));
68 }
69 }
70
71 fn stop(&mut self) {
72 let regs = self.regs();
73 unsafe {
74 regs.cr1().modify(|r| r.set_cen(false));
75 }
76 }
77
78 fn reset(&mut self) {
79 unsafe {
80 self.regs().cnt().write(|r| r.set_cnt(0));
81 }
82 }
83
84 fn set_frequency<F: Into<Hertz>>(&mut self, frequency: F) {
85 use core::convert::TryInto;
86 let f = frequency.into().0;
87 let timer_f = Self::frequency().0;
88 let pclk_ticks_per_timer_period = timer_f / f;
89 let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 16)).try_into());
90 let arr: u16 =
91 unwrap!((pclk_ticks_per_timer_period / (u32::from(psc) + 1)).try_into());
92
93 let regs = self.regs();
94 unsafe {
95 regs.psc().write(|r| r.set_psc(psc));
96 regs.arr().write(|r| r.set_arr(arr));
97
98 regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY));
99 regs.egr().write(|r| r.set_ug(true));
100 regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
101 }
102 }
103
104 fn clear_update_interrupt(&mut self) -> bool {
105 unsafe {
106 let sr = self.regs().sr().read();
107 if sr.uif() {
108 self.regs().sr().modify(|r| {
109 r.set_uif(false);
110 });
111 true
112 } else {
113 false
114 }
115 }
116 }
117
118 fn enable_update_interrupt(&mut self, enable: bool) {
119 unsafe {
120 self.regs().dier().write(|r| r.set_uie(enable));
121 }
122 }
123 }
124 };
125}
126
127#[allow(unused)]
128macro_rules! impl_32bit_timer {
129 ($inst:ident) => {
130 impl sealed::GeneralPurpose32bitInstance for crate::peripherals::$inst {
131 fn regs_gp32(&self) -> crate::pac::timer::TimGp32 {
132 crate::pac::$inst
133 }
134
135 fn set_frequency<F: Into<Hertz>>(&mut self, frequency: F) {
136 use core::convert::TryInto;
137 let f = frequency.into().0;
138 let timer_f = Self::frequency().0;
139 let pclk_ticks_per_timer_period = (timer_f / f) as u64;
140 let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 32)).try_into());
141 let arr: u32 =
142 unwrap!(((pclk_ticks_per_timer_period / (psc as u64 + 1)).try_into()));
143
144 let regs = self.regs_gp32();
145 unsafe {
146 regs.psc().write(|r| r.set_psc(psc));
147 regs.arr().write(|r| r.set_arr(arr));
148
149 regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY));
150 regs.egr().write(|r| r.set_ug(true));
151 regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
152 }
153 }
154 }
155 };
156}
157
158crate::pac::interrupts! {
159 ($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => {
160 impl_basic_16bit_timer!($inst, $irq);
161
162 impl Basic16bitInstance for crate::peripherals::$inst {
163 }
164 };
165 ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => {
166 impl_basic_16bit_timer!($inst, $irq);
167
168 impl Basic16bitInstance for crate::peripherals::$inst {
169 }
170
171 impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst {
172 fn regs_gp16(&self) -> crate::pac::timer::TimGp16 {
173 crate::pac::$inst
174 }
175 }
176
177 impl GeneralPurpose16bitInstance for crate::peripherals::$inst {
178 }
179 };
180
181 ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => {
182 impl_basic_16bit_timer!($inst, $irq);
183
184 impl Basic16bitInstance for crate::peripherals::$inst {
185 }
186
187 impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst {
188 fn regs_gp16(&self) -> crate::pac::timer::TimGp16 {
189 crate::pac::timer::TimGp16(crate::pac::$inst.0)
190 }
191 }
192
193 impl GeneralPurpose16bitInstance for crate::peripherals::$inst {
194 }
195
196 impl_32bit_timer!($inst);
197
198 impl GeneralPurpose32bitInstance for crate::peripherals::$inst {
199 }
200 };
201
202 ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => {
203 impl_basic_16bit_timer!($inst, $irq);
204
205 impl Basic16bitInstance for crate::peripherals::$inst {
206 }
207
208 impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst {
209 fn regs_gp16(&self) -> crate::pac::timer::TimGp16 {
210 crate::pac::timer::TimGp16(crate::pac::$inst.0)
211 }
212 }
213
214 impl GeneralPurpose16bitInstance for crate::peripherals::$inst {
215 }
216 impl sealed::AdvancedControlInstance for crate::peripherals::$inst {
217 fn regs_advanced(&self) -> crate::pac::timer::TimAdv {
218 crate::pac::$inst
219 }
220 }
221 impl AdvancedControlInstance for crate::peripherals::$inst {
222 }
223 };
224}
diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs
index 1aa7b85f3..3dd45318d 100644
--- a/examples/stm32g4/src/bin/pwm.rs
+++ b/examples/stm32g4/src/bin/pwm.rs
@@ -7,7 +7,7 @@ mod example_common;
7use embassy::executor::Spawner; 7use embassy::executor::Spawner;
8use embassy::time::{Duration, Timer}; 8use embassy::time::{Duration, Timer};
9use embassy_stm32::gpio::NoPin; 9use embassy_stm32::gpio::NoPin;
10use embassy_stm32::pwm::{Channel, Pwm}; 10use embassy_stm32::pwm::{simple_pwm::SimplePwm, Channel};
11use embassy_stm32::time::U32Ext; 11use embassy_stm32::time::U32Ext;
12use embassy_stm32::Peripherals; 12use embassy_stm32::Peripherals;
13use example_common::*; 13use example_common::*;
@@ -16,7 +16,7 @@ use example_common::*;
16async fn main(_spawner: Spawner, p: Peripherals) { 16async fn main(_spawner: Spawner, p: Peripherals) {
17 info!("Hello World!"); 17 info!("Hello World!");
18 18
19 let mut pwm = Pwm::new(p.TIM2, p.PA5, NoPin, NoPin, NoPin, 10000.hz()); 19 let mut pwm = SimplePwm::new(p.TIM2, p.PA5, NoPin, NoPin, NoPin, 10000.hz());
20 let max = pwm.get_max_duty(); 20 let max = pwm.get_max_duty();
21 pwm.enable(Channel::Ch1); 21 pwm.enable(Channel::Ch1);
22 22
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml
index a34ac6ad9..de294318f 100644
--- a/examples/stm32h7/Cargo.toml
+++ b/examples/stm32h7/Cargo.toml
@@ -10,8 +10,9 @@ resolver = "2"
10[dependencies] 10[dependencies]
11embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt"] } 11embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt"] }
12embassy-traits = { version = "0.1.0", path = "../../embassy-traits", features = ["defmt"] } 12embassy-traits = { version = "0.1.0", path = "../../embassy-traits", features = ["defmt"] }
13embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h743zi", "net", "time-driver-tim2", "exti"] } 13embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h743zi", "net", "time-driver-tim2", "exti", "unstable-pac"] }
14embassy-net = { path = "../../embassy-net", default-features = false, features = ["defmt", "tcp", "medium-ethernet", "pool-16"] } 14embassy-net = { path = "../../embassy-net", default-features = false, features = ["defmt", "tcp", "medium-ethernet", "pool-16"] }
15embassy-hal-common = { path = "../../embassy-hal-common", default-features = false, features = ["defmt"] }
15 16
16defmt = "0.3" 17defmt = "0.3"
17defmt-rtt = "0.3" 18defmt-rtt = "0.3"
diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs
new file mode 100644
index 000000000..2640f249d
--- /dev/null
+++ b/examples/stm32h7/src/bin/low_level_timer_api.rs
@@ -0,0 +1,147 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5#[path = "../example_common.rs"]
6mod example_common;
7use core::marker::PhantomData;
8
9use embassy::executor::Spawner;
10use embassy::time::{Duration, Timer};
11use embassy::util::Unborrow;
12use embassy_hal_common::unborrow;
13use embassy_stm32::gpio::NoPin;
14use embassy_stm32::pwm::{pins::*, Channel, OutputCompareMode};
15use embassy_stm32::time::{Hertz, U32Ext};
16use embassy_stm32::timer::GeneralPurpose32bitInstance;
17use embassy_stm32::{Config, Peripherals};
18use example_common::*;
19
20pub fn config() -> Config {
21 let mut config = Config::default();
22 config.rcc.sys_ck = Some(400.mhz().into());
23 config.rcc.hclk = Some(400.mhz().into());
24 config.rcc.pll1.q_ck = Some(100.mhz().into());
25 config.rcc.pclk1 = Some(100.mhz().into());
26 config.rcc.pclk2 = Some(100.mhz().into());
27 config.rcc.pclk3 = Some(100.mhz().into());
28 config.rcc.pclk4 = Some(100.mhz().into());
29 config
30}
31
32#[embassy::main(config = "config()")]
33async fn main(_spawner: Spawner, p: Peripherals) {
34 info!("Hello World!");
35
36 let mut pwm = SimplePwm32::new(p.TIM5, p.PA0, NoPin, NoPin, NoPin, 10000.hz());
37 let max = pwm.get_max_duty();
38 pwm.enable(Channel::Ch1);
39
40 info!("PWM initialized");
41 info!("PWM max duty {}", max);
42
43 loop {
44 pwm.set_duty(Channel::Ch1, 0);
45 Timer::after(Duration::from_millis(300)).await;
46 pwm.set_duty(Channel::Ch1, max / 4);
47 Timer::after(Duration::from_millis(300)).await;
48 pwm.set_duty(Channel::Ch1, max / 2);
49 Timer::after(Duration::from_millis(300)).await;
50 pwm.set_duty(Channel::Ch1, max - 1);
51 Timer::after(Duration::from_millis(300)).await;
52 }
53}
54pub struct SimplePwm32<'d, T: GeneralPurpose32bitInstance> {
55 phantom: PhantomData<&'d mut T>,
56 inner: T,
57}
58
59impl<'d, T: GeneralPurpose32bitInstance> SimplePwm32<'d, T> {
60 pub fn new<F: Into<Hertz>>(
61 tim: impl Unborrow<Target = T> + 'd,
62 ch1: impl Unborrow<Target = impl Channel1Pin<T>> + 'd,
63 ch2: impl Unborrow<Target = impl Channel2Pin<T>> + 'd,
64 ch3: impl Unborrow<Target = impl Channel3Pin<T>> + 'd,
65 ch4: impl Unborrow<Target = impl Channel4Pin<T>> + 'd,
66 freq: F,
67 ) -> Self {
68 unborrow!(tim, ch1, ch2, ch3, ch4);
69
70 T::enable();
71 <T as embassy_stm32::rcc::low_level::RccPeripheral>::reset();
72
73 unsafe {
74 ch1.configure();
75 ch2.configure();
76 ch3.configure();
77 ch4.configure();
78 }
79
80 let mut this = Self {
81 inner: tim,
82 phantom: PhantomData,
83 };
84
85 this.set_freq(freq);
86 this.inner.start();
87
88 unsafe {
89 this.inner
90 .regs_gp32()
91 .ccmr_output(0)
92 .modify(|w| w.set_ocm(0, OutputCompareMode::PwmMode1.into()));
93 this.inner
94 .regs_gp32()
95 .ccmr_output(0)
96 .modify(|w| w.set_ocm(1, OutputCompareMode::PwmMode1.into()));
97 this.inner
98 .regs_gp32()
99 .ccmr_output(1)
100 .modify(|w| w.set_ocm(0, OutputCompareMode::PwmMode1.into()));
101 this.inner
102 .regs_gp32()
103 .ccmr_output(1)
104 .modify(|w| w.set_ocm(1, OutputCompareMode::PwmMode1.into()));
105 }
106 this
107 }
108
109 pub fn enable(&mut self, channel: Channel) {
110 unsafe {
111 self.inner
112 .regs_gp32()
113 .ccer()
114 .modify(|w| w.set_cce(channel.raw(), true));
115 }
116 }
117
118 pub fn disable(&mut self, channel: Channel) {
119 unsafe {
120 self.inner
121 .regs_gp32()
122 .ccer()
123 .modify(|w| w.set_cce(channel.raw(), false));
124 }
125 }
126
127 pub fn set_freq<F: Into<Hertz>>(&mut self, freq: F) {
128 <T as embassy_stm32::timer::low_level::GeneralPurpose32bitInstance>::set_frequency(
129 &mut self.inner,
130 freq,
131 );
132 }
133
134 pub fn get_max_duty(&self) -> u32 {
135 unsafe { self.inner.regs_gp32().arr().read().arr() }
136 }
137
138 pub fn set_duty(&mut self, channel: Channel, duty: u32) {
139 defmt::assert!(duty < self.get_max_duty());
140 unsafe {
141 self.inner
142 .regs_gp32()
143 .ccr(channel.raw())
144 .modify(|w| w.set_ccr(duty))
145 }
146 }
147}
diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs
new file mode 100644
index 000000000..020150a39
--- /dev/null
+++ b/examples/stm32h7/src/bin/pwm.rs
@@ -0,0 +1,48 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5#[path = "../example_common.rs"]
6mod example_common;
7use embassy::executor::Spawner;
8use embassy::time::{Duration, Timer};
9use embassy_stm32::gpio::NoPin;
10use embassy_stm32::pwm::{simple_pwm::SimplePwm, Channel};
11use embassy_stm32::time::U32Ext;
12use embassy_stm32::{Config, Peripherals};
13use example_common::*;
14
15pub fn config() -> Config {
16 let mut config = Config::default();
17 config.rcc.sys_ck = Some(400.mhz().into());
18 config.rcc.hclk = Some(400.mhz().into());
19 config.rcc.pll1.q_ck = Some(100.mhz().into());
20 config.rcc.pclk1 = Some(100.mhz().into());
21 config.rcc.pclk2 = Some(100.mhz().into());
22 config.rcc.pclk3 = Some(100.mhz().into());
23 config.rcc.pclk4 = Some(100.mhz().into());
24 config
25}
26
27#[embassy::main(config = "config()")]
28async fn main(_spawner: Spawner, p: Peripherals) {
29 info!("Hello World!");
30
31 let mut pwm = SimplePwm::new(p.TIM3, p.PA6, NoPin, NoPin, NoPin, 10000.hz());
32 let max = pwm.get_max_duty();
33 pwm.enable(Channel::Ch1);
34
35 info!("PWM initialized");
36 info!("PWM max duty {}", max);
37
38 loop {
39 pwm.set_duty(Channel::Ch1, 0);
40 Timer::after(Duration::from_millis(300)).await;
41 pwm.set_duty(Channel::Ch1, max / 4);
42 Timer::after(Duration::from_millis(300)).await;
43 pwm.set_duty(Channel::Ch1, max / 2);
44 Timer::after(Duration::from_millis(300)).await;
45 pwm.set_duty(Channel::Ch1, max - 1);
46 Timer::after(Duration::from_millis(300)).await;
47 }
48}