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