aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-11-27 02:07:43 +0000
committerGitHub <[email protected]>2021-11-27 02:07:43 +0000
commit543cc65e569d24d07c90e97752b4cfc995d33dc0 (patch)
tree34615657d5507b9d043f86cb48048e22cb69c432
parent793f4b1f7d6d1452d665fc7ec70bddd3ac7a69b9 (diff)
parent006e567716362acfde90afcfd8a25be1fe0d6897 (diff)
Merge #449
449: STM32: Add PWM support r=Dirbaio a=bgamari Here is a first-cut at implementing PWM support for STM32 targets via the TIM peripherals. Currently this only contains pin configuration for the STM32G0 but it would be straightforward to extend to other platforms. Co-authored-by: Ben Gamari <[email protected]> Co-authored-by: Dario Nieuwenhuis <[email protected]>
-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}