From 1e040f403b155ebc3fd8ac30f1ac60f55f4a4abd Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Sun, 27 Jan 2019 21:05:35 +0100 Subject: [PATCH] Implement TSC (touch sensitive controller) support Signed-off-by: Daniel Egger --- CHANGELOG.md | 1 + src/lib.rs | 14 ++ src/tsc.rs | 361 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 376 insertions(+) create mode 100644 src/tsc.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a02270..081fa36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - Support for stm32f0x8 line - @jessebraham +- Support for capacitive touch sensing (TSC) ### Changed diff --git a/src/lib.rs b/src/lib.rs index 510e1b4..ff2b8a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,5 +46,19 @@ pub mod spi; pub mod time; #[cfg(feature = "device-selected")] pub mod timers; +#[cfg(any( + feature = "stm32f031", + feature = "stm32f051", + feature = "stm32f071", + feature = "stm32f091", + feature = "stm32f042", + feature = "stm32f072", + feature = "stm32f038", + feature = "stm32f048", + feature = "stm32f058", + feature = "stm32f078", + feature = "stm32f098", +))] +pub mod tsc; #[cfg(feature = "device-selected")] pub mod watchdog; diff --git a/src/tsc.rs b/src/tsc.rs new file mode 100644 index 0000000..cab2268 --- /dev/null +++ b/src/tsc.rs @@ -0,0 +1,361 @@ +//! 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::{gpioa, gpiob, Alternate, AF3}; +use crate::rcc::Rcc; +use crate::stm32::TSC; + +#[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), +); + +tsc_pins!( + gpiob::PB0> => (3_u8, 2_u8), + gpiob::PB1> => (3_u8, 3_u8), + 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), +); + +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 + } +}