Skip to content

Commit

Permalink
STM32-TSC: enable discriminating between pins within same TSC group a…
Browse files Browse the repository at this point in the history
…nd improve TSC library in general
  • Loading branch information
michelrandahl committed Oct 1, 2024
1 parent e6ce810 commit c7742e7
Show file tree
Hide file tree
Showing 27 changed files with 3,007 additions and 1,437 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,8 @@ Cargo.lock
third_party
/Cargo.toml
out/

# editor artifacts
.zed
.neoconf.json
*.vim
209 changes: 209 additions & 0 deletions embassy-stm32/src/tsc/acquisition_banks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
#[cfg(any(tsc_v2, tsc_v3))]
use super::pin_groups::G7;
#[cfg(tsc_v3)]
use super::pin_groups::G8;
use super::pin_groups::{tsc_pin_roles, G1, G2, G3, G4, G5, G6};
use super::tsc_io_pin::*;
use super::types::{Group, GroupStatus};
use super::TSC_NUM_GROUPS;

/// Represents a collection of TSC (Touch Sensing Controller) pins for an acquisition bank.
///
/// This struct holds optional `TscIOPin` values for each TSC group, allowing for flexible
/// configuration of TSC acquisition banks. Each field corresponds to a specific TSC group
/// and can be set to `Some(TscIOPin)` if that group is to be included in the acquisition,
/// or `None` if it should be excluded.
#[allow(missing_docs)]
#[derive(Default)]
pub struct TscAcquisitionBankPins {
pub g1_pin: Option<TscIOPinWithRole<G1, tsc_pin_roles::Channel>>,
pub g2_pin: Option<TscIOPinWithRole<G2, tsc_pin_roles::Channel>>,
pub g3_pin: Option<TscIOPinWithRole<G3, tsc_pin_roles::Channel>>,
pub g4_pin: Option<TscIOPinWithRole<G4, tsc_pin_roles::Channel>>,
pub g5_pin: Option<TscIOPinWithRole<G5, tsc_pin_roles::Channel>>,
pub g6_pin: Option<TscIOPinWithRole<G6, tsc_pin_roles::Channel>>,
#[cfg(any(tsc_v2, tsc_v3))]
pub g7_pin: Option<TscIOPinWithRole<G7, tsc_pin_roles::Channel>>,
#[cfg(tsc_v3)]
pub g8_pin: Option<TscIOPinWithRole<G8, tsc_pin_roles::Channel>>,
}

impl TscAcquisitionBankPins {
/// Returns an iterator over the pins in this acquisition bank.
///
/// This method allows for easy traversal of all configured pins in the bank.
pub fn iter(&self) -> TscAcquisitionBankPinsIterator {
TscAcquisitionBankPinsIterator(TscAcquisitionBankIterator::new(self))
}
}

/// Iterator for TSC acquisition banks.
///
/// This iterator allows traversing through the pins of a `TscAcquisitionBankPins` struct,
/// yielding each configured pin in order of the TSC groups.
pub struct TscAcquisitionBankIterator<'a> {
pins: &'a TscAcquisitionBankPins,
current_group: u8,
}

impl<'a> TscAcquisitionBankIterator<'a> {
fn new(pins: &'a TscAcquisitionBankPins) -> Self {
Self { pins, current_group: 0 }
}

fn next_pin(&mut self) -> Option<TscIOPin> {
while self.current_group < TSC_NUM_GROUPS as u8 {
let pin = match self.current_group {
0 => self.pins.g1_pin.map(TscIOPinWithRole::get_pin),
1 => self.pins.g2_pin.map(TscIOPinWithRole::get_pin),
2 => self.pins.g3_pin.map(TscIOPinWithRole::get_pin),
3 => self.pins.g4_pin.map(TscIOPinWithRole::get_pin),
4 => self.pins.g5_pin.map(TscIOPinWithRole::get_pin),
5 => self.pins.g6_pin.map(TscIOPinWithRole::get_pin),
#[cfg(any(tsc_v2, tsc_v3))]
6 => self.pins.g7_pin.map(TscIOPinWithRole::get_pin),
#[cfg(tsc_v3)]
7 => self.pins.g8_pin.map(TscIOPinWithRole::get_pin),
_ => None,
};
self.current_group += 1;
if pin.is_some() {
return pin;
}
}
None
}
}

/// Iterator for TSC acquisition bank pins.
///
/// This iterator yields `TscIOPin` values for each configured pin in the acquisition bank.
pub struct TscAcquisitionBankPinsIterator<'a>(TscAcquisitionBankIterator<'a>);

impl<'a> Iterator for TscAcquisitionBankPinsIterator<'a> {
type Item = TscIOPin;

fn next(&mut self) -> Option<Self::Item> {
self.0.next_pin()
}
}

impl TscAcquisitionBankPins {
/// Returns an iterator over the available pins in the bank
pub fn pins_iterator(&self) -> TscAcquisitionBankPinsIterator {
TscAcquisitionBankPinsIterator(TscAcquisitionBankIterator::new(self))
}
}

