aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRomain Goyet <[email protected]>2024-08-29 16:18:43 -0400
committerDario Nieuwenhuis <[email protected]>2024-09-11 01:18:52 +0200
commita1c9a2e8bd83852d774255c82e71d2054c7c02e4 (patch)
treed65579800bae63a45e43a16c56c9e9fd14d409b5
parent1ea29f1d2e94e85d87bbe45598a096d1f8da0f34 (diff)
First working draft of a lptim driver
This driver is able to PWM a pin
-rw-r--r--embassy-stm32/build.rs2
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--embassy-stm32/src/lptim/mod.rs145
-rw-r--r--embassy-stm32/src/lptim/traits.rs95
4 files changed, 244 insertions, 0 deletions
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 1984a1420..33985a3f4 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -1015,6 +1015,8 @@ fn main() {
1015 (("hrtim", "CHE2"), quote!(crate::hrtim::ChannelEComplementaryPin)), 1015 (("hrtim", "CHE2"), quote!(crate::hrtim::ChannelEComplementaryPin)),
1016 (("hrtim", "CHF1"), quote!(crate::hrtim::ChannelFPin)), 1016 (("hrtim", "CHF1"), quote!(crate::hrtim::ChannelFPin)),
1017 (("hrtim", "CHF2"), quote!(crate::hrtim::ChannelFComplementaryPin)), 1017 (("hrtim", "CHF2"), quote!(crate::hrtim::ChannelFComplementaryPin)),
1018 (("lptim", "CH1"), quote!(crate::lptim::Channel1Pin)),
1019 (("lptim", "CH2"), quote!(crate::lptim::Channel1Pin)),
1018 (("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)), 1020 (("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)),
1019 (("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)), 1021 (("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)),
1020 (("sdmmc", "D0"), quote!(crate::sdmmc::D0Pin)), 1022 (("sdmmc", "D0"), quote!(crate::sdmmc::D0Pin)),
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 98695e738..451f595e0 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -89,6 +89,8 @@ pub mod i2s;
89pub mod ipcc; 89pub mod ipcc;
90#[cfg(feature = "low-power")] 90#[cfg(feature = "low-power")]
91pub mod low_power; 91pub mod low_power;
92#[cfg(lptim)]
93pub mod lptim;
92#[cfg(ltdc)] 94#[cfg(ltdc)]
93pub mod ltdc; 95pub mod ltdc;
94#[cfg(opamp)] 96#[cfg(opamp)]
diff --git a/embassy-stm32/src/lptim/mod.rs b/embassy-stm32/src/lptim/mod.rs
new file mode 100644
index 000000000..3cf95149e
--- /dev/null
+++ b/embassy-stm32/src/lptim/mod.rs
@@ -0,0 +1,145 @@
1//! Low-power timer (LPTIM)
2
3mod traits;
4
5use core::marker::PhantomData;
6
7use embassy_hal_internal::{into_ref, PeripheralRef};
8pub use traits::Instance;
9
10use crate::gpio::{AfType, AnyPin, OutputType, Speed};
11// use crate::time::Hertz;
12use crate::{rcc, Peripheral};
13
14/// LPTIM master instance.
15pub struct Master<T: Instance> {
16 phantom: PhantomData<T>,
17}
18
19/// LPTIM channel 1 instance.
20pub struct Ch1<T: Instance> {
21 phantom: PhantomData<T>,
22}
23
24/// LPTIM channel 2 instance.
25pub struct Ch2<T: Instance> {
26 phantom: PhantomData<T>,
27}
28
29trait SealedChannel<T: Instance> {
30 fn raw() -> usize;
31}
32
33/// channel instance trait.
34#[allow(private_bounds)]
35pub trait Channel<T: Instance>: SealedChannel<T> {}
36
37/// LPTIM PWM pin.
38pub struct PwmPin<'d, T, C> {
39 _pin: PeripheralRef<'d, AnyPin>,
40 phantom: PhantomData<(T, C)>,
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 }
67 }
68 impl<T: Instance> Channel<T> for $channel<T> {}
69 };
70}
71
72channel_impl!(new_ch1, Ch1, 0, Channel1Pin);
73channel_impl!(new_ch2, Ch2, 1, Channel2Pin);
74
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 }
131 }
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}
143
144pin_trait!(Channel1Pin, Instance);
145pin_trait!(Channel2Pin, Instance);
diff --git a/embassy-stm32/src/lptim/traits.rs b/embassy-stm32/src/lptim/traits.rs
new file mode 100644
index 000000000..a8f890a78
--- /dev/null
+++ b/embassy-stm32/src/lptim/traits.rs
@@ -0,0 +1,95 @@
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}