mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-21 22:32:29 +00:00
First working draft of a lptim driver
This driver is able to PWM a pin
This commit is contained in:
parent
1ea29f1d2e
commit
a1c9a2e8bd
@ -1015,6 +1015,8 @@ fn main() {
|
||||
(("hrtim", "CHE2"), quote!(crate::hrtim::ChannelEComplementaryPin)),
|
||||
(("hrtim", "CHF1"), quote!(crate::hrtim::ChannelFPin)),
|
||||
(("hrtim", "CHF2"), quote!(crate::hrtim::ChannelFComplementaryPin)),
|
||||
(("lptim", "CH1"), quote!(crate::lptim::Channel1Pin)),
|
||||
(("lptim", "CH2"), quote!(crate::lptim::Channel1Pin)),
|
||||
(("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)),
|
||||
(("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)),
|
||||
(("sdmmc", "D0"), quote!(crate::sdmmc::D0Pin)),
|
||||
|
@ -89,6 +89,8 @@ pub mod i2s;
|
||||
pub mod ipcc;
|
||||
#[cfg(feature = "low-power")]
|
||||
pub mod low_power;
|
||||
#[cfg(lptim)]
|
||||
pub mod lptim;
|
||||
#[cfg(ltdc)]
|
||||
pub mod ltdc;
|
||||
#[cfg(opamp)]
|
||||
|
145
embassy-stm32/src/lptim/mod.rs
Normal file
145
embassy-stm32/src/lptim/mod.rs
Normal file
@ -0,0 +1,145 @@
|
||||
//! Low-power timer (LPTIM)
|
||||
|
||||
mod traits;
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
pub use traits::Instance;
|
||||
|
||||
use crate::gpio::{AfType, AnyPin, OutputType, Speed};
|
||||
// use crate::time::Hertz;
|
||||
use crate::{rcc, Peripheral};
|
||||
|
||||
/// LPTIM master instance.
|
||||
pub struct Master<T: Instance> {
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
/// LPTIM channel 1 instance.
|
||||
pub struct Ch1<T: Instance> {
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
/// LPTIM channel 2 instance.
|
||||
pub struct Ch2<T: Instance> {
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
trait SealedChannel<T: Instance> {
|
||||
fn raw() -> usize;
|
||||
}
|
||||
|
||||
/// channel instance trait.
|
||||
#[allow(private_bounds)]
|
||||
pub trait Channel<T: Instance>: SealedChannel<T> {}
|
||||
|
||||
/// LPTIM PWM pin.
|
||||
pub struct PwmPin<'d, T, C> {
|
||||
_pin: PeripheralRef<'d, AnyPin>,
|
||||
phantom: PhantomData<(T, C)>,
|
||||
}
|
||||
|
||||
macro_rules! channel_impl {
|
||||
($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident) => {
|
||||
impl<'d, T: Instance> PwmPin<'d, T, $channel<T>> {
|
||||
#[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")]
|
||||
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd) -> Self {
|
||||
into_ref!(pin);
|
||||
critical_section::with(|_| {
|
||||
pin.set_low();
|
||||
pin.set_as_af(
|
||||
pin.af_num(),
|
||||
AfType::output(OutputType::PushPull, Speed::VeryHigh),
|
||||
);
|
||||
});
|
||||
PwmPin {
|
||||
_pin: pin.map_into(),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Instance> SealedChannel<T> for $channel<T> {
|
||||
fn raw() -> usize {
|
||||
$ch_num
|
||||
}
|
||||
}
|
||||
impl<T: Instance> Channel<T> for $channel<T> {}
|
||||
};
|
||||
}
|
||||
|
||||
channel_impl!(new_ch1, Ch1, 0, Channel1Pin);
|
||||
channel_impl!(new_ch2, Ch2, 1, Channel2Pin);
|
||||
|
||||
/// Struct used to divide a high resolution timer into multiple channels
|
||||
pub struct Pwm<'d, T: Instance> {
|
||||
_inner: PeripheralRef<'d, T>,
|
||||
/// Master instance.
|
||||
pub master: Master<T>,
|
||||
/// Channel 1.
|
||||
pub ch_1: Ch1<T>,
|
||||
/// Channel 2.
|
||||
pub ch_2: Ch2<T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Pwm<'d, T> {
|
||||
/// Create a new LPTIM driver.
|
||||
///
|
||||
/// This splits the LPTIM into its constituent parts, which you can then use individually.
|
||||
pub fn new(
|
||||
tim: impl Peripheral<P = T> + 'd,
|
||||
_ch1: Option<PwmPin<'d, T, Ch1<T>>>,
|
||||
_ch2: Option<PwmPin<'d, T, Ch2<T>>>,
|
||||
) -> Self {
|
||||
Self::new_inner(tim)
|
||||
}
|
||||
|
||||
fn new_inner(tim: impl Peripheral<P = T> + 'd) -> Self {
|
||||
into_ref!(tim);
|
||||
|
||||
rcc::enable_and_reset::<T>();
|
||||
|
||||
T::regs().cr().modify(|w| w.set_enable(true));
|
||||
|
||||
// Set frequency. Should be configurable.
|
||||
// By default, 16MHz. We want 440Hz.
|
||||
// That's 36363 cycles
|
||||
T::regs().arr().write_value(stm32_metapac::lptim::regs::Arr(36363));
|
||||
|
||||
// Set duty cycle. Should be configurable. Should take care of channel too (only Ch1 now)
|
||||
T::regs().ccr(0).write_value(stm32_metapac::lptim::regs::Ccr(18181));
|
||||
|
||||
// Enable channel as PWM. Default state anyway. Implement later.
|
||||
// T::regs().ccmr().modify(|w| {
|
||||
// w.set_ccsel(0, 0);
|
||||
// w.set_ccsel(1, 0);
|
||||
// })
|
||||
|
||||
// Enable output on pins. Should care about the channels!
|
||||
T::regs().ccmr().modify(|w| {
|
||||
w.set_cce(0, true);
|
||||
w.set_cce(1, true);
|
||||
});
|
||||
|
||||
Self {
|
||||
_inner: tim,
|
||||
master: Master { phantom: PhantomData },
|
||||
ch_1: Ch1 { phantom: PhantomData },
|
||||
ch_2: Ch2 { phantom: PhantomData },
|
||||
}
|
||||
}
|
||||
|
||||
/// Start
|
||||
pub fn start(&mut self) {
|
||||
T::regs().cr().modify(|w| w.set_cntstrt(true));
|
||||
}
|
||||
|
||||
/// Stop
|
||||
pub fn stop(&mut self) {
|
||||
T::regs().cr().modify(|w| w.set_cntstrt(false));
|
||||
}
|
||||
}
|
||||
|
||||
pin_trait!(Channel1Pin, Instance);
|
||||
pin_trait!(Channel2Pin, Instance);
|
95
embassy-stm32/src/lptim/traits.rs
Normal file
95
embassy-stm32/src/lptim/traits.rs
Normal file
@ -0,0 +1,95 @@
|
||||
use crate::rcc::RccPeripheral;
|
||||
use crate::time::Hertz;
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) enum Prescaler {
|
||||
Div1 = 1,
|
||||
Div2 = 2,
|
||||
Div4 = 4,
|
||||
Div8 = 8,
|
||||
Div16 = 16,
|
||||
Div32 = 32,
|
||||
Div64 = 64,
|
||||
Div128 = 128,
|
||||
}
|
||||
|
||||
impl From<Prescaler> for u8 {
|
||||
fn from(val: Prescaler) -> Self {
|
||||
match val {
|
||||
Prescaler::Div1 => 0b000,
|
||||
Prescaler::Div2 => 0b001,
|
||||
Prescaler::Div4 => 0b010,
|
||||
Prescaler::Div8 => 0b011,
|
||||
Prescaler::Div16 => 0b100,
|
||||
Prescaler::Div32 => 0b101,
|
||||
Prescaler::Div64 => 0b110,
|
||||
Prescaler::Div128 => 0b111,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for Prescaler {
|
||||
fn from(val: u8) -> Self {
|
||||
match val {
|
||||
0b000 => Prescaler::Div1,
|
||||
0b001 => Prescaler::Div2,
|
||||
0b010 => Prescaler::Div4,
|
||||
0b011 => Prescaler::Div8,
|
||||
0b100 => Prescaler::Div16,
|
||||
0b101 => Prescaler::Div32,
|
||||
0b110 => Prescaler::Div64,
|
||||
0b111 => Prescaler::Div128,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Prescaler {
|
||||
pub fn compute_min_high_res(val: u32) -> Self {
|
||||
*[
|
||||
Prescaler::Div1,
|
||||
Prescaler::Div2,
|
||||
Prescaler::Div4,
|
||||
Prescaler::Div8,
|
||||
Prescaler::Div16,
|
||||
Prescaler::Div32,
|
||||
Prescaler::Div64,
|
||||
Prescaler::Div128,
|
||||
]
|
||||
.iter()
|
||||
.skip_while(|psc| **psc as u32 <= val)
|
||||
.next()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn compute_min_low_res(val: u32) -> Self {
|
||||
*[Prescaler::Div32, Prescaler::Div64, Prescaler::Div128]
|
||||
.iter()
|
||||
.skip_while(|psc| **psc as u32 <= val)
|
||||
.next()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait SealedInstance: RccPeripheral {
|
||||
fn regs() -> crate::pac::lptim::LptimAdv;
|
||||
}
|
||||
|
||||
/// LPTIM instance trait.
|
||||
#[allow(private_bounds)]
|
||||
pub trait Instance: SealedInstance + 'static {}
|
||||
|
||||
foreach_interrupt! {
|
||||
($inst:ident, lptim, LPTIM_ADV, UP, $irq:ident) => {
|
||||
impl SealedInstance for crate::peripherals::$inst {
|
||||
fn regs() -> crate::pac::lptim::LptimAdv {
|
||||
crate::pac::$inst
|
||||
}
|
||||
}
|
||||
|
||||
impl Instance for crate::peripherals::$inst {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user