Skip to content

Commit 7fde354

Browse files
authored
Merge pull request #3310 from Ecco/lptim
stm32: Add a first Lptim driver
2 parents 1ea29f1 + c739091 commit 7fde354

File tree

10 files changed

+491
-9
lines changed

10 files changed

+491
-9
lines changed

embassy-stm32/Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ rand_core = "0.6.3"
7272
sdio-host = "0.5.0"
7373
critical-section = "1.1"
7474
#stm32-metapac = { version = "15" }
75-
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5ef354f3e49f790e47f5c818f243459742c9b83b" }
75+
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ad00827345b4b758b2453082809d6e3b634b5364" }
7676

7777
vcell = "0.1.3"
7878
nb = "1.0.0"
@@ -99,7 +99,7 @@ proc-macro2 = "1.0.36"
9999
quote = "1.0.15"
100100

101101
#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
102-
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5ef354f3e49f790e47f5c818f243459742c9b83b", default-features = false, features = ["metadata"] }
102+
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ad00827345b4b758b2453082809d6e3b634b5364", default-features = false, features = ["metadata"] }
103103

104104
[features]
105105
default = ["rt"]

embassy-stm32/build.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,9 @@ fn main() {
10151015
(("hrtim", "CHE2"), quote!(crate::hrtim::ChannelEComplementaryPin)),
10161016
(("hrtim", "CHF1"), quote!(crate::hrtim::ChannelFPin)),
10171017
(("hrtim", "CHF2"), quote!(crate::hrtim::ChannelFComplementaryPin)),
1018+
(("lptim", "CH1"), quote!(crate::lptim::Channel1Pin)),
1019+
(("lptim", "CH2"), quote!(crate::lptim::Channel2Pin)),
1020+
(("lptim", "OUT"), quote!(crate::lptim::OutputPin)),
10181021
(("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)),
10191022
(("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)),
10201023
(("sdmmc", "D0"), quote!(crate::sdmmc::D0Pin)),

embassy-stm32/src/adc/mod.rs

+9-7
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#![allow(missing_docs)] // TODO
55
#![cfg_attr(adc_f3_v2, allow(unused))]
66

7-
#[cfg(not(adc_f3_v2))]
7+
#[cfg(not(any(adc_f3_v2, adc_u5)))]
88
#[cfg_attr(adc_f1, path = "f1.rs")]
99
#[cfg_attr(adc_f3, path = "f3.rs")]
1010
#[cfg_attr(adc_f3_v1_1, path = "f3_v1_1.rs")]
@@ -19,14 +19,16 @@ mod _version;
1919
use core::marker::PhantomData;
2020

2121
#[allow(unused)]
22-
#[cfg(not(adc_f3_v2))]
22+
#[cfg(not(any(adc_f3_v2, adc_u5)))]
2323
pub use _version::*;
2424
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
2525
use embassy_sync::waitqueue::AtomicWaker;
2626

27+
#[cfg(not(any(adc_u5)))]
2728
pub use crate::pac::adc::vals;
28-
#[cfg(not(any(adc_f1, adc_f3_v2)))]
29+
#[cfg(not(any(adc_f1, adc_f3_v2, adc_u5)))]
2930
pub use crate::pac::adc::vals::Res as Resolution;
31+
#[cfg(not(any(adc_u5)))]
3032
pub use crate::pac::adc::vals::SampleTime;
3133
use crate::peripherals;
3234

@@ -36,7 +38,7 @@ dma_trait!(RxDma, Instance);
3638
pub struct Adc<'d, T: Instance> {
3739
#[allow(unused)]
3840
adc: crate::PeripheralRef<'d, T>,
39-
#[cfg(not(any(adc_f3_v2, adc_f3_v1_1)))]
41+
#[cfg(not(any(adc_f3_v2, adc_f3_v1_1, adc_u5)))]
4042
sample_time: SampleTime,
4143
}
4244

@@ -57,7 +59,7 @@ impl State {
5759
trait SealedInstance {
5860
#[allow(unused)]
5961
fn regs() -> crate::pac::adc::Adc;
60-
#[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))]
62+
#[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0, adc_u5)))]
6163
#[allow(unused)]
6264
fn common_regs() -> crate::pac::adccommon::AdcCommon;
6365
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
@@ -163,7 +165,7 @@ foreach_adc!(
163165
crate::pac::$inst
164166
}
165167

166-
#[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))]
168+
#[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0, adc_u5)))]
167169
fn common_regs() -> crate::pac::adccommon::AdcCommon {
168170
return crate::pac::$common_inst
169171
}
@@ -200,7 +202,7 @@ macro_rules! impl_adc_pin {
200202
/// Get the maximum reading value for this resolution.
201203
///
202204
/// This is `2**n - 1`.
203-
#[cfg(not(any(adc_f1, adc_f3_v2)))]
205+
#[cfg(not(any(adc_f1, adc_f3_v2, adc_u5)))]
204206
pub const fn resolution_to_max_count(res: Resolution) -> u32 {
205207
match res {
206208
#[cfg(adc_v4)]

embassy-stm32/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ pub mod i2s;
8989
pub mod ipcc;
9090
#[cfg(feature = "low-power")]
9191
pub mod low_power;
92+
#[cfg(lptim)]
93+
pub mod lptim;
9294
#[cfg(ltdc)]
9395
pub mod ltdc;
9496
#[cfg(opamp)]

embassy-stm32/src/lptim/channel.rs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/// Timer channel.
2+
#[derive(Clone, Copy)]
3+
pub enum Channel {
4+
/// Channel 1.
5+
Ch1,
6+
/// Channel 2.
7+
Ch2,
8+
}
9+
10+
impl 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+
}

embassy-stm32/src/lptim/mod.rs

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//! Low-power timer (LPTIM)
2+
3+
pub mod pwm;
4+
pub mod timer;
5+
6+
use crate::rcc::RccPeripheral;
7+
8+
/// Timer channel.
9+
#[cfg(any(lptim_v2a, lptim_v2b))]
10+
mod channel;
11+
#[cfg(any(lptim_v2a, lptim_v2b))]
12+
pub use channel::Channel;
13+
14+
pin_trait!(OutputPin, BasicInstance);
15+
pin_trait!(Channel1Pin, BasicInstance);
16+
pin_trait!(Channel2Pin, BasicInstance);
17+
18+
pub(crate) trait SealedInstance: RccPeripheral {
19+
fn regs() -> crate::pac::lptim::Lptim;
20+
}
21+
pub(crate) trait SealedBasicInstance: RccPeripheral {}
22+
23+
/// LPTIM basic instance trait.
24+
#[allow(private_bounds)]
25+
pub trait BasicInstance: SealedBasicInstance + 'static {}
26+
27+
/// LPTIM instance trait.
28+
#[allow(private_bounds)]
29+
pub trait Instance: BasicInstance + SealedInstance + 'static {}
30+
31+
foreach_interrupt! {
32+
($inst:ident, lptim, LPTIM, GLOBAL, $irq:ident) => {
33+
impl SealedInstance for crate::peripherals::$inst {
34+
fn regs() -> crate::pac::lptim::Lptim {
35+
crate::pac::$inst
36+
}
37+
}
38+
impl SealedBasicInstance for crate::peripherals::$inst {
39+
}
40+
impl BasicInstance for crate::peripherals::$inst {}
41+
impl Instance for crate::peripherals::$inst {}
42+
};
43+
($inst:ident, lptim, LPTIM_BASIC, GLOBAL, $irq:ident) => {
44+
impl SealedBasicInstance for crate::peripherals::$inst {
45+
}
46+
impl BasicInstance for crate::peripherals::$inst {}
47+
};
48+
}

embassy-stm32/src/lptim/pwm.rs

+168
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
//! PWM driver.
2+
3+
use core::marker::PhantomData;
4+
5+
use embassy_hal_internal::{into_ref, PeripheralRef};
6+
7+
use super::timer::Timer;
8+
#[cfg(not(any(lptim_v2a, lptim_v2b)))]
9+
use super::OutputPin;
10+
#[cfg(any(lptim_v2a, lptim_v2b))]
11+
use super::{channel::Channel, timer::ChannelDirection, Channel1Pin, Channel2Pin};
12+
use super::{BasicInstance, Instance};
13+
use crate::gpio::{AfType, AnyPin, OutputType, Speed};
14+
use crate::time::Hertz;
15+
use crate::Peripheral;
16+
17+
/// Output marker type.
18+
pub enum Output {}
19+
/// Channel 1 marker type.
20+
pub enum Ch1 {}
21+
/// Channel 2 marker type.
22+
pub enum Ch2 {}
23+
24+
/// PWM pin wrapper.
25+
///
26+
/// This wraps a pin to make it usable with PWM.
27+
pub struct PwmPin<'d, T, C> {
28+
_pin: PeripheralRef<'d, AnyPin>,
29+
phantom: PhantomData<(T, C)>,
30+
}
31+
32+
macro_rules! channel_impl {
33+
($new_chx:ident, $channel:ident, $pin_trait:ident) => {
34+
impl<'d, T: BasicInstance> PwmPin<'d, T, $channel> {
35+
#[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")]
36+
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd) -> Self {
37+
into_ref!(pin);
38+
critical_section::with(|_| {
39+
pin.set_low();
40+
pin.set_as_af(
41+
pin.af_num(),
42+
AfType::output(OutputType::PushPull, Speed::VeryHigh),
43+
);
44+
});
45+
PwmPin {
46+
_pin: pin.map_into(),
47+
phantom: PhantomData,
48+
}
49+
}
50+
}
51+
};
52+
}
53+
54+
#[cfg(not(any(lptim_v2a, lptim_v2b)))]
55+
channel_impl!(new, Output, OutputPin);
56+
#[cfg(any(lptim_v2a, lptim_v2b))]
57+
channel_impl!(new_ch1, Ch1, Channel1Pin);
58+
#[cfg(any(lptim_v2a, lptim_v2b))]
59+
channel_impl!(new_ch2, Ch2, Channel2Pin);
60+
61+
/// PWM driver.
62+
pub struct Pwm<'d, T: Instance> {
63+
inner: Timer<'d, T>,
64+
}
65+
66+
#[cfg(not(any(lptim_v2a, lptim_v2b)))]
67+
impl<'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))]
92+
impl<'d, T: Instance> Pwm<'d, T> {
93+
/// Create a new PWM driver.
94+
pub fn new(
95+
tim: impl Peripheral<P = T> + 'd,
96+
_ch1_pin: Option<PwmPin<'d, T, Ch1>>,
97+
_ch2_pin: Option<PwmPin<'d, T, Ch2>>,
98+
freq: Hertz,
99+
) -> Self {
100+
Self::new_inner(tim, freq)
101+
}
102+
103+
/// Enable the given channel.
104+
pub fn enable(&mut self, channel: Channel) {
105+
self.inner.enable_channel(channel, true);
106+
}
107+
108+
/// Disable the given channel.
109+
pub fn disable(&mut self, channel: Channel) {
110+
self.inner.enable_channel(channel, false);
111+
}
112+
113+
/// Check whether given channel is enabled
114+
pub fn is_enabled(&self, channel: Channel) -> bool {
115+
self.inner.get_channel_enable_state(channel)
116+
}
117+
118+
/// Set the duty for a given channel.
119+
///
120+
/// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included.
121+
pub fn set_duty(&mut self, channel: Channel, duty: u16) {
122+
assert!(duty <= self.get_max_duty());
123+
self.inner.set_compare_value(channel, duty)
124+
}
125+
126+
/// Get the duty for a given channel.
127+
///
128+
/// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included.
129+
pub fn get_duty(&self, channel: Channel) -> u16 {
130+
self.inner.get_compare_value(channel)
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+
140+
impl<'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+
}
168+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
use crate::pac::lptim::vals;
2+
3+
/// Direction of a low-power timer channel
4+
pub enum ChannelDirection {
5+
/// Use channel as a PWM output
6+
OutputPwm,
7+
/// Use channel as an input capture
8+
InputCapture,
9+
}
10+
11+
impl 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+
}

0 commit comments

Comments
 (0)