Implement TSC (touch sensitive controller) support
Signed-off-by: Daniel Egger <daniel@eggers-club.de>
This commit is contained in:
parent
cdac82c305
commit
1e040f403b
|
@ -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
|
||||
|
||||
|
|
14
src/lib.rs
14
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;
|
||||
|
|
|
@ -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<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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue