diff options
| author | Ben Gamari <[email protected]> | 2021-09-29 10:10:20 -0400 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2021-11-27 02:50:30 +0100 |
| commit | 8211d58ee29362edc7f210e6b6d692881254f6da (patch) | |
| tree | 7886921766a78062fb2194954c77ec1ec8ed2e3d | |
| parent | 88d4b0c00d5164f2fe6307bacce74887b3f8d4da (diff) | |
stm32/pwm: initial commit
| -rw-r--r-- | embassy-stm32/src/lib.rs | 2 | ||||
| -rw-r--r-- | embassy-stm32/src/pwm/mod.rs | 195 |
2 files changed, 197 insertions, 0 deletions
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 424b1c994..a7b8b3224 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs | |||
| @@ -51,6 +51,8 @@ pub mod sdmmc; | |||
| 51 | pub mod spi; | 51 | pub mod spi; |
| 52 | #[cfg(usart)] | 52 | #[cfg(usart)] |
| 53 | pub mod usart; | 53 | pub mod usart; |
| 54 | //#[cfg(pwm)] | ||
| 55 | pub mod pwm; | ||
| 54 | 56 | ||
| 55 | #[cfg(feature = "subghz")] | 57 | #[cfg(feature = "subghz")] |
| 56 | pub mod subghz; | 58 | pub mod subghz; |
diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs new file mode 100644 index 000000000..f1ef1f1dd --- /dev/null +++ b/embassy-stm32/src/pwm/mod.rs | |||
| @@ -0,0 +1,195 @@ | |||
| 1 | use crate::gpio; | ||
| 2 | use crate::rcc::RccPeripheral; | ||
| 3 | use crate::time::Hertz; | ||
| 4 | use core::marker::PhantomData; | ||
| 5 | use embassy::util::Unborrow; | ||
| 6 | use embassy_hal_common::unborrow; | ||
| 7 | use stm32_metapac::timer::vals::Ocm; | ||
| 8 | |||
| 9 | pub struct Pwm<'d, T: Instance> { | ||
| 10 | phantom: PhantomData<&'d mut T>, | ||
| 11 | } | ||
| 12 | |||
| 13 | // TIM2 | ||
| 14 | |||
| 15 | pub struct Ch1 {} | ||
| 16 | pub struct Ch2 {} | ||
| 17 | pub struct Ch3 {} | ||
| 18 | pub struct Ch4 {} | ||
| 19 | |||
| 20 | #[derive(Clone, Copy)] | ||
| 21 | pub enum Channel { | ||
| 22 | Ch1, | ||
| 23 | Ch2, | ||
| 24 | Ch3, | ||
| 25 | Ch4, | ||
| 26 | } | ||
| 27 | |||
| 28 | impl<'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 { | ||
| 91 | self.set_enable(channel, true); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | pub fn disable(&mut self, channel: Channel) { | ||
| 96 | unsafe { | ||
| 97 | self.set_enable(channel, false); | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | pub fn set_freq<F: Into<Hertz>>(&mut self, freq: F) { | ||
| 102 | use core::convert::TryInto; | ||
| 103 | let clk = T::frequency(); | ||
| 104 | let r = T::regs(); | ||
| 105 | let freq: Hertz = freq.into(); | ||
| 106 | let ticks: u32 = clk.0 / freq.0; | ||
| 107 | let psc: u16 = (ticks / (1 << 16)).try_into().unwrap(); | ||
| 108 | let arr: u16 = (ticks / (u32::from(psc) + 1)).try_into().unwrap(); | ||
| 109 | unsafe { | ||
| 110 | r.psc().write(|w| w.set_psc(psc)); | ||
| 111 | r.arr().write(|w| w.set_arr(arr)); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | pub fn get_max_duty(&self) -> u32 { | ||
| 116 | let r = T::regs(); | ||
| 117 | unsafe { r.arr().read().arr() as u32 } | ||
| 118 | } | ||
| 119 | |||
| 120 | pub fn set_duty(&mut self, channel: Channel, duty: u32) { | ||
| 121 | use core::convert::TryInto; | ||
| 122 | assert!(duty < self.get_max_duty()); | ||
| 123 | let duty: u16 = duty.try_into().unwrap(); | ||
| 124 | let r = T::regs(); | ||
| 125 | unsafe { | ||
| 126 | match channel { | ||
| 127 | Channel::Ch1 => r.ccr(0).modify(|w| w.set_ccr(duty)), | ||
| 128 | Channel::Ch2 => r.ccr(1).modify(|w| w.set_ccr(duty)), | ||
| 129 | Channel::Ch3 => r.ccr(2).modify(|w| w.set_ccr(duty)), | ||
| 130 | Channel::Ch4 => r.ccr(3).modify(|w| w.set_ccr(duty)), | ||
| 131 | } | ||
| 132 | } | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | pub(crate) mod sealed { | ||
| 137 | pub trait Instance { | ||
| 138 | fn regs() -> crate::pac::timer::TimGp16; | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | pub trait Instance: sealed::Instance + Sized + RccPeripheral + 'static {} | ||
| 143 | |||
| 144 | #[allow(unused)] | ||
| 145 | macro_rules! impl_timer { | ||
| 146 | ($inst:ident) => { | ||
| 147 | impl crate::pwm::sealed::Instance for crate::peripherals::$inst { | ||
| 148 | fn regs() -> crate::pac::timer::TimGp16 { | ||
| 149 | crate::pac::$inst | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | impl crate::pwm::Instance for crate::peripherals::$inst {} | ||
| 154 | }; | ||
| 155 | } | ||
| 156 | |||
| 157 | pub trait PwmPin<Timer, Channel>: gpio::OptionalPin { | ||
| 158 | unsafe fn configure(&mut self); | ||
| 159 | } | ||
| 160 | |||
| 161 | impl<Timer, Channel> PwmPin<Timer, Channel> for gpio::NoPin { | ||
| 162 | unsafe fn configure(&mut self) {} | ||
| 163 | } | ||
| 164 | |||
| 165 | #[allow(unused)] | ||
| 166 | macro_rules! impl_pwm_pin { | ||
| 167 | ($timer:ident, $channel:ident, $pin:ident, $af:expr) => { | ||
| 168 | impl crate::pwm::PwmPin<crate::peripherals::$timer, crate::pwm::$channel> | ||
| 169 | for crate::peripherals::$pin | ||
| 170 | { | ||
| 171 | unsafe fn configure(&mut self) { | ||
| 172 | use crate::gpio::sealed::{AFType, Pin}; | ||
| 173 | use crate::gpio::Speed; | ||
| 174 | self.set_low(); | ||
| 175 | self.set_speed(Speed::VeryHigh); | ||
| 176 | self.set_as_af($af, AFType::OutputPushPull); | ||
| 177 | } | ||
| 178 | } | ||
| 179 | }; | ||
| 180 | } | ||
| 181 | |||
| 182 | #[cfg(rcc_g0)] | ||
| 183 | mod impls { | ||
| 184 | crate::pac::peripherals!( | ||
| 185 | (timer, TIM2) => { impl_timer!(TIM2); }; | ||
| 186 | (timer, TIM3) => { impl_timer!(TIM3); }; | ||
| 187 | (timer, TIM4) => { impl_timer!(TIM4); }; | ||
| 188 | (timer, TIM5) => { impl_timer!(TIM5); }; | ||
| 189 | ); | ||
| 190 | |||
| 191 | impl_pwm_pin!(TIM2, Ch1, PA0, 2); | ||
| 192 | impl_pwm_pin!(TIM2, Ch2, PA1, 2); | ||
| 193 | impl_pwm_pin!(TIM2, Ch3, PA2, 2); | ||
| 194 | impl_pwm_pin!(TIM2, Ch4, PA3, 2); | ||
| 195 | } | ||
