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}
457