aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/lptim
diff options
context:
space:
mode:
authorRomain Goyet <[email protected]>2024-09-05 15:10:39 -0400
committerDario Nieuwenhuis <[email protected]>2024-09-11 01:18:52 +0200
commit652133bce48f39ea5f379d056ca46a7f2ca3a99a (patch)
tree39334d0b1cbad22f2f865ddb6c12818644d9457b /embassy-stm32/src/lptim
parent37130f45e4f74f7b4faeb7cac017c4588e5b422a (diff)
stm32: Support LPTIM v1 and v2
Diffstat (limited to 'embassy-stm32/src/lptim')
-rw-r--r--embassy-stm32/src/lptim/channel.rs18
-rw-r--r--embassy-stm32/src/lptim/mod.rs22
-rw-r--r--embassy-stm32/src/lptim/pwm.rs107
-rw-r--r--embassy-stm32/src/lptim/timer/channel_direction.rs18
-rw-r--r--embassy-stm32/src/lptim/timer/mod.rs (renamed from embassy-stm32/src/lptim/timer.rs)154
-rw-r--r--embassy-stm32/src/lptim/timer/prescaler.rs90
6 files changed, 243 insertions, 166 deletions
diff --git a/embassy-stm32/src/lptim/channel.rs b/embassy-stm32/src/lptim/channel.rs
new file mode 100644
index 000000000..17fc2fb86
--- /dev/null
+++ b/embassy-stm32/src/lptim/channel.rs
@@ -0,0 +1,18 @@
1/// Timer channel.
2#[derive(Clone, Copy)]
3pub enum Channel {
4 /// Channel 1.
5 Ch1,
6 /// Channel 2.
7 Ch2,
8}
9
10impl Channel {
11 /// Get the channel index (0..1)
12 pub fn index(&self) -> usize {
13 match self {
14 Channel::Ch1 => 0,
15 Channel::Ch2 => 1,
16 }
17 }
18}
diff --git a/embassy-stm32/src/lptim/mod.rs b/embassy-stm32/src/lptim/mod.rs
index dc5f1d5bb..9c10efa43 100644
--- a/embassy-stm32/src/lptim/mod.rs
+++ b/embassy-stm32/src/lptim/mod.rs
@@ -6,24 +6,12 @@ pub mod timer;
6use crate::rcc::RccPeripheral; 6use crate::rcc::RccPeripheral;
7 7
8/// Timer channel. 8/// Timer channel.
9#[derive(Clone, Copy)] 9#[cfg(any(lptim_v2a, lptim_v2b))]
10pub enum Channel { 10mod channel;
11 /// Channel 1. 11#[cfg(any(lptim_v2a, lptim_v2b))]
12 Ch1, 12pub use channel::Channel;
13 /// Channel 2.
14 Ch2,
15}
16
17impl Channel {
18 /// Get the channel index (0..1)
19 pub fn index(&self) -> usize {
20 match self {
21 Channel::Ch1 => 0,
22 Channel::Ch2 => 1,
23 }
24 }
25}
26 13
14pin_trait!(OutputPin, Instance);
27pin_trait!(Channel1Pin, Instance); 15pin_trait!(Channel1Pin, Instance);
28pin_trait!(Channel2Pin, Instance); 16pin_trait!(Channel2Pin, Instance);
29 17
diff --git a/embassy-stm32/src/lptim/pwm.rs b/embassy-stm32/src/lptim/pwm.rs
index 24725f625..586148848 100644
--- a/embassy-stm32/src/lptim/pwm.rs
+++ b/embassy-stm32/src/lptim/pwm.rs
@@ -4,12 +4,18 @@ use core::marker::PhantomData;
4 4
5use embassy_hal_internal::{into_ref, PeripheralRef}; 5use embassy_hal_internal::{into_ref, PeripheralRef};
6 6
7use super::timer::{ChannelDirection, Timer}; 7use super::timer::Timer;
8use super::{Channel, Channel1Pin, Channel2Pin, Instance}; 8use super::Instance;
9#[cfg(not(any(lptim_v2a, lptim_v2b)))]
10use super::OutputPin;
11#[cfg(any(lptim_v2a, lptim_v2b))]
12use super::{channel::Channel, timer::ChannelDirection, Channel1Pin, Channel2Pin};
9use crate::gpio::{AfType, AnyPin, OutputType, Speed}; 13use crate::gpio::{AfType, AnyPin, OutputType, Speed};
10use crate::time::Hertz; 14use crate::time::Hertz;
11use crate::Peripheral; 15use crate::Peripheral;
12 16
17/// Output marker type.
18pub enum Output {}
13/// Channel 1 marker type. 19/// Channel 1 marker type.
14pub enum Ch1 {} 20pub enum Ch1 {}
15/// Channel 2 marker type. 21/// Channel 2 marker type.
@@ -45,14 +51,44 @@ macro_rules! channel_impl {
45 }; 51 };
46} 52}
47 53
54#[cfg(not(any(lptim_v2a, lptim_v2b)))]
55channel_impl!(new, Output, OutputPin);
56#[cfg(any(lptim_v2a, lptim_v2b))]
48channel_impl!(new_ch1, Ch1, Channel1Pin); 57channel_impl!(new_ch1, Ch1, Channel1Pin);
58#[cfg(any(lptim_v2a, lptim_v2b))]
49channel_impl!(new_ch2, Ch2, Channel2Pin); 59channel_impl!(new_ch2, Ch2, Channel2Pin);
50 60
51/// PWM driver. 61/// PWM driver.
52pub struct Pwm<'d, T: Instance> { 62pub struct Pwm<'d, T: Instance> {
53 inner: Timer<'d, T>, // _inner: PeripheralRef<'d, T>, 63 inner: Timer<'d, T>,
54} 64}
55 65
66#[cfg(not(any(lptim_v2a, lptim_v2b)))]
67impl<'d, T: Instance> Pwm<'d, T> {
68 /// Create a new PWM driver.
69 pub fn new(tim: impl Peripheral<P = T> + 'd, _output_pin: PwmPin<'d, T, Output>, freq: Hertz) -> Self {
70 Self::new_inner(tim, freq)
71 }
72
73 /// Set the duty.
74 ///
75 /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included.
76 pub fn set_duty(&mut self, duty: u16) {
77 assert!(duty <= self.get_max_duty());
78 self.inner.set_compare_value(duty)
79 }
80
81 /// Get the duty.
82 ///
83 /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included.
84 pub fn get_duty(&self) -> u16 {
85 self.inner.get_compare_value()
86 }
87
88 fn post_init(&mut self) {}
89}
90
91#[cfg(any(lptim_v2a, lptim_v2b))]
56impl<'d, T: Instance> Pwm<'d, T> { 92impl<'d, T: Instance> Pwm<'d, T> {
57 /// Create a new PWM driver. 93 /// Create a new PWM driver.
58 pub fn new( 94 pub fn new(
@@ -61,19 +97,7 @@ impl<'d, T: Instance> Pwm<'d, T> {
61 _ch2_pin: Option<PwmPin<'d, T, Ch2>>, 97 _ch2_pin: Option<PwmPin<'d, T, Ch2>>,
62 freq: Hertz, 98 freq: Hertz,
63 ) -> Self { 99 ) -> Self {
64 let mut this = Self { inner: Timer::new(tim) }; 100 Self::new_inner(tim, freq)
65
66 this.inner.enable();
67 this.set_frequency(freq);
68
69 #[cfg(any(lptim_v2a, lptim_v2b))]
70 [Channel::Ch1, Channel::Ch2].iter().for_each(|&channel| {
71 this.inner.set_channel_direction(channel, ChannelDirection::OutputPwm);
72 });
73
74 this.inner.continuous_mode_start();
75
76 this
77 } 101 }
78 102
79 /// Enable the given channel. 103 /// Enable the given channel.
@@ -91,21 +115,6 @@ impl<'d, T: Instance> Pwm<'d, T> {
91 self.inner.get_channel_enable_state(channel) 115 self.inner.get_channel_enable_state(channel)
92 } 116 }
93 117
94 /// Set PWM frequency.
95 ///
96 /// Note: when you call this, the max duty value changes, so you will have to
97 /// call `set_duty` on all channels with the duty calculated based on the new max duty.
98 pub fn set_frequency(&mut self, frequency: Hertz) {
99 self.inner.set_frequency(frequency);
100 }
101
102 /// Get max duty value.
103 ///
104 /// This value depends on the configured frequency and the timer's clock rate from RCC.
105 pub fn get_max_duty(&self) -> u16 {
106 self.inner.get_max_compare_value() + 1
107 }
108
109 /// Set the duty for a given channel. 118 /// Set the duty for a given channel.
110 /// 119 ///
111 /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. 120 /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included.
@@ -120,4 +129,40 @@ impl<'d, T: Instance> Pwm<'d, T> {
120 pub fn get_duty(&self, channel: Channel) -> u16 { 129 pub fn get_duty(&self, channel: Channel) -> u16 {
121 self.inner.get_compare_value(channel) 130 self.inner.get_compare_value(channel)
122 } 131 }
132
133 fn post_init(&mut self) {
134 [Channel::Ch1, Channel::Ch2].iter().for_each(|&channel| {
135 self.inner.set_channel_direction(channel, ChannelDirection::OutputPwm);
136 });
137 }
138}
139
140impl<'d, T: Instance> Pwm<'d, T> {
141 fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz) -> Self {
142 let mut this = Self { inner: Timer::new(tim) };
143
144 this.inner.enable();
145 this.set_frequency(freq);
146
147 this.post_init();
148
149 this.inner.continuous_mode_start();
150
151 this
152 }
153
154 /// Set PWM frequency.
155 ///
156 /// Note: when you call this, the max duty value changes, so you will have to
157 /// call `set_duty` on all channels with the duty calculated based on the new max duty.
158 pub fn set_frequency(&mut self, frequency: Hertz) {
159 self.inner.set_frequency(frequency);
160 }
161
162 /// Get max duty value.
163 ///
164 /// This value depends on the configured frequency and the timer's clock rate from RCC.
165 pub fn get_max_duty(&self) -> u16 {
166 self.inner.get_max_compare_value() + 1
167 }
123} 168}
diff --git a/embassy-stm32/src/lptim/timer/channel_direction.rs b/embassy-stm32/src/lptim/timer/channel_direction.rs
new file mode 100644
index 000000000..a38df63cd
--- /dev/null
+++ b/embassy-stm32/src/lptim/timer/channel_direction.rs
@@ -0,0 +1,18 @@
1use crate::pac::lptim::vals;
2
3/// Direction of a low-power timer channel
4pub enum ChannelDirection {
5 /// Use channel as a PWM output
6 OutputPwm,
7 /// Use channel as an input capture
8 InputCapture,
9}
10
11impl From<ChannelDirection> for vals::Ccsel {
12 fn from(direction: ChannelDirection) -> Self {
13 match direction {
14 ChannelDirection::OutputPwm => vals::Ccsel::OUTPUTCOMPARE,
15 ChannelDirection::InputCapture => vals::Ccsel::INPUTCAPTURE,
16 }
17 }
18}
diff --git a/embassy-stm32/src/lptim/timer.rs b/embassy-stm32/src/lptim/timer/mod.rs
index b354c1f61..e62fcab49 100644
--- a/embassy-stm32/src/lptim/timer.rs
+++ b/embassy-stm32/src/lptim/timer/mod.rs
@@ -1,117 +1,19 @@
1//! Low-level timer driver. 1//! Low-level timer driver.
2mod prescaler;
2 3
3use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; 4use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
4 5
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
11#[cfg(any(lptim_v2a, lptim_v2b))] 6#[cfg(any(lptim_v2a, lptim_v2b))]
12pub enum ChannelDirection { 7use super::channel::Channel;
13 /// Use channel as a PWM output
14 OutputPwm,
15 /// Use channel as an input capture
16 InputCapture,
17}
18
19#[cfg(any(lptim_v2a, lptim_v2b))] 8#[cfg(any(lptim_v2a, lptim_v2b))]
20impl From<ChannelDirection> for vals::Ccsel { 9mod channel_direction;
21 fn from(direction: ChannelDirection) -> Self { 10#[cfg(any(lptim_v2a, lptim_v2b))]
22 match direction { 11pub use channel_direction::ChannelDirection;
23 ChannelDirection::OutputPwm => vals::Ccsel::OUTPUTCOMPARE, 12use prescaler::Prescaler;
24 ChannelDirection::InputCapture => vals::Ccsel::INPUTCAPTURE,
25 }
26 }
27}
28
29enum Prescaler {
30 Div1,
31 Div2,
32 Div4,
33 Div8,
34 Div16,
35 Div32,
36 Div64,
37 Div128,
38}
39
40impl From<&Prescaler> for vals::Presc {
41 fn from(prescaler: &Prescaler) -> Self {
42 match prescaler {
43 Prescaler::Div1 => vals::Presc::DIV1,
44 Prescaler::Div2 => vals::Presc::DIV2,
45 Prescaler::Div4 => vals::Presc::DIV4,
46 Prescaler::Div8 => vals::Presc::DIV8,
47 Prescaler::Div16 => vals::Presc::DIV16,
48 Prescaler::Div32 => vals::Presc::DIV32,
49 Prescaler::Div64 => vals::Presc::DIV64,
50 Prescaler::Div128 => vals::Presc::DIV128,
51 }
52 }
53}
54
55impl From<vals::Presc> for Prescaler {
56 fn from(prescaler: vals::Presc) -> Self {
57 match prescaler {
58 vals::Presc::DIV1 => Prescaler::Div1,
59 vals::Presc::DIV2 => Prescaler::Div2,
60 vals::Presc::DIV4 => Prescaler::Div4,
61 vals::Presc::DIV8 => Prescaler::Div8,
62 vals::Presc::DIV16 => Prescaler::Div16,
63 vals::Presc::DIV32 => Prescaler::Div32,
64 vals::Presc::DIV64 => Prescaler::Div64,
65 vals::Presc::DIV128 => Prescaler::Div128,
66 }
67 }
68}
69
70impl From<&Prescaler> for u32 {
71 fn from(prescaler: &Prescaler) -> Self {
72 match prescaler {
73 Prescaler::Div1 => 1,
74 Prescaler::Div2 => 2,
75 Prescaler::Div4 => 4,
76 Prescaler::Div8 => 8,
77 Prescaler::Div16 => 16,
78 Prescaler::Div32 => 32,
79 Prescaler::Div64 => 64,
80 Prescaler::Div128 => 128,
81 }
82 }
83}
84
85impl From<u32> for Prescaler {
86 fn from(prescaler: u32) -> Self {
87 match prescaler {
88 1 => Prescaler::Div1,
89 2 => Prescaler::Div2,
90 4 => Prescaler::Div4,
91 8 => Prescaler::Div8,
92 16 => Prescaler::Div16,
93 32 => Prescaler::Div32,
94 64 => Prescaler::Div64,
95 128 => Prescaler::Div128,
96 _ => unreachable!(),
97 }
98 }
99}
100
101impl Prescaler {
102 pub fn from_ticks(ticks: u32) -> Self {
103 // We need to scale down to a 16-bit range
104 (ticks >> 16).next_power_of_two().into()
105 }
106
107 pub fn scale_down(&self, ticks: u32) -> u16 {
108 (ticks / u32::from(self)).try_into().unwrap()
109 }
110 13
111 pub fn scale_up(&self, ticks: u16) -> u32 { 14use super::Instance;
112 u32::from(self) * ticks as u32 15use crate::rcc;
113 } 16use crate::time::Hertz;
114}
115 17
116/// Low-level timer driver. 18/// Low-level timer driver.
117pub struct Timer<'d, T: Instance> { 19pub struct Timer<'d, T: Instance> {
@@ -148,14 +50,6 @@ impl<'d, T: Instance> Timer<'d, T> {
148 T::regs().cr().modify(|w| w.set_cntstrt(true)); 50 T::regs().cr().modify(|w| w.set_cntstrt(true));
149 } 51 }
150 52
151 /// Set channel direction.
152 #[cfg(any(lptim_v2a, lptim_v2b))]
153 pub fn set_channel_direction(&self, channel: Channel, direction: ChannelDirection) {
154 T::regs()
155 .ccmr(0)
156 .modify(|w| w.set_ccsel(channel.index(), direction.into()));
157 }
158
159 /// Set the frequency of how many times per second the timer counts up to the max value or down to 0. 53 /// Set the frequency of how many times per second the timer counts up to the max value or down to 0.
160 pub fn set_frequency(&self, frequency: Hertz) { 54 pub fn set_frequency(&self, frequency: Hertz) {
161 let f = frequency.0; 55 let f = frequency.0;
@@ -186,6 +80,14 @@ impl<'d, T: Instance> Timer<'d, T> {
186 T::frequency() 80 T::frequency()
187 } 81 }
188 82
83 /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC.
84 pub fn get_max_compare_value(&self) -> u16 {
85 T::regs().arr().read().arr()
86 }
87}
88
89#[cfg(any(lptim_v2a, lptim_v2b))]
90impl<'d, T: Instance> Timer<'d, T> {
189 /// Enable/disable a channel. 91 /// Enable/disable a channel.
190 pub fn enable_channel(&self, channel: Channel, enable: bool) { 92 pub fn enable_channel(&self, channel: Channel, enable: bool) {
191 T::regs().ccmr(0).modify(|w| { 93 T::regs().ccmr(0).modify(|w| {
@@ -208,8 +110,24 @@ impl<'d, T: Instance> Timer<'d, T> {
208 T::regs().ccr(channel.index()).read().ccr() 110 T::regs().ccr(channel.index()).read().ccr()
209 } 111 }
210 112
211 /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. 113 /// Set channel direction.
212 pub fn get_max_compare_value(&self) -> u16 { 114 #[cfg(any(lptim_v2a, lptim_v2b))]
213 T::regs().arr().read().arr() 115 pub fn set_channel_direction(&self, channel: Channel, direction: ChannelDirection) {
116 T::regs()
117 .ccmr(0)
118 .modify(|w| w.set_ccsel(channel.index(), direction.into()));
119 }
120}
121
122#[cfg(not(any(lptim_v2a, lptim_v2b)))]
123impl<'d, T: Instance> Timer<'d, T> {
124 /// Set compare value for a channel.
125 pub fn set_compare_value(&self, value: u16) {
126 T::regs().cmp().modify(|w| w.set_cmp(value));
127 }
128
129 /// Get compare value for a channel.
130 pub fn get_compare_value(&self) -> u16 {
131 T::regs().cmp().read().cmp()
214 } 132 }
215} 133}
diff --git a/embassy-stm32/src/lptim/timer/prescaler.rs b/embassy-stm32/src/lptim/timer/prescaler.rs
new file mode 100644
index 000000000..5d2326faf
--- /dev/null
+++ b/embassy-stm32/src/lptim/timer/prescaler.rs
@@ -0,0 +1,90 @@
1//! Low-level timer driver.
2
3use crate::pac::lptim::vals;
4
5pub enum Prescaler {
6 Div1,
7 Div2,
8 Div4,
9 Div8,
10 Div16,
11 Div32,
12 Div64,
13 Div128,
14}
15
16impl From<&Prescaler> for vals::Presc {
17 fn from(prescaler: &Prescaler) -> Self {
18 match prescaler {
19 Prescaler::Div1 => vals::Presc::DIV1,
20 Prescaler::Div2 => vals::Presc::DIV2,
21 Prescaler::Div4 => vals::Presc::DIV4,
22 Prescaler::Div8 => vals::Presc::DIV8,
23 Prescaler::Div16 => vals::Presc::DIV16,
24 Prescaler::Div32 => vals::Presc::DIV32,
25 Prescaler::Div64 => vals::Presc::DIV64,
26 Prescaler::Div128 => vals::Presc::DIV128,
27 }
28 }
29}
30
31impl From<vals::Presc> for Prescaler {
32 fn from(prescaler: vals::Presc) -> Self {
33 match prescaler {
34 vals::Presc::DIV1 => Prescaler::Div1,
35 vals::Presc::DIV2 => Prescaler::Div2,
36 vals::Presc::DIV4 => Prescaler::Div4,
37 vals::Presc::DIV8 => Prescaler::Div8,
38 vals::Presc::DIV16 => Prescaler::Div16,
39 vals::Presc::DIV32 => Prescaler::Div32,
40 vals::Presc::DIV64 => Prescaler::Div64,
41 vals::Presc::DIV128 => Prescaler::Div128,
42 }
43 }
44}
45
46impl From<&Prescaler> for u32 {
47 fn from(prescaler: &Prescaler) -> Self {
48 match prescaler {
49 Prescaler::Div1 => 1,
50 Prescaler::Div2 => 2,
51 Prescaler::Div4 => 4,
52 Prescaler::Div8 => 8,
53 Prescaler::Div16 => 16,
54 Prescaler::Div32 => 32,
55 Prescaler::Div64 => 64,
56 Prescaler::Div128 => 128,
57 }
58 }
59}
60
61impl From<u32> for Prescaler {
62 fn from(prescaler: u32) -> Self {
63 match prescaler {
64 1 => Prescaler::Div1,
65 2 => Prescaler::Div2,
66 4 => Prescaler::Div4,
67 8 => Prescaler::Div8,
68 16 => Prescaler::Div16,
69 32 => Prescaler::Div32,
70 64 => Prescaler::Div64,
71 128 => Prescaler::Div128,
72 _ => unreachable!(),
73 }
74 }
75}
76
77impl Prescaler {
78 pub fn from_ticks(ticks: u32) -> Self {
79 // We need to scale down to a 16-bit range
80 (ticks >> 16).next_power_of_two().into()
81 }
82
83 pub fn scale_down(&self, ticks: u32) -> u16 {
84 (ticks / u32::from(self)).try_into().unwrap()
85 }
86
87 pub fn scale_up(&self, ticks: u16) -> u32 {
88 u32::from(self) * ticks as u32
89 }
90}