diff options
| author | Romain Goyet <[email protected]> | 2024-08-29 16:18:43 -0400 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2024-09-11 01:18:52 +0200 |
| commit | a1c9a2e8bd83852d774255c82e71d2054c7c02e4 (patch) | |
| tree | d65579800bae63a45e43a16c56c9e9fd14d409b5 | |
| parent | 1ea29f1d2e94e85d87bbe45598a096d1f8da0f34 (diff) | |
First working draft of a lptim driver
This driver is able to PWM a pin
| -rw-r--r-- | embassy-stm32/build.rs | 2 | ||||
| -rw-r--r-- | embassy-stm32/src/lib.rs | 2 | ||||
| -rw-r--r-- | embassy-stm32/src/lptim/mod.rs | 145 | ||||
| -rw-r--r-- | embassy-stm32/src/lptim/traits.rs | 95 |
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; | |||
| 89 | pub mod ipcc; | 89 | pub mod ipcc; |
| 90 | #[cfg(feature = "low-power")] | 90 | #[cfg(feature = "low-power")] |
| 91 | pub mod low_power; | 91 | pub mod low_power; |
| 92 | #[cfg(lptim)] | ||
| 93 | pub mod lptim; | ||
| 92 | #[cfg(ltdc)] | 94 | #[cfg(ltdc)] |
| 93 | pub mod ltdc; | 95 | pub 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 | |||
| 3 | mod traits; | ||
| 4 | |||
| 5 | use core::marker::PhantomData; | ||
| 6 | |||
| 7 | use embassy_hal_internal::{into_ref, PeripheralRef}; | ||
| 8 | pub use traits::Instance; | ||
| 9 | |||
| 10 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | ||
| 11 | // use crate::time::Hertz; | ||
| 12 | use crate::{rcc, Peripheral}; | ||
| 13 | |||
| 14 | /// LPTIM master instance. | ||
| 15 | pub struct Master<T: Instance> { | ||
| 16 | phantom: PhantomData<T>, | ||
| 17 | } | ||
| 18 | |||
| 19 | /// LPTIM channel 1 instance. | ||
| 20 | pub struct Ch1<T: Instance> { | ||
| 21 | phantom: PhantomData<T>, | ||
| 22 | } | ||
| 23 | |||
| 24 | /// LPTIM channel 2 instance. | ||
| 25 | pub struct Ch2<T: Instance> { | ||
| 26 | phantom: PhantomData<T>, | ||
| 27 | } | ||
| 28 | |||
| 29 | trait SealedChannel<T: Instance> { | ||
| 30 | fn raw() -> usize; | ||
| 31 | } | ||
| 32 | |||
| 33 | /// channel instance trait. | ||
| 34 | #[allow(private_bounds)] | ||
| 35 | pub trait Channel<T: Instance>: SealedChannel<T> {} | ||
| 36 | |||
| 37 | /// LPTIM PWM pin. | ||
| 38 | pub struct PwmPin<'d, T, C> { | ||
| 39 | _pin: PeripheralRef<'d, AnyPin>, | ||
| 40 | phantom: PhantomData<(T, C)>, | ||
| 41 | } | ||
| 42 | |||
| 43 | macro_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 | |||
| 72 | channel_impl!(new_ch1, Ch1, 0, Channel1Pin); | ||
| 73 | channel_impl!(new_ch2, Ch2, 1, Channel2Pin); | ||
| 74 | |||
| 75 | /// Struct used to divide a high resolution timer into multiple channels | ||
| 76 | pub 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 | |||
| 86 | impl<'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 | |||
| 144 | pin_trait!(Channel1Pin, Instance); | ||
| 145 | pin_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 @@ | |||
| 1 | use crate::rcc::RccPeripheral; | ||
| 2 | use crate::time::Hertz; | ||
| 3 | |||
| 4 | #[repr(u8)] | ||
| 5 | #[derive(Clone, Copy)] | ||
| 6 | pub(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 | |||
| 17 | impl 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 | |||
| 32 | impl 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 | |||
| 48 | impl 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 | |||
| 75 | pub(crate) trait SealedInstance: RccPeripheral { | ||
| 76 | fn regs() -> crate::pac::lptim::LptimAdv; | ||
| 77 | } | ||
| 78 | |||
| 79 | /// LPTIM instance trait. | ||
| 80 | #[allow(private_bounds)] | ||
| 81 | pub trait Instance: SealedInstance + 'static {} | ||
| 82 | |||
| 83 | foreach_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 | } | ||
