aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2024-12-03 01:11:33 +0100
committerGitHub <[email protected]>2024-12-03 01:11:33 +0100
commit4acc0f84b084235b576de3b9e1d12a3472a5274b (patch)
treec9905c08383623af24865b761905585e0d7747d5
parent86b53a2ce3d93d07e5c8e4f4122e9588b87aafb7 (diff)
parentdcd6284996c501b2d376f5c1d4af6fc5a0f00521 (diff)
Merge pull request #3274 from michelrandahl/discriminating-pins-within-tsc-group
STM32-TSC: enable discriminating between pins within same TSC group and improve TSC library in general
-rw-r--r--.gitignore3
-rw-r--r--embassy-stm32/src/tsc/acquisition_banks.rs209
-rw-r--r--embassy-stm32/src/tsc/config.rs175
-rw-r--r--embassy-stm32/src/tsc/enums.rs238
-rw-r--r--embassy-stm32/src/tsc/errors.rs21
-rw-r--r--embassy-stm32/src/tsc/io_pin.rs200
-rw-r--r--embassy-stm32/src/tsc/mod.rs1011
-rw-r--r--embassy-stm32/src/tsc/pin_groups.rs669
-rw-r--r--embassy-stm32/src/tsc/tsc.rs456
-rw-r--r--embassy-stm32/src/tsc/types.rs93
-rw-r--r--examples/stm32f3/README.md24
-rw-r--r--examples/stm32f3/src/bin/blocking-tsc.rs98
-rw-r--r--examples/stm32f3/src/bin/tsc_blocking.rs138
-rw-r--r--examples/stm32f3/src/bin/tsc_multipin.rs204
-rw-r--r--examples/stm32l0/.cargo/config.toml2
-rw-r--r--examples/stm32l0/Cargo.toml2
-rw-r--r--examples/stm32l0/README.md24
-rw-r--r--examples/stm32l0/src/bin/async-tsc.rs122
-rw-r--r--examples/stm32l0/src/bin/blocking-tsc.rs116
-rw-r--r--examples/stm32l0/src/bin/tsc_async.rs116
-rw-r--r--examples/stm32l0/src/bin/tsc_blocking.rs142
-rw-r--r--examples/stm32l0/src/bin/tsc_multipin.rs209
-rw-r--r--examples/stm32l4/.cargo/config.toml3
-rw-r--r--examples/stm32l4/Cargo.toml2
-rw-r--r--examples/stm32l4/README.md24
-rw-r--r--examples/stm32l4/src/bin/tsc_async.rs108
-rw-r--r--examples/stm32l4/src/bin/tsc_blocking.rs147
-rw-r--r--examples/stm32l4/src/bin/tsc_multipin.rs198
-rw-r--r--examples/stm32u5/src/bin/tsc.rs75
29 files changed, 3274 insertions, 1555 deletions
diff --git a/.gitignore b/.gitignore
index a0b5d6a70..352c1f1af 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,7 @@ Cargo.lock
5third_party 5third_party
6/Cargo.toml 6/Cargo.toml
7out/ 7out/
8# editor artifacts
8.zed 9.zed
10.neoconf.json
11*.vim
diff --git a/embassy-stm32/src/tsc/acquisition_banks.rs b/embassy-stm32/src/tsc/acquisition_banks.rs
new file mode 100644
index 000000000..6791ef6c1
--- /dev/null
+++ b/embassy-stm32/src/tsc/acquisition_banks.rs
@@ -0,0 +1,209 @@
1use super::io_pin::*;
2#[cfg(any(tsc_v2, tsc_v3))]
3use super::pin_groups::G7;
4#[cfg(tsc_v3)]
5use super::pin_groups::G8;
6use super::pin_groups::{pin_roles, G1, G2, G3, G4, G5, G6};
7use super::types::{Group, GroupStatus};
8use super::TSC_NUM_GROUPS;
9
10/// Represents a collection of TSC (Touch Sensing Controller) pins for an acquisition bank.
11///
12/// This struct holds optional `tsc::IOPin` values for each TSC group, allowing for flexible
13/// configuration of TSC acquisition banks. Each field corresponds to a specific TSC group
14/// and can be set to `Some(tsc::IOPin)` if that group is to be included in the acquisition,
15/// or `None` if it should be excluded.
16#[allow(missing_docs)]
17#[derive(Default)]
18pub struct AcquisitionBankPins {
19 pub g1_pin: Option<IOPinWithRole<G1, pin_roles::Channel>>,
20 pub g2_pin: Option<IOPinWithRole<G2, pin_roles::Channel>>,
21 pub g3_pin: Option<IOPinWithRole<G3, pin_roles::Channel>>,
22 pub g4_pin: Option<IOPinWithRole<G4, pin_roles::Channel>>,
23 pub g5_pin: Option<IOPinWithRole<G5, pin_roles::Channel>>,
24 pub g6_pin: Option<IOPinWithRole<G6, pin_roles::Channel>>,
25 #[cfg(any(tsc_v2, tsc_v3))]
26 pub g7_pin: Option<IOPinWithRole<G7, pin_roles::Channel>>,
27 #[cfg(tsc_v3)]
28 pub g8_pin: Option<IOPinWithRole<G8, pin_roles::Channel>>,
29}
30
31impl AcquisitionBankPins {
32 /// Returns an iterator over the pins in this acquisition bank.
33 ///
34 /// This method allows for easy traversal of all configured pins in the bank.
35 pub fn iter(&self) -> AcquisitionBankPinsIterator {
36 AcquisitionBankPinsIterator(AcquisitionBankIterator::new(self))
37 }
38}
39
40/// Iterator for TSC acquisition banks.
41///
42/// This iterator allows traversing through the pins of a `AcquisitionBankPins` struct,
43/// yielding each configured pin in order of the TSC groups.
44pub struct AcquisitionBankIterator<'a> {
45 pins: &'a AcquisitionBankPins,
46 current_group: u8,
47}
48
49impl<'a> AcquisitionBankIterator<'a> {
50 fn new(pins: &'a AcquisitionBankPins) -> Self {
51 Self { pins, current_group: 0 }
52 }
53
54 fn next_pin(&mut self) -> Option<IOPin> {
55 while self.current_group < TSC_NUM_GROUPS as u8 {
56 let pin = match self.current_group {
57 0 => self.pins.g1_pin.map(IOPinWithRole::get_pin),
58 1 => self.pins.g2_pin.map(IOPinWithRole::get_pin),
59 2 => self.pins.g3_pin.map(IOPinWithRole::get_pin),
60 3 => self.pins.g4_pin.map(IOPinWithRole::get_pin),
61 4 => self.pins.g5_pin.map(IOPinWithRole::get_pin),
62 5 => self.pins.g6_pin.map(IOPinWithRole::get_pin),
63 #[cfg(any(tsc_v2, tsc_v3))]
64 6 => self.pins.g7_pin.map(IOPinWithRole::get_pin),
65 #[cfg(tsc_v3)]
66 7 => self.pins.g8_pin.map(IOPinWithRole::get_pin),
67 _ => None,
68 };
69 self.current_group += 1;
70 if pin.is_some() {
71 return pin;
72 }
73 }
74 None
75 }
76}
77
78/// Iterator for TSC acquisition bank pins.
79///
80/// This iterator yields `tsc::IOPin` values for each configured pin in the acquisition bank.
81pub struct AcquisitionBankPinsIterator<'a>(AcquisitionBankIterator<'a>);
82
83impl<'a> Iterator for AcquisitionBankPinsIterator<'a> {
84 type Item = IOPin;
85
86 fn next(&mut self) -> Option<Self::Item> {
87 self.0.next_pin()
88 }
89}
90
91impl AcquisitionBankPins {
92 /// Returns an iterator over the available pins in the bank
93 pub fn pins_iterator(&self) -> AcquisitionBankPinsIterator {
94 AcquisitionBankPinsIterator(AcquisitionBankIterator::new(self))
95 }
96}
97
98/// Represents a collection of TSC pins to be acquired simultaneously.
99///
100/// This struct contains a set of pins to be used in a TSC acquisition with a pre-computed and
101/// verified mask for efficiently setting up the TSC peripheral before performing an acquisition.
102/// It ensures that only one channel pin per TSC group is included, adhering to hardware limitations.
103pub struct AcquisitionBank {
104 pub(super) pins: AcquisitionBankPins,
105 pub(super) mask: u32,
106}
107
108impl AcquisitionBank {
109 /// Returns an iterator over the available pins in the bank.
110 pub fn pins_iterator(&self) -> AcquisitionBankPinsIterator {
111 self.pins.pins_iterator()
112 }
113
114 /// Returns the mask for this bank.
115 pub fn mask(&self) -> u32 {
116 self.mask
117 }
118
119 /// Retrieves the TSC I/O pin for a given group in this acquisition bank.
120 ///
121 /// # Arguments
122 /// * `group` - The TSC group to retrieve the pin for.
123 ///
124 /// # Returns
125 /// An `Option<tsc::IOPin>` containing the pin if it exists for the given group, or `None` if not.
126 pub fn get_pin(&self, group: Group) -> Option<IOPin> {
127 match group {
128 Group::One => self.pins.g1_pin.map(|p| p.pin),
129 Group::Two => self.pins.g2_pin.map(|p| p.pin),
130 Group::Three => self.pins.g3_pin.map(|p| p.pin),
131 Group::Four => self.pins.g4_pin.map(|p| p.pin),
132 Group::Five => self.pins.g5_pin.map(|p| p.pin),
133 Group::Six => self.pins.g6_pin.map(|p| p.pin),
134 #[cfg(any(tsc_v2, tsc_v3))]
135 Group::Seven => self.pins.g7_pin.map(|p| p.pin),
136 #[cfg(tsc_v3)]
137 Group::Eight => self.pins.g8_pin.map(|p| p.pin),
138 }
139 }
140}
141
142/// Represents the status of all TSC groups in an acquisition bank
143#[derive(Default)]
144pub struct AcquisitionBankStatus {
145 pub(super) groups: [Option<GroupStatus>; TSC_NUM_GROUPS],
146}
147
148impl AcquisitionBankStatus {
149 /// Check if all groups in the bank are complete
150 pub fn all_complete(&self) -> bool {
151 self.groups
152 .iter()
153 .all(|&status| status.map_or(true, |s| s == GroupStatus::Complete))
154 }
155
156 /// Check if any group in the bank is ongoing
157 pub fn any_ongoing(&self) -> bool {
158 self.groups.iter().any(|&status| status == Some(GroupStatus::Ongoing))
159 }
160
161 /// Get the status of a specific group, if the group is present in the bank
162 pub fn get_group_status(&self, group: Group) -> Option<GroupStatus> {
163 let index: usize = group.into();
164 self.groups[index]
165 }
166
167 /// Iterator for groups present in the bank
168 pub fn iter(&self) -> impl Iterator<Item = (Group, GroupStatus)> + '_ {
169 self.groups.iter().enumerate().filter_map(|(group_num, status)| {
170 status.and_then(|s| Group::try_from(group_num).ok().map(|group| (group, s)))
171 })
172 }
173}
174
175/// Represents the result of a Touch Sensing Controller (TSC) acquisition for a specific pin.
176///
177/// This struct contains a reference to the `tsc::IOPin` from which a value was read,
178/// along with the actual sensor reading for that pin. It provides a convenient way
179/// to associate TSC readings with their corresponding pins after an acquisition.
180#[cfg_attr(feature = "defmt", derive(defmt::Format))]
181#[derive(Clone, Copy, Debug)]
182pub struct ChannelReading {
183 /// The sensor reading value obtained from the TSC acquisition.
184 /// Lower values typically indicate a detected touch, while higher values indicate no touch.
185 pub sensor_value: u16,
186
187 /// The `tsc::IOPin` associated with this reading.
188 /// This allows for easy identification of which pin the reading corresponds to.
189 pub tsc_pin: IOPin,
190}
191
192/// Represents the readings from all TSC groups
193#[derive(Default)]
194pub struct AcquisitionBankReadings {
195 pub(super) groups: [Option<ChannelReading>; TSC_NUM_GROUPS],
196}
197
198impl AcquisitionBankReadings {
199 /// Get the reading for a specific group, if the group is present in the bank
200 pub fn get_group_reading(&self, group: Group) -> Option<ChannelReading> {
201 let index: usize = group.into();
202 self.groups[index]
203 }
204
205 /// Iterator for readings for groups present in the bank
206 pub fn iter(&self) -> impl Iterator<Item = ChannelReading> + '_ {
207 self.groups.iter().filter_map(|&x| x)
208 }
209}
diff --git a/embassy-stm32/src/tsc/config.rs b/embassy-stm32/src/tsc/config.rs
new file mode 100644
index 000000000..efa1f9a0d
--- /dev/null
+++ b/embassy-stm32/src/tsc/config.rs
@@ -0,0 +1,175 @@
1/// Charge transfer pulse cycles
2#[allow(missing_docs)]
3#[derive(Copy, Clone, PartialEq)]
4pub enum ChargeTransferPulseCycle {
5 _1,
6 _2,
7 _3,
8 _4,
9 _5,
10 _6,
11 _7,
12 _8,
13 _9,
14 _10,
15 _11,
16 _12,
17 _13,
18 _14,
19 _15,
20 _16,
21}
22
23impl Into<u8> for ChargeTransferPulseCycle {
24 fn into(self) -> u8 {
25 match self {
26 ChargeTransferPulseCycle::_1 => 0,
27 ChargeTransferPulseCycle::_2 => 1,
28 ChargeTransferPulseCycle::_3 => 2,
29 ChargeTransferPulseCycle::_4 => 3,
30 ChargeTransferPulseCycle::_5 => 4,
31 ChargeTransferPulseCycle::_6 => 5,
32 ChargeTransferPulseCycle::_7 => 6,
33 ChargeTransferPulseCycle::_8 => 7,
34 ChargeTransferPulseCycle::_9 => 8,
35 ChargeTransferPulseCycle::_10 => 9,
36 ChargeTransferPulseCycle::_11 => 10,
37 ChargeTransferPulseCycle::_12 => 11,
38 ChargeTransferPulseCycle::_13 => 12,
39 ChargeTransferPulseCycle::_14 => 13,
40 ChargeTransferPulseCycle::_15 => 14,
41 ChargeTransferPulseCycle::_16 => 15,
42 }
43 }
44}
45
46/// Max count
47#[allow(missing_docs)]
48#[derive(Copy, Clone)]
49pub enum MaxCount {
50 _255,
51 _511,
52 _1023,
53 _2047,
54 _4095,
55 _8191,
56 _16383,
57}
58
59impl Into<u8> for MaxCount {
60 fn into(self) -> u8 {
61 match self {
62 MaxCount::_255 => 0,
63 MaxCount::_511 => 1,
64 MaxCount::_1023 => 2,
65 MaxCount::_2047 => 3,
66 MaxCount::_4095 => 4,
67 MaxCount::_8191 => 5,
68 MaxCount::_16383 => 6,
69 }
70 }
71}
72
73/// Prescaler divider
74#[allow(missing_docs)]
75#[derive(Copy, Clone, PartialEq)]
76pub enum PGPrescalerDivider {
77 _1,
78 _2,
79 _4,
80 _8,
81 _16,
82 _32,
83 _64,
84 _128,
85}
86
87impl Into<u8> for PGPrescalerDivider {
88 fn into(self) -> u8 {
89 match self {
90 PGPrescalerDivider::_1 => 0,
91 PGPrescalerDivider::_2 => 1,
92 PGPrescalerDivider::_4 => 2,
93 PGPrescalerDivider::_8 => 3,
94 PGPrescalerDivider::_16 => 4,
95 PGPrescalerDivider::_32 => 5,
96 PGPrescalerDivider::_64 => 6,
97 PGPrescalerDivider::_128 => 7,
98 }
99 }
100}
101
102/// Error type for SSDeviation
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104pub enum SSDeviationError {
105 /// The provided value is too low (0)
106 ValueTooLow,
107 /// The provided value is too high (greater than 128)
108 ValueTooHigh,
109}
110
111/// Spread Spectrum Deviation
112#[derive(Copy, Clone)]
113pub struct SSDeviation(u8);
114impl SSDeviation {
115 /// Create new deviation value, acceptable inputs are 1-128
116 pub fn new(val: u8) -> Result<Self, SSDeviationError> {
117 if val == 0 {
118 return Err(SSDeviationError::ValueTooLow);
119 } else if val > 128 {
120 return Err(SSDeviationError::ValueTooHigh);
121 }
122 Ok(Self(val - 1))
123 }
124}
125
126impl Into<u8> for SSDeviation {
127 fn into(self) -> u8 {
128 self.0
129 }
130}
131
132/// Peripheral configuration
133#[derive(Clone, Copy)]
134pub struct Config {
135 /// Duration of high state of the charge transfer pulse
136 pub ct_pulse_high_length: ChargeTransferPulseCycle,
137 /// Duration of the low state of the charge transfer pulse
138 pub ct_pulse_low_length: ChargeTransferPulseCycle,
139 /// Enable/disable of spread spectrum feature
140 pub spread_spectrum: bool,
141 /// Adds variable number of periods of the SS clk to pulse high state
142 pub spread_spectrum_deviation: SSDeviation,
143 /// Selects AHB clock divider used to generate SS clk
144 pub spread_spectrum_prescaler: bool,
145 /// Selects AHB clock divider used to generate pulse generator clk
146 pub pulse_generator_prescaler: PGPrescalerDivider,
147 /// Maximum number of charge transfer pulses that can be generated before error
148 pub max_count_value: MaxCount,
149 /// Defines config of all IOs when no ongoing acquisition
150 pub io_default_mode: bool,
151 /// Polarity of sync input pin
152 pub synchro_pin_polarity: bool,
153 /// Acquisition starts when start bit is set or with sync pin input
154 pub acquisition_mode: bool,
155 /// Enable max count interrupt
156 pub max_count_interrupt: bool,
157}
158
159impl Default for Config {
160 fn default() -> Self {
161 Self {
162 ct_pulse_high_length: ChargeTransferPulseCycle::_1,
163 ct_pulse_low_length: ChargeTransferPulseCycle::_1,
164 spread_spectrum: false,
165 spread_spectrum_deviation: SSDeviation::new(1).unwrap(),
166 spread_spectrum_prescaler: false,
167 pulse_generator_prescaler: PGPrescalerDivider::_1,
168 max_count_value: MaxCount::_255,
169 io_default_mode: false,
170 synchro_pin_polarity: false,
171 acquisition_mode: false,
172 max_count_interrupt: false,
173 }
174 }
175}
diff --git a/embassy-stm32/src/tsc/enums.rs b/embassy-stm32/src/tsc/enums.rs
deleted file mode 100644
index 0d34a43ec..000000000
--- a/embassy-stm32/src/tsc/enums.rs
+++ /dev/null
@@ -1,238 +0,0 @@
1use core::ops::BitOr;
2
3/// Pin defines
4#[allow(missing_docs)]
5pub enum TscIOPin {
6 Group1Io1,
7 Group1Io2,
8 Group1Io3,
9 Group1Io4,
10 Group2Io1,
11 Group2Io2,
12 Group2Io3,
13 Group2Io4,
14 Group3Io1,
15 Group3Io2,
16 Group3Io3,
17 Group3Io4,
18 Group4Io1,
19 Group4Io2,
20 Group4Io3,
21 Group4Io4,
22 Group5Io1,
23 Group5Io2,
24 Group5Io3,
25 Group5Io4,
26 Group6Io1,
27 Group6Io2,
28 Group6Io3,
29 Group6Io4,
30 #[cfg(any(tsc_v2, tsc_v3))]
31 Group7Io1,
32 #[cfg(any(tsc_v2, tsc_v3))]
33 Group7Io2,
34 #[cfg(any(tsc_v2, tsc_v3))]
35 Group7Io3,
36 #[cfg(any(tsc_v2, tsc_v3))]
37 Group7Io4,
38 #[cfg(tsc_v3)]
39 Group8Io1,
40 #[cfg(tsc_v3)]
41 Group8Io2,
42 #[cfg(tsc_v3)]
43 Group8Io3,
44 #[cfg(tsc_v3)]
45 Group8Io4,
46}
47
48impl BitOr<TscIOPin> for u32 {
49 type Output = u32;
50 fn bitor(self, rhs: TscIOPin) -> Self::Output {
51 let rhs: u32 = rhs.into();
52 self | rhs
53 }
54}
55
56impl BitOr<u32> for TscIOPin {
57 type Output = u32;
58 fn bitor(self, rhs: u32) -> Self::Output {
59 let val: u32 = self.into();
60 val | rhs
61 }
62}
63
64impl BitOr for TscIOPin {
65 type Output = u32;
66 fn bitor(self, rhs: Self) -> Self::Output {
67 let val: u32 = self.into();
68 let rhs: u32 = rhs.into();
69 val | rhs
70 }
71}
72
73impl Into<u32> for TscIOPin {
74 fn into(self) -> u32 {
75 match self {
76 TscIOPin::Group1Io1 => 0x00000001,
77 TscIOPin::Group1Io2 => 0x00000002,
78 TscIOPin::Group1Io3 => 0x00000004,
79 TscIOPin::Group1Io4 => 0x00000008,
80 TscIOPin::Group2Io1 => 0x00000010,
81 TscIOPin::Group2Io2 => 0x00000020,
82 TscIOPin::Group2Io3 => 0x00000040,
83 TscIOPin::Group2Io4 => 0x00000080,
84 TscIOPin::Group3Io1 => 0x00000100,
85 TscIOPin::Group3Io2 => 0x00000200,
86 TscIOPin::Group3Io3 => 0x00000400,
87 TscIOPin::Group3Io4 => 0x00000800,
88 TscIOPin::Group4Io1 => 0x00001000,
89 TscIOPin::Group4Io2 => 0x00002000,
90 TscIOPin::Group4Io3 => 0x00004000,
91 TscIOPin::Group4Io4 => 0x00008000,
92 TscIOPin::Group5Io1 => 0x00010000,
93 TscIOPin::Group5Io2 => 0x00020000,
94 TscIOPin::Group5Io3 => 0x00040000,
95 TscIOPin::Group5Io4 => 0x00080000,
96 TscIOPin::Group6Io1 => 0x00100000,
97 TscIOPin::Group6Io2 => 0x00200000,
98 TscIOPin::Group6Io3 => 0x00400000,
99 TscIOPin::Group6Io4 => 0x00800000,
100 #[cfg(any(tsc_v2, tsc_v3))]
101 TscIOPin::Group7Io1 => 0x01000000,
102 #[cfg(any(tsc_v2, tsc_v3))]
103 TscIOPin::Group7Io2 => 0x02000000,
104 #[cfg(any(tsc_v2, tsc_v3))]
105 TscIOPin::Group7Io3 => 0x04000000,
106 #[cfg(any(tsc_v2, tsc_v3))]
107 TscIOPin::Group7Io4 => 0x08000000,
108 #[cfg(tsc_v3)]
109 TscIOPin::Group8Io1 => 0x10000000,
110 #[cfg(tsc_v3)]
111 TscIOPin::Group8Io2 => 0x20000000,
112 #[cfg(tsc_v3)]
113 TscIOPin::Group8Io3 => 0x40000000,
114 #[cfg(tsc_v3)]
115 TscIOPin::Group8Io4 => 0x80000000,
116 }
117 }
118}
119
120/// Spread Spectrum Deviation
121#[derive(Copy, Clone)]
122pub struct SSDeviation(u8);
123impl SSDeviation {
124 /// Create new deviation value, acceptable inputs are 1-128
125 pub fn new(val: u8) -> Result<Self, ()> {
126 if val == 0 || val > 128 {
127 return Err(());
128 }
129 Ok(Self(val - 1))
130 }
131}
132
133impl Into<u8> for SSDeviation {
134 fn into(self) -> u8 {
135 self.0
136 }
137}
138
139/// Charge transfer pulse cycles
140#[allow(missing_docs)]
141#[derive(Copy, Clone, PartialEq)]
142pub enum ChargeTransferPulseCycle {
143 _1,
144 _2,
145 _3,
146 _4,
147 _5,
148 _6,
149 _7,
150 _8,
151 _9,
152 _10,
153 _11,
154 _12,
155 _13,
156 _14,
157 _15,
158 _16,
159}
160
161impl Into<u8> for ChargeTransferPulseCycle {
162 fn into(self) -> u8 {
163 match self {
164 ChargeTransferPulseCycle::_1 => 0,
165 ChargeTransferPulseCycle::_2 => 1,
166 ChargeTransferPulseCycle::_3 => 2,
167 ChargeTransferPulseCycle::_4 => 3,
168 ChargeTransferPulseCycle::_5 => 4,
169 ChargeTransferPulseCycle::_6 => 5,
170 ChargeTransferPulseCycle::_7 => 6,
171 ChargeTransferPulseCycle::_8 => 7,
172 ChargeTransferPulseCycle::_9 => 8,
173 ChargeTransferPulseCycle::_10 => 9,
174 ChargeTransferPulseCycle::_11 => 10,
175 ChargeTransferPulseCycle::_12 => 11,
176 ChargeTransferPulseCycle::_13 => 12,
177 ChargeTransferPulseCycle::_14 => 13,
178 ChargeTransferPulseCycle::_15 => 14,
179 ChargeTransferPulseCycle::_16 => 15,
180 }
181 }
182}
183
184/// Prescaler divider
185#[allow(missing_docs)]
186#[derive(Copy, Clone, PartialEq)]
187pub enum PGPrescalerDivider {
188 _1,
189 _2,
190 _4,
191 _8,
192 _16,
193 _32,
194 _64,
195 _128,
196}
197
198impl Into<u8> for PGPrescalerDivider {
199 fn into(self) -> u8 {
200 match self {
201 PGPrescalerDivider::_1 => 0,
202 PGPrescalerDivider::_2 => 1,
203 PGPrescalerDivider::_4 => 2,
204 PGPrescalerDivider::_8 => 3,
205 PGPrescalerDivider::_16 => 4,
206 PGPrescalerDivider::_32 => 5,
207 PGPrescalerDivider::_64 => 6,
208 PGPrescalerDivider::_128 => 7,
209 }
210 }
211}
212
213/// Max count
214#[allow(missing_docs)]
215#[derive(Copy, Clone)]
216pub enum MaxCount {
217 _255,
218 _511,
219 _1023,
220 _2047,
221 _4095,
222 _8191,
223 _16383,
224}
225
226impl Into<u8> for MaxCount {
227 fn into(self) -> u8 {
228 match self {
229 MaxCount::_255 => 0,
230 MaxCount::_511 => 1,
231 MaxCount::_1023 => 2,
232 MaxCount::_2047 => 3,
233 MaxCount::_4095 => 4,
234 MaxCount::_8191 => 5,
235 MaxCount::_16383 => 6,
236 }
237 }
238}
diff --git a/embassy-stm32/src/tsc/errors.rs b/embassy-stm32/src/tsc/errors.rs
new file mode 100644
index 000000000..21f6441ba
--- /dev/null
+++ b/embassy-stm32/src/tsc/errors.rs
@@ -0,0 +1,21 @@
1/// Represents errors that can occur when configuring or validating TSC pin groups.
2#[derive(Debug)]
3pub enum GroupError {
4 /// Error when a group has no sampling capacitor
5 NoSamplingCapacitor,
6 /// Error when a group has neither channel IOs nor a shield IO
7 NoChannelOrShield,
8 /// Error when a group has both channel IOs and a shield IO
9 MixedChannelAndShield,
10 /// Error when there is more than one shield IO across all groups
11 MultipleShields,
12}
13
14/// Error returned when attempting to set an invalid channel pin as active in the TSC.
15#[derive(Debug)]
16pub enum AcquisitionBankError {
17 /// Indicates that one or more of the provided pins is not a valid channel pin.
18 InvalidChannelPin,
19 /// Indicates that multiple channels from the same group were provided.
20 MultipleChannelsPerGroup,
21}
diff --git a/embassy-stm32/src/tsc/io_pin.rs b/embassy-stm32/src/tsc/io_pin.rs
new file mode 100644
index 000000000..ad2010ed7
--- /dev/null
+++ b/embassy-stm32/src/tsc/io_pin.rs
@@ -0,0 +1,200 @@
1use core::marker::PhantomData;
2use core::ops::{BitAnd, BitOr, BitOrAssign};
3
4use super::pin_roles;
5use super::types::Group;
6
7/// Pin defines
8#[allow(missing_docs)]
9#[cfg_attr(feature = "defmt", derive(defmt::Format))]
10#[derive(PartialEq, Clone, Copy, Debug)]
11pub enum IOPin {
12 Group1Io1,
13 Group1Io2,
14 Group1Io3,
15 Group1Io4,
16 Group2Io1,
17 Group2Io2,
18 Group2Io3,
19 Group2Io4,
20 Group3Io1,
21 Group3Io2,
22 Group3Io3,
23 Group3Io4,
24 Group4Io1,
25 Group4Io2,
26 Group4Io3,
27 Group4Io4,
28 Group5Io1,
29 Group5Io2,
30 Group5Io3,
31 Group5Io4,
32 Group6Io1,
33 Group6Io2,
34 Group6Io3,
35 Group6Io4,
36 #[cfg(any(tsc_v2, tsc_v3))]
37 Group7Io1,
38 #[cfg(any(tsc_v2, tsc_v3))]
39 Group7Io2,
40 #[cfg(any(tsc_v2, tsc_v3))]
41 Group7Io3,
42 #[cfg(any(tsc_v2, tsc_v3))]
43 Group7Io4,
44 #[cfg(tsc_v3)]
45 Group8Io1,
46 #[cfg(tsc_v3)]
47 Group8Io2,
48 #[cfg(tsc_v3)]
49 Group8Io3,
50 #[cfg(tsc_v3)]
51 Group8Io4,
52}
53
54/// Represents a TSC I/O pin with associated group and role information.
55///
56/// This type combines an `tsc::IOPin` with phantom type parameters to statically
57/// encode the pin's group and role. This allows for type-safe operations
58/// on TSC pins within their specific contexts.
59///
60/// - `Group`: A type parameter representing the TSC group (e.g., `G1`, `G2`).
61/// - `Role`: A type parameter representing the pin's role (e.g., `Channel`, `Sample`).
62#[derive(Clone, Copy, Debug)]
63pub struct IOPinWithRole<Group, Role: pin_roles::Role> {
64 /// The underlying TSC I/O pin.
65 pub pin: IOPin,
66 pub(super) phantom: PhantomData<(Group, Role)>,
67}
68
69impl<G, R: pin_roles::Role> IOPinWithRole<G, R> {
70 pub(super) fn get_pin(wrapped_pin: IOPinWithRole<G, R>) -> IOPin {
71 wrapped_pin.pin
72 }
73}
74
75impl IOPin {
76 /// Maps this IOPin to the Group it belongs to.
77 ///
78 /// This method provides a convenient way to determine which Group
79 /// a specific TSC I/O pin is associated with.
80 pub const fn group(&self) -> Group {
81 match self {
82 IOPin::Group1Io1 | IOPin::Group1Io2 | IOPin::Group1Io3 | IOPin::Group1Io4 => Group::One,
83 IOPin::Group2Io1 | IOPin::Group2Io2 | IOPin::Group2Io3 | IOPin::Group2Io4 => Group::Two,
84 IOPin::Group3Io1 | IOPin::Group3Io2 | IOPin::Group3Io3 | IOPin::Group3Io4 => Group::Three,
85 IOPin::Group4Io1 | IOPin::Group4Io2 | IOPin::Group4Io3 | IOPin::Group4Io4 => Group::Four,
86 IOPin::Group5Io1 | IOPin::Group5Io2 | IOPin::Group5Io3 | IOPin::Group5Io4 => Group::Five,
87 IOPin::Group6Io1 | IOPin::Group6Io2 | IOPin::Group6Io3 | IOPin::Group6Io4 => Group::Six,
88 #[cfg(any(tsc_v2, tsc_v3))]
89 IOPin::Group7Io1 | IOPin::Group7Io2 | IOPin::Group7Io3 | IOPin::Group7Io4 => Group::Seven,
90 #[cfg(tsc_v3)]
91 IOPin::Group8Io1 | IOPin::Group8Io2 | IOPin::Group8Io3 | IOPin::Group8Io4 => Group::Eight,
92 }
93 }
94
95 /// Returns the `Group` associated with the given `IOPin`.
96 pub fn get_group(pin: IOPin) -> Group {
97 pin.group()
98 }
99}
100
101impl BitOr<IOPin> for u32 {
102 type Output = u32;
103 fn bitor(self, rhs: IOPin) -> Self::Output {
104 let rhs: u32 = rhs.into();
105 self | rhs
106 }
107}
108
109impl BitOr<u32> for IOPin {
110 type Output = u32;
111 fn bitor(self, rhs: u32) -> Self::Output {
112 let val: u32 = self.into();
113 val | rhs
114 }
115}
116
117impl BitOr for IOPin {
118 type Output = u32;
119 fn bitor(self, rhs: Self) -> Self::Output {
120 let val: u32 = self.into();
121 let rhs: u32 = rhs.into();
122 val | rhs
123 }
124}
125
126impl BitOrAssign<IOPin> for u32 {
127 fn bitor_assign(&mut self, rhs: IOPin) {
128 let rhs: u32 = rhs.into();
129 *self |= rhs;
130 }
131}
132
133impl BitAnd<IOPin> for u32 {
134 type Output = u32;
135 fn bitand(self, rhs: IOPin) -> Self::Output {
136 let rhs: u32 = rhs.into();
137 self & rhs
138 }
139}
140
141impl BitAnd<u32> for IOPin {
142 type Output = u32;
143 fn bitand(self, rhs: u32) -> Self::Output {
144 let val: u32 = self.into();
145 val & rhs
146 }
147}
148
149impl IOPin {
150 const fn to_u32(self) -> u32 {
151 match self {
152 IOPin::Group1Io1 => 0x00000001,
153 IOPin::Group1Io2 => 0x00000002,
154 IOPin::Group1Io3 => 0x00000004,
155 IOPin::Group1Io4 => 0x00000008,
156 IOPin::Group2Io1 => 0x00000010,
157 IOPin::Group2Io2 => 0x00000020,
158 IOPin::Group2Io3 => 0x00000040,
159 IOPin::Group2Io4 => 0x00000080,
160 IOPin::Group3Io1 => 0x00000100,
161 IOPin::Group3Io2 => 0x00000200,
162 IOPin::Group3Io3 => 0x00000400,
163 IOPin::Group3Io4 => 0x00000800,
164 IOPin::Group4Io1 => 0x00001000,
165 IOPin::Group4Io2 => 0x00002000,
166 IOPin::Group4Io3 => 0x00004000,
167 IOPin::Group4Io4 => 0x00008000,
168 IOPin::Group5Io1 => 0x00010000,
169 IOPin::Group5Io2 => 0x00020000,
170 IOPin::Group5Io3 => 0x00040000,
171 IOPin::Group5Io4 => 0x00080000,
172 IOPin::Group6Io1 => 0x00100000,
173 IOPin::Group6Io2 => 0x00200000,
174 IOPin::Group6Io3 => 0x00400000,
175 IOPin::Group6Io4 => 0x00800000,
176 #[cfg(any(tsc_v2, tsc_v3))]
177 IOPin::Group7Io1 => 0x01000000,
178 #[cfg(any(tsc_v2, tsc_v3))]
179 IOPin::Group7Io2 => 0x02000000,
180 #[cfg(any(tsc_v2, tsc_v3))]
181 IOPin::Group7Io3 => 0x04000000,
182 #[cfg(any(tsc_v2, tsc_v3))]
183 IOPin::Group7Io4 => 0x08000000,
184 #[cfg(tsc_v3)]
185 IOPin::Group8Io1 => 0x10000000,
186 #[cfg(tsc_v3)]
187 IOPin::Group8Io2 => 0x20000000,
188 #[cfg(tsc_v3)]
189 IOPin::Group8Io3 => 0x40000000,
190 #[cfg(tsc_v3)]
191 IOPin::Group8Io4 => 0x80000000,
192 }
193 }
194}
195
196impl Into<u32> for IOPin {
197 fn into(self) -> u32 {
198 self.to_u32()
199 }
200}
diff --git a/embassy-stm32/src/tsc/mod.rs b/embassy-stm32/src/tsc/mod.rs
index 17240e6bc..0d5c27465 100644
--- a/embassy-stm32/src/tsc/mod.rs
+++ b/embassy-stm32/src/tsc/mod.rs
@@ -1,90 +1,119 @@
1//! TSC Peripheral Interface 1//! TSC Peripheral Interface
2//! 2//!
3//! This module provides an interface for the Touch Sensing Controller (TSC) peripheral.
4//! It supports both blocking and async modes of operation, as well as different TSC versions (v1, v2, v3).
3//! 5//!
4//! # Example (stm32) 6//! # Key Concepts
5//! ``` rust, ignore
6//! 7//!
7//! let mut device_config = embassy_stm32::Config::default(); 8//! - **Pin Groups**: TSC pins are organized into groups, each containing up to four IOs.
8//! { 9//! - **Pin Roles**: Each pin in a group can have a role: Channel, Sample, or Shield.
9//! device_config.rcc.mux = ClockSrc::MSI(Msirange::RANGE_4MHZ); 10//! - **Acquisition Banks**: Used for efficient, repeated TSC acquisitions on specific sets of pins.
10//! } 11//!
12//! # Example (stm32)
11//! 13//!
14//! ```rust
15//! let device_config = embassy_stm32::Config::default();
12//! let context = embassy_stm32::init(device_config); 16//! let context = embassy_stm32::init(device_config);
13//! 17//!
14//! let config = tsc::Config { 18//! let config = tsc::Config {
15//! ct_pulse_high_length: ChargeTransferPulseCycle::_2, 19//! ct_pulse_high_length: ChargeTransferPulseCycle::_4,
16//! ct_pulse_low_length: ChargeTransferPulseCycle::_2, 20//! ct_pulse_low_length: ChargeTransferPulseCycle::_4,
17//! spread_spectrum: false, 21//! spread_spectrum: false,
18//! spread_spectrum_deviation: SSDeviation::new(2).unwrap(), 22//! spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
19//! spread_spectrum_prescaler: false, 23//! spread_spectrum_prescaler: false,
20//! pulse_generator_prescaler: PGPrescalerDivider::_4, 24//! pulse_generator_prescaler: PGPrescalerDivider::_16,
21//! max_count_value: MaxCount::_8191, 25//! max_count_value: MaxCount::_255,
22//! io_default_mode: false, 26//! io_default_mode: false,
23//! synchro_pin_polarity: false, 27//! synchro_pin_polarity: false,
24//! acquisition_mode: false, 28//! acquisition_mode: false,
25//! max_count_interrupt: false, 29//! max_count_interrupt: false,
26//! channel_ios: TscIOPin::Group2Io2 | TscIOPin::Group7Io3,
27//! shield_ios: TscIOPin::Group1Io3.into(),
28//! sampling_ios: TscIOPin::Group1Io2 | TscIOPin::Group2Io1 | TscIOPin::Group7Io2,
29//! }; 30//! };
30//! 31//!
31//! let mut g1: PinGroup<embassy_stm32::peripherals::TSC, G1> = PinGroup::new(); 32//! let mut g2: PinGroupWithRoles<embassy_stm32::peripherals::TSC, G2> = PinGroupWithRoles::new();
32//! g1.set_io2(context.PB13, PinType::Sample); 33//! g2.set_io1::<tsc_pin_roles::Sample>(context.PB4);
33//! g1.set_io3(context.PB14, PinType::Shield); 34//! let sensor_pin = g2.set_io2::<tsc_pin_roles::Channel>(context.PB5);
34//!
35//! let mut g2: PinGroup<embassy_stm32::peripherals::TSC, G2> = PinGroup::new();
36//! g2.set_io1(context.PB4, PinType::Sample);
37//! g2.set_io2(context.PB5, PinType::Channel);
38//! 35//!
39//! let mut g7: PinGroup<embassy_stm32::peripherals::TSC, G7> = PinGroup::new(); 36//! let pin_groups = PinGroups {
40//! g7.set_io2(context.PE3, PinType::Sample); 37//! g2: Some(g2.pin_group),
41//! g7.set_io3(context.PE4, PinType::Channel); 38//! ..Default::default()
39//! };
42//! 40//!
43//! let mut touch_controller = tsc::Tsc::new_blocking( 41//! let mut touch_controller = tsc::Tsc::new_blocking(
44//! context.TSC, 42//! context.TSC,
45//! Some(g1), 43//! pin_groups,
46//! Some(g2),
47//! None,
48//! None,
49//! None,
50//! None,
51//! Some(g7),
52//! None,
53//! config, 44//! config,
54//! ); 45//! ).unwrap();
55//! 46//!
56//! touch_controller.discharge_io(true); 47//! let discharge_delay = 5; // ms
57//! Timer::after_millis(1).await;
58//! 48//!
59//! touch_controller.start(); 49//! loop {
50//! touch_controller.set_active_channels_mask(sensor_pin.pin.into());
51//! touch_controller.start();
52//! touch_controller.poll_for_acquisition();
53//! touch_controller.discharge_io(true);
54//! Timer::after_millis(discharge_delay).await;
60//! 55//!
56//! match touch_controller.group_get_status(sensor_pin.pin.group()) {
57//! GroupStatus::Complete => {
58//! let group_val = touch_controller.group_get_value(sensor_pin.pin.group());
59//! // Process the touch value
60//! // ...
61//! }
62//! GroupStatus::Ongoing => {
63//! // Handle ongoing acquisition
64//! // ...
65//! }
66//! }
67//! }
61//! ``` 68//! ```
69//!
70//! # Async Usage
71//!
72//! For async operation, use `Tsc::new_async` and `pend_for_acquisition` instead of polling.
62 73
63#![macro_use] 74#![macro_use]
64 75
65/// Enums defined for peripheral parameters 76/// Configuration structures and enums for the TSC peripheral.
66pub mod enums; 77pub mod config;
78
79/// Definitions and implementations for TSC pin groups.
80pub mod pin_groups;
81
82/// Definitions and implementations for individual TSC I/O pins.
83pub mod io_pin;
84
85/// Structures and implementations for TSC acquisition banks.
86pub mod acquisition_banks;
87
88/// Core implementation of the TSC (Touch Sensing Controller) driver.
89pub mod tsc;
90
91/// Type definitions used throughout the TSC module.
92pub mod types;
93
94/// Error types and definitions for the TSC module.
95pub mod errors;
67 96
68use core::future::poll_fn;
69use core::marker::PhantomData; 97use core::marker::PhantomData;
70use core::task::Poll;
71 98
72use embassy_hal_internal::{into_ref, PeripheralRef}; 99pub use acquisition_banks::*;
100pub use config::*;
73use embassy_sync::waitqueue::AtomicWaker; 101use embassy_sync::waitqueue::AtomicWaker;
74pub use enums::*; 102pub use errors::*;
103pub use io_pin::*;
104pub use pin_groups::*;
105pub use tsc::*;
106pub use types::*;
75 107
76use crate::gpio::{AfType, AnyPin, OutputType, Speed}; 108use crate::rcc::RccPeripheral;
77use crate::interrupt::typelevel::Interrupt;
78use crate::mode::{Async, Blocking, Mode as PeriMode};
79use crate::rcc::{self, RccPeripheral};
80use crate::{interrupt, peripherals, Peripheral}; 109use crate::{interrupt, peripherals, Peripheral};
81 110
82#[cfg(tsc_v1)] 111#[cfg(tsc_v1)]
83const TSC_NUM_GROUPS: u32 = 6; 112const TSC_NUM_GROUPS: usize = 6;
84#[cfg(tsc_v2)] 113#[cfg(tsc_v2)]
85const TSC_NUM_GROUPS: u32 = 7; 114const TSC_NUM_GROUPS: usize = 7;
86#[cfg(tsc_v3)] 115#[cfg(tsc_v3)]
87const TSC_NUM_GROUPS: u32 = 8; 116const TSC_NUM_GROUPS: usize = 8;
88 117
89/// Error type defined for TSC 118/// Error type defined for TSC
90#[derive(Debug, Clone, Copy)] 119#[derive(Debug, Clone, Copy)]
@@ -106,859 +135,6 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
106 } 135 }
107} 136}
108 137
109/// Pin type definition to control IO parameters
110pub enum PinType {
111 /// Sensing channel pin connected to an electrode
112 Channel,
113 /// Sampling capacitor pin, one required for every pin group
114 Sample,
115 /// Shield pin connected to capacitive sensing shield
116 Shield,
117}
118
119/// Peripheral state
120#[derive(PartialEq, Clone, Copy)]
121pub enum State {
122 /// Peripheral is being setup or reconfigured
123 Reset,
124 /// Ready to start acquisition
125 Ready,
126 /// In process of sensor acquisition
127 Busy,
128 /// Error occured during acquisition
129 Error,
130}
131
132/// Individual group status checked after acquisition reported as complete
133/// For groups with multiple channel pins, may take longer because acquisitions
134/// are done sequentially. Check this status before pulling count for each
135/// sampled channel
136#[derive(PartialEq)]
137pub enum GroupStatus {
138 /// Acquisition for channel still in progress
139 Ongoing,
140 /// Acquisition either not started or complete
141 Complete,
142}
143
144/// Group identifier used to interrogate status
145#[allow(missing_docs)]
146pub enum Group {
147 One,
148 Two,
149 Three,
150 Four,
151 Five,
152 Six,
153 #[cfg(any(tsc_v2, tsc_v3))]
154 Seven,
155 #[cfg(tsc_v3)]
156 Eight,
157}
158
159impl Into<usize> for Group {
160 fn into(self) -> usize {
161 match self {
162 Group::One => 0,
163 Group::Two => 1,
164 Group::Three => 2,
165 Group::Four => 3,
166 Group::Five => 4,
167 Group::Six => 5,
168 #[cfg(any(tsc_v2, tsc_v3))]
169 Group::Seven => 6,
170 #[cfg(tsc_v3)]
171 Group::Eight => 7,
172 }
173 }
174}
175
176/// Peripheral configuration
177#[derive(Clone, Copy)]
178pub struct Config {
179 /// Duration of high state of the charge transfer pulse
180 pub ct_pulse_high_length: ChargeTransferPulseCycle,
181 /// Duration of the low state of the charge transfer pulse
182 pub ct_pulse_low_length: ChargeTransferPulseCycle,
183 /// Enable/disable of spread spectrum feature
184 pub spread_spectrum: bool,
185 /// Adds variable number of periods of the SS clk to pulse high state
186 pub spread_spectrum_deviation: SSDeviation,
187 /// Selects AHB clock divider used to generate SS clk
188 pub spread_spectrum_prescaler: bool,
189 /// Selects AHB clock divider used to generate pulse generator clk
190 pub pulse_generator_prescaler: PGPrescalerDivider,
191 /// Maximum number of charge transfer pulses that can be generated before error
192 pub max_count_value: MaxCount,
193 /// Defines config of all IOs when no ongoing acquisition
194 pub io_default_mode: bool,
195 /// Polarity of sync input pin
196 pub synchro_pin_polarity: bool,
197 /// Acquisition starts when start bit is set or with sync pin input
198 pub acquisition_mode: bool,
199 /// Enable max count interrupt
200 pub max_count_interrupt: bool,
201 /// Channel IO mask
202 pub channel_ios: u32,
203 /// Shield IO mask
204 pub shield_ios: u32,
205 /// Sampling IO mask
206 pub sampling_ios: u32,
207}
208
209impl Default for Config {
210 fn default() -> Self {
211 Self {
212 ct_pulse_high_length: ChargeTransferPulseCycle::_1,
213 ct_pulse_low_length: ChargeTransferPulseCycle::_1,
214 spread_spectrum: false,
215 spread_spectrum_deviation: SSDeviation::new(1).unwrap(),
216 spread_spectrum_prescaler: false,
217 pulse_generator_prescaler: PGPrescalerDivider::_1,
218 max_count_value: MaxCount::_255,
219 io_default_mode: false,
220 synchro_pin_polarity: false,
221 acquisition_mode: false,
222 max_count_interrupt: false,
223 channel_ios: 0,
224 shield_ios: 0,
225 sampling_ios: 0,
226 }
227 }
228}
229
230/// Pin struct that maintains usage
231#[allow(missing_docs)]
232pub struct TscPin<'d, T, C> {
233 _pin: PeripheralRef<'d, AnyPin>,
234 role: PinType,
235 phantom: PhantomData<(T, C)>,
236}
237
238enum GroupError {
239 NoSample,
240 ChannelShield,
241}
242
243/// Pin group definition
244/// Pins are organized into groups of four IOs, all groups with a
245/// sampling channel must also have a sampling capacitor channel.
246#[allow(missing_docs)]
247#[derive(Default)]
248pub struct PinGroup<'d, T, C> {
249 d1: Option<TscPin<'d, T, C>>,
250 d2: Option<TscPin<'d, T, C>>,
251 d3: Option<TscPin<'d, T, C>>,
252 d4: Option<TscPin<'d, T, C>>,
253}
254
255impl<'d, T: Instance, C> PinGroup<'d, T, C> {
256 /// Create new sensing group
257 pub fn new() -> Self {
258 Self {
259 d1: None,
260 d2: None,
261 d3: None,
262 d4: None,
263 }
264 }
265
266 fn contains_shield(&self) -> bool {
267 let mut shield_count = 0;
268
269 if let Some(pin) = &self.d1 {
270 if let PinType::Shield = pin.role {
271 shield_count += 1;
272 }
273 }
274
275 if let Some(pin) = &self.d2 {
276 if let PinType::Shield = pin.role {
277 shield_count += 1;
278 }
279 }
280
281 if let Some(pin) = &self.d3 {
282 if let PinType::Shield = pin.role {
283 shield_count += 1;
284 }
285 }
286
287 if let Some(pin) = &self.d4 {
288 if let PinType::Shield = pin.role {
289 shield_count += 1;
290 }
291 }
292
293 shield_count == 1
294 }
295
296 fn check_group(&self) -> Result<(), GroupError> {
297 let mut channel_count = 0;
298 let mut shield_count = 0;
299 let mut sample_count = 0;
300 if let Some(pin) = &self.d1 {
301 match pin.role {
302 PinType::Channel => {
303 channel_count += 1;
304 }
305 PinType::Shield => {
306 shield_count += 1;
307 }
308 PinType::Sample => {
309 sample_count += 1;
310 }
311 }
312 }
313
314 if let Some(pin) = &self.d2 {
315 match pin.role {
316 PinType::Channel => {
317 channel_count += 1;
318 }
319 PinType::Shield => {
320 shield_count += 1;
321 }
322 PinType::Sample => {
323 sample_count += 1;
324 }
325 }
326 }
327
328 if let Some(pin) = &self.d3 {
329 match pin.role {
330 PinType::Channel => {
331 channel_count += 1;
332 }
333 PinType::Shield => {
334 shield_count += 1;
335 }
336 PinType::Sample => {
337 sample_count += 1;
338 }
339 }
340 }
341
342 if let Some(pin) = &self.d4 {
343 match pin.role {
344 PinType::Channel => {
345 channel_count += 1;
346 }
347 PinType::Shield => {
348 shield_count += 1;
349 }
350 PinType::Sample => {
351 sample_count += 1;
352 }
353 }
354 }
355
356 // Every group requires one sampling capacitor
357 if sample_count != 1 {
358 return Err(GroupError::NoSample);
359 }
360
361 // Each group must have at least one shield or channel IO
362 if shield_count == 0 && channel_count == 0 {
363 return Err(GroupError::ChannelShield);
364 }
365
366 // Any group can either contain channel ios or a shield IO
367 if shield_count != 0 && channel_count != 0 {
368 return Err(GroupError::ChannelShield);
369 }
370
371 // No more than one shield IO is allow per group and amongst all groups
372 if shield_count > 1 {
373 return Err(GroupError::ChannelShield);
374 }
375
376 Ok(())
377 }
378}
379
380macro_rules! group_impl {
381 ($group:ident, $trait1:ident, $trait2:ident, $trait3:ident, $trait4:ident) => {
382 impl<'d, T: Instance> PinGroup<'d, T, $group> {
383 #[doc = concat!("Create a new pin1 for ", stringify!($group), " TSC group instance.")]
384 pub fn set_io1(&mut self, pin: impl Peripheral<P = impl $trait1<T>> + 'd, role: PinType) {
385 into_ref!(pin);
386 critical_section::with(|_| {
387 pin.set_low();
388 pin.set_as_af(
389 pin.af_num(),
390 AfType::output(
391 match role {
392 PinType::Channel => OutputType::PushPull,
393 PinType::Sample => OutputType::OpenDrain,
394 PinType::Shield => OutputType::PushPull,
395 },
396 Speed::VeryHigh,
397 ),
398 );
399 self.d1 = Some(TscPin {
400 _pin: pin.map_into(),
401 role: role,
402 phantom: PhantomData,
403 })
404 })
405 }
406
407 #[doc = concat!("Create a new pin2 for ", stringify!($group), " TSC group instance.")]
408 pub fn set_io2(&mut self, pin: impl Peripheral<P = impl $trait2<T>> + 'd, role: PinType) {
409 into_ref!(pin);
410 critical_section::with(|_| {
411 pin.set_low();
412 pin.set_as_af(
413 pin.af_num(),
414 AfType::output(
415 match role {
416 PinType::Channel => OutputType::PushPull,
417 PinType::Sample => OutputType::OpenDrain,
418 PinType::Shield => OutputType::PushPull,
419 },
420 Speed::VeryHigh,
421 ),
422 );
423 self.d2 = Some(TscPin {
424 _pin: pin.map_into(),
425 role: role,
426 phantom: PhantomData,
427 })
428 })
429 }
430
431 #[doc = concat!("Create a new pin3 for ", stringify!($group), " TSC group instance.")]
432 pub fn set_io3(&mut self, pin: impl Peripheral<P = impl $trait3<T>> + 'd, role: PinType) {
433 into_ref!(pin);
434 critical_section::with(|_| {
435 pin.set_low();
436 pin.set_as_af(
437 pin.af_num(),
438 AfType::output(
439 match role {
440 PinType::Channel => OutputType::PushPull,
441 PinType::Sample => OutputType::OpenDrain,
442 PinType::Shield => OutputType::PushPull,
443 },
444 Speed::VeryHigh,
445 ),
446 );
447 self.d3 = Some(TscPin {
448 _pin: pin.map_into(),
449 role: role,
450 phantom: PhantomData,
451 })
452 })
453 }
454
455 #[doc = concat!("Create a new pin4 for ", stringify!($group), " TSC group instance.")]
456 pub fn set_io4(&mut self, pin: impl Peripheral<P = impl $trait4<T>> + 'd, role: PinType) {
457 into_ref!(pin);
458 critical_section::with(|_| {
459 pin.set_low();
460 pin.set_as_af(
461 pin.af_num(),
462 AfType::output(
463 match role {
464 PinType::Channel => OutputType::PushPull,
465 PinType::Sample => OutputType::OpenDrain,
466 PinType::Shield => OutputType::PushPull,
467 },
468 Speed::VeryHigh,
469 ),
470 );
471 self.d4 = Some(TscPin {
472 _pin: pin.map_into(),
473 role: role,
474 phantom: PhantomData,
475 })
476 })
477 }
478 }
479 };
480}
481
482group_impl!(G1, G1IO1Pin, G1IO2Pin, G1IO3Pin, G1IO4Pin);
483group_impl!(G2, G2IO1Pin, G2IO2Pin, G2IO3Pin, G2IO4Pin);
484group_impl!(G3, G3IO1Pin, G3IO2Pin, G3IO3Pin, G3IO4Pin);
485group_impl!(G4, G4IO1Pin, G4IO2Pin, G4IO3Pin, G4IO4Pin);
486group_impl!(G5, G5IO1Pin, G5IO2Pin, G5IO3Pin, G5IO4Pin);
487group_impl!(G6, G6IO1Pin, G6IO2Pin, G6IO3Pin, G6IO4Pin);
488group_impl!(G7, G7IO1Pin, G7IO2Pin, G7IO3Pin, G7IO4Pin);
489group_impl!(G8, G8IO1Pin, G8IO2Pin, G8IO3Pin, G8IO4Pin);
490
491/// Group 1 marker type.
492pub enum G1 {}
493/// Group 2 marker type.
494pub enum G2 {}
495/// Group 3 marker type.
496pub enum G3 {}
497/// Group 4 marker type.
498pub enum G4 {}
499/// Group 5 marker type.
500pub enum G5 {}
501/// Group 6 marker type.
502pub enum G6 {}
503/// Group 7 marker type.
504pub enum G7 {}
505/// Group 8 marker type.
506pub enum G8 {}
507
508/// TSC driver
509pub struct Tsc<'d, T: Instance, K: PeriMode> {
510 _peri: PeripheralRef<'d, T>,
511 _g1: Option<PinGroup<'d, T, G1>>,
512 _g2: Option<PinGroup<'d, T, G2>>,
513 _g3: Option<PinGroup<'d, T, G3>>,
514 _g4: Option<PinGroup<'d, T, G4>>,
515 _g5: Option<PinGroup<'d, T, G5>>,
516 _g6: Option<PinGroup<'d, T, G6>>,
517 #[cfg(any(tsc_v2, tsc_v3))]
518 _g7: Option<PinGroup<'d, T, G7>>,
519 #[cfg(tsc_v3)]
520 _g8: Option<PinGroup<'d, T, G8>>,
521 state: State,
522 config: Config,
523 _kind: PhantomData<K>,
524}
525
526impl<'d, T: Instance> Tsc<'d, T, Async> {
527 /// Create a Tsc instance that can be awaited for completion
528 pub fn new_async(
529 peri: impl Peripheral<P = T> + 'd,
530 g1: Option<PinGroup<'d, T, G1>>,
531 g2: Option<PinGroup<'d, T, G2>>,
532 g3: Option<PinGroup<'d, T, G3>>,
533 g4: Option<PinGroup<'d, T, G4>>,
534 g5: Option<PinGroup<'d, T, G5>>,
535 g6: Option<PinGroup<'d, T, G6>>,
536 #[cfg(any(tsc_v2, tsc_v3))] g7: Option<PinGroup<'d, T, G7>>,
537 #[cfg(tsc_v3)] g8: Option<PinGroup<'d, T, G8>>,
538 config: Config,
539 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
540 ) -> Self {
541 // Need to check valid pin configuration input
542 let g1 = g1.filter(|b| b.check_group().is_ok());
543 let g2 = g2.filter(|b| b.check_group().is_ok());
544 let g3 = g3.filter(|b| b.check_group().is_ok());
545 let g4 = g4.filter(|b| b.check_group().is_ok());
546 let g5 = g5.filter(|b| b.check_group().is_ok());
547 let g6 = g6.filter(|b| b.check_group().is_ok());
548 #[cfg(any(tsc_v2, tsc_v3))]
549 let g7 = g7.filter(|b| b.check_group().is_ok());
550 #[cfg(tsc_v3)]
551 let g8 = g8.filter(|b| b.check_group().is_ok());
552
553 match Self::check_shields(
554 &g1,
555 &g2,
556 &g3,
557 &g4,
558 &g5,
559 &g6,
560 #[cfg(any(tsc_v2, tsc_v3))]
561 &g7,
562 #[cfg(tsc_v3)]
563 &g8,
564 ) {
565 Ok(()) => Self::new_inner(
566 peri,
567 g1,
568 g2,
569 g3,
570 g4,
571 g5,
572 g6,
573 #[cfg(any(tsc_v2, tsc_v3))]
574 g7,
575 #[cfg(tsc_v3)]
576 g8,
577 config,
578 ),
579 Err(_) => Self::new_inner(
580 peri,
581 None,
582 None,
583 None,
584 None,
585 None,
586 None,
587 #[cfg(any(tsc_v2, tsc_v3))]
588 None,
589 #[cfg(tsc_v3)]
590 None,
591 config,
592 ),
593 }
594 }
595 /// Asyncronously wait for the end of an acquisition
596 pub async fn pend_for_acquisition(&mut self) {
597 poll_fn(|cx| match self.get_state() {
598 State::Busy => {
599 T::waker().register(cx.waker());
600 T::regs().ier().write(|w| w.set_eoaie(true));
601 if self.get_state() != State::Busy {
602 T::regs().ier().write(|w| w.set_eoaie(false));
603 return Poll::Ready(());
604 }
605 Poll::Pending
606 }
607 _ => {
608 T::regs().ier().write(|w| w.set_eoaie(false));
609 Poll::Ready(())
610 }
611 })
612 .await;
613 }
614}
615
616impl<'d, T: Instance> Tsc<'d, T, Blocking> {
617 /// Create a Tsc instance that must be polled for completion
618 pub fn new_blocking(
619 peri: impl Peripheral<P = T> + 'd,
620 g1: Option<PinGroup<'d, T, G1>>,
621 g2: Option<PinGroup<'d, T, G2>>,
622 g3: Option<PinGroup<'d, T, G3>>,
623 g4: Option<PinGroup<'d, T, G4>>,
624 g5: Option<PinGroup<'d, T, G5>>,
625 g6: Option<PinGroup<'d, T, G6>>,
626 #[cfg(any(tsc_v2, tsc_v3))] g7: Option<PinGroup<'d, T, G7>>,
627 #[cfg(tsc_v3)] g8: Option<PinGroup<'d, T, G8>>,
628 config: Config,
629 ) -> Self {
630 // Need to check valid pin configuration input
631 let g1 = g1.filter(|b| b.check_group().is_ok());
632 let g2 = g2.filter(|b| b.check_group().is_ok());
633 let g3 = g3.filter(|b| b.check_group().is_ok());
634 let g4 = g4.filter(|b| b.check_group().is_ok());
635 let g5 = g5.filter(|b| b.check_group().is_ok());
636 let g6 = g6.filter(|b| b.check_group().is_ok());
637 #[cfg(any(tsc_v2, tsc_v3))]
638 let g7 = g7.filter(|b| b.check_group().is_ok());
639 #[cfg(tsc_v3)]
640 let g8 = g8.filter(|b| b.check_group().is_ok());
641
642 match Self::check_shields(
643 &g1,
644 &g2,
645 &g3,
646 &g4,
647 &g5,
648 &g6,
649 #[cfg(any(tsc_v2, tsc_v3))]
650 &g7,
651 #[cfg(tsc_v3)]
652 &g8,
653 ) {
654 Ok(()) => Self::new_inner(
655 peri,
656 g1,
657 g2,
658 g3,
659 g4,
660 g5,
661 g6,
662 #[cfg(any(tsc_v2, tsc_v3))]
663 g7,
664 #[cfg(tsc_v3)]
665 g8,
666 config,
667 ),
668 Err(_) => Self::new_inner(
669 peri,
670 None,
671 None,
672 None,
673 None,
674 None,
675 None,
676 #[cfg(any(tsc_v2, tsc_v3))]
677 None,
678 #[cfg(tsc_v3)]
679 None,
680 config,
681 ),
682 }
683 }
684 /// Wait for end of acquisition
685 pub fn poll_for_acquisition(&mut self) {
686 while self.get_state() == State::Busy {}
687 }
688}
689
690impl<'d, T: Instance, K: PeriMode> Tsc<'d, T, K> {
691 /// Create new TSC driver
692 fn check_shields(
693 g1: &Option<PinGroup<'d, T, G1>>,
694 g2: &Option<PinGroup<'d, T, G2>>,
695 g3: &Option<PinGroup<'d, T, G3>>,
696 g4: &Option<PinGroup<'d, T, G4>>,
697 g5: &Option<PinGroup<'d, T, G5>>,
698 g6: &Option<PinGroup<'d, T, G6>>,
699 #[cfg(any(tsc_v2, tsc_v3))] g7: &Option<PinGroup<'d, T, G7>>,
700 #[cfg(tsc_v3)] g8: &Option<PinGroup<'d, T, G8>>,
701 ) -> Result<(), GroupError> {
702 let mut shield_count = 0;
703
704 if let Some(pin_group) = g1 {
705 if pin_group.contains_shield() {
706 shield_count += 1;
707 }
708 };
709 if let Some(pin_group) = g2 {
710 if pin_group.contains_shield() {
711 shield_count += 1;
712 }
713 };
714 if let Some(pin_group) = g3 {
715 if pin_group.contains_shield() {
716 shield_count += 1;
717 }
718 };
719 if let Some(pin_group) = g4 {
720 if pin_group.contains_shield() {
721 shield_count += 1;
722 }
723 };
724 if let Some(pin_group) = g5 {
725 if pin_group.contains_shield() {
726 shield_count += 1;
727 }
728 };
729 if let Some(pin_group) = g6 {
730 if pin_group.contains_shield() {
731 shield_count += 1;
732 }
733 };
734 #[cfg(any(tsc_v2, tsc_v3))]
735 if let Some(pin_group) = g7 {
736 if pin_group.contains_shield() {
737 shield_count += 1;
738 }
739 };
740 #[cfg(tsc_v3)]
741 if let Some(pin_group) = g8 {
742 if pin_group.contains_shield() {
743 shield_count += 1;
744 }
745 };
746
747 if shield_count > 1 {
748 return Err(GroupError::ChannelShield);
749 }
750
751 Ok(())
752 }
753
754 fn extract_groups(io_mask: u32) -> u32 {
755 let mut groups: u32 = 0;
756 for idx in 0..TSC_NUM_GROUPS {
757 if io_mask & (0x0F << idx * 4) != 0 {
758 groups |= 1 << idx
759 }
760 }
761 groups
762 }
763
764 fn new_inner(
765 peri: impl Peripheral<P = T> + 'd,
766 g1: Option<PinGroup<'d, T, G1>>,
767 g2: Option<PinGroup<'d, T, G2>>,
768 g3: Option<PinGroup<'d, T, G3>>,
769 g4: Option<PinGroup<'d, T, G4>>,
770 g5: Option<PinGroup<'d, T, G5>>,
771 g6: Option<PinGroup<'d, T, G6>>,
772 #[cfg(any(tsc_v2, tsc_v3))] g7: Option<PinGroup<'d, T, G7>>,
773 #[cfg(tsc_v3)] g8: Option<PinGroup<'d, T, G8>>,
774 config: Config,
775 ) -> Self {
776 into_ref!(peri);
777
778 rcc::enable_and_reset::<T>();
779
780 T::regs().cr().modify(|w| {
781 w.set_tsce(true);
782 w.set_ctph(config.ct_pulse_high_length.into());
783 w.set_ctpl(config.ct_pulse_low_length.into());
784 w.set_sse(config.spread_spectrum);
785 // Prevent invalid configuration for pulse generator prescaler
786 if config.ct_pulse_low_length == ChargeTransferPulseCycle::_1
787 && (config.pulse_generator_prescaler == PGPrescalerDivider::_1
788 || config.pulse_generator_prescaler == PGPrescalerDivider::_2)
789 {
790 w.set_pgpsc(PGPrescalerDivider::_4.into());
791 } else if config.ct_pulse_low_length == ChargeTransferPulseCycle::_2
792 && config.pulse_generator_prescaler == PGPrescalerDivider::_1
793 {
794 w.set_pgpsc(PGPrescalerDivider::_2.into());
795 } else {
796 w.set_pgpsc(config.pulse_generator_prescaler.into());
797 }
798 w.set_ssd(config.spread_spectrum_deviation.into());
799 w.set_sspsc(config.spread_spectrum_prescaler);
800
801 w.set_mcv(config.max_count_value.into());
802 w.set_syncpol(config.synchro_pin_polarity);
803 w.set_am(config.acquisition_mode);
804 });
805
806 // Set IO configuration
807 // Disable Schmitt trigger hysteresis on all used TSC IOs
808 T::regs()
809 .iohcr()
810 .write(|w| w.0 = !(config.channel_ios | config.shield_ios | config.sampling_ios));
811
812 // Set channel and shield IOs
813 T::regs()
814 .ioccr()
815 .write(|w| w.0 = config.channel_ios | config.shield_ios);
816
817 // Set sampling IOs
818 T::regs().ioscr().write(|w| w.0 = config.sampling_ios);
819
820 // Set the groups to be acquired
821 T::regs()
822 .iogcsr()
823 .write(|w| w.0 = Self::extract_groups(config.channel_ios));
824
825 // Disable interrupts
826 T::regs().ier().modify(|w| {
827 w.set_eoaie(false);
828 w.set_mceie(false);
829 });
830
831 // Clear flags
832 T::regs().icr().modify(|w| {
833 w.set_eoaic(true);
834 w.set_mceic(true);
835 });
836
837 unsafe {
838 T::Interrupt::enable();
839 }
840
841 Self {
842 _peri: peri,
843 _g1: g1,
844 _g2: g2,
845 _g3: g3,
846 _g4: g4,
847 _g5: g5,
848 _g6: g6,
849 #[cfg(any(tsc_v2, tsc_v3))]
850 _g7: g7,
851 #[cfg(tsc_v3)]
852 _g8: g8,
853 state: State::Ready,
854 config,
855 _kind: PhantomData,
856 }
857 }
858
859 /// Start charge transfer acquisition
860 pub fn start(&mut self) {
861 self.state = State::Busy;
862
863 // Disable interrupts
864 T::regs().ier().modify(|w| {
865 w.set_eoaie(false);
866 w.set_mceie(false);
867 });
868
869 // Clear flags
870 T::regs().icr().modify(|w| {
871 w.set_eoaic(true);
872 w.set_mceic(true);
873 });
874
875 // Set the touch sensing IOs not acquired to the default mode
876 T::regs().cr().modify(|w| {
877 w.set_iodef(self.config.io_default_mode);
878 });
879
880 // Start the acquisition
881 T::regs().cr().modify(|w| {
882 w.set_start(true);
883 });
884 }
885
886 /// Stop charge transfer acquisition
887 pub fn stop(&mut self) {
888 T::regs().cr().modify(|w| {
889 w.set_start(false);
890 });
891
892 // Set the touch sensing IOs in low power mode
893 T::regs().cr().modify(|w| {
894 w.set_iodef(false);
895 });
896
897 // Clear flags
898 T::regs().icr().modify(|w| {
899 w.set_eoaic(true);
900 w.set_mceic(true);
901 });
902
903 self.state = State::Ready;
904 }
905
906 /// Get current state of acquisition
907 pub fn get_state(&mut self) -> State {
908 if self.state == State::Busy {
909 if T::regs().isr().read().eoaf() {
910 if T::regs().isr().read().mcef() {
911 self.state = State::Error
912 } else {
913 self.state = State::Ready
914 }
915 }
916 }
917 self.state
918 }
919
920 /// Get the individual group status to check acquisition complete
921 pub fn group_get_status(&mut self, index: Group) -> GroupStatus {
922 // Status bits are set by hardware when the acquisition on the corresponding
923 // enabled analog IO group is complete, cleared when new acquisition is started
924 let status = match index {
925 Group::One => T::regs().iogcsr().read().g1s(),
926 Group::Two => T::regs().iogcsr().read().g2s(),
927 Group::Three => T::regs().iogcsr().read().g3s(),
928 Group::Four => T::regs().iogcsr().read().g4s(),
929 Group::Five => T::regs().iogcsr().read().g5s(),
930 Group::Six => T::regs().iogcsr().read().g6s(),
931 #[cfg(any(tsc_v2, tsc_v3))]
932 Group::Seven => T::regs().iogcsr().read().g7s(),
933 #[cfg(tsc_v3)]
934 Group::Eight => T::regs().iogcsr().read().g8s(),
935 };
936 match status {
937 true => GroupStatus::Complete,
938 false => GroupStatus::Ongoing,
939 }
940 }
941
942 /// Get the count for the acquisiton, valid once group status is set
943 pub fn group_get_value(&mut self, index: Group) -> u16 {
944 T::regs().iogcr(index.into()).read().cnt()
945 }
946
947 /// Discharge the IOs for subsequent acquisition
948 pub fn discharge_io(&mut self, status: bool) {
949 // Set the touch sensing IOs in low power mode
950 T::regs().cr().modify(|w| {
951 w.set_iodef(!status);
952 });
953 }
954}
955
956impl<'d, T: Instance, K: PeriMode> Drop for Tsc<'d, T, K> {
957 fn drop(&mut self) {
958 rcc::disable::<T>();
959 }
960}
961
962pub(crate) trait SealedInstance { 138pub(crate) trait SealedInstance {
963 fn regs() -> crate::pac::tsc::Tsc; 139 fn regs() -> crate::pac::tsc::Tsc;
964 fn waker() -> &'static AtomicWaker; 140 fn waker() -> &'static AtomicWaker;
@@ -988,36 +164,3 @@ foreach_interrupt!(
988 } 164 }
989 }; 165 };
990); 166);
991
992pin_trait!(G1IO1Pin, Instance);
993pin_trait!(G1IO2Pin, Instance);
994pin_trait!(G1IO3Pin, Instance);
995pin_trait!(G1IO4Pin, Instance);
996pin_trait!(G2IO1Pin, Instance);
997pin_trait!(G2IO2Pin, Instance);
998pin_trait!(G2IO3Pin, Instance);
999pin_trait!(G2IO4Pin, Instance);
1000pin_trait!(G3IO1Pin, Instance);
1001pin_trait!(G3IO2Pin, Instance);
1002pin_trait!(G3IO3Pin, Instance);
1003pin_trait!(G3IO4Pin, Instance);
1004pin_trait!(G4IO1Pin, Instance);
1005pin_trait!(G4IO2Pin, Instance);
1006pin_trait!(G4IO3Pin, Instance);
1007pin_trait!(G4IO4Pin, Instance);
1008pin_trait!(G5IO1Pin, Instance);
1009pin_trait!(G5IO2Pin, Instance);
1010pin_trait!(G5IO3Pin, Instance);
1011pin_trait!(G5IO4Pin, Instance);
1012pin_trait!(G6IO1Pin, Instance);
1013pin_trait!(G6IO2Pin, Instance);
1014pin_trait!(G6IO3Pin, Instance);
1015pin_trait!(G6IO4Pin, Instance);
1016pin_trait!(G7IO1Pin, Instance);
1017pin_trait!(G7IO2Pin, Instance);
1018pin_trait!(G7IO3Pin, Instance);
1019pin_trait!(G7IO4Pin, Instance);
1020pin_trait!(G8IO1Pin, Instance);
1021pin_trait!(G8IO2Pin, Instance);
1022pin_trait!(G8IO3Pin, Instance);
1023pin_trait!(G8IO4Pin, Instance);
diff --git a/embassy-stm32/src/tsc/pin_groups.rs b/embassy-stm32/src/tsc/pin_groups.rs
new file mode 100644
index 000000000..1f3aafa35
--- /dev/null
+++ b/embassy-stm32/src/tsc/pin_groups.rs
@@ -0,0 +1,669 @@
1use core::marker::PhantomData;
2use core::ops::BitOr;
3
4use embassy_hal_internal::{into_ref, PeripheralRef};
5
6use super::errors::GroupError;
7use super::io_pin::*;
8use super::Instance;
9use crate::gpio::{AfType, AnyPin, OutputType, Speed};
10use crate::Peripheral;
11
12/// Pin type definition to control IO parameters
13#[derive(PartialEq, Clone, Copy)]
14pub enum PinType {
15 /// Sensing channel pin connected to an electrode
16 Channel,
17 /// Sampling capacitor pin, one required for every pin group
18 Sample,
19 /// Shield pin connected to capacitive sensing shield
20 Shield,
21}
22
23/// Pin struct that maintains usage
24#[allow(missing_docs)]
25pub struct Pin<'d, T, Group> {
26 _pin: PeripheralRef<'d, AnyPin>,
27 role: PinType,
28 tsc_io_pin: IOPin,
29 phantom: PhantomData<(T, Group)>,
30}
31
32impl<'d, T, Group> Pin<'d, T, Group> {
33 /// Returns the role of this TSC pin.
34 ///
35 /// The role indicates whether this pin is configured as a channel,
36 /// sampling capacitor, or shield in the TSC group.
37 ///
38 /// # Returns
39 /// The `PinType` representing the role of this pin.
40 pub fn role(&self) -> PinType {
41 self.role
42 }
43
44 /// Returns the TSC IO pin associated with this pin.
45 ///
46 /// This method provides access to the specific TSC IO pin configuration,
47 /// which includes information about the pin's group and position within that group.
48 ///
49 /// # Returns
50 /// The `IOPin` representing this pin's TSC-specific configuration.
51 pub fn tsc_io_pin(&self) -> IOPin {
52 self.tsc_io_pin
53 }
54}
55
56/// Represents a group of TSC (Touch Sensing Controller) pins.
57///
58/// In the TSC peripheral, pins are organized into groups of four IOs. Each group
59/// must have exactly one sampling capacitor pin and can have multiple channel pins
60/// or a single shield pin. This structure encapsulates these pin configurations
61/// for a single TSC group.
62///
63/// # Pin Roles
64/// - Sampling Capacitor: One required per group, used for charge transfer.
65/// - Channel: Sensing pins connected to electrodes for touch detection.
66/// - Shield: Optional, used for active shielding to improve sensitivity.
67///
68/// # Constraints
69/// - Each group must have exactly one sampling capacitor pin.
70/// - A group can have either channel pins or a shield pin, but not both.
71/// - No more than one shield pin is allowed across all groups.
72#[allow(missing_docs)]
73pub struct PinGroup<'d, T, Group> {
74 pin1: Option<Pin<'d, T, Group>>,
75 pin2: Option<Pin<'d, T, Group>>,
76 pin3: Option<Pin<'d, T, Group>>,
77 pin4: Option<Pin<'d, T, Group>>,
78}
79
80impl<'d, T, G> Default for PinGroup<'d, T, G> {
81 fn default() -> Self {
82 Self {
83 pin1: None,
84 pin2: None,
85 pin3: None,
86 pin4: None,
87 }
88 }
89}
90
91/// Defines roles and traits for TSC (Touch Sensing Controller) pins.
92///
93/// This module contains marker types and traits that represent different roles
94/// a TSC pin can have, such as channel, sample, or shield.
95pub mod pin_roles {
96 use super::{OutputType, PinType};
97
98 /// Marker type for a TSC channel pin.
99 #[derive(PartialEq, Clone, Copy, Debug)]
100 pub struct Channel;
101
102 /// Marker type for a TSC sampling pin.
103 #[derive(PartialEq, Clone, Copy, Debug)]
104 pub struct Sample;
105
106 /// Marker type for a TSC shield pin.
107 #[derive(PartialEq, Clone, Copy, Debug)]
108 pub struct Shield;
109
110 /// Trait for TSC pin roles.
111 ///
112 /// This trait defines the behavior and properties of different TSC pin roles.
113 /// It is implemented by the marker types `Channel`, `Sample`, and `Shield`.
114 pub trait Role {
115 /// Returns the `PinType` associated with this role.
116 fn pin_type() -> PinType;
117
118 /// Returns the `OutputType` associated with this role.
119 fn output_type() -> OutputType;
120 }
121
122 impl Role for Channel {
123 fn pin_type() -> PinType {
124 PinType::Channel
125 }
126 fn output_type() -> OutputType {
127 OutputType::PushPull
128 }
129 }
130
131 impl Role for Sample {
132 fn pin_type() -> PinType {
133 PinType::Sample
134 }
135 fn output_type() -> OutputType {
136 OutputType::OpenDrain
137 }
138 }
139
140 impl Role for Shield {
141 fn pin_type() -> PinType {
142 PinType::Shield
143 }
144 fn output_type() -> OutputType {
145 OutputType::PushPull
146 }
147 }
148}
149
150/// Represents a group of TSC pins with their associated roles.
151///
152/// This struct allows for type-safe configuration of TSC pin groups,
153/// ensuring that pins are assigned appropriate roles within their group.
154/// This type is essentially just a wrapper type around a `PinGroup` value.
155///
156/// # Type Parameters
157/// - `'d`: Lifetime of the pin group.
158/// - `T`: The TSC instance type.
159/// - `G`: The group identifier.
160/// - `R1`, `R2`, `R3`, `R4`: Role types for each pin in the group, defaulting to `Channel`.
161pub struct PinGroupWithRoles<
162 'd,
163 T: Instance,
164 G,
165 R1 = pin_roles::Channel,
166 R2 = pin_roles::Channel,
167 R3 = pin_roles::Channel,
168 R4 = pin_roles::Channel,
169> {
170 /// The underlying pin group without role information.
171 pub pin_group: PinGroup<'d, T, G>,
172 _phantom: PhantomData<(R1, R2, R3, R4)>,
173}
174
175impl<'d, T: Instance, G, R1, R2, R3, R4> Default for PinGroupWithRoles<'d, T, G, R1, R2, R3, R4> {
176 fn default() -> Self {
177 Self {
178 pin_group: PinGroup::default(),
179 _phantom: PhantomData,
180 }
181 }
182}
183
184impl<'d, T: Instance, G> PinGroup<'d, T, G> {
185 fn contains_exactly_one_shield_pin(&self) -> bool {
186 let shield_count = self.shield_pins().count();
187 shield_count == 1
188 }
189
190 fn check_group(&self) -> Result<(), GroupError> {
191 let mut channel_count = 0;
192 let mut shield_count = 0;
193 let mut sample_count = 0;
194 for pin in self.pins().into_iter().flatten() {
195 match pin.role {
196 PinType::Channel => {
197 channel_count += 1;
198 }
199 PinType::Shield => {
200 shield_count += 1;
201 }
202 PinType::Sample => {
203 sample_count += 1;
204 }
205 }
206 }
207
208 // Every group requires exactly one sampling capacitor
209 if sample_count != 1 {
210 return Err(GroupError::NoSamplingCapacitor);
211 }
212
213 // Each group must have at least one shield or channel IO
214 if shield_count == 0 && channel_count == 0 {
215 return Err(GroupError::NoChannelOrShield);
216 }
217
218 // Any group can either contain channel ios or a shield IO.
219 // (An active shield requires its own sampling capacitor)
220 if shield_count != 0 && channel_count != 0 {
221 return Err(GroupError::MixedChannelAndShield);
222 }
223
224 // No more than one shield IO is allow per group and amongst all groups
225 if shield_count > 1 {
226 return Err(GroupError::MultipleShields);
227 }
228
229 Ok(())
230 }
231
232 /// Returns a reference to the first pin in the group, if configured.
233 pub fn pin1(&self) -> Option<&Pin<'d, T, G>> {
234 self.pin1.as_ref()
235 }
236
237 /// Returns a reference to the second pin in the group, if configured.
238 pub fn pin2(&self) -> Option<&Pin<'d, T, G>> {
239 self.pin2.as_ref()
240 }
241
242 /// Returns a reference to the third pin in the group, if configured.
243 pub fn pin3(&self) -> Option<&Pin<'d, T, G>> {
244 self.pin3.as_ref()
245 }
246
247 /// Returns a reference to the fourth pin in the group, if configured.
248 pub fn pin4(&self) -> Option<&Pin<'d, T, G>> {
249 self.pin4.as_ref()
250 }
251
252 fn sample_pins(&self) -> impl Iterator<Item = IOPin> + '_ {
253 self.pins_filtered(PinType::Sample)
254 }
255
256 fn shield_pins(&self) -> impl Iterator<Item = IOPin> + '_ {
257 self.pins_filtered(PinType::Shield)
258 }
259
260 fn channel_pins(&self) -> impl Iterator<Item = IOPin> + '_ {
261 self.pins_filtered(PinType::Channel)
262 }
263
264 fn pins_filtered(&self, pin_type: PinType) -> impl Iterator<Item = IOPin> + '_ {
265 self.pins().into_iter().filter_map(move |pin| {
266 pin.as_ref()
267 .and_then(|p| if p.role == pin_type { Some(p.tsc_io_pin) } else { None })
268 })
269 }
270
271 fn make_channel_ios_mask(&self) -> u32 {
272 self.channel_pins().fold(0, u32::bitor)
273 }
274
275 fn make_shield_ios_mask(&self) -> u32 {
276 self.shield_pins().fold(0, u32::bitor)
277 }
278
279 fn make_sample_ios_mask(&self) -> u32 {
280 self.sample_pins().fold(0, u32::bitor)
281 }
282
283 fn pins(&self) -> [&Option<Pin<'d, T, G>>; 4] {
284 [&self.pin1, &self.pin2, &self.pin3, &self.pin4]
285 }
286
287 fn pins_mut(&mut self) -> [&mut Option<Pin<'d, T, G>>; 4] {
288 [&mut self.pin1, &mut self.pin2, &mut self.pin3, &mut self.pin4]
289 }
290}
291
292#[cfg(any(tsc_v2, tsc_v3))]
293macro_rules! TSC_V2_V3_GUARD {
294 ($e:expr) => {{
295 #[cfg(any(tsc_v2, tsc_v3))]
296 {
297 $e
298 }
299 #[cfg(not(any(tsc_v2, tsc_v3)))]
300 {
301 compile_error!("Group 7 is not supported in this TSC version")
302 }
303 }};
304}
305
306#[cfg(tsc_v3)]
307macro_rules! TSC_V3_GUARD {
308 ($e:expr) => {{
309 #[cfg(tsc_v3)]
310 {
311 $e
312 }
313 #[cfg(not(tsc_v3))]
314 {
315 compile_error!("Group 8 is not supported in this TSC version")
316 }
317 }};
318}
319
320macro_rules! trait_to_io_pin {
321 (G1IO1Pin) => {
322 IOPin::Group1Io1
323 };
324 (G1IO2Pin) => {
325 IOPin::Group1Io2
326 };
327 (G1IO3Pin) => {
328 IOPin::Group1Io3
329 };
330 (G1IO4Pin) => {
331 IOPin::Group1Io4
332 };
333
334 (G2IO1Pin) => {
335 IOPin::Group2Io1
336 };
337 (G2IO2Pin) => {
338 IOPin::Group2Io2
339 };
340 (G2IO3Pin) => {
341 IOPin::Group2Io3
342 };
343 (G2IO4Pin) => {
344 IOPin::Group2Io4
345 };
346
347 (G3IO1Pin) => {
348 IOPin::Group3Io1
349 };
350 (G3IO2Pin) => {
351 IOPin::Group3Io2
352 };
353 (G3IO3Pin) => {
354 IOPin::Group3Io3
355 };
356 (G3IO4Pin) => {
357 IOPin::Group3Io4
358 };
359
360 (G4IO1Pin) => {
361 IOPin::Group4Io1
362 };
363 (G4IO2Pin) => {
364 IOPin::Group4Io2
365 };
366 (G4IO3Pin) => {
367 IOPin::Group4Io3
368 };
369 (G4IO4Pin) => {
370 IOPin::Group4Io4
371 };
372
373 (G5IO1Pin) => {
374 IOPin::Group5Io1
375 };
376 (G5IO2Pin) => {
377 IOPin::Group5Io2
378 };
379 (G5IO3Pin) => {
380 IOPin::Group5Io3
381 };
382 (G5IO4Pin) => {
383 IOPin::Group5Io4
384 };
385
386 (G6IO1Pin) => {
387 IOPin::Group6Io1
388 };
389 (G6IO2Pin) => {
390 IOPin::Group6Io2
391 };
392 (G6IO3Pin) => {
393 IOPin::Group6Io3
394 };
395 (G6IO4Pin) => {
396 IOPin::Group6Io4
397 };
398
399 (G7IO1Pin) => {
400 TSC_V2_V3_GUARD!(IOPin::Group7Io1)
401 };
402 (G7IO2Pin) => {
403 TSC_V2_V3_GUARD!(IOPin::Group7Io2)
404 };
405 (G7IO3Pin) => {
406 TSC_V2_V3_GUARD!(IOPin::Group7Io3)
407 };
408 (G7IO4Pin) => {
409 TSC_V2_V3_GUARD!(IOPin::Group7Io4)
410 };
411
412 (G8IO1Pin) => {
413 TSC_V3_GUARD!(IOPin::Group8Io1)
414 };
415 (G8IO2Pin) => {
416 TSC_V3_GUARD!(IOPin::Group8Io2)
417 };
418 (G8IO3Pin) => {
419 TSC_V3_GUARD!(IOPin::Group8Io3)
420 };
421 (G8IO4Pin) => {
422 TSC_V3_GUARD!(IOPin::Group8Io4)
423 };
424}
425
426macro_rules! impl_set_io {
427 ($method:ident, $group:ident, $trait:ident, $index:expr) => {
428 #[doc = concat!("Create a new pin1 for ", stringify!($group), " TSC group instance.")]
429 pub fn $method<Role: pin_roles::Role>(
430 &mut self,
431 pin: impl Peripheral<P = impl $trait<T>> + 'd,
432 ) -> IOPinWithRole<$group, Role> {
433 into_ref!(pin);
434 critical_section::with(|_| {
435 pin.set_low();
436 pin.set_as_af(pin.af_num(), AfType::output(Role::output_type(), Speed::VeryHigh));
437 let tsc_io_pin = trait_to_io_pin!($trait);
438 let new_pin = Pin {
439 _pin: pin.map_into(),
440 role: Role::pin_type(),
441 tsc_io_pin,
442 phantom: PhantomData,
443 };
444 *self.pin_group.pins_mut()[$index] = Some(new_pin);
445 IOPinWithRole {
446 pin: tsc_io_pin,
447 phantom: PhantomData,
448 }
449 })
450 }
451 };
452}
453
454macro_rules! group_impl {
455 ($group:ident, $trait1:ident, $trait2:ident, $trait3:ident, $trait4:ident) => {
456 impl<'d, T: Instance, R1: pin_roles::Role, R2: pin_roles::Role, R3: pin_roles::Role, R4: pin_roles::Role>
457 PinGroupWithRoles<'d, T, $group, R1, R2, R3, R4>
458 {
459 impl_set_io!(set_io1, $group, $trait1, 0);
460 impl_set_io!(set_io2, $group, $trait2, 1);
461 impl_set_io!(set_io3, $group, $trait3, 2);
462 impl_set_io!(set_io4, $group, $trait4, 3);
463 }
464 };
465}
466
467group_impl!(G1, G1IO1Pin, G1IO2Pin, G1IO3Pin, G1IO4Pin);
468group_impl!(G2, G2IO1Pin, G2IO2Pin, G2IO3Pin, G2IO4Pin);
469group_impl!(G3, G3IO1Pin, G3IO2Pin, G3IO3Pin, G3IO4Pin);
470group_impl!(G4, G4IO1Pin, G4IO2Pin, G4IO3Pin, G4IO4Pin);
471group_impl!(G5, G5IO1Pin, G5IO2Pin, G5IO3Pin, G5IO4Pin);
472group_impl!(G6, G6IO1Pin, G6IO2Pin, G6IO3Pin, G6IO4Pin);
473#[cfg(any(tsc_v2, tsc_v3))]
474group_impl!(G7, G7IO1Pin, G7IO2Pin, G7IO3Pin, G7IO4Pin);
475#[cfg(tsc_v3)]
476group_impl!(G8, G8IO1Pin, G8IO2Pin, G8IO3Pin, G8IO4Pin);
477
478/// Group 1 marker type.
479#[derive(Clone, Copy, Debug)]
480pub enum G1 {}
481/// Group 2 marker type.
482#[derive(Clone, Copy, Debug)]
483pub enum G2 {}
484/// Group 3 marker type.
485#[derive(Clone, Copy, Debug)]
486pub enum G3 {}
487/// Group 4 marker type.
488#[derive(Clone, Copy, Debug)]
489pub enum G4 {}
490/// Group 5 marker type.
491#[derive(Clone, Copy, Debug)]
492pub enum G5 {}
493/// Group 6 marker type.
494#[derive(Clone, Copy, Debug)]
495pub enum G6 {}
496/// Group 7 marker type.
497#[derive(Clone, Copy, Debug)]
498pub enum G7 {}
499/// Group 8 marker type.
500#[derive(Clone, Copy, Debug)]
501pub enum G8 {}
502
503/// Represents the collection of pin groups for the Touch Sensing Controller (TSC).
504///
505/// Each field corresponds to a specific group of TSC pins:
506#[allow(missing_docs)]
507pub struct PinGroups<'d, T: Instance> {
508 pub g1: Option<PinGroup<'d, T, G1>>,
509 pub g2: Option<PinGroup<'d, T, G2>>,
510 pub g3: Option<PinGroup<'d, T, G3>>,
511 pub g4: Option<PinGroup<'d, T, G4>>,
512 pub g5: Option<PinGroup<'d, T, G5>>,
513 pub g6: Option<PinGroup<'d, T, G6>>,
514 #[cfg(any(tsc_v2, tsc_v3))]
515 pub g7: Option<PinGroup<'d, T, G7>>,
516 #[cfg(tsc_v3)]
517 pub g8: Option<PinGroup<'d, T, G8>>,
518}
519
520impl<'d, T: Instance> PinGroups<'d, T> {
521 pub(super) fn check(&self) -> Result<(), GroupError> {
522 let mut shield_count = 0;
523
524 // Helper function to check a single group
525 fn check_group<C, T: Instance>(
526 group: &Option<PinGroup<'_, T, C>>,
527 shield_count: &mut u32,
528 ) -> Result<(), GroupError> {
529 if let Some(group) = group {
530 group.check_group()?;
531 if group.contains_exactly_one_shield_pin() {
532 *shield_count += 1;
533 if *shield_count > 1 {
534 return Err(GroupError::MultipleShields);
535 }
536 }
537 }
538 Ok(())
539 }
540
541 // Check each group
542 check_group(&self.g1, &mut shield_count)?;
543 check_group(&self.g2, &mut shield_count)?;
544 check_group(&self.g3, &mut shield_count)?;
545 check_group(&self.g4, &mut shield_count)?;
546 check_group(&self.g5, &mut shield_count)?;
547 check_group(&self.g6, &mut shield_count)?;
548 #[cfg(any(tsc_v2, tsc_v3))]
549 check_group(&self.g7, &mut shield_count)?;
550 #[cfg(tsc_v3)]
551 check_group(&self.g8, &mut shield_count)?;
552
553 Ok(())
554 }
555
556 pub(super) fn make_channel_ios_mask(&self) -> u32 {
557 #[allow(unused_mut)]
558 let mut mask = self.g1.as_ref().map_or(0, |g| g.make_channel_ios_mask())
559 | self.g2.as_ref().map_or(0, |g| g.make_channel_ios_mask())
560 | self.g3.as_ref().map_or(0, |g| g.make_channel_ios_mask())
561 | self.g4.as_ref().map_or(0, |g| g.make_channel_ios_mask())
562 | self.g5.as_ref().map_or(0, |g| g.make_channel_ios_mask())
563 | self.g6.as_ref().map_or(0, |g| g.make_channel_ios_mask());
564 #[cfg(any(tsc_v2, tsc_v3))]
565 {
566 mask |= self.g7.as_ref().map_or(0, |g| g.make_channel_ios_mask());
567 }
568 #[cfg(tsc_v3)]
569 {
570 mask |= self.g8.as_ref().map_or(0, |g| g.make_channel_ios_mask());
571 }
572 mask
573 }
574
575 pub(super) fn make_shield_ios_mask(&self) -> u32 {
576 #[allow(unused_mut)]
577 let mut mask = self.g1.as_ref().map_or(0, |g| g.make_shield_ios_mask())
578 | self.g2.as_ref().map_or(0, |g| g.make_shield_ios_mask())
579 | self.g3.as_ref().map_or(0, |g| g.make_shield_ios_mask())
580 | self.g4.as_ref().map_or(0, |g| g.make_shield_ios_mask())
581 | self.g5.as_ref().map_or(0, |g| g.make_shield_ios_mask())
582 | self.g6.as_ref().map_or(0, |g| g.make_shield_ios_mask());
583 #[cfg(any(tsc_v2, tsc_v3))]
584 {
585 mask |= self.g7.as_ref().map_or(0, |g| g.make_shield_ios_mask());
586 }
587 #[cfg(tsc_v3)]
588 {
589 mask |= self.g8.as_ref().map_or(0, |g| g.make_shield_ios_mask());
590 }
591 mask
592 }
593
594 pub(super) fn make_sample_ios_mask(&self) -> u32 {
595 #[allow(unused_mut)]
596 let mut mask = self.g1.as_ref().map_or(0, |g| g.make_sample_ios_mask())
597 | self.g2.as_ref().map_or(0, |g| g.make_sample_ios_mask())
598 | self.g3.as_ref().map_or(0, |g| g.make_sample_ios_mask())
599 | self.g4.as_ref().map_or(0, |g| g.make_sample_ios_mask())
600 | self.g5.as_ref().map_or(0, |g| g.make_sample_ios_mask())
601 | self.g6.as_ref().map_or(0, |g| g.make_sample_ios_mask());
602 #[cfg(any(tsc_v2, tsc_v3))]
603 {
604 mask |= self.g7.as_ref().map_or(0, |g| g.make_sample_ios_mask());
605 }
606 #[cfg(tsc_v3)]
607 {
608 mask |= self.g8.as_ref().map_or(0, |g| g.make_sample_ios_mask());
609 }
610 mask
611 }
612}
613
614impl<'d, T: Instance> Default for PinGroups<'d, T> {
615 fn default() -> Self {
616 Self {
617 g1: None,
618 g2: None,
619 g3: None,
620 g4: None,
621 g5: None,
622 g6: None,
623 #[cfg(any(tsc_v2, tsc_v3))]
624 g7: None,
625 #[cfg(tsc_v3)]
626 g8: None,
627 }
628 }
629}
630
631pin_trait!(G1IO1Pin, Instance);
632pin_trait!(G1IO2Pin, Instance);
633pin_trait!(G1IO3Pin, Instance);
634pin_trait!(G1IO4Pin, Instance);
635
636pin_trait!(G2IO1Pin, Instance);
637pin_trait!(G2IO2Pin, Instance);
638pin_trait!(G2IO3Pin, Instance);
639pin_trait!(G2IO4Pin, Instance);
640
641pin_trait!(G3IO1Pin, Instance);
642pin_trait!(G3IO2Pin, Instance);
643pin_trait!(G3IO3Pin, Instance);
644pin_trait!(G3IO4Pin, Instance);
645
646pin_trait!(G4IO1Pin, Instance);
647pin_trait!(G4IO2Pin, Instance);
648pin_trait!(G4IO3Pin, Instance);
649pin_trait!(G4IO4Pin, Instance);
650
651pin_trait!(G5IO1Pin, Instance);
652pin_trait!(G5IO2Pin, Instance);
653pin_trait!(G5IO3Pin, Instance);
654pin_trait!(G5IO4Pin, Instance);
655
656pin_trait!(G6IO1Pin, Instance);
657pin_trait!(G6IO2Pin, Instance);
658pin_trait!(G6IO3Pin, Instance);
659pin_trait!(G6IO4Pin, Instance);
660
661pin_trait!(G7IO1Pin, Instance);
662pin_trait!(G7IO2Pin, Instance);
663pin_trait!(G7IO3Pin, Instance);
664pin_trait!(G7IO4Pin, Instance);
665
666pin_trait!(G8IO1Pin, Instance);
667pin_trait!(G8IO2Pin, Instance);
668pin_trait!(G8IO3Pin, Instance);
669pin_trait!(G8IO4Pin, Instance);
diff --git a/embassy-stm32/src/tsc/tsc.rs b/embassy-stm32/src/tsc/tsc.rs
new file mode 100644
index 000000000..17d2da82f
--- /dev/null
+++ b/embassy-stm32/src/tsc/tsc.rs
@@ -0,0 +1,456 @@
1use core::future::poll_fn;
2use core::marker::PhantomData;
3use core::ops::BitOr;
4use core::task::Poll;
5
6use embassy_hal_internal::{into_ref, PeripheralRef};
7
8use super::acquisition_banks::*;
9use super::config::*;
10use super::errors::*;
11use super::io_pin::*;
12use super::pin_groups::*;
13use super::types::*;
14use super::{Instance, InterruptHandler, TSC_NUM_GROUPS};
15use crate::interrupt::typelevel::Interrupt;
16use crate::mode::{Async, Blocking, Mode as PeriMode};
17use crate::{interrupt, rcc, Peripheral};
18
19/// Internal structure holding masks for different types of TSC IOs.
20///
21/// These masks are used during the initial configuration of the TSC peripheral
22/// and for validating pin types during operations like creating acquisition banks.
23struct IOMasks {
24 /// Mask representing all configured channel IOs
25 channel_ios: u32,
26 /// Mask representing all configured shield IOs
27 shield_ios: u32,
28 /// Mask representing all configured sampling IOs
29 sampling_ios: u32,
30}
31
32/// TSC driver
33pub struct Tsc<'d, T: Instance, K: PeriMode> {
34 _peri: PeripheralRef<'d, T>,
35 _pin_groups: PinGroups<'d, T>,
36 state: State,
37 config: Config,
38 masks: IOMasks,
39 _kind: PhantomData<K>,
40}
41
42impl<'d, T: Instance, K: PeriMode> Tsc<'d, T, K> {
43 // Helper method to check if a pin is a channel pin
44 fn is_channel_pin(&self, pin: IOPin) -> bool {
45 (self.masks.channel_ios & pin) != 0
46 }
47
48 /// Get the status of all groups involved in a AcquisitionBank
49 pub fn get_acquisition_bank_status(&self, bank: &AcquisitionBank) -> AcquisitionBankStatus {
50 let mut bank_status = AcquisitionBankStatus::default();
51 for pin in bank.pins_iterator() {
52 let group = pin.group();
53 let group_status = self.group_get_status(group);
54 let index: usize = group.into();
55 bank_status.groups[index] = Some(group_status);
56 }
57 bank_status
58 }
59
60 /// Get the values for all channels involved in a AcquisitionBank
61 pub fn get_acquisition_bank_values(&self, bank: &AcquisitionBank) -> AcquisitionBankReadings {
62 let mut bank_readings = AcquisitionBankReadings::default();
63 for pin in bank.pins_iterator() {
64 let group = pin.group();
65 let value = self.group_get_value(group);
66 let reading = ChannelReading {
67 sensor_value: value,
68 tsc_pin: pin,
69 };
70 let index: usize = group.into();
71 bank_readings.groups[index] = Some(reading);
72 }
73 bank_readings
74 }
75
76 /// Creates a new TSC acquisition bank from the provided pin configuration.
77 ///
78 /// This method creates a `AcquisitionBank` that can be used for efficient,
79 /// repeated TSC acquisitions. It automatically generates the appropriate mask
80 /// for the provided pins.
81 ///
82 /// # Note on TSC Hardware Limitation
83 ///
84 /// The TSC hardware can only read one channel pin from each TSC group per acquisition.
85 ///
86 /// # Arguments
87 /// * `acquisition_bank_pins` - The pin configuration for the acquisition bank.
88 ///
89 /// # Returns
90 /// A new `AcquisitionBank` instance.
91 ///
92 /// # Example
93 ///
94 /// ```
95 /// let tsc = // ... initialize TSC
96 /// let tsc_sensor1: tsc::IOPinWithRole<G1, tsc_pin_roles::Channel> = ...;
97 /// let tsc_sensor2: tsc::IOPinWithRole<G2, tsc_pin_roles::Channel> = ...;
98 ///
99 /// let bank = tsc.create_acquisition_bank(AcquisitionBankPins {
100 /// g1_pin: Some(tsc_sensor1),
101 /// g2_pin: Some(tsc_sensor2),
102 /// ..Default::default()
103 /// });
104 ///
105 /// // Use the bank for acquisitions
106 /// tsc.set_active_channels_bank(&bank);
107 /// tsc.start();
108 /// // ... perform acquisition ...
109 /// ```
110 pub fn create_acquisition_bank(&self, acquisition_bank_pins: AcquisitionBankPins) -> AcquisitionBank {
111 let bank_mask = acquisition_bank_pins.iter().fold(0u32, BitOr::bitor);
112
113 AcquisitionBank {
114 pins: acquisition_bank_pins,
115 mask: bank_mask,
116 }
117 }
118
119 fn make_channels_mask<Itt>(&self, channels: Itt) -> Result<u32, AcquisitionBankError>
120 where
121 Itt: IntoIterator<Item = IOPin>,
122 {
123 let mut group_mask = 0u32;
124 let mut channel_mask = 0u32;
125
126 for channel in channels {
127 if !self.is_channel_pin(channel) {
128 return Err(AcquisitionBankError::InvalidChannelPin);
129 }
130
131 let group = channel.group();
132 let group_bit: u32 = 1 << Into::<usize>::into(group);
133 if group_mask & group_bit != 0 {
134 return Err(AcquisitionBankError::MultipleChannelsPerGroup);
135 }
136
137 group_mask |= group_bit;
138 channel_mask |= channel;
139 }
140
141 Ok(channel_mask)
142 }
143
144 /// Sets the active channels for the next TSC acquisition.
145 ///
146 /// This is a low-level method that directly sets the channel mask. For most use cases,
147 /// consider using `set_active_channels_bank` with a `AcquisitionBank` instead, which
148 /// provides a higher-level interface and additional safety checks.
149 ///
150 /// This method configures which sensor channels will be read during the next
151 /// touch sensing acquisition cycle. It should be called before starting a new
152 /// acquisition with the start() method.
153 ///
154 /// # Arguments
155 /// * `mask` - A 32-bit mask where each bit represents a channel. Set bits indicate
156 /// active channels.
157 ///
158 /// # Note
159 /// Only one pin from each TSC group can be read for each acquisition. This method
160 /// does not perform checks to ensure this limitation is met. Incorrect masks may
161 /// lead to unexpected behavior.
162 ///
163 /// # Safety
164 /// This method doesn't perform extensive checks on the provided mask. Ensure that
165 /// the mask is valid and adheres to hardware limitations to avoid undefined behavior.
166 pub fn set_active_channels_mask(&mut self, mask: u32) {
167 T::regs().ioccr().write(|w| w.0 = mask | self.masks.shield_ios);
168 }
169
170 /// Convenience method for setting active channels directly from a slice of tsc::IOPin.
171 /// This method performs safety checks but is less efficient for repeated use.
172 pub fn set_active_channels(&mut self, channels: &[IOPin]) -> Result<(), AcquisitionBankError> {
173 let mask = self.make_channels_mask(channels.iter().cloned())?;
174 self.set_active_channels_mask(mask);
175 Ok(())
176 }
177
178 /// Sets the active channels for the next TSC acquisition using a pre-configured acquisition bank.
179 ///
180 /// This method efficiently configures the TSC peripheral to read the channels specified
181 /// in the provided `AcquisitionBank`. It's the recommended way to set up
182 /// channel configurations for acquisition, especially when using the same set of channels repeatedly.
183 ///
184 /// # Arguments
185 ///
186 /// * `bank` - A reference to a `AcquisitionBank` containing the pre-configured
187 /// TSC channel mask.
188 ///
189 /// # Example
190 ///
191 /// ```
192 /// let tsc_sensor1: tsc::IOPinWithRole<G1, Channel> = ...;
193 /// let tsc_sensor2: tsc::IOPinWithRole<G5, Channel> = ...;
194 /// let mut touch_controller: Tsc<'_, TSC, Async> = ...;
195 /// let bank = touch_controller.create_acquisition_bank(AcquisitionBankPins {
196 /// g1_pin: Some(tsc_sensor1),
197 /// g2_pin: Some(tsc_sensor2),
198 /// ..Default::default()
199 /// });
200 ///
201 /// touch_controller.set_active_channels_bank(&bank);
202 /// touch_controller.start();
203 /// // ... perform acquisition ...
204 /// ```
205 ///
206 /// This method should be called before starting a new acquisition with the `start()` method.
207 pub fn set_active_channels_bank(&mut self, bank: &AcquisitionBank) {
208 self.set_active_channels_mask(bank.mask)
209 }
210
211 fn extract_groups(io_mask: u32) -> u32 {
212 let mut groups: u32 = 0;
213 for idx in 0..TSC_NUM_GROUPS {
214 if io_mask & (0x0F << (idx * 4)) != 0 {
215 groups |= 1 << idx
216 }
217 }
218 groups
219 }
220
221 fn new_inner(
222 peri: impl Peripheral<P = T> + 'd,
223 pin_groups: PinGroups<'d, T>,
224 config: Config,
225 ) -> Result<Self, GroupError> {
226 into_ref!(peri);
227
228 pin_groups.check()?;
229
230 let masks = IOMasks {
231 channel_ios: pin_groups.make_channel_ios_mask(),
232 shield_ios: pin_groups.make_shield_ios_mask(),
233 sampling_ios: pin_groups.make_sample_ios_mask(),
234 };
235
236 rcc::enable_and_reset::<T>();
237
238 T::regs().cr().modify(|w| {
239 w.set_tsce(true);
240 w.set_ctph(config.ct_pulse_high_length.into());
241 w.set_ctpl(config.ct_pulse_low_length.into());
242 w.set_sse(config.spread_spectrum);
243 // Prevent invalid configuration for pulse generator prescaler
244 if config.ct_pulse_low_length == ChargeTransferPulseCycle::_1
245 && (config.pulse_generator_prescaler == PGPrescalerDivider::_1
246 || config.pulse_generator_prescaler == PGPrescalerDivider::_2)
247 {
248 w.set_pgpsc(PGPrescalerDivider::_4.into());
249 } else if config.ct_pulse_low_length == ChargeTransferPulseCycle::_2
250 && config.pulse_generator_prescaler == PGPrescalerDivider::_1
251 {
252 w.set_pgpsc(PGPrescalerDivider::_2.into());
253 } else {
254 w.set_pgpsc(config.pulse_generator_prescaler.into());
255 }
256 w.set_ssd(config.spread_spectrum_deviation.into());
257 w.set_sspsc(config.spread_spectrum_prescaler);
258
259 w.set_mcv(config.max_count_value.into());
260 w.set_syncpol(config.synchro_pin_polarity);
261 w.set_am(config.acquisition_mode);
262 });
263
264 // Set IO configuration
265 // Disable Schmitt trigger hysteresis on all used TSC IOs
266 T::regs()
267 .iohcr()
268 .write(|w| w.0 = !(masks.channel_ios | masks.shield_ios | masks.sampling_ios));
269
270 // Set channel and shield IOs
271 T::regs().ioccr().write(|w| w.0 = masks.channel_ios | masks.shield_ios);
272
273 // Set sampling IOs
274 T::regs().ioscr().write(|w| w.0 = masks.sampling_ios);
275
276 // Set the groups to be acquired
277 // Lower bits of `iogcsr` are for enabling groups, while the higher bits are for reading
278 // status of acquisiton for a group, see method `Tsc::group_get_status`.
279 T::regs()
280 .iogcsr()
281 .write(|w| w.0 = Self::extract_groups(masks.channel_ios));
282
283 // Disable interrupts
284 T::regs().ier().modify(|w| {
285 w.set_eoaie(false);
286 w.set_mceie(false);
287 });
288
289 // Clear flags
290 T::regs().icr().modify(|w| {
291 w.set_eoaic(true);
292 w.set_mceic(true);
293 });
294
295 unsafe {
296 T::Interrupt::enable();
297 }
298
299 Ok(Self {
300 _peri: peri,
301 _pin_groups: pin_groups,
302 state: State::Ready,
303 config,
304 masks,
305 _kind: PhantomData,
306 })
307 }
308
309 /// Start charge transfer acquisition
310 pub fn start(&mut self) {
311 self.state = State::Busy;
312
313 // Disable interrupts
314 T::regs().ier().modify(|w| {
315 w.set_eoaie(false);
316 w.set_mceie(false);
317 });
318
319 // Clear flags
320 T::regs().icr().modify(|w| {
321 w.set_eoaic(true);
322 w.set_mceic(true);
323 });
324
325 // Set the touch sensing IOs not acquired to the default mode
326 T::regs().cr().modify(|w| {
327 w.set_iodef(self.config.io_default_mode);
328 });
329
330 // Start the acquisition
331 T::regs().cr().modify(|w| {
332 w.set_start(true);
333 });
334 }
335
336 /// Stop charge transfer acquisition
337 pub fn stop(&mut self) {
338 T::regs().cr().modify(|w| {
339 w.set_start(false);
340 });
341
342 // Set the touch sensing IOs in low power mode
343 T::regs().cr().modify(|w| {
344 w.set_iodef(false);
345 });
346
347 // Clear flags
348 T::regs().icr().modify(|w| {
349 w.set_eoaic(true);
350 w.set_mceic(true);
351 });
352
353 self.state = State::Ready;
354 }
355
356 /// Get current state of acquisition
357 pub fn get_state(&mut self) -> State {
358 if self.state == State::Busy && T::regs().isr().read().eoaf() {
359 if T::regs().isr().read().mcef() {
360 self.state = State::Error
361 } else {
362 self.state = State::Ready
363 }
364 }
365 self.state
366 }
367
368 /// Get the individual group status to check acquisition complete
369 pub fn group_get_status(&self, index: Group) -> GroupStatus {
370 // Status bits are set by hardware when the acquisition on the corresponding
371 // enabled analog IO group is complete, cleared when new acquisition is started
372 let status = match index {
373 Group::One => T::regs().iogcsr().read().g1s(),
374 Group::Two => T::regs().iogcsr().read().g2s(),
375 Group::Three => T::regs().iogcsr().read().g3s(),
376 Group::Four => T::regs().iogcsr().read().g4s(),
377 Group::Five => T::regs().iogcsr().read().g5s(),
378 Group::Six => T::regs().iogcsr().read().g6s(),
379 #[cfg(any(tsc_v2, tsc_v3))]
380 Group::Seven => T::regs().iogcsr().read().g7s(),
381 #[cfg(tsc_v3)]
382 Group::Eight => T::regs().iogcsr().read().g8s(),
383 };
384 match status {
385 true => GroupStatus::Complete,
386 false => GroupStatus::Ongoing,
387 }
388 }
389
390 /// Get the count for the acquisiton, valid once group status is set
391 pub fn group_get_value(&self, index: Group) -> u16 {
392 T::regs().iogcr(index.into()).read().cnt()
393 }
394
395 /// Discharge the IOs for subsequent acquisition
396 pub fn discharge_io(&mut self, status: bool) {
397 // Set the touch sensing IOs in low power mode
398 T::regs().cr().modify(|w| {
399 w.set_iodef(!status);
400 });
401 }
402}
403
404impl<'d, T: Instance, K: PeriMode> Drop for Tsc<'d, T, K> {
405 fn drop(&mut self) {
406 rcc::disable::<T>();
407 }
408}
409
410impl<'d, T: Instance> Tsc<'d, T, Async> {
411 /// Create a Tsc instance that can be awaited for completion
412 pub fn new_async(
413 peri: impl Peripheral<P = T> + 'd,
414 pin_groups: PinGroups<'d, T>,
415 config: Config,
416 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
417 ) -> Result<Self, GroupError> {
418 Self::new_inner(peri, pin_groups, config)
419 }
420
421 /// Asyncronously wait for the end of an acquisition
422 pub async fn pend_for_acquisition(&mut self) {
423 poll_fn(|cx| match self.get_state() {
424 State::Busy => {
425 T::waker().register(cx.waker());
426 T::regs().ier().write(|w| w.set_eoaie(true));
427 if self.get_state() != State::Busy {
428 T::regs().ier().write(|w| w.set_eoaie(false));
429 return Poll::Ready(());
430 }
431 Poll::Pending
432 }
433 _ => {
434 T::regs().ier().write(|w| w.set_eoaie(false));
435 Poll::Ready(())
436 }
437 })
438 .await;
439 }
440}
441
442impl<'d, T: Instance> Tsc<'d, T, Blocking> {
443 /// Create a Tsc instance that must be polled for completion
444 pub fn new_blocking(
445 peri: impl Peripheral<P = T> + 'd,
446 pin_groups: PinGroups<'d, T>,
447 config: Config,
448 ) -> Result<Self, GroupError> {
449 Self::new_inner(peri, pin_groups, config)
450 }
451
452 /// Wait for end of acquisition
453 pub fn poll_for_acquisition(&mut self) {
454 while self.get_state() == State::Busy {}
455 }
456}
diff --git a/embassy-stm32/src/tsc/types.rs b/embassy-stm32/src/tsc/types.rs
new file mode 100644
index 000000000..0e8fa7f28
--- /dev/null
+++ b/embassy-stm32/src/tsc/types.rs
@@ -0,0 +1,93 @@
1/// Peripheral state
2#[cfg_attr(feature = "defmt", derive(defmt::Format))]
3#[derive(PartialEq, Clone, Copy)]
4pub enum State {
5 /// Peripheral is being setup or reconfigured
6 Reset,
7 /// Ready to start acquisition
8 Ready,
9 /// In process of sensor acquisition
10 Busy,
11 /// Error occured during acquisition
12 Error,
13}
14
15/// Individual group status checked after acquisition reported as complete
16/// For groups with multiple channel pins, may take longer because acquisitions
17/// are done sequentially. Check this status before pulling count for each
18/// sampled channel
19#[cfg_attr(feature = "defmt", derive(defmt::Format))]
20#[derive(PartialEq, Clone, Copy)]
21pub enum GroupStatus {
22 /// Acquisition for channel still in progress
23 Ongoing,
24 /// Acquisition either not started or complete
25 Complete,
26}
27
28/// Group identifier used to interrogate status
29#[cfg_attr(feature = "defmt", derive(defmt::Format))]
30#[allow(missing_docs)]
31#[derive(PartialEq, Clone, Copy)]
32pub enum Group {
33 One,
34 Two,
35 Three,
36 Four,
37 Five,
38 Six,
39 #[cfg(any(tsc_v2, tsc_v3))]
40 Seven,
41 #[cfg(tsc_v3)]
42 Eight,
43}
44
45impl Into<usize> for Group {
46 fn into(self) -> usize {
47 match self {
48 Group::One => 0,
49 Group::Two => 1,
50 Group::Three => 2,
51 Group::Four => 3,
52 Group::Five => 4,
53 Group::Six => 5,
54 #[cfg(any(tsc_v2, tsc_v3))]
55 Group::Seven => 6,
56 #[cfg(tsc_v3)]
57 Group::Eight => 7,
58 }
59 }
60}
61
62/// Error returned when attempting to create a Group from an invalid numeric value.
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub struct InvalidGroupError {
65 invalid_value: usize,
66}
67
68impl InvalidGroupError {
69 #[allow(missing_docs)]
70 pub fn new(value: usize) -> Self {
71 Self { invalid_value: value }
72 }
73}
74
75impl TryFrom<usize> for Group {
76 type Error = InvalidGroupError;
77
78 fn try_from(value: usize) -> Result<Self, Self::Error> {
79 match value {
80 0 => Ok(Group::One),
81 1 => Ok(Group::Two),
82 2 => Ok(Group::Three),
83 3 => Ok(Group::Four),
84 4 => Ok(Group::Five),
85 5 => Ok(Group::Six),
86 #[cfg(any(tsc_v2, tsc_v3))]
87 6 => Ok(Group::Two),
88 #[cfg(tsc_v3)]
89 7 => Ok(Group::Two),
90 n => Err(InvalidGroupError::new(n)),
91 }
92 }
93}
diff --git a/examples/stm32f3/README.md b/examples/stm32f3/README.md
new file mode 100644
index 000000000..0a85c4858
--- /dev/null
+++ b/examples/stm32f3/README.md
@@ -0,0 +1,24 @@
1# Examples for STM32F3 family
2Run individual examples with
3```
4cargo run --bin <module-name>
5```
6for example
7```
8cargo run --bin blinky
9```
10
11## Checklist before running examples
12You might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.
13
14* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for F303ZE it should be `probe-rs run --chip STM32F303ZETx`. (use `probe-rs chip list` to find your chip)
15* [ ] Update Cargo.toml to have the correct `embassy-stm32` feature. For example for F303ZE it should be `stm32f303ze`. Look in the `Cargo.toml` file of the `embassy-stm32` project to find the correct feature flag for your chip.
16* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.
17* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic
18
19If you are unsure, please drop by the Embassy Matrix chat for support, and let us know:
20
21* Which example you are trying to run
22* Which chip and board you are using
23
24Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org
diff --git a/examples/stm32f3/src/bin/blocking-tsc.rs b/examples/stm32f3/src/bin/blocking-tsc.rs
deleted file mode 100644
index 5c8dac94f..000000000
--- a/examples/stm32f3/src/bin/blocking-tsc.rs
+++ /dev/null
@@ -1,98 +0,0 @@
1// Example of polling TSC (Touch Sensing Controller) that lights an LED when touch is detected.
2//
3// Suggested physical setup on STM32F303ZE Nucleo board:
4// - Connect a 1000pF capacitor between pin A0 and GND. This is your sampling capacitor.
5// - Connect one end of a 1K resistor to pin A1 and leave the other end loose.
6// The loose end will act as touch sensor which will register your touch.
7//
8// Troubleshooting the setup:
9// - If no touch seems to be registered, then try to disconnect the sampling capacitor from GND momentarily,
10// now the led should light up. Next try using a different value for the sampling capacitor.
11// Also experiment with increasing the values for `ct_pulse_high_length`, `ct_pulse_low_length`, `pulse_generator_prescaler`, `max_count_value` and `discharge_delay`.
12//
13// All configuration values and sampling capacitor value have been determined experimentally.
14// Suitable configuration and discharge delay values are highly dependent on the value of the sample capacitor. For example, a shorter discharge delay can be used with smaller capacitor values.
15//
16#![no_std]
17#![no_main]
18
19use defmt::*;
20use embassy_stm32::gpio::{Level, Output, Speed};
21use embassy_stm32::tsc::{self, *};
22use embassy_time::Timer;
23use {defmt_rtt as _, panic_probe as _};
24
25/// This example is written for the nucleo-stm32f303ze, with a stm32f303ze chip.
26///
27/// Make sure you check/update the following (whether you use the F303ZE or another board):
28///
29/// * [ ] Update .cargo/config.toml with the correct `probe-rs run --chip STM32F303ZETx`chip name.
30/// * [ ] Update Cargo.toml to have the correct `embassy-stm32` feature, for F303ZE it should be `stm32f303ze`.
31/// * [ ] If your board has a special clock or power configuration, make sure that it is
32/// set up appropriately.
33/// * [ ] If your board has different pin mapping, update any pin numbers or peripherals
34/// to match your schematic
35///
36/// If you are unsure, please drop by the Embassy Matrix chat for support, and let us know:
37///
38/// * Which example you are trying to run
39/// * Which chip and board you are using
40///
41/// Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org
42#[embassy_executor::main]
43async fn main(_spawner: embassy_executor::Spawner) {
44 let device_config = embassy_stm32::Config::default();
45 let context = embassy_stm32::init(device_config);
46
47 let tsc_conf = Config {
48 ct_pulse_high_length: ChargeTransferPulseCycle::_8,
49 ct_pulse_low_length: ChargeTransferPulseCycle::_8,
50 spread_spectrum: false,
51 spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
52 spread_spectrum_prescaler: false,
53 pulse_generator_prescaler: PGPrescalerDivider::_32,
54 max_count_value: MaxCount::_255,
55 io_default_mode: false,
56 synchro_pin_polarity: false,
57 acquisition_mode: false,
58 max_count_interrupt: false,
59 channel_ios: TscIOPin::Group1Io1.into(),
60 shield_ios: 0, // no shield
61 sampling_ios: TscIOPin::Group1Io2.into(),
62 };
63
64 let mut g1: PinGroup<embassy_stm32::peripherals::TSC, G1> = PinGroup::new();
65 g1.set_io1(context.PA0, PinType::Sample);
66 g1.set_io2(context.PA1, PinType::Channel);
67
68 let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, Some(g1), None, None, None, None, None, tsc_conf);
69
70 // LED2 on the STM32F303ZE nucleo-board
71 let mut led = Output::new(context.PB7, Level::High, Speed::Low);
72
73 // smaller sample capacitor discharge faster and can be used with shorter delay.
74 let discharge_delay = 5; // ms
75
76 // the interval at which the loop polls for new touch sensor values
77 let polling_interval = 100; // ms
78
79 info!("polling for touch");
80 loop {
81 touch_controller.start();
82 touch_controller.poll_for_acquisition();
83 touch_controller.discharge_io(true);
84 Timer::after_millis(discharge_delay).await;
85
86 let grp1_status = touch_controller.group_get_status(Group::One);
87 match grp1_status {
88 GroupStatus::Complete => {
89 let group_one_val = touch_controller.group_get_value(Group::One);
90 info!("{}", group_one_val);
91 led.set_high();
92 }
93 GroupStatus::Ongoing => led.set_low(),
94 }
95
96 Timer::after_millis(polling_interval).await;
97 }
98}
diff --git a/examples/stm32f3/src/bin/tsc_blocking.rs b/examples/stm32f3/src/bin/tsc_blocking.rs
new file mode 100644
index 000000000..2c33838e5
--- /dev/null
+++ b/examples/stm32f3/src/bin/tsc_blocking.rs
@@ -0,0 +1,138 @@
1// Example of blocking TSC (Touch Sensing Controller) that lights an LED when touch is detected.
2//
3// This example demonstrates:
4// 1. Configuring a single TSC channel pin
5// 2. Using the blocking TSC interface with polling
6// 3. Waiting for acquisition completion using `poll_for_acquisition`
7// 4. Reading touch values and controlling an LED based on the results
8//
9// Suggested physical setup on STM32F303ZE Nucleo board:
10// - Connect a 1000pF capacitor between pin PA10 and GND. This is your sampling capacitor.
11// - Connect one end of a 1K resistor to pin PA9 and leave the other end loose.
12// The loose end will act as the touch sensor which will register your touch.
13//
14// The example uses two pins from Group 4 of the TSC:
15// - PA10 as the sampling capacitor, TSC group 4 IO2 (D68 on the STM32F303ZE nucleo-board)
16// - PA9 as the channel pin, TSC group 4 IO1 (D69 on the STM32F303ZE nucleo-board)
17//
18// The program continuously reads the touch sensor value:
19// - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value.
20// - The LED is turned on when touch is detected (sensor value < 40).
21// - Touch values are logged to the console.
22//
23// Troubleshooting:
24// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value.
25// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length,
26// pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.
27//
28// Note: Configuration values and sampling capacitor value have been determined experimentally.
29// Optimal values may vary based on your specific hardware setup.
30// Pins have been chosen for their convenient locations on the STM32F303ZE board. Refer to the
31// official relevant STM32 datasheets and user nucleo-board user manuals to find suitable
32// alternative pins.
33
34#![no_std]
35#![no_main]
36
37use defmt::*;
38use embassy_stm32::gpio::{Level, Output, Speed};
39use embassy_stm32::tsc::{self, *};
40use embassy_stm32::{mode, peripherals};
41use embassy_time::Timer;
42use {defmt_rtt as _, panic_probe as _};
43
44const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup
45
46#[embassy_executor::main]
47async fn main(_spawner: embassy_executor::Spawner) {
48 let device_config = embassy_stm32::Config::default();
49 let context = embassy_stm32::init(device_config);
50
51 let tsc_conf = Config {
52 ct_pulse_high_length: ChargeTransferPulseCycle::_4,
53 ct_pulse_low_length: ChargeTransferPulseCycle::_4,
54 spread_spectrum: false,
55 spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
56 spread_spectrum_prescaler: false,
57 pulse_generator_prescaler: PGPrescalerDivider::_16,
58 max_count_value: MaxCount::_255,
59 io_default_mode: false,
60 synchro_pin_polarity: false,
61 acquisition_mode: false,
62 max_count_interrupt: false,
63 };
64
65 let mut g: PinGroupWithRoles<peripherals::TSC, G4> = PinGroupWithRoles::default();
66 // D68 on the STM32F303ZE nucleo-board
67 g.set_io2::<tsc::pin_roles::Sample>(context.PA10);
68 // D69 on the STM32F303ZE nucleo-board
69 let tsc_sensor = g.set_io1::<tsc::pin_roles::Channel>(context.PA9);
70
71 let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
72 g4: Some(g.pin_group),
73 ..Default::default()
74 };
75
76 let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, pin_groups, tsc_conf).unwrap();
77
78 // Check if TSC is ready
79 if touch_controller.get_state() != State::Ready {
80 crate::panic!("TSC not ready!");
81 }
82 info!("TSC initialized successfully");
83
84 // LED2 on the STM32F303ZE nucleo-board
85 let mut led = Output::new(context.PB7, Level::High, Speed::Low);
86
87 // smaller sample capacitor discharge faster and can be used with shorter delay.
88 let discharge_delay = 5; // ms
89
90 // the interval at which the loop polls for new touch sensor values
91 let polling_interval = 100; // ms
92
93 info!("polling for touch");
94 loop {
95 touch_controller.set_active_channels_mask(tsc_sensor.pin.into());
96 touch_controller.start();
97 touch_controller.poll_for_acquisition();
98 touch_controller.discharge_io(true);
99 Timer::after_millis(discharge_delay).await;
100
101 match read_touch_value(&mut touch_controller, tsc_sensor.pin).await {
102 Some(v) => {
103 info!("sensor value {}", v);
104 if v < SENSOR_THRESHOLD {
105 led.set_high();
106 } else {
107 led.set_low();
108 }
109 }
110 None => led.set_low(),
111 }
112
113 Timer::after_millis(polling_interval).await;
114 }
115}
116
117const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10;
118
119// attempt to read group status and delay when still ongoing
120async fn read_touch_value(
121 touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Blocking>,
122 sensor_pin: tsc::IOPin,
123) -> Option<u16> {
124 for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS {
125 match touch_controller.group_get_status(sensor_pin.group()) {
126 GroupStatus::Complete => {
127 return Some(touch_controller.group_get_value(sensor_pin.group()));
128 }
129 GroupStatus::Ongoing => {
130 // if you end up here a lot, then you prob need to increase discharge_delay
131 // or consider changing the code to adjust the discharge_delay dynamically
132 info!("Acquisition still ongoing");
133 Timer::after_millis(1).await;
134 }
135 }
136 }
137 None
138}
diff --git a/examples/stm32f3/src/bin/tsc_multipin.rs b/examples/stm32f3/src/bin/tsc_multipin.rs
new file mode 100644
index 000000000..c524c3760
--- /dev/null
+++ b/examples/stm32f3/src/bin/tsc_multipin.rs
@@ -0,0 +1,204 @@
1// Example of TSC (Touch Sensing Controller) using multiple pins from the same tsc-group.
2//
3// What is special about using multiple TSC pins as sensor channels from the same TSC group,
4// is that only one TSC pin for each TSC group can be acquired and read at the time.
5// To control which channel pins are acquired and read, we must write a mask before initiating an
6// acquisition. To help manage and abstract all this business away, we can organize our channel
7// pins into acquisition banks. Each acquisition bank can contain exactly one channel pin per TSC
8// group and it will contain the relevant mask.
9//
10// This example demonstrates how to:
11// 1. Configure multiple channel pins within a single TSC group
12// 2. Use the set_active_channels_bank method to switch between sets of different channels (acquisition banks)
13// 3. Read and interpret touch values from multiple channels in the same group
14//
15// Suggested physical setup on STM32F303ZE Nucleo board:
16// - Connect a 1000pF capacitor between pin PA10 and GND. This is the sampling capacitor for TSC
17// group 4.
18// - Connect one end of a 1K resistor to pin PA9 and leave the other end loose.
19// The loose end will act as a touch sensor.
20//
21// - Connect a 1000pF capacitor between pin PA7 and GND. This is the sampling capacitor for TSC
22// group 2.
23// - Connect one end of another 1K resistor to pin PA6 and leave the other end loose.
24// The loose end will act as a touch sensor.
25// - Connect one end of another 1K resistor to pin PA5 and leave the other end loose.
26// The loose end will act as a touch sensor.
27//
28// The example uses pins from two TSC groups.
29// - PA10 as sampling capacitor, TSC group 4 IO2
30// - PA9 as channel, TSC group 4 IO1
31// - PA7 as sampling capacitor, TSC group 2 IO4
32// - PA6 as channel, TSC group 2 IO3
33// - PA5 as channel, TSC group 2 IO2
34//
35// The pins have been chosen to make it easy to simply add capacitors directly onto the board and
36// connect one leg to GND, and to easily add resistors to the board with no special connectors,
37// breadboards, special wires or soldering required. All you need is the capacitors and resistors.
38//
39// The program reads the designated channel pins and adjusts the LED blinking
40// pattern based on which sensor(s) are touched:
41// - No touch: LED off
42// - one sensor touched: Slow blinking
43// - two sensors touched: Fast blinking
44// - three sensors touched: LED constantly on
45//
46// ## Troubleshooting:
47//
48// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value (currently set to 20).
49// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.
50// - Be aware that for some boards there will be overlapping concerns between some pins, for
51// example UART connection for the programmer to the MCU and a TSC pin. No errors or warning will
52// be emitted if you try to use such a pin for TSC, but you will get strange sensor readings.
53//
54// Note: Configuration values and sampling capacitor values have been determined experimentally. Optimal values may vary based on your specific hardware setup. Refer to the official STM32 datasheet and user manuals for more information on pin configurations and TSC functionality.
55
56#![no_std]
57#![no_main]
58
59use defmt::*;
60use embassy_stm32::gpio::{Level, Output, Speed};
61use embassy_stm32::tsc::{self, *};
62use embassy_stm32::{mode, peripherals};
63use embassy_time::Timer;
64use {defmt_rtt as _, panic_probe as _};
65
66const SENSOR_THRESHOLD: u16 = 10;
67
68async fn acquire_sensors(
69 touch_controller: &mut Tsc<'static, peripherals::TSC, mode::Blocking>,
70 tsc_acquisition_bank: &AcquisitionBank,
71) {
72 touch_controller.set_active_channels_bank(tsc_acquisition_bank);
73 touch_controller.start();
74 touch_controller.poll_for_acquisition();
75 touch_controller.discharge_io(true);
76 let discharge_delay = 5; // ms
77 Timer::after_millis(discharge_delay).await;
78}
79
80#[embassy_executor::main]
81async fn main(_spawner: embassy_executor::Spawner) {
82 let device_config = embassy_stm32::Config::default();
83 let context = embassy_stm32::init(device_config);
84
85 // ---------- initial configuration of TSC ----------
86 //
87 let mut pin_group4: PinGroupWithRoles<peripherals::TSC, G4> = PinGroupWithRoles::default();
88 // D68 on the STM32F303ZE nucleo-board
89 pin_group4.set_io2::<tsc::pin_roles::Sample>(context.PA10);
90 // D69 on the STM32F303ZE nucleo-board
91 let tsc_sensor0 = pin_group4.set_io1(context.PA9);
92
93 let mut pin_group2: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default();
94 // D11 on the STM32F303ZE nucleo-board
95 pin_group2.set_io4::<tsc::pin_roles::Sample>(context.PA7);
96 // D12 on the STM32F303ZE nucleo-board
97 let tsc_sensor1 = pin_group2.set_io3(context.PA6);
98 // D13 on the STM32F303ZE nucleo-board
99 let tsc_sensor2 = pin_group2.set_io2(context.PA5);
100
101 let config = Config {
102 ct_pulse_high_length: ChargeTransferPulseCycle::_4,
103 ct_pulse_low_length: ChargeTransferPulseCycle::_4,
104 spread_spectrum: false,
105 spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
106 spread_spectrum_prescaler: false,
107 pulse_generator_prescaler: PGPrescalerDivider::_16,
108 max_count_value: MaxCount::_255,
109 io_default_mode: false,
110 synchro_pin_polarity: false,
111 acquisition_mode: false,
112 max_count_interrupt: false,
113 };
114
115 let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
116 g4: Some(pin_group4.pin_group),
117 g2: Some(pin_group2.pin_group),
118 ..Default::default()
119 };
120
121 let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, pin_groups, config).unwrap();
122
123 // ---------- setting up acquisition banks ----------
124 // sensor0 and sensor1 in this example belong to different TSC-groups,
125 // therefore we can acquire and read them both in one go.
126 let bank1 = touch_controller.create_acquisition_bank(AcquisitionBankPins {
127 g4_pin: Some(tsc_sensor0),
128 g2_pin: Some(tsc_sensor1),
129 ..Default::default()
130 });
131 // `sensor1` and `sensor2` belongs to the same TSC-group, therefore we must make sure to
132 // acquire them one at the time. Therefore, we organize them into different acquisition banks.
133 let bank2 = touch_controller.create_acquisition_bank(AcquisitionBankPins {
134 g2_pin: Some(tsc_sensor2),
135 ..Default::default()
136 });
137
138 // Check if TSC is ready
139 if touch_controller.get_state() != State::Ready {
140 crate::panic!("TSC not ready!");
141 }
142
143 info!("TSC initialized successfully");
144
145 // LED2 on the STM32F303ZE nucleo-board
146 let mut led = Output::new(context.PB7, Level::High, Speed::Low);
147
148 let mut led_state = false;
149
150 loop {
151 acquire_sensors(&mut touch_controller, &bank1).await;
152 let readings1 = touch_controller.get_acquisition_bank_values(&bank1);
153 acquire_sensors(&mut touch_controller, &bank2).await;
154 let readings2 = touch_controller.get_acquisition_bank_values(&bank2);
155
156 let mut touched_sensors_count = 0;
157 for reading in readings1.iter() {
158 info!("{}", reading);
159 if reading.sensor_value < SENSOR_THRESHOLD {
160 touched_sensors_count += 1;
161 }
162 }
163 for reading in readings2.iter() {
164 info!("{}", reading);
165 if reading.sensor_value < SENSOR_THRESHOLD {
166 touched_sensors_count += 1;
167 }
168 }
169
170 match touched_sensors_count {
171 0 => {
172 // No sensors touched, turn off the LED
173 led.set_low();
174 led_state = false;
175 }
176 1 => {
177 // One sensor touched, blink slowly
178 led_state = !led_state;
179 if led_state {
180 led.set_high();
181 } else {
182 led.set_low();
183 }
184 Timer::after_millis(200).await;
185 }
186 2 => {
187 // Two sensors touched, blink faster
188 led_state = !led_state;
189 if led_state {
190 led.set_high();
191 } else {
192 led.set_low();
193 }
194 Timer::after_millis(50).await;
195 }
196 3 => {
197 // All three sensors touched, LED constantly on
198 led.set_high();
199 led_state = true;
200 }
201 _ => crate::unreachable!(), // This case should never occur with 3 sensors
202 }
203 }
204}
diff --git a/examples/stm32l0/.cargo/config.toml b/examples/stm32l0/.cargo/config.toml
index b050334b2..fed9cf9ce 100644
--- a/examples/stm32l0/.cargo/config.toml
+++ b/examples/stm32l0/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace your chip as listed in `probe-rs chip list` 2# replace your chip as listed in `probe-rs chip list`
3runner = "probe-rs run --chip STM32L053R8Tx" 3runner = "probe-rs run --chip STM32L073RZTx"
4 4
5[build] 5[build]
6target = "thumbv6m-none-eabi" 6target = "thumbv6m-none-eabi"
diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml
index 95e215b6f..9d234804a 100644
--- a/examples/stm32l0/Cargo.toml
+++ b/examples/stm32l0/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8# Change stm32l072cz to your chip name, if necessary. 8# Change stm32l072cz to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "unstable-pac", "time-driver-any", "exti", "memory-x"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l073rz", "unstable-pac", "time-driver-any", "exti", "memory-x"] }
10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
diff --git a/examples/stm32l0/README.md b/examples/stm32l0/README.md
new file mode 100644
index 000000000..82d222027
--- /dev/null
+++ b/examples/stm32l0/README.md
@@ -0,0 +1,24 @@
1# Examples for STM32L0 family
2Run individual examples with
3```
4cargo run --bin <module-name>
5```
6for example
7```
8cargo run --bin blinky
9```
10
11## Checklist before running examples
12You might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.
13
14* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L073RZ it should be `probe-rs run --chip STM32L073RZTx`. (use `probe-rs chip list` to find your chip)
15* [ ] Update Cargo.toml to have the correct `embassy-stm32` feature. For example for L073RZ it should be `stm32l073rz`. Look in the `Cargo.toml` file of the `embassy-stm32` project to find the correct feature flag for your chip.
16* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.
17* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic
18
19If you are unsure, please drop by the Embassy Matrix chat for support, and let us know:
20
21* Which example you are trying to run
22* Which chip and board you are using
23
24Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org
diff --git a/examples/stm32l0/src/bin/async-tsc.rs b/examples/stm32l0/src/bin/async-tsc.rs
deleted file mode 100644
index c40b86af9..000000000
--- a/examples/stm32l0/src/bin/async-tsc.rs
+++ /dev/null
@@ -1,122 +0,0 @@
1// Example of async TSC (Touch Sensing Controller) that lights an LED when touch is detected.
2//
3// Suggested physical setup on STM32L073RZ Nucleo board:
4// - Connect a 1000pF capacitor between pin A0 and GND. This is your sampling capacitor.
5// - Connect one end of a 1K resistor to pin A1 and leave the other end loose.
6// The loose end will act as touch sensor which will register your touch.
7//
8// Troubleshooting the setup:
9// - If no touch seems to be registered, then try to disconnect the sampling capacitor from GND momentarily,
10// now the led should light up. Next try using a different value for the sampling capacitor.
11// Also experiment with increasing the values for `ct_pulse_high_length`, `ct_pulse_low_length`, `pulse_generator_prescaler`, `max_count_value` and `discharge_delay`.
12//
13// All configuration values and sampling capacitor value have been determined experimentally.
14// Suitable configuration and discharge delay values are highly dependent on the value of the sample capacitor. For example, a shorter discharge delay can be used with smaller capacitor values.
15//
16#![no_std]
17#![no_main]
18
19use defmt::*;
20use embassy_stm32::bind_interrupts;
21use embassy_stm32::gpio::{Level, Output, Speed};
22use embassy_stm32::tsc::{self, *};
23use embassy_time::Timer;
24use {defmt_rtt as _, panic_probe as _};
25
26bind_interrupts!(struct Irqs {
27 TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;
28});
29
30#[cortex_m_rt::exception]
31unsafe fn HardFault(_: &cortex_m_rt::ExceptionFrame) -> ! {
32 cortex_m::peripheral::SCB::sys_reset();
33}
34
35/// This example is written for the nucleo-stm32l073rz, with a stm32l073rz chip.
36///
37/// Make sure you check/update the following (whether you use the L073RZ or another board):
38///
39/// * [ ] Update .cargo/config.toml with the correct `probe-rs run --chip STM32L073RZTx`chip name.
40/// * [ ] Update Cargo.toml to have the correct `embassy-stm32` feature, for L073RZ it should be `stm32l073rz`.
41/// * [ ] If your board has a special clock or power configuration, make sure that it is
42/// set up appropriately.
43/// * [ ] If your board has different pin mapping, update any pin numbers or peripherals
44/// to match your schematic
45///
46/// If you are unsure, please drop by the Embassy Matrix chat for support, and let us know:
47///
48/// * Which example you are trying to run
49/// * Which chip and board you are using
50///
51/// Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org
52#[embassy_executor::main]
53async fn main(_spawner: embassy_executor::Spawner) {
54 let device_config = embassy_stm32::Config::default();
55 let context = embassy_stm32::init(device_config);
56
57 let config = tsc::Config {
58 ct_pulse_high_length: ChargeTransferPulseCycle::_4,
59 ct_pulse_low_length: ChargeTransferPulseCycle::_4,
60 spread_spectrum: false,
61 spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
62 spread_spectrum_prescaler: false,
63 pulse_generator_prescaler: PGPrescalerDivider::_16,
64 max_count_value: MaxCount::_255,
65 io_default_mode: false,
66 synchro_pin_polarity: false,
67 acquisition_mode: false,
68 max_count_interrupt: false,
69 channel_ios: TscIOPin::Group1Io1.into(),
70 shield_ios: 0, // no shield
71 sampling_ios: TscIOPin::Group1Io2.into(),
72 };
73
74 let mut g1: PinGroup<embassy_stm32::peripherals::TSC, G1> = PinGroup::new();
75 g1.set_io1(context.PA0, PinType::Sample);
76 g1.set_io2(context.PA1, PinType::Channel);
77
78 let mut touch_controller = tsc::Tsc::new_async(
79 context.TSC,
80 Some(g1),
81 None,
82 None,
83 None,
84 None,
85 None,
86 None,
87 None,
88 config,
89 Irqs,
90 );
91
92 // Check if TSC is ready
93 if touch_controller.get_state() != State::Ready {
94 info!("TSC not ready!");
95 loop {} // Halt execution
96 }
97 info!("TSC initialized successfully");
98
99 // LED2 on the STM32L073RZ nucleo-board (PA5)
100 let mut led = Output::new(context.PA5, Level::High, Speed::Low);
101
102 // smaller sample capacitor discharge faster and can be used with shorter delay.
103 let discharge_delay = 5; // ms
104
105 info!("Starting touch_controller interface");
106 loop {
107 touch_controller.start();
108 touch_controller.pend_for_acquisition().await;
109 touch_controller.discharge_io(true);
110 Timer::after_millis(discharge_delay).await;
111
112 let grp1_status = touch_controller.group_get_status(Group::One);
113 match grp1_status {
114 GroupStatus::Complete => {
115 let group_one_val = touch_controller.group_get_value(Group::One);
116 info!("{}", group_one_val);
117 led.set_high();
118 }
119 GroupStatus::Ongoing => led.set_low(),
120 }
121 }
122}
diff --git a/examples/stm32l0/src/bin/blocking-tsc.rs b/examples/stm32l0/src/bin/blocking-tsc.rs
deleted file mode 100644
index 7e4f40946..000000000
--- a/examples/stm32l0/src/bin/blocking-tsc.rs
+++ /dev/null
@@ -1,116 +0,0 @@
1// Example of polling TSC (Touch Sensing Controller) that lights an LED when touch is detected.
2//
3// Suggested physical setup on STM32L073RZ Nucleo board:
4// - Connect a 1000pF capacitor between pin A0 and GND. This is your sampling capacitor.
5// - Connect one end of a 1K resistor to pin A1 and leave the other end loose.
6// The loose end will act as touch sensor which will register your touch.
7//
8// Troubleshooting the setup:
9// - If no touch seems to be registered, then try to disconnect the sampling capacitor from GND momentarily,
10// now the led should light up. Next try using a different value for the sampling capacitor.
11// Also experiment with increasing the values for `ct_pulse_high_length`, `ct_pulse_low_length`, `pulse_generator_prescaler`, `max_count_value` and `discharge_delay`.
12//
13// All configuration values and sampling capacitor value have been determined experimentally.
14// Suitable configuration and discharge delay values are highly dependent on the value of the sample capacitor. For example, a shorter discharge delay can be used with smaller capacitor values.
15//
16#![no_std]
17#![no_main]
18
19use defmt::*;
20use embassy_stm32::gpio::{Level, Output, Speed};
21use embassy_stm32::tsc::{self, *};
22use embassy_time::Timer;
23use {defmt_rtt as _, panic_probe as _};
24
25/// This example is written for the nucleo-stm32l073rz, with a stm32l073rz chip.
26///
27/// Make sure you check/update the following (whether you use the L073RZ or another board):
28///
29/// * [ ] Update .cargo/config.toml with the correct `probe-rs run --chip STM32L073RZTx`chip name.
30/// * [ ] Update Cargo.toml to have the correct `embassy-stm32` feature, for L073RZ it should be `stm32l073rz`.
31/// * [ ] If your board has a special clock or power configuration, make sure that it is
32/// set up appropriately.
33/// * [ ] If your board has different pin mapping, update any pin numbers or peripherals
34/// to match your schematic
35///
36/// If you are unsure, please drop by the Embassy Matrix chat for support, and let us know:
37///
38/// * Which example you are trying to run
39/// * Which chip and board you are using
40///
41/// Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org
42#[embassy_executor::main]
43async fn main(_spawner: embassy_executor::Spawner) {
44 let device_config = embassy_stm32::Config::default();
45 let context = embassy_stm32::init(device_config);
46
47 let tsc_conf = Config {
48 ct_pulse_high_length: ChargeTransferPulseCycle::_4,
49 ct_pulse_low_length: ChargeTransferPulseCycle::_4,
50 spread_spectrum: false,
51 spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
52 spread_spectrum_prescaler: false,
53 pulse_generator_prescaler: PGPrescalerDivider::_16,
54 max_count_value: MaxCount::_255,
55 io_default_mode: false,
56 synchro_pin_polarity: false,
57 acquisition_mode: false,
58 max_count_interrupt: false,
59 channel_ios: TscIOPin::Group1Io1.into(),
60 shield_ios: 0, // no shield
61 sampling_ios: TscIOPin::Group1Io2.into(),
62 };
63
64 let mut g1: PinGroup<embassy_stm32::peripherals::TSC, G1> = PinGroup::new();
65 g1.set_io1(context.PA0, PinType::Sample);
66 g1.set_io2(context.PA1, PinType::Channel);
67
68 let mut touch_controller = tsc::Tsc::new_blocking(
69 context.TSC,
70 Some(g1),
71 None,
72 None,
73 None,
74 None,
75 None,
76 None,
77 None,
78 tsc_conf,
79 );
80
81 // Check if TSC is ready
82 if touch_controller.get_state() != State::Ready {
83 info!("TSC not ready!");
84 loop {} // Halt execution
85 }
86 info!("TSC initialized successfully");
87
88 // LED2 on the STM32L073RZ nucleo-board (PA5)
89 let mut led = Output::new(context.PA5, Level::High, Speed::Low);
90
91 // smaller sample capacitor discharge faster and can be used with shorter delay.
92 let discharge_delay = 5; // ms
93
94 // the interval at which the loop polls for new touch sensor values
95 let polling_interval = 100; // ms
96
97 info!("polling for touch");
98 loop {
99 touch_controller.start();
100 touch_controller.poll_for_acquisition();
101 touch_controller.discharge_io(true);
102 Timer::after_millis(discharge_delay).await;
103
104 let grp1_status = touch_controller.group_get_status(Group::One);
105 match grp1_status {
106 GroupStatus::Complete => {
107 let group_one_val = touch_controller.group_get_value(Group::One);
108 info!("{}", group_one_val);
109 led.set_high();
110 }
111 GroupStatus::Ongoing => led.set_low(),
112 }
113
114 Timer::after_millis(polling_interval).await;
115 }
116}
diff --git a/examples/stm32l0/src/bin/tsc_async.rs b/examples/stm32l0/src/bin/tsc_async.rs
new file mode 100644
index 000000000..dae351c2e
--- /dev/null
+++ b/examples/stm32l0/src/bin/tsc_async.rs
@@ -0,0 +1,116 @@
1// Example of async TSC (Touch Sensing Controller) that lights an LED when touch is detected.
2//
3// This example demonstrates:
4// 1. Configuring a single TSC channel pin
5// 2. Using the blocking TSC interface with polling
6// 3. Waiting for acquisition completion using `poll_for_acquisition`
7// 4. Reading touch values and controlling an LED based on the results
8//
9// Suggested physical setup on STM32L073RZ Nucleo board:
10// - Connect a 1000pF capacitor between pin PA0 and GND. This is your sampling capacitor.
11// - Connect one end of a 1K resistor to pin PA1 and leave the other end loose.
12// The loose end will act as the touch sensor which will register your touch.
13//
14// The example uses two pins from Group 1 of the TSC on the STM32L073RZ Nucleo board:
15// - PA0 as the sampling capacitor, TSC group 1 IO1 (label A0)
16// - PA1 as the channel pin, TSC group 1 IO2 (label A1)
17//
18// The program continuously reads the touch sensor value:
19// - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value.
20// - The LED is turned on when touch is detected (sensor value < 25).
21// - Touch values are logged to the console.
22//
23// Troubleshooting:
24// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value.
25// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length,
26// pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.
27//
28// Note: Configuration values and sampling capacitor value have been determined experimentally.
29// Optimal values may vary based on your specific hardware setup.
30// Pins have been chosen for their convenient locations on the STM32L073RZ board. Refer to the
31// official relevant STM32 datasheets and nucleo-board user manuals to find suitable
32// alternative pins.
33//
34// Beware for STM32L073RZ nucleo-board, that PA2 and PA3 is used for the uart connection to
35// the programmer chip. If you try to use these two pins for TSC, you will get strange
36// readings, unless you somehow reconfigure/re-wire your nucleo-board.
37// No errors or warnings will be emitted, they will just silently not work as expected.
38// (see nucleo user manual UM1724, Rev 14, page 25)
39
40#![no_std]
41#![no_main]
42
43use defmt::*;
44use embassy_stm32::gpio::{Level, Output, Speed};
45use embassy_stm32::tsc::{self, *};
46use embassy_stm32::{bind_interrupts, peripherals};
47use embassy_time::Timer;
48use {defmt_rtt as _, panic_probe as _};
49
50bind_interrupts!(struct Irqs {
51 TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;
52});
53const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup
54
55#[embassy_executor::main]
56async fn main(_spawner: embassy_executor::Spawner) {
57 let device_config = embassy_stm32::Config::default();
58 let context = embassy_stm32::init(device_config);
59
60 let mut pin_group: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default();
61 pin_group.set_io1::<tsc::pin_roles::Sample>(context.PA0);
62 let sensor = pin_group.set_io2::<tsc::pin_roles::Channel>(context.PA1);
63
64 let tsc_conf = Config {
65 ct_pulse_high_length: ChargeTransferPulseCycle::_4,
66 ct_pulse_low_length: ChargeTransferPulseCycle::_4,
67 spread_spectrum: false,
68 spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
69 spread_spectrum_prescaler: false,
70 pulse_generator_prescaler: PGPrescalerDivider::_16,
71 max_count_value: MaxCount::_255,
72 io_default_mode: false,
73 synchro_pin_polarity: false,
74 acquisition_mode: false,
75 max_count_interrupt: false,
76 };
77
78 let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
79 g1: Some(pin_group.pin_group),
80 ..Default::default()
81 };
82
83 let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, tsc_conf, Irqs).unwrap();
84
85 // Check if TSC is ready
86 if touch_controller.get_state() != State::Ready {
87 info!("TSC not ready!");
88 return;
89 }
90 info!("TSC initialized successfully");
91
92 // LED2 on the STM32L073RZ nucleo-board (PA5)
93 let mut led = Output::new(context.PA5, Level::Low, Speed::Low);
94
95 let discharge_delay = 5; // ms
96
97 info!("Starting touch_controller interface");
98 loop {
99 touch_controller.set_active_channels_mask(sensor.pin.into());
100 touch_controller.start();
101 touch_controller.pend_for_acquisition().await;
102 touch_controller.discharge_io(true);
103 Timer::after_millis(discharge_delay).await;
104
105 let group_val = touch_controller.group_get_value(sensor.pin.group());
106 info!("Touch value: {}", group_val);
107
108 if group_val < SENSOR_THRESHOLD {
109 led.set_high();
110 } else {
111 led.set_low();
112 }
113
114 Timer::after_millis(100).await;
115 }
116}
diff --git a/examples/stm32l0/src/bin/tsc_blocking.rs b/examples/stm32l0/src/bin/tsc_blocking.rs
new file mode 100644
index 000000000..e1f24639b
--- /dev/null
+++ b/examples/stm32l0/src/bin/tsc_blocking.rs
@@ -0,0 +1,142 @@
1// Example of blocking TSC (Touch Sensing Controller) that lights an LED when touch is detected.
2//
3// This example demonstrates:
4// 1. Configuring a single TSC channel pin
5// 2. Using the blocking TSC interface with polling
6// 3. Waiting for acquisition completion using `poll_for_acquisition`
7// 4. Reading touch values and controlling an LED based on the results
8//
9// Suggested physical setup on STM32L073RZ Nucleo board:
10// - Connect a 1000pF capacitor between pin PA0 and GND. This is your sampling capacitor.
11// - Connect one end of a 1K resistor to pin PA1 and leave the other end loose.
12// The loose end will act as the touch sensor which will register your touch.
13//
14// The example uses two pins from Group 1 of the TSC on the STM32L073RZ Nucleo board:
15// - PA0 as the sampling capacitor, TSC group 1 IO1 (label A0)
16// - PA1 as the channel pin, TSC group 1 IO2 (label A1)
17//
18// The program continuously reads the touch sensor value:
19// - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value.
20// - The LED is turned on when touch is detected (sensor value < 25).
21// - Touch values are logged to the console.
22//
23// Troubleshooting:
24// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value.
25// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length,
26// pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.
27//
28// Note: Configuration values and sampling capacitor value have been determined experimentally.
29// Optimal values may vary based on your specific hardware setup.
30// Pins have been chosen for their convenient locations on the STM32L073RZ board. Refer to the
31// official relevant STM32 datasheets and nucleo-board user manuals to find suitable
32// alternative pins.
33//
34// Beware for STM32L073RZ nucleo-board, that PA2 and PA3 is used for the uart connection to
35// the programmer chip. If you try to use these two pins for TSC, you will get strange
36// readings, unless you somehow reconfigure/re-wire your nucleo-board.
37// No errors or warnings will be emitted, they will just silently not work as expected.
38// (see nucleo user manual UM1724, Rev 14, page 25)
39
40#![no_std]
41#![no_main]
42
43use defmt::*;
44use embassy_stm32::gpio::{Level, Output, Speed};
45use embassy_stm32::tsc::{self, *};
46use embassy_stm32::{mode, peripherals};
47use embassy_time::Timer;
48use {defmt_rtt as _, panic_probe as _};
49
50const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup
51
52#[embassy_executor::main]
53async fn main(_spawner: embassy_executor::Spawner) {
54 let device_config = embassy_stm32::Config::default();
55 let context = embassy_stm32::init(device_config);
56
57 let tsc_conf = Config {
58 ct_pulse_high_length: ChargeTransferPulseCycle::_4,
59 ct_pulse_low_length: ChargeTransferPulseCycle::_4,
60 spread_spectrum: false,
61 spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
62 spread_spectrum_prescaler: false,
63 pulse_generator_prescaler: PGPrescalerDivider::_16,
64 max_count_value: MaxCount::_255,
65 io_default_mode: false,
66 synchro_pin_polarity: false,
67 acquisition_mode: false,
68 max_count_interrupt: false,
69 };
70
71 let mut g1: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default();
72 g1.set_io1::<tsc::pin_roles::Sample>(context.PA0);
73 let tsc_sensor = g1.set_io2::<tsc::pin_roles::Channel>(context.PA1);
74
75 let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
76 g1: Some(g1.pin_group),
77 ..Default::default()
78 };
79
80 let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, pin_groups, tsc_conf).unwrap();
81
82 // Check if TSC is ready
83 if touch_controller.get_state() != State::Ready {
84 crate::panic!("TSC not ready!");
85 }
86 info!("TSC initialized successfully");
87
88 // LED2 on the STM32L073RZ nucleo-board (PA5)
89 let mut led = Output::new(context.PA5, Level::High, Speed::Low);
90
91 // smaller sample capacitor discharge faster and can be used with shorter delay.
92 let discharge_delay = 5; // ms
93
94 // the interval at which the loop polls for new touch sensor values
95 let polling_interval = 100; // ms
96
97 info!("polling for touch");
98 loop {
99 touch_controller.set_active_channels_mask(tsc_sensor.pin.into());
100 touch_controller.start();
101 touch_controller.poll_for_acquisition();
102 touch_controller.discharge_io(true);
103 Timer::after_millis(discharge_delay).await;
104
105 match read_touch_value(&mut touch_controller, tsc_sensor.pin).await {
106 Some(v) => {
107 info!("sensor value {}", v);
108 if v < SENSOR_THRESHOLD {
109 led.set_high();
110 } else {
111 led.set_low();
112 }
113 }
114 None => led.set_low(),
115 }
116
117 Timer::after_millis(polling_interval).await;
118 }
119}
120
121const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10;
122
123// attempt to read group status and delay when still ongoing
124async fn read_touch_value(
125 touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Blocking>,
126 sensor_pin: tsc::IOPin,
127) -> Option<u16> {
128 for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS {
129 match touch_controller.group_get_status(sensor_pin.group()) {
130 GroupStatus::Complete => {
131 return Some(touch_controller.group_get_value(sensor_pin.group()));
132 }
133 GroupStatus::Ongoing => {
134 // if you end up here a lot, then you prob need to increase discharge_delay
135 // or consider changing the code to adjust the discharge_delay dynamically
136 info!("Acquisition still ongoing");
137 Timer::after_millis(1).await;
138 }
139 }
140 }
141 None
142}
diff --git a/examples/stm32l0/src/bin/tsc_multipin.rs b/examples/stm32l0/src/bin/tsc_multipin.rs
new file mode 100644
index 000000000..6343de141
--- /dev/null
+++ b/examples/stm32l0/src/bin/tsc_multipin.rs
@@ -0,0 +1,209 @@
1// Example of TSC (Touch Sensing Controller) using multiple pins from the same tsc-group.
2//
3// What is special about using multiple TSC pins as sensor channels from the same TSC group,
4// is that only one TSC pin for each TSC group can be acquired and read at the time.
5// To control which channel pins are acquired and read, we must write a mask before initiating an
6// acquisition. To help manage and abstract all this business away, we can organize our channel
7// pins into acquisition banks. Each acquisition bank can contain exactly one channel pin per TSC
8// group and it will contain the relevant mask.
9//
10// This example demonstrates how to:
11// 1. Configure multiple channel pins within a single TSC group
12// 2. Use the set_active_channels_bank method to switch between sets of different channels (acquisition banks)
13// 3. Read and interpret touch values from multiple channels in the same group
14//
15// Suggested physical setup on STM32L073RZ Nucleo board:
16// - Connect a 1000pF capacitor between pin PA0 (label A0) and GND. This is the sampling capacitor for TSC
17// group 1.
18// - Connect one end of a 1K resistor to pin PA1 (label A1) and leave the other end loose.
19// The loose end will act as a touch sensor.
20//
21// - Connect a 1000pF capacitor between pin PB3 (label D3) and GND. This is the sampling capacitor for TSC
22// group 5.
23// - Connect one end of another 1K resistor to pin PB4 and leave the other end loose.
24// The loose end will act as a touch sensor.
25// - Connect one end of another 1K resistor to pin PB6 and leave the other end loose.
26// The loose end will act as a touch sensor.
27//
28// The example uses pins from two TSC groups.
29// - PA0 as sampling capacitor, TSC group 1 IO1 (label A0)
30// - PA1 as channel, TSC group 1 IO2 (label A1)
31// - PB3 as sampling capacitor, TSC group 5 IO1 (label D3)
32// - PB4 as channel, TSC group 5 IO2 (label D10)
33// - PB6 as channel, TSC group 5 IO3 (label D5)
34//
35// The pins have been chosen to make it easy to simply add capacitors directly onto the board and
36// connect one leg to GND, and to easily add resistors to the board with no special connectors,
37// breadboards, special wires or soldering required. All you need is the capacitors and resistors.
38//
39// The program reads the designated channel pins and adjusts the LED blinking
40// pattern based on which sensor(s) are touched:
41// - No touch: LED off
42// - one sensor touched: Slow blinking
43// - two sensors touched: Fast blinking
44// - three sensors touched: LED constantly on
45//
46// Troubleshooting:
47// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value.
48// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length,
49// pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.
50//
51// Note: Configuration values and sampling capacitor value have been determined experimentally.
52// Optimal values may vary based on your specific hardware setup.
53// Pins have been chosen for their convenient locations on the STM32L073RZ board. Refer to the
54// official relevant STM32 datasheets and nucleo-board user manuals to find suitable
55// alternative pins.
56//
57// Beware for STM32L073RZ nucleo-board, that PA2 and PA3 is used for the uart connection to
58// the programmer chip. If you try to use these two pins for TSC, you will get strange
59// readings, unless you somehow reconfigure/re-wire your nucleo-board.
60// No errors or warnings will be emitted, they will just silently not work as expected.
61// (see nucleo user manual UM1724, Rev 14, page 25)
62
63#![no_std]
64#![no_main]
65
66use defmt::*;
67use embassy_stm32::gpio::{Level, Output, Speed};
68use embassy_stm32::tsc::{self, *};
69use embassy_stm32::{bind_interrupts, mode, peripherals};
70use embassy_time::Timer;
71use {defmt_rtt as _, panic_probe as _};
72
73bind_interrupts!(struct Irqs {
74 TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;
75});
76
77const SENSOR_THRESHOLD: u16 = 35;
78
79async fn acquire_sensors(
80 touch_controller: &mut Tsc<'static, peripherals::TSC, mode::Async>,
81 tsc_acquisition_bank: &AcquisitionBank,
82) {
83 touch_controller.set_active_channels_bank(tsc_acquisition_bank);
84 touch_controller.start();
85 touch_controller.pend_for_acquisition().await;
86 touch_controller.discharge_io(true);
87 let discharge_delay = 5; // ms
88 Timer::after_millis(discharge_delay).await;
89}
90
91#[embassy_executor::main]
92async fn main(_spawner: embassy_executor::Spawner) {
93 let device_config = embassy_stm32::Config::default();
94 let context = embassy_stm32::init(device_config);
95
96 // ---------- initial configuration of TSC ----------
97 let mut pin_group1: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default();
98 pin_group1.set_io1::<tsc::pin_roles::Sample>(context.PA0);
99 let tsc_sensor0 = pin_group1.set_io2(context.PA1);
100
101 let mut pin_group5: PinGroupWithRoles<peripherals::TSC, G5> = PinGroupWithRoles::default();
102 pin_group5.set_io1::<tsc::pin_roles::Sample>(context.PB3);
103 let tsc_sensor1 = pin_group5.set_io2(context.PB4);
104 let tsc_sensor2 = pin_group5.set_io3(context.PB6);
105
106 let config = tsc::Config {
107 ct_pulse_high_length: ChargeTransferPulseCycle::_16,
108 ct_pulse_low_length: ChargeTransferPulseCycle::_16,
109 spread_spectrum: false,
110 spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
111 spread_spectrum_prescaler: false,
112 pulse_generator_prescaler: PGPrescalerDivider::_16,
113 max_count_value: MaxCount::_255,
114 io_default_mode: false,
115 synchro_pin_polarity: false,
116 acquisition_mode: false,
117 max_count_interrupt: false,
118 };
119
120 let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
121 g1: Some(pin_group1.pin_group),
122 g5: Some(pin_group5.pin_group),
123 ..Default::default()
124 };
125
126 let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, config, Irqs).unwrap();
127
128 // ---------- setting up acquisition banks ----------
129 // sensor0 and sensor1 in this example belong to different TSC-groups,
130 // therefore we can acquire and read them both in one go.
131 let bank1 = touch_controller.create_acquisition_bank(AcquisitionBankPins {
132 g1_pin: Some(tsc_sensor0),
133 g5_pin: Some(tsc_sensor1),
134 ..Default::default()
135 });
136 // `sensor1` and `sensor2` belongs to the same TSC-group, therefore we must make sure to
137 // acquire them one at the time. Therefore, we organize them into different acquisition banks.
138 let bank2 = touch_controller.create_acquisition_bank(AcquisitionBankPins {
139 g5_pin: Some(tsc_sensor2),
140 ..Default::default()
141 });
142
143 // Check if TSC is ready
144 if touch_controller.get_state() != State::Ready {
145 crate::panic!("TSC not ready!");
146 }
147
148 info!("TSC initialized successfully");
149
150 // LED2 on the STM32L073RZ nucleo-board (PA5)
151 let mut led = Output::new(context.PA5, Level::High, Speed::Low);
152
153 let mut led_state = false;
154
155 loop {
156 acquire_sensors(&mut touch_controller, &bank1).await;
157 let readings1 = touch_controller.get_acquisition_bank_values(&bank1);
158 acquire_sensors(&mut touch_controller, &bank2).await;
159 let readings2 = touch_controller.get_acquisition_bank_values(&bank2);
160
161 let mut touched_sensors_count = 0;
162 for reading in readings1.iter() {
163 info!("{}", reading);
164 if reading.sensor_value < SENSOR_THRESHOLD {
165 touched_sensors_count += 1;
166 }
167 }
168 for reading in readings2.iter() {
169 info!("{}", reading);
170 if reading.sensor_value < SENSOR_THRESHOLD {
171 touched_sensors_count += 1;
172 }
173 }
174
175 match touched_sensors_count {
176 0 => {
177 // No sensors touched, turn off the LED
178 led.set_low();
179 led_state = false;
180 }
181 1 => {
182 // One sensor touched, blink slowly
183 led_state = !led_state;
184 if led_state {
185 led.set_high();
186 } else {
187 led.set_low();
188 }
189 Timer::after_millis(200).await;
190 }
191 2 => {
192 // Two sensors touched, blink faster
193 led_state = !led_state;
194 if led_state {
195 led.set_high();
196 } else {
197 led.set_low();
198 }
199 Timer::after_millis(50).await;
200 }
201 3 => {
202 // All three sensors touched, LED constantly on
203 led.set_high();
204 led_state = true;
205 }
206 _ => crate::unreachable!(), // This case should never occur with 3 sensors
207 }
208 }
209}
diff --git a/examples/stm32l4/.cargo/config.toml b/examples/stm32l4/.cargo/config.toml
index 83fc6d6f8..d71fb1517 100644
--- a/examples/stm32l4/.cargo/config.toml
+++ b/examples/stm32l4/.cargo/config.toml
@@ -2,7 +2,8 @@
2# replace STM32F429ZITx with your chip as listed in `probe-rs chip list` 2# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
3#runner = "probe-rs run --chip STM32L475VGT6" 3#runner = "probe-rs run --chip STM32L475VGT6"
4#runner = "probe-rs run --chip STM32L475VG" 4#runner = "probe-rs run --chip STM32L475VG"
5runner = "probe-rs run --chip STM32L4S5QI" 5#runner = "probe-rs run --chip STM32L4S5QI"
6runner = "probe-rs run --chip STM32L4R5ZITxP"
6 7
7[build] 8[build]
8target = "thumbv7em-none-eabi" 9target = "thumbv7em-none-eabi"
diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml
index b172878c1..512bb8064 100644
--- a/examples/stm32l4/Cargo.toml
+++ b/examples/stm32l4/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8# Change stm32l4s5vi to your chip name, if necessary. 8# Change stm32l4s5vi to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4s5qi", "memory-x", "time-driver-any", "exti", "chrono"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4r5zi", "memory-x", "time-driver-any", "exti", "chrono"] }
10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] } 12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] }
diff --git a/examples/stm32l4/README.md b/examples/stm32l4/README.md
new file mode 100644
index 000000000..e463c18a0
--- /dev/null
+++ b/examples/stm32l4/README.md
@@ -0,0 +1,24 @@
1# Examples for STM32L4 family
2Run individual examples with
3```
4cargo run --bin <module-name>
5```
6for example
7```
8cargo run --bin blinky
9```
10
11## Checklist before running examples
12You might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.
13
14* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L4R5ZI-P it should be `probe-rs run --chip STM32L4R5ZITxP`. (use `probe-rs chip list` to find your chip)
15* [ ] Update Cargo.toml to have the correct `embassy-stm32` feature. For example for L4R5ZI-P it should be `stm32l4r5zi`. Look in the `Cargo.toml` file of the `embassy-stm32` project to find the correct feature flag for your chip.
16* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.
17* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic
18
19If you are unsure, please drop by the Embassy Matrix chat for support, and let us know:
20
21* Which example you are trying to run
22* Which chip and board you are using
23
24Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org
diff --git a/examples/stm32l4/src/bin/tsc_async.rs b/examples/stm32l4/src/bin/tsc_async.rs
new file mode 100644
index 000000000..b9a059e2e
--- /dev/null
+++ b/examples/stm32l4/src/bin/tsc_async.rs
@@ -0,0 +1,108 @@
1// Example of async TSC (Touch Sensing Controller) that lights an LED when touch is detected.
2//
3// This example demonstrates:
4// 1. Configuring a single TSC channel pin
5// 2. Using the async TSC interface
6// 3. Waiting for acquisition completion using `pend_for_acquisition`
7// 4. Reading touch values and controlling an LED based on the results
8//
9// Suggested physical setup on STM32L4R5ZI-P board:
10// - Connect a 1000pF capacitor between pin PB4 (D25) and GND. This is your sampling capacitor.
11// - Connect one end of a 1K resistor to pin PB5 (D21) and leave the other end loose.
12// The loose end will act as the touch sensor which will register your touch.
13//
14// The example uses two pins from Group 2 of the TSC:
15// - PB4 (D25) as the sampling capacitor, TSC group 2 IO1
16// - PB5 (D21) as the channel pin, TSC group 2 IO2
17//
18// The program continuously reads the touch sensor value:
19// - It starts acquisition, waits for completion using `pend_for_acquisition`, and reads the value.
20// - The LED (connected to PB14) is turned on when touch is detected (sensor value < SENSOR_THRESHOLD).
21// - Touch values are logged to the console.
22//
23// Troubleshooting:
24// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value.
25// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length,
26// pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.
27//
28// Note: Configuration values and sampling capacitor value have been determined experimentally.
29// Optimal values may vary based on your specific hardware setup.
30
31#![no_std]
32#![no_main]
33
34use defmt::*;
35use embassy_stm32::gpio::{Level, Output, Speed};
36use embassy_stm32::tsc::{self, *};
37use embassy_stm32::{bind_interrupts, peripherals};
38use embassy_time::Timer;
39use {defmt_rtt as _, panic_probe as _};
40
41bind_interrupts!(struct Irqs {
42 TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;
43});
44const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup
45
46#[embassy_executor::main]
47async fn main(_spawner: embassy_executor::Spawner) {
48 let device_config = embassy_stm32::Config::default();
49 let context = embassy_stm32::init(device_config);
50
51 let mut pin_group: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default();
52 // D25
53 pin_group.set_io1::<tsc::pin_roles::Sample>(context.PB4);
54 // D21
55 let tsc_sensor = pin_group.set_io2::<tsc::pin_roles::Channel>(context.PB5);
56
57 let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
58 g2: Some(pin_group.pin_group),
59 ..Default::default()
60 };
61
62 let tsc_conf = Config {
63 ct_pulse_high_length: ChargeTransferPulseCycle::_4,
64 ct_pulse_low_length: ChargeTransferPulseCycle::_4,
65 spread_spectrum: false,
66 spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
67 spread_spectrum_prescaler: false,
68 pulse_generator_prescaler: PGPrescalerDivider::_16,
69 max_count_value: MaxCount::_255,
70 io_default_mode: false,
71 synchro_pin_polarity: false,
72 acquisition_mode: false,
73 max_count_interrupt: false,
74 };
75
76 let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, tsc_conf, Irqs).unwrap();
77
78 // Check if TSC is ready
79 if touch_controller.get_state() != State::Ready {
80 info!("TSC not ready!");
81 return;
82 }
83 info!("TSC initialized successfully");
84
85 let mut led = Output::new(context.PB14, Level::High, Speed::Low);
86
87 let discharge_delay = 1; // ms
88
89 info!("Starting touch_controller interface");
90 loop {
91 touch_controller.set_active_channels_mask(tsc_sensor.pin.into());
92 touch_controller.start();
93 touch_controller.pend_for_acquisition().await;
94 touch_controller.discharge_io(true);
95 Timer::after_millis(discharge_delay).await;
96
97 let group_val = touch_controller.group_get_value(tsc_sensor.pin.group());
98 info!("Touch value: {}", group_val);
99
100 if group_val < SENSOR_THRESHOLD {
101 led.set_high();
102 } else {
103 led.set_low();
104 }
105
106 Timer::after_millis(100).await;
107 }
108}
diff --git a/examples/stm32l4/src/bin/tsc_blocking.rs b/examples/stm32l4/src/bin/tsc_blocking.rs
new file mode 100644
index 000000000..12084f8e2
--- /dev/null
+++ b/examples/stm32l4/src/bin/tsc_blocking.rs
@@ -0,0 +1,147 @@
1// # Example of blocking TSC (Touch Sensing Controller) that lights an LED when touch is detected
2//
3// This example demonstrates how to use the Touch Sensing Controller (TSC) in blocking mode on an STM32L4R5ZI-P board.
4//
5// ## This example demonstrates:
6//
7// 1. Configuring a single TSC channel pin
8// 2. Using the blocking TSC interface with polling
9// 3. Waiting for acquisition completion using `poll_for_acquisition`
10// 4. Reading touch values and controlling an LED based on the results
11//
12// ## Suggested physical setup on STM32L4R5ZI-P board:
13//
14// - Connect a 1000pF capacitor between pin PB4 (D25) and GND. This is your sampling capacitor.
15// - Connect one end of a 1K resistor to pin PB5 (D21) and leave the other end loose.
16// The loose end will act as the touch sensor which will register your touch.
17//
18// ## Pin Configuration:
19//
20// The example uses two pins from Group 2 of the TSC:
21// - PB4 (D25) as the sampling capacitor, TSC group 2 IO1
22// - PB5 (D21) as the channel pin, TSC group 2 IO2
23//
24// ## Program Behavior:
25//
26// The program continuously reads the touch sensor value:
27// - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value.
28// - The LED (connected to PB14) is turned on when touch is detected (sensor value < SENSOR_THRESHOLD).
29// - Touch values are logged to the console.
30//
31// ## Troubleshooting:
32//
33// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value (currently set to 25).
34// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length,
35// pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.
36// - Be aware that for some boards, there might be overlapping concerns between some pins,
37// such as UART connections for the programmer. No errors or warnings will be emitted if you
38// try to use such a pin for TSC, but you may get strange sensor readings.
39//
40// Note: Configuration values and sampling capacitor value have been determined experimentally.
41// Optimal values may vary based on your specific hardware setup. Refer to the official
42// STM32L4R5ZI-P datasheet and user manuals for more information on pin configurations and TSC functionality.
43
44#![no_std]
45#![no_main]
46
47use defmt::*;
48use embassy_stm32::gpio::{Level, Output, Speed};
49use embassy_stm32::tsc::{self, *};
50use embassy_stm32::{mode, peripherals};
51use embassy_time::Timer;
52use {defmt_rtt as _, panic_probe as _};
53
54const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup
55
56#[embassy_executor::main]
57async fn main(_spawner: embassy_executor::Spawner) {
58 let device_config = embassy_stm32::Config::default();
59 let context = embassy_stm32::init(device_config);
60
61 let tsc_conf = Config {
62 ct_pulse_high_length: ChargeTransferPulseCycle::_4,
63 ct_pulse_low_length: ChargeTransferPulseCycle::_4,
64 spread_spectrum: false,
65 spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
66 spread_spectrum_prescaler: false,
67 pulse_generator_prescaler: PGPrescalerDivider::_16,
68 max_count_value: MaxCount::_255,
69 io_default_mode: false,
70 synchro_pin_polarity: false,
71 acquisition_mode: false,
72 max_count_interrupt: false,
73 };
74
75 let mut g2: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default();
76 // D25
77 g2.set_io1::<tsc::pin_roles::Sample>(context.PB4);
78 // D21
79 let tsc_sensor = g2.set_io2::<tsc::pin_roles::Channel>(context.PB5);
80
81 let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
82 g2: Some(g2.pin_group),
83 ..Default::default()
84 };
85
86 let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, pin_groups, tsc_conf).unwrap();
87
88 // Check if TSC is ready
89 if touch_controller.get_state() != State::Ready {
90 crate::panic!("TSC not ready!");
91 }
92 info!("TSC initialized successfully");
93
94 let mut led = Output::new(context.PB14, Level::High, Speed::Low);
95
96 // smaller sample capacitor discharge faster and can be used with shorter delay.
97 let discharge_delay = 5; // ms
98
99 // the interval at which the loop polls for new touch sensor values
100 let polling_interval = 100; // ms
101
102 info!("polling for touch");
103 loop {
104 touch_controller.set_active_channels_mask(tsc_sensor.pin.into());
105 touch_controller.start();
106 touch_controller.poll_for_acquisition();
107 touch_controller.discharge_io(true);
108 Timer::after_millis(discharge_delay).await;
109
110 match read_touch_value(&mut touch_controller, tsc_sensor.pin).await {
111 Some(v) => {
112 info!("sensor value {}", v);
113 if v < SENSOR_THRESHOLD {
114 led.set_high();
115 } else {
116 led.set_low();
117 }
118 }
119 None => led.set_low(),
120 }
121
122 Timer::after_millis(polling_interval).await;
123 }
124}
125
126const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10;
127
128// attempt to read group status and delay when still ongoing
129async fn read_touch_value(
130 touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Blocking>,
131 sensor_pin: tsc::IOPin,
132) -> Option<u16> {
133 for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS {
134 match touch_controller.group_get_status(sensor_pin.group()) {
135 GroupStatus::Complete => {
136 return Some(touch_controller.group_get_value(sensor_pin.group()));
137 }
138 GroupStatus::Ongoing => {
139 // if you end up here a lot, then you prob need to increase discharge_delay
140 // or consider changing the code to adjust the discharge_delay dynamically
141 info!("Acquisition still ongoing");
142 Timer::after_millis(1).await;
143 }
144 }
145 }
146 None
147}
diff --git a/examples/stm32l4/src/bin/tsc_multipin.rs b/examples/stm32l4/src/bin/tsc_multipin.rs
new file mode 100644
index 000000000..8fec5ddc4
--- /dev/null
+++ b/examples/stm32l4/src/bin/tsc_multipin.rs
@@ -0,0 +1,198 @@
1// # Example of TSC (Touch Sensing Controller) using multiple pins from the same TSC group
2//
3// This example demonstrates how to use the Touch Sensing Controller (TSC) with multiple pins, including pins from the same TSC group, on an STM32L4R5ZI-P board.
4//
5// ## Key Concepts
6//
7// - Only one TSC pin for each TSC group can be acquired and read at a time.
8// - To control which channel pins are acquired and read, we must write a mask before initiating an acquisition.
9// - We organize channel pins into acquisition banks to manage this process efficiently.
10// - Each acquisition bank can contain exactly one channel pin per TSC group and will contain the relevant mask.
11//
12// ## This example demonstrates how to:
13//
14// 1. Configure multiple channel pins within a single TSC group
15// 2. Use the set_active_channels_bank method to switch between sets of different channels (acquisition banks)
16// 3. Read and interpret touch values from multiple channels in the same group
17//
18// ## Suggested physical setup on STM32L4R5ZI-P board:
19//
20// - Connect a 1000pF capacitor between pin PB12 (D19) and GND. This is the sampling capacitor for TSC group 1.
21// - Connect one end of a 1K resistor to pin PB13 (D18) and leave the other end loose. This will act as a touch sensor.
22// - Connect a 1000pF capacitor between pin PB4 (D25) and GND. This is the sampling capacitor for TSC group 2.
23// - Connect one end of a 1K resistor to pin PB5 (D22) and leave the other end loose. This will act as a touch sensor.
24// - Connect one end of another 1K resistor to pin PB6 (D71) and leave the other end loose. This will act as a touch sensor.
25//
26// ## Pin Configuration:
27//
28// The example uses pins from two TSC groups:
29//
30// - Group 1:
31// - PB12 (D19) as sampling capacitor (TSC group 1 IO1)
32// - PB13 (D18) as channel (TSC group 1 IO2)
33// - Group 2:
34// - PB4 (D25) as sampling capacitor (TSC group 2 IO1)
35// - PB5 (D22) as channel (TSC group 2 IO2)
36// - PB6 (D71) as channel (TSC group 2 IO3)
37//
38// The pins have been chosen for their convenient locations on the STM32L4R5ZI-P board, making it easy to add capacitors and resistors directly to the board without special connectors, breadboards, or soldering.
39//
40// ## Program Behavior:
41//
42// The program reads the designated channel pins and adjusts the LED (connected to PB14) blinking pattern based on which sensor(s) are touched:
43//
44// - No touch: LED off
45// - One sensor touched: Slow blinking
46// - Two sensors touched: Fast blinking
47// - Three sensors touched: LED constantly on
48//
49// ## Troubleshooting:
50//
51// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value (currently set to 20).
52// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.
53// - Be aware that for some boards there will be overlapping concerns between some pins, for
54// example UART connection for the programmer to the MCU and a TSC pin. No errors or warning will
55// be emitted if you try to use such a pin for TSC, but you will get strange sensor readings.
56//
57// Note: Configuration values and sampling capacitor values have been determined experimentally. Optimal values may vary based on your specific hardware setup. Refer to the official STM32L4R5ZI-P datasheet and user manuals for more information on pin configurations and TSC functionality.
58
59#![no_std]
60#![no_main]
61
62use defmt::*;
63use embassy_stm32::gpio::{Level, Output, Speed};
64use embassy_stm32::tsc::{self, *};
65use embassy_stm32::{bind_interrupts, mode, peripherals};
66use embassy_time::Timer;
67use {defmt_rtt as _, panic_probe as _};
68
69bind_interrupts!(struct Irqs {
70 TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;
71});
72
73const SENSOR_THRESHOLD: u16 = 20;
74
75async fn acquire_sensors(
76 touch_controller: &mut Tsc<'static, peripherals::TSC, mode::Async>,
77 tsc_acquisition_bank: &AcquisitionBank,
78) {
79 touch_controller.set_active_channels_bank(tsc_acquisition_bank);
80 touch_controller.start();
81 touch_controller.pend_for_acquisition().await;
82 touch_controller.discharge_io(true);
83 let discharge_delay = 1; // ms
84 Timer::after_millis(discharge_delay).await;
85}
86
87#[embassy_executor::main]
88async fn main(_spawner: embassy_executor::Spawner) {
89 let device_config = embassy_stm32::Config::default();
90 let context = embassy_stm32::init(device_config);
91
92 // ---------- initial configuration of TSC ----------
93 let mut g1: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default();
94 g1.set_io1::<tsc::pin_roles::Sample>(context.PB12);
95 let sensor0 = g1.set_io2::<tsc::pin_roles::Channel>(context.PB13);
96
97 let mut g2: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default();
98 g2.set_io1::<tsc::pin_roles::Sample>(context.PB4);
99 let sensor1 = g2.set_io2(context.PB5);
100 let sensor2 = g2.set_io3(context.PB6);
101
102 let config = tsc::Config {
103 ct_pulse_high_length: ChargeTransferPulseCycle::_16,
104 ct_pulse_low_length: ChargeTransferPulseCycle::_16,
105 spread_spectrum: false,
106 spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
107 spread_spectrum_prescaler: false,
108 pulse_generator_prescaler: PGPrescalerDivider::_16,
109 max_count_value: MaxCount::_255,
110 io_default_mode: false,
111 synchro_pin_polarity: false,
112 acquisition_mode: false,
113 max_count_interrupt: false,
114 };
115
116 let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
117 g1: Some(g1.pin_group),
118 g2: Some(g2.pin_group),
119 ..Default::default()
120 };
121
122 let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, config, Irqs).unwrap();
123
124 // ---------- setting up acquisition banks ----------
125 // sensor0 and sensor1 belong to different TSC-groups, therefore we can acquire and
126 // read them both in one go.
127 let bank1 = touch_controller.create_acquisition_bank(AcquisitionBankPins {
128 g1_pin: Some(sensor0),
129 g2_pin: Some(sensor1),
130 ..Default::default()
131 });
132 // `sensor1` and `sensor2` belongs to the same TSC-group, therefore we must make sure to
133 // acquire them one at the time. We do this by organizing them into different acquisition banks.
134 let bank2 = touch_controller.create_acquisition_bank(AcquisitionBankPins {
135 g2_pin: Some(sensor2),
136 ..Default::default()
137 });
138
139 // Check if TSC is ready
140 if touch_controller.get_state() != State::Ready {
141 crate::panic!("TSC not ready!");
142 }
143
144 info!("TSC initialized successfully");
145
146 let mut led = Output::new(context.PB14, Level::High, Speed::Low);
147
148 let mut led_state = false;
149
150 loop {
151 acquire_sensors(&mut touch_controller, &bank1).await;
152 let readings1 = touch_controller.get_acquisition_bank_values(&bank1);
153 acquire_sensors(&mut touch_controller, &bank2).await;
154 let readings2 = touch_controller.get_acquisition_bank_values(&bank2);
155
156 let mut touched_sensors_count = 0;
157 for reading in readings1.iter().chain(readings2.iter()) {
158 info!("{}", reading);
159 if reading.sensor_value < SENSOR_THRESHOLD {
160 touched_sensors_count += 1;
161 }
162 }
163
164 match touched_sensors_count {
165 0 => {
166 // No sensors touched, turn off the LED
167 led.set_low();
168 led_state = false;
169 }
170 1 => {
171 // One sensor touched, blink slowly
172 led_state = !led_state;
173 if led_state {
174 led.set_high();
175 } else {
176 led.set_low();
177 }
178 Timer::after_millis(200).await;
179 }
180 2 => {
181 // Two sensors touched, blink faster
182 led_state = !led_state;
183 if led_state {
184 led.set_high();
185 } else {
186 led.set_low();
187 }
188 Timer::after_millis(50).await;
189 }
190 3 => {
191 // All three sensors touched, LED constantly on
192 led.set_high();
193 led_state = true;
194 }
195 _ => crate::unreachable!(), // This case should never occur with 3 sensors
196 }
197 }
198}
diff --git a/examples/stm32u5/src/bin/tsc.rs b/examples/stm32u5/src/bin/tsc.rs
index eb15d275a..a85acc4c7 100644
--- a/examples/stm32u5/src/bin/tsc.rs
+++ b/examples/stm32u5/src/bin/tsc.rs
@@ -2,8 +2,8 @@
2#![no_main] 2#![no_main]
3 3
4use defmt::*; 4use defmt::*;
5use embassy_stm32::bind_interrupts;
6use embassy_stm32::tsc::{self, *}; 5use embassy_stm32::tsc::{self, *};
6use embassy_stm32::{bind_interrupts, peripherals};
7use embassy_time::Timer; 7use embassy_time::Timer;
8use {defmt_rtt as _, panic_probe as _}; 8use {defmt_rtt as _, panic_probe as _};
9 9
@@ -33,63 +33,52 @@ async fn main(_spawner: embassy_executor::Spawner) {
33 synchro_pin_polarity: false, 33 synchro_pin_polarity: false,
34 acquisition_mode: false, 34 acquisition_mode: false,
35 max_count_interrupt: false, 35 max_count_interrupt: false,
36 channel_ios: TscIOPin::Group2Io2 | TscIOPin::Group7Io3,
37 shield_ios: TscIOPin::Group1Io3.into(),
38 sampling_ios: TscIOPin::Group1Io2 | TscIOPin::Group2Io1 | TscIOPin::Group7Io2,
39 }; 36 };
40 37
41 let mut g1: PinGroup<embassy_stm32::peripherals::TSC, G1> = PinGroup::new(); 38 let mut g1: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default();
42 g1.set_io2(context.PB13, PinType::Sample); 39 g1.set_io2::<tsc::pin_roles::Sample>(context.PB13);
43 g1.set_io3(context.PB14, PinType::Shield); 40 g1.set_io3::<tsc::pin_roles::Shield>(context.PB14);
44 41
45 let mut g2: PinGroup<embassy_stm32::peripherals::TSC, G2> = PinGroup::new(); 42 let mut g2: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default();
46 g2.set_io1(context.PB4, PinType::Sample); 43 g2.set_io1::<tsc::pin_roles::Sample>(context.PB4);
47 g2.set_io2(context.PB5, PinType::Channel); 44 let sensor0 = g2.set_io2(context.PB5);
48 45
49 let mut g7: PinGroup<embassy_stm32::peripherals::TSC, G7> = PinGroup::new(); 46 let mut g7: PinGroupWithRoles<peripherals::TSC, G7> = PinGroupWithRoles::default();
50 g7.set_io2(context.PE3, PinType::Sample); 47 g7.set_io2::<tsc::pin_roles::Sample>(context.PE3);
51 g7.set_io3(context.PE4, PinType::Channel); 48 let sensor1 = g7.set_io3(context.PE4);
52 49
53 let mut touch_controller = tsc::Tsc::new_async( 50 let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
54 context.TSC, 51 g1: Some(g1.pin_group),
55 Some(g1), 52 g2: Some(g2.pin_group),
56 Some(g2), 53 g7: Some(g7.pin_group),
57 None, 54 ..Default::default()
58 None, 55 };
59 None, 56
60 None, 57 let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, config, Irqs).unwrap();
61 Some(g7),
62 None,
63 config,
64 Irqs,
65 );
66 58
67 touch_controller.discharge_io(true); 59 let acquisition_bank = touch_controller.create_acquisition_bank(AcquisitionBankPins {
68 Timer::after_millis(1).await; 60 g2_pin: Some(sensor0),
61 g7_pin: Some(sensor1),
62 ..Default::default()
63 });
69 64
70 touch_controller.start(); 65 touch_controller.set_active_channels_bank(&acquisition_bank);
71 66
72 let mut group_two_val = 0;
73 let mut group_seven_val = 0;
74 info!("Starting touch_controller interface"); 67 info!("Starting touch_controller interface");
75 loop { 68 loop {
69 touch_controller.start();
76 touch_controller.pend_for_acquisition().await; 70 touch_controller.pend_for_acquisition().await;
77 touch_controller.discharge_io(true); 71 touch_controller.discharge_io(true);
78 Timer::after_millis(1).await; 72 Timer::after_millis(1).await;
79 73
80 if touch_controller.group_get_status(Group::Two) == GroupStatus::Complete { 74 let status = touch_controller.get_acquisition_bank_status(&acquisition_bank);
81 group_two_val = touch_controller.group_get_value(Group::Two);
82 }
83 75
84 if touch_controller.group_get_status(Group::Seven) == GroupStatus::Complete { 76 if status.all_complete() {
85 group_seven_val = touch_controller.group_get_value(Group::Seven); 77 let read_values = touch_controller.get_acquisition_bank_values(&acquisition_bank);
78 let group2_reading = read_values.get_group_reading(Group::Two).unwrap();
79 let group7_reading = read_values.get_group_reading(Group::Seven).unwrap();
80 info!("group 2 value: {}", group2_reading.sensor_value);
81 info!("group 7 value: {}", group7_reading.sensor_value);
86 } 82 }
87
88 info!(
89 "Group Two value: {}, Group Seven value: {},",
90 group_two_val, group_seven_val
91 );
92
93 touch_controller.start();
94 } 83 }
95} 84}