aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.vscode/settings.json1
-rw-r--r--embassy-stm32/src/lib.rs1
-rw-r--r--embassy-stm32/src/pwm/mod.rs195
-rw-r--r--examples/stm32g4/Cargo.toml2
-rw-r--r--examples/stm32g4/src/bin/pwm.rs36
5 files changed, 234 insertions, 1 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 0e67ab824..87dd158ec 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -6,6 +6,7 @@
6 "rust-analyzer.checkOnSave.allTargets": false, 6 "rust-analyzer.checkOnSave.allTargets": false,
7 "rust-analyzer.checkOnSave.command": "clippy", 7 "rust-analyzer.checkOnSave.command": "clippy",
8 "rust-analyzer.cargo.noDefaultFeatures": true, 8 "rust-analyzer.cargo.noDefaultFeatures": true,
9 "rust-analyzer.experimental.procAttrMacros": false,
9 "rust-analyzer.checkOnSave.noDefaultFeatures": true, 10 "rust-analyzer.checkOnSave.noDefaultFeatures": true,
10 "rust-analyzer.cargo.target": "thumbv7em-none-eabi", 11 "rust-analyzer.cargo.target": "thumbv7em-none-eabi",
11 "rust-analyzer.cargo.features": [ 12 "rust-analyzer.cargo.features": [
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 424b1c994..649b25f10 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -41,6 +41,7 @@ pub mod i2c;
41 41
42#[cfg(crc)] 42#[cfg(crc)]
43pub mod crc; 43pub mod crc;
44pub mod pwm;
44#[cfg(pwr)] 45#[cfg(pwr)]
45pub mod pwr; 46pub mod pwr;
46#[cfg(rng)] 47#[cfg(rng)]
diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs
new file mode 100644
index 000000000..8357b6cdc
--- /dev/null
+++ b/embassy-stm32/src/pwm/mod.rs
@@ -0,0 +1,195 @@
1use crate::gpio;
2use crate::rcc::RccPeripheral;
3use crate::time::Hertz;
4use core::marker::PhantomData;
5use embassy::util::Unborrow;
6use embassy_hal_common::unborrow;
7use stm32_metapac::timer::vals::Ocm;
8
9pub struct Pwm<'d, T: Instance> {
10 phantom: PhantomData<&'d mut T>,
11}
12
13// TIM2
14
15pub struct Ch1 {}
16pub struct Ch2 {}
17pub struct Ch3 {}
18pub struct Ch4 {}
19
20#[derive(Clone, Copy)]
21pub enum Channel {
22 Ch1,
23 Ch2,
24 Ch3,
25 Ch4,
26}
27
28impl<'d, T: Instance> Pwm<'d, T> {
29 pub fn new<F: Into<Hertz>>(
30 _tim: impl Unborrow<Target = T> + 'd,
31 ch1: impl Unborrow<Target = impl PwmPin<T, Ch1>> + 'd,
32 ch2: impl Unborrow<Target = impl PwmPin<T, Ch2>> + 'd,
33 ch3: impl Unborrow<Target = impl PwmPin<T, Ch3>> + 'd,
34 ch4: impl Unborrow<Target = impl PwmPin<T, Ch4>> + 'd,
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 }
66 this
67 }
68
69 unsafe fn set_ocm(&mut self, channel: Channel, mode: Ocm) {
70 let r = T::regs();
71 match channel {
72 Channel::Ch1 => r.ccmr_output(0).modify(|w| w.set_ocm(0, mode)),
73 Channel::Ch2 => r.ccmr_output(0).modify(|w| w.set_ocm(1, mode)),
74 Channel::Ch3 => r.ccmr_output(1).modify(|w| w.set_ocm(0, mode)),
75 Channel::Ch4 => r.ccmr_output(1).modify(|w| w.set_ocm(1, mode)),
76 }
77 }
78
79 unsafe fn set_enable(&mut self, channel: Channel, enable: bool) {
80 let r = T::regs();
81 match channel {
82 Channel::Ch1 => r.ccer().modify(|w| w.set_cce(0, enable)),
83 Channel::Ch2 => r.ccer().modify(|w| w.set_cce(1, enable)),
84 Channel::Ch3 => r.ccer().modify(|w| w.set_cce(2, enable)),
85 Channel::Ch4 => r.ccer().modify(|w| w.set_cce(3, enable)),
86 }
87 }
88
89 pub fn enable(&mut self, channel: Channel) {
90 unsafe { self.set_enable(channel, true) }
91 }
92
93 pub fn disable(&mut self, channel: Channel) {
94 unsafe { self.set_enable(channel, false) }
95 }
96
97 pub fn set_freq<F: Into<Hertz>>(&mut self, freq: F) {
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
111 pub fn get_max_duty(&self) -> u32 {
112 let r = T::regs();
113 unsafe { r.arr().read().arr() as u32 }
114 }
115
116 pub fn set_duty(&mut self, channel: Channel, duty: u32) {
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 }
130}
131
132pub(crate) mod sealed {
133 pub trait Instance {
134 fn regs() -> crate::pac::timer::TimGp16;
135 }
136}
137
138pub trait Instance: sealed::Instance + Sized + RccPeripheral + 'static {}
139
140#[allow(unused)]
141macro_rules! impl_timer {
142 ($inst:ident) => {
143 impl crate::pwm::sealed::Instance for crate::peripherals::$inst {
144 fn regs() -> crate::pac::timer::TimGp16 {
145 crate::pac::timer::TimGp16(crate::pac::$inst.0)
146 }
147 }
148
149 impl crate::pwm::Instance for crate::peripherals::$inst {}
150 };
151}
152
153pub trait PwmPin<Timer, Channel>: gpio::OptionalPin {
154 unsafe fn configure(&mut self);
155}
156
157impl<Timer, Channel> PwmPin<Timer, Channel> for gpio::NoPin {
158 unsafe fn configure(&mut self) {}
159}
160
161#[allow(unused)]
162macro_rules! impl_pwm_pin {
163 ($timer:ident, $channel:ident, $pin:ident, $af:expr) => {
164 impl crate::pwm::PwmPin<crate::peripherals::$timer, crate::pwm::$channel>
165 for crate::peripherals::$pin
166 {
167 unsafe fn configure(&mut self) {
168 use crate::gpio::sealed::{AFType, Pin};
169 use crate::gpio::Speed;
170 self.set_low();
171 self.set_speed(Speed::VeryHigh);
172 self.set_as_af($af, AFType::OutputPushPull);
173 }
174 }
175 };
176}
177
178crate::pac::peripherals!(
179 (timer, $inst:ident) => { impl_timer!($inst); };
180);
181
182crate::pac::peripheral_pins!(
183 ($inst:ident, timer,TIM_GP16, $pin:ident, CH1, $af:expr) => {
184 impl_pwm_pin!($inst, Ch1, $pin, $af);
185 };
186 ($inst:ident, timer,TIM_GP16, $pin:ident, CH2, $af:expr) => {
187 impl_pwm_pin!($inst, Ch2, $pin, $af);
188 };
189 ($inst:ident, timer,TIM_GP16, $pin:ident, CH3, $af:expr) => {
190 impl_pwm_pin!($inst, Ch3, $pin, $af);
191 };
192 ($inst:ident, timer,TIM_GP16, $pin:ident, CH4, $af:expr) => {
193 impl_pwm_pin!($inst, Ch4, $pin, $af);
194 };
195);
diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml
index 0f9d77f5e..f4378309a 100644
--- a/examples/stm32g4/Cargo.toml
+++ b/examples/stm32g4/Cargo.toml
@@ -8,7 +8,7 @@ resolver = "2"
8[dependencies] 8[dependencies]
9embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt"] } 9embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt"] }
10embassy-traits = { version = "0.1.0", path = "../../embassy-traits", features = ["defmt"] } 10embassy-traits = { version = "0.1.0", path = "../../embassy-traits", features = ["defmt"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "time-driver-tim2", "stm32g491re", "memory-x", "unstable-pac"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "time-driver-tim3", "stm32g491re", "memory-x", "unstable-pac"] }
12embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } 12embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" }
13 13
14defmt = "0.3" 14defmt = "0.3"
diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs
new file mode 100644
index 000000000..1aa7b85f3
--- /dev/null
+++ b/examples/stm32g4/src/bin/pwm.rs
@@ -0,0 +1,36 @@
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::{Channel, Pwm};
11use embassy_stm32::time::U32Ext;
12use embassy_stm32::Peripherals;
13use example_common::*;
14
15#[embassy::main]
16async fn main(_spawner: Spawner, p: Peripherals) {
17 info!("Hello World!");
18
19 let mut pwm = Pwm::new(p.TIM2, p.PA5, NoPin, NoPin, NoPin, 10000.hz());
20 let max = pwm.get_max_duty();
21 pwm.enable(Channel::Ch1);
22
23 info!("PWM initialized");
24 info!("PWM max duty {}", max);
25
26 loop {
27 pwm.set_duty(Channel::Ch1, 0);
28 Timer::after(Duration::from_millis(300)).await;
29 pwm.set_duty(Channel::Ch1, max / 4);
30 Timer::after(Duration::from_millis(300)).await;
31 pwm.set_duty(Channel::Ch1, max / 2);
32 Timer::after(Duration::from_millis(300)).await;
33 pwm.set_duty(Channel::Ch1, max - 1);
34 Timer::after(Duration::from_millis(300)).await;
35 }
36}