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 Sep 13, 2024
1 parent 3d6a270 commit f6384d3
Show file tree
Hide file tree
Showing 27 changed files with 3,076 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,3 +5,7 @@ Cargo.lock
third_party
/Cargo.toml
out/

# editor artifacts
.neoconf.json
*.vim
311 changes: 311 additions & 0 deletions embassy-stm32/src/tsc/acquisition_banks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,311 @@
use super::pin_groups::{tsc_pin_roles, G1, G2, G3, G4, G5, G6};
#[cfg(any(tsc_v2, tsc_v3))]
use super::pin_groups::G7;
#[cfg(tsc_v3)]
use super::pin_groups::G8;
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()
}
}

/// Iterator for TSC acquisition bank groups.
///
/// This iterator yields `Group` values for each group that has a configured pin in the acquisition bank.
pub struct TscAcquisitionBankGroupsIterator<'a>(TscAcquisitionBankIterator<'a>);

impl<'a> Iterator for TscAcquisitionBankGroupsIterator<'a> {
type Item = Group;

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

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

/// Returns an iterator over the available groups in the bank
pub fn groups_iterator(&self) -> TscAcquisitionBankGroupsIterator {
TscAcquisitionBankGroupsIterator(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 an iterator over the available groups in the bank.
pub fn groups_iterator(&self) -> TscAcquisitionBankGroupsIterator {
self.pins.groups_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
#[allow(missing_docs)]
#[derive(Default)]
pub struct TscAcquisitionBankStatus {
pub g1: Option<GroupStatus>,
pub g2: Option<GroupStatus>,
pub g3: Option<GroupStatus>,
pub g4: Option<GroupStatus>,
pub g5: Option<GroupStatus>,
pub g6: Option<GroupStatus>,
#[cfg(any(tsc_v2, tsc_v3))]
pub g7: Option<GroupStatus>,
#[cfg(tsc_v3)]
pub g8: Option<GroupStatus>,
}

impl TscAcquisitionBankStatus {
/// Check if all groups in the mask are complete
pub fn all_complete(&self) -> bool {
let check_group = |status: &Option<GroupStatus>| status.map_or(true, |s| s == GroupStatus::Complete);

#[allow(unused_mut)]
let mut result = check_group(&self.g1)
&& check_group(&self.g2)
&& check_group(&self.g3)
&& check_group(&self.g4)
&& check_group(&self.g5)
&& check_group(&self.g6);
#[cfg(any(tsc_v2, tsc_v3))]
{
result &= check_group(&self.g7);
}
#[cfg(tsc_v3)]
{
result &= check_group(&self.g8);
}
result
}

/// Check if any group in the mask is ongoing
pub fn any_ongoing(&self) -> bool {
let check_group = |status: &Option<GroupStatus>| status.map_or(false, |s| s == GroupStatus::Ongoing);

#[allow(unused_mut)]
let mut result = check_group(&self.g1)
|| check_group(&self.g2)
|| check_group(&self.g3)
|| check_group(&self.g4)
|| check_group(&self.g5)
|| check_group(&self.g6);
#[cfg(any(tsc_v2, tsc_v3))]
{
result |= check_group(&self.g7);
}
#[cfg(tsc_v3)]
{
result |= check_group(&self.g8);
}
result
}
}

/// 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
#[allow(missing_docs)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Default, Debug)]
pub struct TscAcquisitionBankReadings {
pub g1: Option<TscChannelReading>,
pub g2: Option<TscChannelReading>,
pub g3: Option<TscChannelReading>,
pub g4: Option<TscChannelReading>,
pub g5: Option<TscChannelReading>,
pub g6: Option<TscChannelReading>,
#[cfg(any(tsc_v2, tsc_v3))]
pub g7: Option<TscChannelReading>,
#[cfg(tsc_v3)]
pub g8: Option<TscChannelReading>,
}

impl TscAcquisitionBankReadings {
/// Returns an iterator over the group readings in the given acquisition bank.
pub fn iter(&self) -> TscAcquisitionBankReadingsIterator {
TscAcquisitionBankReadingsIterator {
readings: self,
next_group: 0,
}
}
}

/// An iterator over the readings in a `BankGroupReadings` instance.
///
/// This iterator yields tuples of `(Group, u16)`, where:
/// - `Group` identifies the TSC group
/// - `u16` is the reading value for that group
///
/// Note: This iterator only yields values for groups that have a reading (i.e., `Some` value).
/// Groups with `None` values are skipped.
pub struct TscAcquisitionBankReadingsIterator<'a> {
/// Reference to the GroupReadings being iterated over
readings: &'a TscAcquisitionBankReadings,
/// Index of the next group to be checked
next_group: u8,
}

impl<'a> Iterator for TscAcquisitionBankReadingsIterator<'a> {
type Item = TscChannelReading;

fn next(&mut self) -> Option<Self::Item> {
while self.next_group < 8 {
let group = match self.next_group {
0 => (Group::One, &self.readings.g1),
1 => (Group::Two, &self.readings.g2),
2 => (Group::Three, &self.readings.g3),
3 => (Group::Four, &self.readings.g4),
4 => (Group::Five, &self.readings.g5),
5 => (Group::Six, &self.readings.g6),
#[cfg(any(tsc_v2, tsc_v3))]
6 => (Group::Seven, &self.readings.g7),
#[cfg(tsc_v3)]
7 => (Group::Eight, &self.readings.g8),
_ => unreachable!(),
};
self.next_group += 1;
if let Some(reading) = group.1 {
return Some(*reading);
}
}
None
}
}
Loading

0 comments on commit f6384d3

Please sign in to comment.