You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
362 lines
9.9 KiB
Rust
362 lines
9.9 KiB
Rust
//! 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<TSC> {
|
|
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<TSC> for $pin {
|
|
type GROUP = u8;
|
|
type OFFSET = u8;
|
|
|
|
fn group() -> u8 { $group }
|
|
fn offset() -> u8 { $offset }
|
|
}
|
|
)+
|
|
};
|
|
}
|
|
|
|
tsc_pins!(
|
|
gpioa::PA0<Alternate<AF3>> => (1_u8, 1_u8),
|
|
gpioa::PA1<Alternate<AF3>> => (1_u8, 2_u8),
|
|
gpioa::PA2<Alternate<AF3>> => (1_u8, 3_u8),
|
|
gpioa::PA3<Alternate<AF3>> => (1_u8, 4_u8),
|
|
);
|
|
|
|
tsc_pins!(
|
|
gpioa::PA4<Alternate<AF3>> => (2_u8, 1_u8),
|
|
gpioa::PA5<Alternate<AF3>> => (2_u8, 2_u8),
|
|
gpioa::PA6<Alternate<AF3>> => (2_u8, 3_u8),
|
|
gpioa::PA7<Alternate<AF3>> => (2_u8, 4_u8),
|
|
);
|
|
|
|
tsc_pins!(
|
|
gpiob::PB0<Alternate<AF3>> => (3_u8, 2_u8),
|
|
gpiob::PB1<Alternate<AF3>> => (3_u8, 3_u8),
|
|
gpiob::PB2<Alternate<AF3>> => (3_u8, 4_u8),
|
|
);
|
|
|
|
tsc_pins!(
|
|
gpioa::PA9<Alternate<AF3>> => (4_u8, 1_u8),
|
|
gpioa::PA10<Alternate<AF3>> => (4_u8, 2_u8),
|
|
gpioa::PA11<Alternate<AF3>> => (4_u8, 3_u8),
|
|
gpioa::PA12<Alternate<AF3>> => (4_u8, 4_u8),
|
|
);
|
|
|
|
tsc_pins!(
|
|
gpiob::PB3<Alternate<AF3>> => (5_u8, 1_u8),
|
|
gpiob::PB4<Alternate<AF3>> => (5_u8, 2_u8),
|
|
gpiob::PB6<Alternate<AF3>> => (5_u8, 3_u8),
|
|
gpiob::PB7<Alternate<AF3>> => (5_u8, 4_u8),
|
|
);
|
|
|
|
pub struct Tsc {
|
|
tsc: TSC,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Config {
|
|
pub clock_prescale: Option<ClockPrescaler>,
|
|
pub max_count: Option<MaxCount>,
|
|
pub charge_transfer_high: Option<ChargeDischargeTime>,
|
|
pub charge_transfer_low: Option<ChargeDischargeTime>,
|
|
}
|
|
|
|
#[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<Config>) -> 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<PIN>(&mut self, _: &mut PIN)
|
|
where
|
|
PIN: TscPin<TSC, GROUP = u8, OFFSET = u8>,
|
|
{
|
|
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<PIN>(&self, _channel: &mut PIN)
|
|
where
|
|
PIN: TscPin<TSC, GROUP = u8, OFFSET = u8>,
|
|
{
|
|
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<PIN>(&self, _channel: &mut PIN)
|
|
where
|
|
PIN: TscPin<TSC, GROUP = u8, OFFSET = u8>,
|
|
{
|
|
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<Event> {
|
|
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<PIN>(&self, _input: &mut PIN) -> Result<u16, Error>
|
|
where
|
|
PIN: TscPin<TSC, GROUP = u8, OFFSET = u8>,
|
|
{
|
|
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
|
|
}
|
|
}
|