1 | use core::future::poll_fn; |
2 | use core::marker::PhantomData; |
3 | use core::ops::BitOr; |
4 | use core::task::Poll; |
5 | |
6 | use embassy_hal_internal::{into_ref, PeripheralRef}; |
7 | |
8 | use super::acquisition_banks::*; |
9 | use super::config::*; |
10 | use super::errors::*; |
11 | use super::io_pin::*; |
12 | use super::pin_groups::*; |
13 | use super::types::*; |
14 | use super::{Instance, InterruptHandler, TSC_NUM_GROUPS}; |
15 | use crate::interrupt::typelevel::Interrupt; |
16 | use crate::mode::{Async, Blocking, Mode as PeriMode}; |
17 | use 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. |
23 | struct 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 |
33 | pub 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 | |
42 | impl<'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 | |
404 | impl<'d, T: Instance, K: PeriMode> Drop for Tsc<'d, T, K> { |
405 | fn drop(&mut self) { |
406 | rcc::disable::<T>(); |
407 | } |
408 | } |
409 | |
410 | impl<'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 | |
442 | impl<'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 | |