aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRomain Goyet <[email protected]>2024-09-04 14:46:35 -0400
committerDario Nieuwenhuis <[email protected]>2024-09-11 01:18:52 +0200
commit3b8859653e4028c194ceed9432a4f76c3c856816 (patch)
tree436c0a6e2dd69dafdc3ccd2b398fa7e57b4a5a9b
parenta1c9a2e8bd83852d774255c82e71d2054c7c02e4 (diff)
stm32: Clean up the lptim driver
-rw-r--r--embassy-stm32/src/lptim/mod.rs158
-rw-r--r--embassy-stm32/src/lptim/pwm.rs122
-rw-r--r--embassy-stm32/src/lptim/timer.rs212
-rw-r--r--embassy-stm32/src/lptim/traits.rs95
4 files changed, 365 insertions, 222 deletions
diff --git a/embassy-stm32/src/lptim/mod.rs b/embassy-stm32/src/lptim/mod.rs
index 3cf95149e..baedf9fe2 100644
--- a/embassy-stm32/src/lptim/mod.rs
+++ b/embassy-stm32/src/lptim/mod.rs
@@ -1,145 +1,49 @@
1//! Low-power timer (LPTIM) 1//! Low-power timer (LPTIM)
2 2
3mod traits; 3pub mod pwm;
4pub mod timer;
4 5
5use core::marker::PhantomData; 6use crate::rcc::RccPeripheral;
6 7
7use embassy_hal_internal::{into_ref, PeripheralRef}; 8/// Timer channel.
8pub use traits::Instance; 9#[derive(Clone, Copy)]
9 10pub enum Channel {
10use crate::gpio::{AfType, AnyPin, OutputType, Speed}; 11 /// Channel 1.
11// use crate::time::Hertz; 12 Ch1,
12use crate::{rcc, Peripheral}; 13 /// Channel 2.
13 14 Ch2,
14/// LPTIM master instance.
15pub struct Master<T: Instance> {
16 phantom: PhantomData<T>,
17} 15}
18 16
19/// LPTIM channel 1 instance. 17impl Channel {
20pub struct Ch1<T: Instance> { 18 /// Get the channel index (0..1)
21 phantom: PhantomData<T>, 19 pub fn index(&self) -> usize {
20 match self {
21 Channel::Ch1 => 0,
22 Channel::Ch2 => 1,
23 }
24 }
22} 25}
23 26
24/// LPTIM channel 2 instance. 27pin_trait!(Channel1Pin, Instance);
25pub struct Ch2<T: Instance> { 28pin_trait!(Channel2Pin, Instance);
26 phantom: PhantomData<T>,
27}
28 29
29trait SealedChannel<T: Instance> { 30pub(crate) trait SealedInstance: RccPeripheral {
30 fn raw() -> usize; 31 fn regs() -> crate::pac::lptim::LptimAdv;
31} 32}
32 33
33/// channel instance trait. 34/// LPTIM instance trait.
34#[allow(private_bounds)] 35#[allow(private_bounds)]
35pub trait Channel<T: Instance>: SealedChannel<T> {} 36pub trait Instance: SealedInstance + 'static {}
36 37foreach_interrupt! {
37/// LPTIM PWM pin. 38 ($inst:ident, lptim, LPTIM_ADV, UP, $irq:ident) => {
38pub struct PwmPin<'d, T, C> { 39 impl SealedInstance for crate::peripherals::$inst {
39 _pin: PeripheralRef<'d, AnyPin>, 40 fn regs() -> crate::pac::lptim::LptimAdv {
40 phantom: PhantomData<(T, C)>, 41 crate::pac::$inst
41}
42
43macro_rules! channel_impl {
44 ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident) => {
45 impl<'d, T: Instance> PwmPin<'d, T, $channel<T>> {
46 #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")]
47 pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd) -> Self {
48 into_ref!(pin);
49 critical_section::with(|_| {
50 pin.set_low();
51 pin.set_as_af(
52 pin.af_num(),
53 AfType::output(OutputType::PushPull, Speed::VeryHigh),
54 );
55 });
56 PwmPin {
57 _pin: pin.map_into(),
58 phantom: PhantomData,
59 }
60 }
61 }
62
63 impl<T: Instance> SealedChannel<T> for $channel<T> {
64 fn raw() -> usize {
65 $ch_num
66 } 42 }
67 } 43 }
68 impl<T: Instance> Channel<T> for $channel<T> {}
69 };
70}
71 44
72channel_impl!(new_ch1, Ch1, 0, Channel1Pin); 45 impl Instance for crate::peripherals::$inst {
73channel_impl!(new_ch2, Ch2, 1, Channel2Pin);
74 46
75/// Struct used to divide a high resolution timer into multiple channels
76pub struct Pwm<'d, T: Instance> {
77 _inner: PeripheralRef<'d, T>,
78 /// Master instance.
79 pub master: Master<T>,
80 /// Channel 1.
81 pub ch_1: Ch1<T>,
82 /// Channel 2.
83 pub ch_2: Ch2<T>,
84}
85
86impl<'d, T: Instance> Pwm<'d, T> {
87 /// Create a new LPTIM driver.
88 ///
89 /// This splits the LPTIM into its constituent parts, which you can then use individually.
90 pub fn new(
91 tim: impl Peripheral<P = T> + 'd,
92 _ch1: Option<PwmPin<'d, T, Ch1<T>>>,
93 _ch2: Option<PwmPin<'d, T, Ch2<T>>>,
94 ) -> Self {
95 Self::new_inner(tim)
96 }
97
98 fn new_inner(tim: impl Peripheral<P = T> + 'd) -> Self {
99 into_ref!(tim);
100
101 rcc::enable_and_reset::<T>();
102
103 T::regs().cr().modify(|w| w.set_enable(true));
104
105 // Set frequency. Should be configurable.
106 // By default, 16MHz. We want 440Hz.
107 // That's 36363 cycles
108 T::regs().arr().write_value(stm32_metapac::lptim::regs::Arr(36363));
109
110 // Set duty cycle. Should be configurable. Should take care of channel too (only Ch1 now)
111 T::regs().ccr(0).write_value(stm32_metapac::lptim::regs::Ccr(18181));
112
113 // Enable channel as PWM. Default state anyway. Implement later.
114 // T::regs().ccmr().modify(|w| {
115 // w.set_ccsel(0, 0);
116 // w.set_ccsel(1, 0);
117 // })
118
119 // Enable output on pins. Should care about the channels!
120 T::regs().ccmr().modify(|w| {
121 w.set_cce(0, true);
122 w.set_cce(1, true);
123 });
124
125 Self {
126 _inner: tim,
127 master: Master { phantom: PhantomData },
128 ch_1: Ch1 { phantom: PhantomData },
129 ch_2: Ch2 { phantom: PhantomData },
130 } 47 }
131 } 48 };
132
133 /// Start
134 pub fn start(&mut self) {
135 T::regs().cr().modify(|w| w.set_cntstrt(true));
136 }
137
138 /// Stop
139 pub fn stop(&mut self) {
140 T::regs().cr().modify(|w| w.set_cntstrt(false));
141 }
142} 49}
143
144pin_trait!(Channel1Pin, Instance);
145pin_trait!(Channel2Pin, Instance);
diff --git a/embassy-stm32/src/lptim/pwm.rs b/embassy-stm32/src/lptim/pwm.rs
new file mode 100644
index 000000000..725aa676e
--- /dev/null
+++ b/embassy-stm32/src/lptim/pwm.rs
@@ -0,0 +1,122 @@
1//! PWM driver.
2
3use core::marker::PhantomData;
4
5use embassy_hal_internal::{into_ref, PeripheralRef};
6
7use super::timer::{ChannelDirection, Timer};
8use super::{Channel, Channel1Pin, Channel2Pin, Instance};
9use crate::gpio::{AfType, AnyPin, OutputType, Speed};
10use crate::time::Hertz;
11use crate::Peripheral;
12
13/// Channel 1 marker type.
14pub enum Ch1 {}
15/// Channel 2 marker type.
16pub enum Ch2 {}
17
18/// PWM pin wrapper.
19///
20/// This wraps a pin to make it usable with PWM.
21pub struct PwmPin<'d, T, C> {
22 _pin: PeripheralRef<'d, AnyPin>,
23 phantom: PhantomData<(T, C)>,
24}
25
26macro_rules! channel_impl {
27 ($new_chx:ident, $channel:ident, $pin_trait:ident) => {
28 impl<'d, T: Instance> PwmPin<'d, T, $channel> {
29 #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")]
30 pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd) -> Self {
31 into_ref!(pin);
32 critical_section::with(|_| {
33 pin.set_low();
34 pin.set_as_af(
35 pin.af_num(),
36 AfType::output(OutputType::PushPull, Speed::VeryHigh),
37 );
38 });
39 PwmPin {
40 _pin: pin.map_into(),
41 phantom: PhantomData,
42 }
43 }
44 }
45 };
46}
47
48channel_impl!(new_ch1, Ch1, Channel1Pin);
49channel_impl!(new_ch2, Ch2, Channel2Pin);
50
51/// PWM driver.
52pub struct Pwm<'d, T: Instance> {
53 inner: Timer<'d, T>, // _inner: PeripheralRef<'d, T>,
54}
55
56impl<'d, T: Instance> Pwm<'d, T> {
57 /// Create a new PWM driver.
58 pub fn new(
59 tim: impl Peripheral<P = T> + 'd,
60 _ch1_pin: Option<PwmPin<'d, T, Ch1>>,
61 _ch2_pin: Option<PwmPin<'d, T, Ch2>>,
62 freq: Hertz,
63 ) -> Self {
64 let mut this = Self { inner: Timer::new(tim) };
65
66 this.inner.enable();
67 this.set_frequency(freq);
68
69 [Channel::Ch1, Channel::Ch2].iter().for_each(|&channel| {
70 this.inner.set_channel_direction(channel, ChannelDirection::OutputPwm);
71 });
72
73 this.inner.continuous_mode_start();
74
75 this
76 }
77
78 /// Enable the given channel.
79 pub fn enable(&mut self, channel: Channel) {
80 self.inner.enable_channel(channel, true);
81 }
82
83 /// Disable the given channel.
84 pub fn disable(&mut self, channel: Channel) {
85 self.inner.enable_channel(channel, false);
86 }
87
88 /// Check whether given channel is enabled
89 pub fn is_enabled(&self, channel: Channel) -> bool {
90 self.inner.get_channel_enable_state(channel)
91 }
92
93 /// Set PWM frequency.
94 ///
95 /// Note: when you call this, the max duty value changes, so you will have to
96 /// call `set_duty` on all channels with the duty calculated based on the new max duty.
97 pub fn set_frequency(&mut self, frequency: Hertz) {
98 self.inner.set_frequency(frequency);
99 }
100
101 /// Get max duty value.
102 ///
103 /// This value depends on the configured frequency and the timer's clock rate from RCC.
104 pub fn get_max_duty(&self) -> u16 {
105 self.inner.get_max_compare_value() + 1
106 }
107
108 /// Set the duty for a given channel.
109 ///
110 /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included.
111 pub fn set_duty(&mut self, channel: Channel, duty: u16) {
112 assert!(duty <= self.get_max_duty());
113 self.inner.set_compare_value(channel, duty)
114 }
115
116 /// Get the duty for a given channel.
117 ///
118 /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included.
119 pub fn get_duty(&self, channel: Channel) -> u16 {
120 self.inner.get_compare_value(channel)
121 }
122}
diff --git a/embassy-stm32/src/lptim/timer.rs b/embassy-stm32/src/lptim/timer.rs
new file mode 100644
index 000000000..06392b2e7
--- /dev/null
+++ b/embassy-stm32/src/lptim/timer.rs
@@ -0,0 +1,212 @@
1//! Low-level timer driver.
2
3use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
4
5use super::{Channel, Instance};
6use crate::pac::lptim::vals;
7use crate::rcc;
8use crate::time::Hertz;
9
10/// Direction of a low-power timer channel
11pub enum ChannelDirection {
12 /// Use channel as a PWM output
13 OutputPwm,
14 /// Use channel as an input capture
15 InputCapture,
16}
17
18impl From<ChannelDirection> for vals::Ccsel {
19 fn from(direction: ChannelDirection) -> Self {
20 match direction {
21 ChannelDirection::OutputPwm => vals::Ccsel::OUTPUTCOMPARE,
22 ChannelDirection::InputCapture => vals::Ccsel::INPUTCAPTURE,
23 }
24 }
25}
26
27enum Prescaler {
28 Div1,
29 Div2,
30 Div4,
31 Div8,
32 Div16,
33 Div32,
34 Div64,
35 Div128,
36}
37
38impl From<&Prescaler> for vals::Presc {
39 fn from(prescaler: &Prescaler) -> Self {
40 match prescaler {
41 Prescaler::Div1 => vals::Presc::DIV1,
42 Prescaler::Div2 => vals::Presc::DIV2,
43 Prescaler::Div4 => vals::Presc::DIV4,
44 Prescaler::Div8 => vals::Presc::DIV8,
45 Prescaler::Div16 => vals::Presc::DIV16,
46 Prescaler::Div32 => vals::Presc::DIV32,
47 Prescaler::Div64 => vals::Presc::DIV64,
48 Prescaler::Div128 => vals::Presc::DIV128,
49 }
50 }
51}
52
53impl From<vals::Presc> for Prescaler {
54 fn from(prescaler: vals::Presc) -> Self {
55 match prescaler {
56 vals::Presc::DIV1 => Prescaler::Div1,
57 vals::Presc::DIV2 => Prescaler::Div2,
58 vals::Presc::DIV4 => Prescaler::Div4,
59 vals::Presc::DIV8 => Prescaler::Div8,
60 vals::Presc::DIV16 => Prescaler::Div16,
61 vals::Presc::DIV32 => Prescaler::Div32,
62 vals::Presc::DIV64 => Prescaler::Div64,
63 vals::Presc::DIV128 => Prescaler::Div128,
64 }
65 }
66}
67
68impl From<&Prescaler> for u32 {
69 fn from(prescaler: &Prescaler) -> Self {
70 match prescaler {
71 Prescaler::Div1 => 1,
72 Prescaler::Div2 => 2,
73 Prescaler::Div4 => 4,
74 Prescaler::Div8 => 8,
75 Prescaler::Div16 => 16,
76 Prescaler::Div32 => 32,
77 Prescaler::Div64 => 64,
78 Prescaler::Div128 => 128,
79 }
80 }
81}
82
83impl From<u32> for Prescaler {
84 fn from(prescaler: u32) -> Self {
85 match prescaler {
86 1 => Prescaler::Div1,
87 2 => Prescaler::Div2,
88 4 => Prescaler::Div4,
89 8 => Prescaler::Div8,
90 16 => Prescaler::Div16,
91 32 => Prescaler::Div32,
92 64 => Prescaler::Div64,
93 128 => Prescaler::Div128,
94 _ => unreachable!(),
95 }
96 }
97}
98
99impl Prescaler {
100 pub fn from_ticks(ticks: u32) -> Self {
101 // We need to scale down to a 16-bit range
102 (ticks >> 16).next_power_of_two().into()
103 }
104
105 pub fn scale_down(&self, ticks: u32) -> u16 {
106 (ticks / u32::from(self)).try_into().unwrap()
107 }
108
109 pub fn scale_up(&self, ticks: u16) -> u32 {
110 u32::from(self) * ticks as u32
111 }
112}
113
114/// Low-level timer driver.
115pub struct Timer<'d, T: Instance> {
116 _tim: PeripheralRef<'d, T>,
117}
118
119impl<'d, T: Instance> Timer<'d, T> {
120 /// Create a new timer driver.
121 pub fn new(tim: impl Peripheral<P = T> + 'd) -> Self {
122 into_ref!(tim);
123
124 rcc::enable_and_reset::<T>();
125
126 Self { _tim: tim }
127 }
128
129 /// Enable the timer.
130 pub fn enable(&self) {
131 T::regs().cr().modify(|w| w.set_enable(true));
132 }
133
134 /// Disable the timer.
135 pub fn disable(&self) {
136 T::regs().cr().modify(|w| w.set_enable(false));
137 }
138
139 /// Start the timer in single pulse mode.
140 pub fn single_mode_start(&self) {
141 T::regs().cr().modify(|w| w.set_sngstrt(true));
142 }
143
144 /// Start the timer in continuous mode.
145 pub fn continuous_mode_start(&self) {
146 T::regs().cr().modify(|w| w.set_cntstrt(true));
147 }
148
149 /// Set channel direction.
150 pub fn set_channel_direction(&self, channel: Channel, direction: ChannelDirection) {
151 T::regs()
152 .ccmr()
153 .modify(|w| w.set_ccsel(channel.index(), direction.into()));
154 }
155
156 /// Set the frequency of how many times per second the timer counts up to the max value or down to 0.
157 pub fn set_frequency(&self, frequency: Hertz) {
158 let f = frequency.0;
159 assert!(f > 0);
160
161 let pclk_f = T::frequency().0;
162
163 let pclk_ticks_per_timer_period = pclk_f / f;
164
165 let psc = Prescaler::from_ticks(pclk_ticks_per_timer_period);
166 let arr = psc.scale_down(pclk_ticks_per_timer_period);
167
168 T::regs().cfgr().modify(|r| r.set_presc((&psc).into()));
169 T::regs().arr().modify(|r| r.set_arr(arr.into()));
170 }
171
172 /// Get the timer frequency.
173 pub fn get_frequency(&self) -> Hertz {
174 let pclk_f = T::frequency();
175 let arr = T::regs().arr().read().arr();
176 let psc = Prescaler::from(T::regs().cfgr().read().presc());
177
178 pclk_f / psc.scale_up(arr)
179 }
180
181 /// Get the clock frequency of the timer (before prescaler is applied).
182 pub fn get_clock_frequency(&self) -> Hertz {
183 T::frequency()
184 }
185
186 /// Enable/disable a channel.
187 pub fn enable_channel(&self, channel: Channel, enable: bool) {
188 T::regs().ccmr().modify(|w| {
189 w.set_cce(channel.index(), enable);
190 });
191 }
192
193 /// Get enable/disable state of a channel
194 pub fn get_channel_enable_state(&self, channel: Channel) -> bool {
195 T::regs().ccmr().read().cce(channel.index())
196 }
197
198 /// Set compare value for a channel.
199 pub fn set_compare_value(&self, channel: Channel, value: u16) {
200 T::regs().ccr(channel.index()).modify(|w| w.set_ccr(value));
201 }
202
203 /// Get compare value for a channel.
204 pub fn get_compare_value(&self, channel: Channel) -> u16 {
205 T::regs().ccr(channel.index()).read().ccr()
206 }
207
208 /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC.
209 pub fn get_max_compare_value(&self) -> u16 {
210 T::regs().arr().read().arr()
211 }
212}
diff --git a/embassy-stm32/src/lptim/traits.rs b/embassy-stm32/src/lptim/traits.rs
deleted file mode 100644
index a8f890a78..000000000
--- a/embassy-stm32/src/lptim/traits.rs
+++ /dev/null
@@ -1,95 +0,0 @@
1use crate::rcc::RccPeripheral;
2use crate::time::Hertz;
3
4#[repr(u8)]
5#[derive(Clone, Copy)]
6pub(crate) enum Prescaler {
7 Div1 = 1,
8 Div2 = 2,
9 Div4 = 4,
10 Div8 = 8,
11 Div16 = 16,
12 Div32 = 32,
13 Div64 = 64,
14 Div128 = 128,
15}
16
17impl From<Prescaler> for u8 {
18 fn from(val: Prescaler) -> Self {
19 match val {
20 Prescaler::Div1 => 0b000,
21 Prescaler::Div2 => 0b001,
22 Prescaler::Div4 => 0b010,
23 Prescaler::Div8 => 0b011,
24 Prescaler::Div16 => 0b100,
25 Prescaler::Div32 => 0b101,
26 Prescaler::Div64 => 0b110,
27 Prescaler::Div128 => 0b111,
28 }
29 }
30}
31
32impl From<u8> for Prescaler {
33 fn from(val: u8) -> Self {
34 match val {
35 0b000 => Prescaler::Div1,
36 0b001 => Prescaler::Div2,
37 0b010 => Prescaler::Div4,
38 0b011 => Prescaler::Div8,
39 0b100 => Prescaler::Div16,
40 0b101 => Prescaler::Div32,
41 0b110 => Prescaler::Div64,
42 0b111 => Prescaler::Div128,
43 _ => unreachable!(),
44 }
45 }
46}
47
48impl Prescaler {
49 pub fn compute_min_high_res(val: u32) -> Self {
50 *[
51 Prescaler::Div1,
52 Prescaler::Div2,
53 Prescaler::Div4,
54 Prescaler::Div8,
55 Prescaler::Div16,
56 Prescaler::Div32,
57 Prescaler::Div64,
58 Prescaler::Div128,
59 ]
60 .iter()
61 .skip_while(|psc| **psc as u32 <= val)
62 .next()
63 .unwrap()
64 }
65
66 pub fn compute_min_low_res(val: u32) -> Self {
67 *[Prescaler::Div32, Prescaler::Div64, Prescaler::Div128]
68 .iter()
69 .skip_while(|psc| **psc as u32 <= val)
70 .next()
71 .unwrap()
72 }
73}
74
75pub(crate) trait SealedInstance: RccPeripheral {
76 fn regs() -> crate::pac::lptim::LptimAdv;
77}
78
79/// LPTIM instance trait.
80#[allow(private_bounds)]
81pub trait Instance: SealedInstance + 'static {}
82
83foreach_interrupt! {
84 ($inst:ident, lptim, LPTIM_ADV, UP, $irq:ident) => {
85 impl SealedInstance for crate::peripherals::$inst {
86 fn regs() -> crate::pac::lptim::LptimAdv {
87 crate::pac::$inst
88 }
89 }
90
91 impl Instance for crate::peripherals::$inst {
92
93 }
94 };
95}