/// Represents a collection of TSC pins to be acquired simultaneously.
///
/// This struct contains a set of pins to be used in a TSC acquisition with a pre-computed and
/// verified mask for efficiently setting up the TSC peripheral before performing an acquisition.
/// It ensures that only one channel pin per TSC group is included, adhering to hardware limitations.
pub struct TscAcquisitionBank {
pub(super) pins: TscAcquisitionBankPins,
pub(super) mask: u32,
}

impl TscAcquisitionBank {
/// Returns an iterator over the available pins in the bank.
pub fn pins_iterator(&self) -> TscAcquisitionBankPinsIterator {
self.pins.pins_iterator()
}

/// Returns the mask for this bank.
pub fn mask(&self) -> u32 {
self.mask
}

/// Retrieves the TSC I/O pin for a given group in this acquisition bank.
///
/// # Arguments
/// * `group` - The TSC group to retrieve the pin for.
///
/// # Returns
/// An `Option<TscIOPin>` containing the pin if it exists for the given group, or `None` if not.
pub fn get_pin(&self, group: Group) -> Option<TscIOPin> {
match group {
Group::One => self.pins.g1_pin.map(|p| p.pin),
Group::Two => self.pins.g2_pin.map(|p| p.pin),
Group::Three => self.pins.g3_pin.map(|p| p.pin),
Group::Four => self.pins.g4_pin.map(|p| p.pin),
Group::Five => self.pins.g5_pin.map(|p| p.pin),
Group::Six => self.pins.g6_pin.map(|p| p.pin),
#[cfg(any(tsc_v2, tsc_v3))]
Group::Seven => self.pins.g7_pin.map(|p| p.pin),
#[cfg(tsc_v3)]
Group::Eight => self.pins.g8_pin.map(|p| p.pin),
}
}
}

/// Represents the status of all TSC groups in an acquisition bank
#[derive(Default)]
pub struct TscAcquisitionBankStatus {
pub(super) groups: [Option<GroupStatus>; TSC_NUM_GROUPS],
}

impl TscAcquisitionBankStatus {
/// Check if all groups in the bank are complete
pub fn all_complete(&self) -> bool {
self.groups
.iter()
.all(|&status| status.map_or(true, |s| s == GroupStatus::Complete))
}

/// Check if any group in the bank is ongoing
pub fn any_ongoing(&self) -> bool {
self.groups.iter().any(|&status| status == Some(GroupStatus::Ongoing))
}

/// Get the status of a specific group, if the group is present in the bank
pub fn get_group_status(&self, group: Group) -> Option<GroupStatus> {
let index: usize = group.into();
self.groups[index]
}

/// Iterator for groups present in the bank
pub fn iter(&self) -> impl Iterator<Item = (Group, GroupStatus)> + '_ {
self.groups.iter().enumerate().filter_map(|(group_num, status)| {
status.and_then(|s| Group::try_from(group_num).ok().map(|group| (group, s)))
})
}
}

/// Represents the result of a Touch Sensing Controller (TSC) acquisition for a specific pin.
///
/// This struct contains a reference to the `TscIOPin` from which a value was read,
/// along with the actual sensor reading for that pin. It provides a convenient way
/// to associate TSC readings with their corresponding pins after an acquisition.
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug)]
pub struct TscChannelReading {
/// The sensor reading value obtained from the TSC acquisition.
/// Lower values typically indicate a detected touch, while higher values indicate no touch.
pub sensor_value: u16,

/// The `TscIOPin` associated with this reading.
/// This allows for easy identification of which pin the reading corresponds to.
pub tsc_pin: TscIOPin,
}

/// Represents the readings from all TSC groups
#[derive(Default)]
pub struct TscAcquisitionBankReadings {
pub(super) groups: [Option<TscChannelReading>; TSC_NUM_GROUPS],
}

impl TscAcquisitionBankReadings {
/// Get the reading for a specific group, if the group is present in the bank
pub fn get_group_reading(&self, group: Group) -> Option<TscChannelReading> {
let index: usize = group.into();
self.groups[index]
}

/// Iterator for readings for groups present in the bank
pub fn iter(&self) -> impl Iterator<Item = TscChannelReading> + '_ {
self.groups.iter().filter_map(|&x| x)
}
}
175 changes: 175 additions & 0 deletions embassy-stm32/src/tsc/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/// 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,
}
}
}

/// 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,
}
}
}

/// 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,
}
}
}

/// Error type for SSDeviation
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SSDeviationError {
/// The provided value is too low (0)
ValueTooLow,
/// The provided value is too high (greater than 128)
ValueTooHigh,
}

/// 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, SSDeviationError> {
if val == 0 {
return Err(SSDeviationError::ValueTooLow);
} else if val > 128 {
return Err(SSDeviationError::ValueTooHigh);
}
Ok(Self(val - 1))
}
}

impl Into<u8> for SSDeviation {
fn into(self) -> u8 {
self.0
}
}

/// 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 transfer 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,
}

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,
}
}
}
Loading

0 comments on commit c7742e7

Please sign in to comment.