diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index e615c6307..4eed6fe7d 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1091,6 +1091,38 @@ fn main() { (("octospi", "NCS"), quote!(crate::ospi::NSSPin)), (("octospi", "CLK"), quote!(crate::ospi::SckPin)), (("octospi", "NCLK"), quote!(crate::ospi::NckPin)), + (("tsc", "G1_IO1"), quote!(crate::tsc::G1IO1Pin)), + (("tsc", "G1_IO2"), quote!(crate::tsc::G1IO2Pin)), + (("tsc", "G1_IO3"), quote!(crate::tsc::G1IO3Pin)), + (("tsc", "G1_IO4"), quote!(crate::tsc::G1IO4Pin)), + (("tsc", "G2_IO1"), quote!(crate::tsc::G2IO1Pin)), + (("tsc", "G2_IO2"), quote!(crate::tsc::G2IO2Pin)), + (("tsc", "G2_IO3"), quote!(crate::tsc::G2IO3Pin)), + (("tsc", "G2_IO4"), quote!(crate::tsc::G2IO4Pin)), + (("tsc", "G3_IO1"), quote!(crate::tsc::G3IO1Pin)), + (("tsc", "G3_IO2"), quote!(crate::tsc::G3IO2Pin)), + (("tsc", "G3_IO3"), quote!(crate::tsc::G3IO3Pin)), + (("tsc", "G3_IO4"), quote!(crate::tsc::G3IO4Pin)), + (("tsc", "G4_IO1"), quote!(crate::tsc::G4IO1Pin)), + (("tsc", "G4_IO2"), quote!(crate::tsc::G4IO2Pin)), + (("tsc", "G4_IO3"), quote!(crate::tsc::G4IO3Pin)), + (("tsc", "G4_IO4"), quote!(crate::tsc::G4IO4Pin)), + (("tsc", "G5_IO1"), quote!(crate::tsc::G5IO1Pin)), + (("tsc", "G5_IO2"), quote!(crate::tsc::G5IO2Pin)), + (("tsc", "G5_IO3"), quote!(crate::tsc::G5IO3Pin)), + (("tsc", "G5_IO4"), quote!(crate::tsc::G5IO4Pin)), + (("tsc", "G6_IO1"), quote!(crate::tsc::G6IO1Pin)), + (("tsc", "G6_IO2"), quote!(crate::tsc::G6IO2Pin)), + (("tsc", "G6_IO3"), quote!(crate::tsc::G6IO3Pin)), + (("tsc", "G6_IO4"), quote!(crate::tsc::G6IO4Pin)), + (("tsc", "G7_IO1"), quote!(crate::tsc::G7IO1Pin)), + (("tsc", "G7_IO2"), quote!(crate::tsc::G7IO2Pin)), + (("tsc", "G7_IO3"), quote!(crate::tsc::G7IO3Pin)), + (("tsc", "G7_IO4"), quote!(crate::tsc::G7IO4Pin)), + (("tsc", "G8_IO1"), quote!(crate::tsc::G8IO1Pin)), + (("tsc", "G8_IO2"), quote!(crate::tsc::G8IO2Pin)), + (("tsc", "G8_IO3"), quote!(crate::tsc::G8IO3Pin)), + (("tsc", "G8_IO4"), quote!(crate::tsc::G8IO4Pin)), ].into(); for p in METADATA.peripherals { diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 029e01480..b4b9d5d12 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -105,6 +105,8 @@ pub mod sai; pub mod sdmmc; #[cfg(spi)] pub mod spi; +#[cfg(tsc)] +pub mod tsc; #[cfg(ucpd)] pub mod ucpd; #[cfg(uid)] diff --git a/embassy-stm32/src/tsc/enums.rs b/embassy-stm32/src/tsc/enums.rs new file mode 100644 index 000000000..0d34a43ec --- /dev/null +++ b/embassy-stm32/src/tsc/enums.rs @@ -0,0 +1,238 @@ +use core::ops::BitOr; + +/// Pin defines +#[allow(missing_docs)] +pub enum TscIOPin { + Group1Io1, + Group1Io2, + Group1Io3, + Group1Io4, + Group2Io1, + Group2Io2, + Group2Io3, + Group2Io4, + Group3Io1, + Group3Io2, + Group3Io3, + Group3Io4, + Group4Io1, + Group4Io2, + Group4Io3, + Group4Io4, + Group5Io1, + Group5Io2, + Group5Io3, + Group5Io4, + Group6Io1, + Group6Io2, + Group6Io3, + Group6Io4, + #[cfg(any(tsc_v2, tsc_v3))] + Group7Io1, + #[cfg(any(tsc_v2, tsc_v3))] + Group7Io2, + #[cfg(any(tsc_v2, tsc_v3))] + Group7Io3, + #[cfg(any(tsc_v2, tsc_v3))] + Group7Io4, + #[cfg(tsc_v3)] + Group8Io1, + #[cfg(tsc_v3)] + Group8Io2, + #[cfg(tsc_v3)] + Group8Io3, + #[cfg(tsc_v3)] + Group8Io4, +} + +impl BitOr<TscIOPin> for u32 { + type Output = u32; + fn bitor(self, rhs: TscIOPin) -> Self::Output { + let rhs: u32 = rhs.into(); + self | rhs + } +} + +impl BitOr<u32> for TscIOPin { + type Output = u32; + fn bitor(self, rhs: u32) -> Self::Output { + let val: u32 = self.into(); + val | rhs + } +} + +impl BitOr for TscIOPin { + type Output = u32; + fn bitor(self, rhs: Self) -> Self::Output { + let val: u32 = self.into(); + let rhs: u32 = rhs.into(); + val | rhs + } +} + +impl Into<u32> for TscIOPin { + fn into(self) -> u32 { + match self { + TscIOPin::Group1Io1 => 0x00000001, + TscIOPin::Group1Io2 => 0x00000002, + TscIOPin::Group1Io3 => 0x00000004, + TscIOPin::Group1Io4 => 0x00000008, + TscIOPin::Group2Io1 => 0x00000010, + TscIOPin::Group2Io2 => 0x00000020, + TscIOPin::Group2Io3 => 0x00000040, + TscIOPin::Group2Io4 => 0x00000080, + TscIOPin::Group3Io1 => 0x00000100, + TscIOPin::Group3Io2 => 0x00000200, + TscIOPin::Group3Io3 => 0x00000400, + TscIOPin::Group3Io4 => 0x00000800, + TscIOPin::Group4Io1 => 0x00001000, + TscIOPin::Group4Io2 => 0x00002000, + TscIOPin::Group4Io3 => 0x00004000, + TscIOPin::Group4Io4 => 0x00008000, + TscIOPin::Group5Io1 => 0x00010000, + TscIOPin::Group5Io2 => 0x00020000, + TscIOPin::Group5Io3 => 0x00040000, + TscIOPin::Group5Io4 => 0x00080000, + TscIOPin::Group6Io1 => 0x00100000, + TscIOPin::Group6Io2 => 0x00200000, + TscIOPin::Group6Io3 => 0x00400000, + TscIOPin::Group6Io4 => 0x00800000, + #[cfg(any(tsc_v2, tsc_v3))] + TscIOPin::Group7Io1 => 0x01000000, + #[cfg(any(tsc_v2, tsc_v3))] + TscIOPin::Group7Io2 => 0x02000000, + #[cfg(any(tsc_v2, tsc_v3))] + TscIOPin::Group7Io3 => 0x04000000, + #[cfg(any(tsc_v2, tsc_v3))] + TscIOPin::Group7Io4 => 0x08000000, + #[cfg(tsc_v3)] + TscIOPin::Group8Io1 => 0x10000000, + #[cfg(tsc_v3)] + TscIOPin::Group8Io2 => 0x20000000, + #[cfg(tsc_v3)] + TscIOPin::Group8Io3 => 0x40000000, + #[cfg(tsc_v3)] + TscIOPin::Group8Io4 => 0x80000000, + } + } +} + +/// Spread Spectrum Deviation +#[derive(Copy, Clone)] +pub struct SSDeviation(u8); +impl SSDeviation { + /// Create new deviation value, acceptable inputs are 1-128 + pub fn new(val: u8) -> Result<Self, ()> { + if val == 0 || val > 128 { + return Err(()); + } + Ok(Self(val - 1)) + } +} + +impl Into<u8> for SSDeviation { + fn into(self) -> u8 { + self.0 + } +} + +/// Charge transfer pulse cycles +#[allow(missing_docs)] +#[derive(Copy, Clone, PartialEq)] +pub enum ChargeTransferPulseCycle { + _1, + _2, + _3, + _4, + _5, + _6, + _7, + _8, + _9, + _10, + _11, + _12, + _13, + _14, + _15, + _16, +} + +impl Into<u8> for ChargeTransferPulseCycle { + fn into(self) -> u8 { + match self { + ChargeTransferPulseCycle::_1 => 0, + ChargeTransferPulseCycle::_2 => 1, + ChargeTransferPulseCycle::_3 => 2, + ChargeTransferPulseCycle::_4 => 3, + ChargeTransferPulseCycle::_5 => 4, + ChargeTransferPulseCycle::_6 => 5, + ChargeTransferPulseCycle::_7 => 6, + ChargeTransferPulseCycle::_8 => 7, + ChargeTransferPulseCycle::_9 => 8, + ChargeTransferPulseCycle::_10 => 9, + ChargeTransferPulseCycle::_11 => 10, + ChargeTransferPulseCycle::_12 => 11, + ChargeTransferPulseCycle::_13 => 12, + ChargeTransferPulseCycle::_14 => 13, + ChargeTransferPulseCycle::_15 => 14, + ChargeTransferPulseCycle::_16 => 15, + } + } +} + +/// Prescaler divider +#[allow(missing_docs)] +#[derive(Copy, Clone, PartialEq)] +pub enum PGPrescalerDivider { + _1, + _2, + _4, + _8, + _16, + _32, + _64, + _128, +} + +impl Into<u8> for PGPrescalerDivider { + fn into(self) -> u8 { + match self { + PGPrescalerDivider::_1 => 0, + PGPrescalerDivider::_2 => 1, + PGPrescalerDivider::_4 => 2, + PGPrescalerDivider::_8 => 3, + PGPrescalerDivider::_16 => 4, + PGPrescalerDivider::_32 => 5, + PGPrescalerDivider::_64 => 6, + PGPrescalerDivider::_128 => 7, + } + } +} + +/// Max count +#[allow(missing_docs)] +#[derive(Copy, Clone)] +pub enum MaxCount { + _255, + _511, + _1023, + _2047, + _4095, + _8191, + _16383, +} + +impl Into<u8> for MaxCount { + fn into(self) -> u8 { + match self { + MaxCount::_255 => 0, + MaxCount::_511 => 1, + MaxCount::_1023 => 2, + MaxCount::_2047 => 3, + MaxCount::_4095 => 4, + MaxCount::_8191 => 5, + MaxCount::_16383 => 6, + } + } +} diff --git a/embassy-stm32/src/tsc/mod.rs b/embassy-stm32/src/tsc/mod.rs new file mode 100644 index 000000000..bf583f04c --- /dev/null +++ b/embassy-stm32/src/tsc/mod.rs @@ -0,0 +1,936 @@ +//! TSC Peripheral Interface +//! +//! +//! # Example (stm32) +//! ``` rust, ignore +//! +//! let mut device_config = embassy_stm32::Config::default(); +//! { +//! device_config.rcc.mux = ClockSrc::MSI(Msirange::RANGE_4MHZ); +//! } +//! +//! let context = embassy_stm32::init(device_config); +//! +//! let config = tsc::Config { +//! ct_pulse_high_length: ChargeTransferPulseCycle::_2, +//! ct_pulse_low_length: ChargeTransferPulseCycle::_2, +//! spread_spectrum: false, +//! spread_spectrum_deviation: SSDeviation::new(2).unwrap(), +//! spread_spectrum_prescaler: false, +//! pulse_generator_prescaler: PGPrescalerDivider::_4, +//! max_count_value: MaxCount::_8191, +//! io_default_mode: false, +//! synchro_pin_polarity: false, +//! acquisition_mode: false, +//! max_count_interrupt: false, +//! channel_ios: TscIOPin::Group2Io2 | TscIOPin::Group7Io3, +//! shield_ios: TscIOPin::Group1Io3.into(), +//! sampling_ios: TscIOPin::Group1Io2 | TscIOPin::Group2Io1 | TscIOPin::Group7Io2, +//! }; +//! +//! let mut g1: PinGroup<embassy_stm32::peripherals::TSC, G1> = PinGroup::new(); +//! g1.set_io2(context.PB13, PinType::Sample); +//! g1.set_io3(context.PB14, PinType::Shield); +//! +//! let mut g2: PinGroup<embassy_stm32::peripherals::TSC, G2> = PinGroup::new(); +//! g2.set_io1(context.PB4, PinType::Sample); +//! g2.set_io2(context.PB5, PinType::Channel); +//! +//! let mut g7: PinGroup<embassy_stm32::peripherals::TSC, G7> = PinGroup::new(); +//! g7.set_io2(context.PE3, PinType::Sample); +//! g7.set_io3(context.PE4, PinType::Channel); +//! +//! let mut touch_controller = tsc::Tsc::new( +//! context.TSC, +//! Some(g1), +//! Some(g2), +//! None, +//! None, +//! None, +//! None, +//! Some(g7), +//! None, +//! config, +//! ); +//! +//! touch_controller.discharge_io(true); +//! Timer::after_millis(1).await; +//! +//! touch_controller.start(); +//! +//! ``` + +#![macro_use] + +/// Enums defined for peripheral parameters +pub mod enums; + +use core::marker::PhantomData; + +use embassy_hal_internal::{into_ref, PeripheralRef}; +pub use enums::*; + +use crate::gpio::{AFType, AnyPin}; +use crate::pac::tsc::Tsc as Regs; +use crate::rcc::RccPeripheral; +use crate::{peripherals, Peripheral}; + +#[cfg(tsc_v1)] +const TSC_NUM_GROUPS: u32 = 6; +#[cfg(tsc_v2)] +const TSC_NUM_GROUPS: u32 = 7; +#[cfg(tsc_v3)] +const TSC_NUM_GROUPS: u32 = 8; + +/// Error type defined for TSC +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// Test error for TSC + Test, +} + +/// Pin type definition to control IO parameters +pub enum PinType { + /// Sensing channel pin connected to an electrode + Channel, + /// Sampling capacitor pin, one required for every pin group + Sample, + /// Shield pin connected to capacitive sensing shield + Shield, +} + +/// Peripheral state +#[derive(PartialEq, Clone, Copy)] +pub enum State { + /// Peripheral is being setup or reconfigured + Reset, + /// Ready to start acquisition + Ready, + /// In process of sensor acquisition + Busy, + /// Error occured during acquisition + Error, +} + +/// Individual group status checked after acquisition reported as complete +/// For groups with multiple channel pins, may take longer because acquisitions +/// are done sequentially. Check this status before pulling count for each +/// sampled channel +#[derive(PartialEq)] +pub enum GroupStatus { + /// Acquisition for channel still in progress + Ongoing, + /// Acquisition either not started or complete + Complete, +} + +/// Group identifier used to interrogate status +#[allow(missing_docs)] +pub enum Group { + One, + Two, + Three, + Four, + Five, + Six, + #[cfg(any(tsc_v2, tsc_v3))] + Seven, + #[cfg(tsc_v3)] + Eight, +} + +impl Into<usize> for Group { + fn into(self) -> usize { + match self { + Group::One => 0, + Group::Two => 1, + Group::Three => 2, + Group::Four => 3, + Group::Five => 4, + Group::Six => 5, + #[cfg(any(tsc_v2, tsc_v3))] + Group::Seven => 6, + #[cfg(tsc_v3)] + Group::Eight => 7, + } + } +} + +/// Peripheral configuration +#[derive(Clone, Copy)] +pub struct Config { + /// Duration of high state of the charge transfer pulse + pub ct_pulse_high_length: ChargeTransferPulseCycle, + /// Duration of the low state of the charge transfer pulse + pub ct_pulse_low_length: ChargeTransferPulseCycle, + /// Enable/disable of spread spectrum feature + pub spread_spectrum: bool, + /// Adds variable number of periods of the SS clk to pulse high state + pub spread_spectrum_deviation: SSDeviation, + /// Selects AHB clock divider used to generate SS clk + pub spread_spectrum_prescaler: bool, + /// Selects AHB clock divider used to generate pulse generator clk + pub pulse_generator_prescaler: PGPrescalerDivider, + /// Maximum number of charge tranfer pulses that can be generated before error + pub max_count_value: MaxCount, + /// Defines config of all IOs when no ongoing acquisition + pub io_default_mode: bool, + /// Polarity of sync input pin + pub synchro_pin_polarity: bool, + /// Acquisition starts when start bit is set or with sync pin input + pub acquisition_mode: bool, + /// Enable max count interrupt + pub max_count_interrupt: bool, + /// Channel IO mask + pub channel_ios: u32, + /// Shield IO mask + pub shield_ios: u32, + /// Sampling IO mask + pub sampling_ios: u32, +} + +impl Default for Config { + fn default() -> Self { + Self { + ct_pulse_high_length: ChargeTransferPulseCycle::_1, + ct_pulse_low_length: ChargeTransferPulseCycle::_1, + spread_spectrum: false, + spread_spectrum_deviation: SSDeviation::new(1).unwrap(), + spread_spectrum_prescaler: false, + pulse_generator_prescaler: PGPrescalerDivider::_1, + max_count_value: MaxCount::_255, + io_default_mode: false, + synchro_pin_polarity: false, + acquisition_mode: false, + max_count_interrupt: false, + channel_ios: 0, + shield_ios: 0, + sampling_ios: 0, + } + } +} + +/// Pin struct that maintains usage +#[allow(missing_docs)] +pub struct TscPin<'d, T, C> { + _pin: PeripheralRef<'d, AnyPin>, + role: PinType, + phantom: PhantomData<(T, C)>, +} + +enum GroupError { + NoSample, + ChannelShield, +} + +/// Pin group definition +/// Pins are organized into groups of four IOs, all groups with a +/// sampling channel must also have a sampling capacitor channel. +#[allow(missing_docs)] +#[derive(Default)] +pub struct PinGroup<'d, T, C> { + d1: Option<TscPin<'d, T, C>>, + d2: Option<TscPin<'d, T, C>>, + d3: Option<TscPin<'d, T, C>>, + d4: Option<TscPin<'d, T, C>>, +} + +impl<'d, T: Instance, C> PinGroup<'d, T, C> { + /// Create new sensing group + pub fn new() -> Self { + Self { + d1: None, + d2: None, + d3: None, + d4: None, + } + } + + fn contains_shield(&self) -> bool { + let mut shield_count = 0; + + if let Some(pin) = &self.d1 { + if let PinType::Shield = pin.role { + shield_count += 1; + } + } + + if let Some(pin) = &self.d2 { + if let PinType::Shield = pin.role { + shield_count += 1; + } + } + + if let Some(pin) = &self.d3 { + if let PinType::Shield = pin.role { + shield_count += 1; + } + } + + if let Some(pin) = &self.d4 { + if let PinType::Shield = pin.role { + shield_count += 1; + } + } + + shield_count == 1 + } + + fn check_group(&self) -> Result<(), GroupError> { + let mut channel_count = 0; + let mut shield_count = 0; + let mut sample_count = 0; + if let Some(pin) = &self.d1 { + match pin.role { + PinType::Channel => { + channel_count += 1; + } + PinType::Shield => { + shield_count += 1; + } + PinType::Sample => { + sample_count += 1; + } + } + } + + if let Some(pin) = &self.d2 { + match pin.role { + PinType::Channel => { + channel_count += 1; + } + PinType::Shield => { + shield_count += 1; + } + PinType::Sample => { + sample_count += 1; + } + } + } + + if let Some(pin) = &self.d3 { + match pin.role { + PinType::Channel => { + channel_count += 1; + } + PinType::Shield => { + shield_count += 1; + } + PinType::Sample => { + sample_count += 1; + } + } + } + + if let Some(pin) = &self.d4 { + match pin.role { + PinType::Channel => { + channel_count += 1; + } + PinType::Shield => { + shield_count += 1; + } + PinType::Sample => { + sample_count += 1; + } + } + } + + // Every group requires one sampling capacitor + if sample_count != 1 { + return Err(GroupError::NoSample); + } + + // Each group must have at least one shield or channel IO + if shield_count == 0 && channel_count == 0 { + return Err(GroupError::ChannelShield); + } + + // Any group can either contain channel ios or a shield IO + if shield_count != 0 && channel_count != 0 { + return Err(GroupError::ChannelShield); + } + + // No more than one shield IO is allow per group and amongst all groups + if shield_count > 1 { + return Err(GroupError::ChannelShield); + } + + Ok(()) + } +} + +macro_rules! group_impl { + ($group:ident, $trait1:ident, $trait2:ident, $trait3:ident, $trait4:ident) => { + impl<'d, T: Instance> PinGroup<'d, T, $group> { + #[doc = concat!("Create a new pin1 for ", stringify!($group), " TSC group instance.")] + pub fn set_io1(&mut self, pin: impl Peripheral<P = impl $trait1<T>> + 'd, role: PinType) { + into_ref!(pin); + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af( + pin.af_num(), + match role { + PinType::Channel => AFType::OutputPushPull, + PinType::Sample => AFType::OutputOpenDrain, + PinType::Shield => AFType::OutputPushPull, + }, + ); + self.d1 = Some(TscPin { + _pin: pin.map_into(), + role: role, + phantom: PhantomData, + }) + }) + } + + #[doc = concat!("Create a new pin2 for ", stringify!($group), " TSC group instance.")] + pub fn set_io2(&mut self, pin: impl Peripheral<P = impl $trait2<T>> + 'd, role: PinType) { + into_ref!(pin); + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af( + pin.af_num(), + match role { + PinType::Channel => AFType::OutputPushPull, + PinType::Sample => AFType::OutputOpenDrain, + PinType::Shield => AFType::OutputPushPull, + }, + ); + self.d2 = Some(TscPin { + _pin: pin.map_into(), + role: role, + phantom: PhantomData, + }) + }) + } + + #[doc = concat!("Create a new pin3 for ", stringify!($group), " TSC group instance.")] + pub fn set_io3(&mut self, pin: impl Peripheral<P = impl $trait3<T>> + 'd, role: PinType) { + into_ref!(pin); + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af( + pin.af_num(), + match role { + PinType::Channel => AFType::OutputPushPull, + PinType::Sample => AFType::OutputOpenDrain, + PinType::Shield => AFType::OutputPushPull, + }, + ); + self.d3 = Some(TscPin { + _pin: pin.map_into(), + role: role, + phantom: PhantomData, + }) + }) + } + + #[doc = concat!("Create a new pin4 for ", stringify!($group), " TSC group instance.")] + pub fn set_io4(&mut self, pin: impl Peripheral<P = impl $trait4<T>> + 'd, role: PinType) { + into_ref!(pin); + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af( + pin.af_num(), + match role { + PinType::Channel => AFType::OutputPushPull, + PinType::Sample => AFType::OutputOpenDrain, + PinType::Shield => AFType::OutputPushPull, + }, + ); + self.d4 = Some(TscPin { + _pin: pin.map_into(), + role: role, + phantom: PhantomData, + }) + }) + } + } + }; +} + +group_impl!(G1, G1IO1Pin, G1IO2Pin, G1IO3Pin, G1IO4Pin); +group_impl!(G2, G2IO1Pin, G2IO2Pin, G2IO3Pin, G2IO4Pin); +group_impl!(G3, G3IO1Pin, G3IO2Pin, G3IO3Pin, G3IO4Pin); +group_impl!(G4, G4IO1Pin, G4IO2Pin, G4IO3Pin, G4IO4Pin); +group_impl!(G5, G5IO1Pin, G5IO2Pin, G5IO3Pin, G5IO4Pin); +group_impl!(G6, G6IO1Pin, G6IO2Pin, G6IO3Pin, G6IO4Pin); +group_impl!(G7, G7IO1Pin, G7IO2Pin, G7IO3Pin, G7IO4Pin); +group_impl!(G8, G8IO1Pin, G8IO2Pin, G8IO3Pin, G8IO4Pin); + +/// Group 1 marker type. +pub enum G1 {} +/// Group 2 marker type. +pub enum G2 {} +/// Group 3 marker type. +pub enum G3 {} +/// Group 4 marker type. +pub enum G4 {} +/// Group 5 marker type. +pub enum G5 {} +/// Group 6 marker type. +pub enum G6 {} +/// Group 7 marker type. +pub enum G7 {} +/// Group 8 marker type. +pub enum G8 {} + +/// TSC driver +pub struct Tsc<'d, T: Instance> { + _peri: PeripheralRef<'d, T>, + _g1: Option<PinGroup<'d, T, G1>>, + _g2: Option<PinGroup<'d, T, G2>>, + _g3: Option<PinGroup<'d, T, G3>>, + _g4: Option<PinGroup<'d, T, G4>>, + _g5: Option<PinGroup<'d, T, G5>>, + _g6: Option<PinGroup<'d, T, G6>>, + #[cfg(any(tsc_v2, tsc_v3))] + _g7: Option<PinGroup<'d, T, G7>>, + #[cfg(tsc_v3)] + _g8: Option<PinGroup<'d, T, G8>>, + state: State, + config: Config, +} + +impl<'d, T: Instance> Tsc<'d, T> { + /// Create new TSC driver + pub fn new( + peri: impl Peripheral<P = T> + 'd, + g1: Option<PinGroup<'d, T, G1>>, + g2: Option<PinGroup<'d, T, G2>>, + g3: Option<PinGroup<'d, T, G3>>, + g4: Option<PinGroup<'d, T, G4>>, + g5: Option<PinGroup<'d, T, G5>>, + g6: Option<PinGroup<'d, T, G6>>, + #[cfg(any(tsc_v2, tsc_v3))] g7: Option<PinGroup<'d, T, G7>>, + #[cfg(tsc_v3)] g8: Option<PinGroup<'d, T, G8>>, + config: Config, + ) -> Self { + // Need to check valid pin configuration input + let g1 = g1.filter(|b| b.check_group().is_ok()); + let g2 = g2.filter(|b| b.check_group().is_ok()); + let g3 = g3.filter(|b| b.check_group().is_ok()); + let g4 = g4.filter(|b| b.check_group().is_ok()); + let g5 = g5.filter(|b| b.check_group().is_ok()); + let g6 = g6.filter(|b| b.check_group().is_ok()); + #[cfg(any(tsc_v2, tsc_v3))] + let g7 = g7.filter(|b| b.check_group().is_ok()); + #[cfg(tsc_v3)] + let g8 = g8.filter(|b| b.check_group().is_ok()); + + match Self::check_shields( + &g1, + &g2, + &g3, + &g4, + &g5, + &g6, + #[cfg(any(tsc_v2, tsc_v3))] + &g7, + #[cfg(tsc_v3)] + &g8, + ) { + Ok(()) => Self::new_inner( + peri, + g1, + g2, + g3, + g4, + g5, + g6, + #[cfg(any(tsc_v2, tsc_v3))] + g7, + #[cfg(tsc_v3)] + g8, + config, + ), + Err(_) => Self::new_inner( + peri, + None, + None, + None, + None, + None, + None, + #[cfg(any(tsc_v2, tsc_v3))] + None, + #[cfg(tsc_v3)] + None, + config, + ), + } + } + + fn check_shields( + g1: &Option<PinGroup<'d, T, G1>>, + g2: &Option<PinGroup<'d, T, G2>>, + g3: &Option<PinGroup<'d, T, G3>>, + g4: &Option<PinGroup<'d, T, G4>>, + g5: &Option<PinGroup<'d, T, G5>>, + g6: &Option<PinGroup<'d, T, G6>>, + #[cfg(any(tsc_v2, tsc_v3))] g7: &Option<PinGroup<'d, T, G7>>, + #[cfg(tsc_v3)] g8: &Option<PinGroup<'d, T, G8>>, + ) -> Result<(), GroupError> { + let mut shield_count = 0; + + if let Some(pin_group) = g1 { + if pin_group.contains_shield() { + shield_count += 1; + } + }; + if let Some(pin_group) = g2 { + if pin_group.contains_shield() { + shield_count += 1; + } + }; + if let Some(pin_group) = g3 { + if pin_group.contains_shield() { + shield_count += 1; + } + }; + if let Some(pin_group) = g4 { + if pin_group.contains_shield() { + shield_count += 1; + } + }; + if let Some(pin_group) = g5 { + if pin_group.contains_shield() { + shield_count += 1; + } + }; + if let Some(pin_group) = g6 { + if pin_group.contains_shield() { + shield_count += 1; + } + }; + #[cfg(any(tsc_v2, tsc_v3))] + if let Some(pin_group) = g7 { + if pin_group.contains_shield() { + shield_count += 1; + } + }; + #[cfg(tsc_v3)] + if let Some(pin_group) = g8 { + if pin_group.contains_shield() { + shield_count += 1; + } + }; + + if shield_count > 1 { + return Err(GroupError::ChannelShield); + } + + Ok(()) + } + + fn extract_groups(io_mask: u32) -> u32 { + let mut groups: u32 = 0; + for idx in 0..TSC_NUM_GROUPS { + if io_mask & (0x0F << idx * 4) != 0 { + groups |= 1 << idx + } + } + groups + } + + fn new_inner( + peri: impl Peripheral<P = T> + 'd, + g1: Option<PinGroup<'d, T, G1>>, + g2: Option<PinGroup<'d, T, G2>>, + g3: Option<PinGroup<'d, T, G3>>, + g4: Option<PinGroup<'d, T, G4>>, + g5: Option<PinGroup<'d, T, G5>>, + g6: Option<PinGroup<'d, T, G6>>, + #[cfg(any(tsc_v2, tsc_v3))] g7: Option<PinGroup<'d, T, G7>>, + #[cfg(tsc_v3)] g8: Option<PinGroup<'d, T, G8>>, + config: Config, + ) -> Self { + into_ref!(peri); + + T::enable_and_reset(); + + T::REGS.cr().modify(|w| { + w.set_tsce(true); + w.set_ctph(config.ct_pulse_high_length.into()); + w.set_ctpl(config.ct_pulse_low_length.into()); + w.set_sse(config.spread_spectrum); + // Prevent invalid configuration for pulse generator prescaler + if config.ct_pulse_low_length == ChargeTransferPulseCycle::_1 + && (config.pulse_generator_prescaler == PGPrescalerDivider::_1 + || config.pulse_generator_prescaler == PGPrescalerDivider::_2) + { + w.set_pgpsc(PGPrescalerDivider::_4.into()); + } else if config.ct_pulse_low_length == ChargeTransferPulseCycle::_2 + && config.pulse_generator_prescaler == PGPrescalerDivider::_1 + { + w.set_pgpsc(PGPrescalerDivider::_2.into()); + } else { + w.set_pgpsc(config.pulse_generator_prescaler.into()); + } + w.set_ssd(config.spread_spectrum_deviation.into()); + w.set_sspsc(config.spread_spectrum_prescaler); + + w.set_mcv(config.max_count_value.into()); + w.set_syncpol(config.synchro_pin_polarity); + w.set_am(config.acquisition_mode); + }); + + // Set IO configuration + // Disable Schmitt trigger hysteresis on all used TSC IOs + T::REGS + .iohcr() + .write(|w| w.0 = !(config.channel_ios | config.shield_ios | config.sampling_ios)); + + // Set channel and shield IOs + T::REGS.ioccr().write(|w| w.0 = config.channel_ios | config.shield_ios); + + // Set sampling IOs + T::REGS.ioscr().write(|w| w.0 = config.sampling_ios); + + // Set the groups to be acquired + T::REGS + .iogcsr() + .write(|w| w.0 = Self::extract_groups(config.channel_ios)); + + // Disable interrupts + T::REGS.ier().modify(|w| { + w.set_eoaie(false); + w.set_mceie(false); + }); + + // Clear flags + T::REGS.icr().modify(|w| { + w.set_eoaic(true); + w.set_mceic(true); + }); + + Self { + _peri: peri, + _g1: g1, + _g2: g2, + _g3: g3, + _g4: g4, + _g5: g5, + _g6: g6, + #[cfg(any(tsc_v2, tsc_v3))] + _g7: g7, + #[cfg(tsc_v3)] + _g8: g8, + state: State::Ready, + config, + } + } + + /// Start charge transfer acquisition + pub fn start(&mut self) { + self.state = State::Busy; + + // Disable interrupts + T::REGS.ier().modify(|w| { + w.set_eoaie(false); + w.set_mceie(false); + }); + + // Clear flags + T::REGS.icr().modify(|w| { + w.set_eoaic(true); + w.set_mceic(true); + }); + + // Set the touch sensing IOs not acquired to the default mode + T::REGS.cr().modify(|w| { + w.set_iodef(self.config.io_default_mode); + }); + + // Start the acquisition + T::REGS.cr().modify(|w| { + w.set_start(true); + }); + } + + /// Start charge transfer acquisition with interrupts enabled + pub fn start_it(&mut self) { + self.state = State::Busy; + + // Enable interrupts + T::REGS.ier().modify(|w| { + w.set_eoaie(true); + w.set_mceie(self.config.max_count_interrupt); + }); + + // Clear flags + T::REGS.icr().modify(|w| { + w.set_eoaic(true); + w.set_mceic(true); + }); + + // Set the touch sensing IOs not acquired to the default mode + T::REGS.cr().modify(|w| { + w.set_iodef(self.config.io_default_mode); + }); + + // Start the acquisition + T::REGS.cr().modify(|w| { + w.set_start(true); + }); + } + + /// Stop charge transfer acquisition + pub fn stop(&mut self) { + T::REGS.cr().modify(|w| { + w.set_start(false); + }); + + // Set the touch sensing IOs in low power mode + T::REGS.cr().modify(|w| { + w.set_iodef(false); + }); + + // Clear flags + T::REGS.icr().modify(|w| { + w.set_eoaic(true); + w.set_mceic(true); + }); + + self.state = State::Ready; + } + + /// Stop charge transfer acquisition and clear interrupts + pub fn stop_it(&mut self) { + T::REGS.cr().modify(|w| { + w.set_start(false); + }); + + // Set the touch sensing IOs in low power mode + T::REGS.cr().modify(|w| { + w.set_iodef(false); + }); + + // Disable interrupts + T::REGS.ier().modify(|w| { + w.set_eoaie(false); + w.set_mceie(false); + }); + + // Clear flags + T::REGS.icr().modify(|w| { + w.set_eoaic(true); + w.set_mceic(true); + }); + + self.state = State::Ready; + } + + /// Wait for end of acquisition + pub fn poll_for_acquisition(&mut self) { + while self.get_state() == State::Busy {} + } + + /// Get current state of acquisition + pub fn get_state(&mut self) -> State { + if self.state == State::Busy { + if T::REGS.isr().read().eoaf() { + if T::REGS.isr().read().mcef() { + self.state = State::Error + } else { + self.state = State::Ready + } + } + } + self.state + } + + /// Get the individual group status to check acquisition complete + pub fn group_get_status(&mut self, index: Group) -> GroupStatus { + // Status bits are set by hardware when the acquisition on the corresponding + // enabled analog IO group is complete, cleared when new acquisition is started + let status = match index { + Group::One => T::REGS.iogcsr().read().g1s(), + Group::Two => T::REGS.iogcsr().read().g2s(), + Group::Three => T::REGS.iogcsr().read().g3s(), + Group::Four => T::REGS.iogcsr().read().g4s(), + Group::Five => T::REGS.iogcsr().read().g5s(), + Group::Six => T::REGS.iogcsr().read().g6s(), + #[cfg(any(tsc_v2, tsc_v3))] + Group::Seven => T::REGS.iogcsr().read().g7s(), + #[cfg(tsc_v3)] + Group::Eight => T::REGS.iogcsr().read().g8s(), + }; + match status { + true => GroupStatus::Complete, + false => GroupStatus::Ongoing, + } + } + + /// Get the count for the acquisiton, valid once group status is set + pub fn group_get_value(&mut self, index: Group) -> u16 { + T::REGS.iogcr(index.into()).read().cnt() + } + + /// Discharge the IOs for subsequent acquisition + pub fn discharge_io(&mut self, status: bool) { + // Set the touch sensing IOs in low power mode + T::REGS.cr().modify(|w| { + w.set_iodef(!status); + }); + } +} + +impl<'d, T: Instance> Drop for Tsc<'d, T> { + fn drop(&mut self) { + T::disable(); + } +} + +pub(crate) trait SealedInstance { + const REGS: Regs; +} + +/// TSC instance trait +#[allow(private_bounds)] +pub trait Instance: Peripheral<P = Self> + SealedInstance + RccPeripheral {} + +foreach_peripheral!( + (tsc, $inst:ident) => { + impl SealedInstance for peripherals::$inst { + const REGS: Regs = crate::pac::$inst; + } + + impl Instance for peripherals::$inst {} + }; +); + +pin_trait!(G1IO1Pin, Instance); +pin_trait!(G1IO2Pin, Instance); +pin_trait!(G1IO3Pin, Instance); +pin_trait!(G1IO4Pin, Instance); +pin_trait!(G2IO1Pin, Instance); +pin_trait!(G2IO2Pin, Instance); +pin_trait!(G2IO3Pin, Instance); +pin_trait!(G2IO4Pin, Instance); +pin_trait!(G3IO1Pin, Instance); +pin_trait!(G3IO2Pin, Instance); +pin_trait!(G3IO3Pin, Instance); +pin_trait!(G3IO4Pin, Instance); +pin_trait!(G4IO1Pin, Instance); +pin_trait!(G4IO2Pin, Instance); +pin_trait!(G4IO3Pin, Instance); +pin_trait!(G4IO4Pin, Instance); +pin_trait!(G5IO1Pin, Instance); +pin_trait!(G5IO2Pin, Instance); +pin_trait!(G5IO3Pin, Instance); +pin_trait!(G5IO4Pin, Instance); +pin_trait!(G6IO1Pin, Instance); +pin_trait!(G6IO2Pin, Instance); +pin_trait!(G6IO3Pin, Instance); +pin_trait!(G6IO4Pin, Instance); +pin_trait!(G7IO1Pin, Instance); +pin_trait!(G7IO2Pin, Instance); +pin_trait!(G7IO3Pin, Instance); +pin_trait!(G7IO4Pin, Instance); +pin_trait!(G8IO1Pin, Instance); +pin_trait!(G8IO2Pin, Instance); +pin_trait!(G8IO3Pin, Instance); +pin_trait!(G8IO4Pin, Instance);