//! Touch sense controller //! //! From STM32 (https://www.st.com/content/ccc/resource/technical/document/application_note/9d/be/03/8c/5d/8c/49/50/DM00088471.pdf/files/DM00088471.pdf/jcr:content/translations/en.DM00088471.pdf): //! //! The Cs capacitance is a key parameter for sensitivity. For touchkey sensors, the Cs value is //! usually comprised between 8.7nF to 22nF. For linear and rotary touch sensors, the value is //! usually comprised between 47nF and 100nF. These values are given as reference for an //! electrode fitting a human finger tip size across a few millimeters dielectric panel. use crate::gpio::*; use crate::pac::TSC; use crate::rcc::Rcc; #[derive(Debug)] pub enum Event { /// Max count error MaxCountError, /// End of acquisition EndOfAcquisition, } #[derive(Debug)] pub enum Error { /// Max count error MaxCountError, /// Wrong GPIO for reading InvalidPin, } pub trait TscPin { type GROUP; type OFFSET; /// Returns the group a pin belongs to fn group() -> Self::GROUP; /// Returns the offset of the pin within the control registers fn offset() -> Self::OFFSET; } macro_rules! tsc_pins { ($($pin:ty => ($group:expr,$offset:expr)),+ $(,)*) => { $( impl TscPin for $pin { type GROUP = u8; type OFFSET = u8; fn group() -> u8 { $group } fn offset() -> u8 { $offset } } )+ }; } tsc_pins!( gpioa::PA0> => (1_u8, 1_u8), gpioa::PA1> => (1_u8, 2_u8), gpioa::PA2> => (1_u8, 3_u8), gpioa::PA3> => (1_u8, 4_u8), ); tsc_pins!( gpioa::PA4> => (2_u8, 1_u8), gpioa::PA5> => (2_u8, 2_u8), gpioa::PA6> => (2_u8, 3_u8), gpioa::PA7> => (2_u8, 4_u8), ); // all with a TSC minus 42 and 48 #[cfg(any( feature = "stm32f051", feature = "stm32f058", feature = "stm32f071", feature = "stm32f072", feature = "stm32f078", feature = "stm32f091", feature = "stm32f098" ))] tsc_pins!( gpioc::PC5> => (3_u8, 1_u8) ); tsc_pins!( gpiob::PB0> => (3_u8, 2_u8), gpiob::PB1> => (3_u8, 3_u8), ); // all with a TCS minus 58, 78 and 98 #[cfg(any( feature = "stm32f042", feature = "stm32f048", feature = "stm32f051", feature = "stm32f071", feature = "stm32f072", feature = "stm32f091" ))] tsc_pins!( gpiob::PB2> => (3_u8, 4_u8) ); tsc_pins!( gpioa::PA9> => (4_u8, 1_u8), gpioa::PA10> => (4_u8, 2_u8), gpioa::PA11> => (4_u8, 3_u8), gpioa::PA12> => (4_u8, 4_u8), ); tsc_pins!( gpiob::PB3> => (5_u8, 1_u8), gpiob::PB4> => (5_u8, 2_u8), gpiob::PB6> => (5_u8, 3_u8), gpiob::PB7> => (5_u8, 4_u8), ); // all with a TSC minus 42 and 48 #[cfg(any( feature = "stm32f051", feature = "stm32f058", feature = "stm32f071", feature = "stm32f072", feature = "stm32f078", feature = "stm32f091", feature = "stm32f098" ))] tsc_pins!( gpiob::PB11> => (6_u8, 1_u8), gpiob::PB12> => (6_u8, 2_u8), gpiob::PB13> => (6_u8, 3_u8), gpiob::PB14> => (6_u8, 4_u8), ); // all with a TSC and gpioe #[cfg(any( feature = "stm32f071", feature = "stm32f072", feature = "stm32f078", feature = "stm32f091", feature = "stm32f098" ))] tsc_pins!( gpioe::PE2> => (7_u8, 1_u8), gpioe::PE3> => (7_u8, 2_u8), gpioe::PE4> => (7_u8, 3_u8), gpioe::PE5> => (7_u8, 4_u8), ); // all with a TSC and gpiod #[cfg(any( feature = "stm32f071", feature = "stm32f072", feature = "stm32f078", feature = "stm32f091", feature = "stm32f098" ))] tsc_pins!( gpiod::PD12> => (8_u8, 1_u8), gpiod::PD13> => (8_u8, 2_u8), gpiod::PD14> => (8_u8, 3_u8), gpiod::PD15> => (8_u8, 4_u8), ); pub struct Tsc { tsc: TSC, } #[derive(Debug)] pub struct Config { pub clock_prescale: Option, pub max_count: Option, pub charge_transfer_high: Option, pub charge_transfer_low: Option, } #[derive(Debug)] pub enum ClockPrescaler { Hclk = 0b000, HclkDiv2 = 0b001, HclkDiv4 = 0b010, HclkDiv8 = 0b011, HclkDiv16 = 0b100, HclkDiv32 = 0b101, HclkDiv64 = 0b110, HclkDiv128 = 0b111, } #[derive(Debug)] pub enum MaxCount { /// 000: 255 U255 = 0b000, /// 001: 511 U511 = 0b001, /// 010: 1023 U1023 = 0b010, /// 011: 2047 U2047 = 0b011, /// 100: 4095 U4095 = 0b100, /// 101: 8191 U8191 = 0b101, /// 110: 16383 U16383 = 0b110, } #[derive(Debug)] /// How many tsc cycles are spent charging / discharging pub enum ChargeDischargeTime { C1 = 0b0000, C2 = 0b0001, C3 = 0b0010, C4 = 0b0011, C5 = 0b0100, C6 = 0b0101, C7 = 0b0110, C8 = 0b0111, C9 = 0b1000, C10 = 0b1001, C11 = 0b1010, C12 = 0b1011, C13 = 0b1100, C14 = 0b1101, C15 = 0b1110, C16 = 0b1111, } impl Tsc { /// Initialise the touch controller peripheral pub fn tsc(tsc: TSC, rcc: &mut Rcc, cfg: Option) -> Self { // Enable the peripheral clock rcc.regs.ahbenr.modify(|_, w| w.tscen().set_bit()); rcc.regs.ahbrstr.modify(|_, w| w.tscrst().set_bit()); rcc.regs.ahbrstr.modify(|_, w| w.tscrst().clear_bit()); let config = cfg.unwrap_or(Config { clock_prescale: None, max_count: None, charge_transfer_high: None, charge_transfer_low: None, }); tsc.cr.write(|w| unsafe { w.ctph() .bits( config .charge_transfer_high .unwrap_or(ChargeDischargeTime::C2) as u8, ) .ctpl() .bits( config .charge_transfer_low .unwrap_or(ChargeDischargeTime::C2) as u8, ) .sse() .set_bit() .ssd() .bits(16) .pgpsc() .bits(config.clock_prescale.unwrap_or(ClockPrescaler::HclkDiv16) as u8) .mcv() .bits(config.max_count.unwrap_or(MaxCount::U8191) as u8) .tsce() .set_bit() }); // clear interrupt & flags tsc.icr.write(|w| w.eoaic().set_bit().mceic().set_bit()); Tsc { tsc } } /// Set up sample group pub fn setup_sample_group(&mut self, _: &mut PIN) where PIN: TscPin, { let bit_pos = PIN::offset() - 1 + (4 * (PIN::group() - 1)); let group_pos = PIN::group() - 1; // Schmitt trigger hysteresis on sample IOs self.tsc .iohcr .modify(|r, w| unsafe { w.bits(r.bits() | 1 << bit_pos) }); // Set the sampling pin self.tsc .ioscr .modify(|r, w| unsafe { w.bits(r.bits() | 1 << bit_pos) }); // Set the acquisition group based on the channel pins self.tsc .iogcsr .modify(|r, w| unsafe { w.bits(r.bits() | 1 << group_pos) }); } /// Add a GPIO for use as a channel pub fn enable_channel(&self, _channel: &mut PIN) where PIN: TscPin, { let bit_pos = PIN::offset() - 1 + (4 * (PIN::group() - 1)); // Set a channel pin self.tsc .ioccr .modify(|r, w| unsafe { w.bits(r.bits() | 1 << bit_pos) }); } /// Remove a GPIO from use as a channel pub fn disable_channel(&self, _channel: &mut PIN) where PIN: TscPin, { let bit_pos = PIN::offset() - 1 + (4 * (PIN::group() - 1)); // Remove a channel pin self.tsc .ioccr .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << bit_pos)) }); } /// Starts a charge acquisition pub fn start(&self) { self.clear(Event::EndOfAcquisition); self.clear(Event::MaxCountError); // Discharge the caps ready for a new reading self.tsc.cr.modify(|_, w| w.iodef().clear_bit()); self.tsc.cr.modify(|_, w| w.start().set_bit()); } /// Check for events on the TSC pub fn check_event(&self) -> Option { let isr = self.tsc.isr.read(); if isr.eoaf().bit_is_set() { Some(Event::EndOfAcquisition) } else if isr.mcef().bit_is_set() { Some(Event::MaxCountError) } else { None } } /// Clear interrupt & flags pub fn clear(&self, event: Event) { match event { Event::EndOfAcquisition => { self.tsc.icr.write(|w| w.eoaic().set_bit()); } Event::MaxCountError => { self.tsc.icr.write(|w| w.mceic().set_bit()); } } } /// Blocks waiting for a acquisition to complete or for a Max Count Error pub fn acquire(&self) -> Result<(), Error> { // Start the acquisition self.start(); loop { match self.check_event() { Some(Event::MaxCountError) => { self.clear(Event::MaxCountError); break Err(Error::MaxCountError); } Some(Event::EndOfAcquisition) => { self.clear(Event::EndOfAcquisition); break Ok(()); } None => {} } } } /// Reads the group count register pub fn read(&self, _input: &mut PIN) -> Result where PIN: TscPin, { let bit_pos = PIN::offset() - 1 + (4 * (PIN::group() - 1)); // Read the current channel config let channel = self.tsc.ioccr.read().bits(); // Check whether one of the enabled pins was supplied if channel & (1 << bit_pos) != 0 { Ok(self.read_unchecked(PIN::group())) } else { Err(Error::InvalidPin) } } /// Reads the tsc group count register pub fn read_unchecked(&self, group: u8) -> u16 { match group { 1 => self.tsc.iog1cr.read().cnt().bits(), 2 => self.tsc.iog2cr.read().cnt().bits(), 3 => self.tsc.iog3cr.read().cnt().bits(), 4 => self.tsc.iog4cr.read().cnt().bits(), 5 => self.tsc.iog5cr.read().cnt().bits(), 6 => self.tsc.iog6cr.read().cnt().bits(), _ => 0, } } /// Enables an interrupt event pub fn listen(&mut self, event: Event) { match event { Event::EndOfAcquisition => { self.tsc.ier.modify(|_, w| w.eoaie().set_bit()); } Event::MaxCountError => { self.tsc.ier.modify(|_, w| w.mceie().set_bit()); } } } /// Disables an interrupt event pub fn unlisten(&self, event: Event) { match event { Event::EndOfAcquisition => { self.tsc.ier.modify(|_, w| w.eoaie().clear_bit()); } Event::MaxCountError => { self.tsc.ier.modify(|_, w| w.mceie().clear_bit()); } } } /// Releases the TSC peripheral pub fn free(self) -> TSC { self.tsc } }