diff options
| author | michel <[email protected]> | 2024-08-07 21:58:49 +0200 |
|---|---|---|
| committer | michel <[email protected]> | 2024-11-29 17:58:33 +0100 |
| commit | 721c6820d4a6e3bbf2546997205a32975e6bad8b (patch) | |
| tree | f725e1f66b0b7f40a3b663f355c2b1d5d389af06 /examples/stm32l4 | |
| parent | 1a1d5c4689a8b6c57ebb74e99fdea8df39adb037 (diff) | |
STM32-TSC: enable discriminating between pins within same TSC group and improve TSC library in general
Diffstat (limited to 'examples/stm32l4')
| -rw-r--r-- | examples/stm32l4/.cargo/config.toml | 3 | ||||
| -rw-r--r-- | examples/stm32l4/Cargo.toml | 2 | ||||
| -rw-r--r-- | examples/stm32l4/README.md | 24 | ||||
| -rw-r--r-- | examples/stm32l4/src/bin/tsc_async.rs | 108 | ||||
| -rw-r--r-- | examples/stm32l4/src/bin/tsc_blocking.rs | 147 | ||||
| -rw-r--r-- | examples/stm32l4/src/bin/tsc_multipin.rs | 222 |
6 files changed, 504 insertions, 2 deletions
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" |
| 5 | runner = "probe-rs run --chip STM32L4S5QI" | 5 | #runner = "probe-rs run --chip STM32L4S5QI" |
| 6 | runner = "probe-rs run --chip STM32L4R5ZITxP" | ||
| 6 | 7 | ||
| 7 | [build] | 8 | [build] |
| 8 | target = "thumbv7em-none-eabi" | 9 | target = "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. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4s5qi", "memory-x", "time-driver-any", "exti", "chrono"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4r5zi", "memory-x", "time-driver-any", "exti", "chrono"] } |
| 10 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] } | 12 | embassy-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 | ||
| 2 | Run individual examples with | ||
| 3 | ``` | ||
| 4 | cargo run --bin <module-name> | ||
| 5 | ``` | ||
| 6 | for example | ||
| 7 | ``` | ||
| 8 | cargo run --bin blinky | ||
| 9 | ``` | ||
| 10 | |||
| 11 | ## Checklist before running examples | ||
| 12 | You 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 | |||
| 19 | If 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 | |||
| 24 | Embassy 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..ada2c468f --- /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 | |||
| 34 | use defmt::*; | ||
| 35 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 36 | use embassy_stm32::tsc::{self, *}; | ||
| 37 | use embassy_stm32::{bind_interrupts, peripherals}; | ||
| 38 | use embassy_time::Timer; | ||
| 39 | use {defmt_rtt as _, panic_probe as _}; | ||
| 40 | |||
| 41 | bind_interrupts!(struct Irqs { | ||
| 42 | TSC => InterruptHandler<embassy_stm32::peripherals::TSC>; | ||
| 43 | }); | ||
| 44 | const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup | ||
| 45 | |||
| 46 | #[embassy_executor::main] | ||
| 47 | async 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..76aba55ba --- /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 | |||
| 47 | use defmt::*; | ||
| 48 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 49 | use embassy_stm32::tsc::{self, *}; | ||
| 50 | use embassy_stm32::{mode, peripherals}; | ||
| 51 | use embassy_time::Timer; | ||
| 52 | use {defmt_rtt as _, panic_probe as _}; | ||
| 53 | |||
| 54 | const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup | ||
| 55 | |||
| 56 | #[embassy_executor::main] | ||
| 57 | async 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 | |||
| 126 | const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10; | ||
| 127 | |||
| 128 | // attempt to read group status and delay when still ongoing | ||
| 129 | async fn read_touch_value( | ||
| 130 | touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Blocking>, | ||
| 131 | sensor_pin: TscIOPin, | ||
| 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..20a559514 --- /dev/null +++ b/examples/stm32l4/src/bin/tsc_multipin.rs | |||
| @@ -0,0 +1,222 @@ | |||
| 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 method to switch between different channels | ||
| 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 | |||
| 62 | use defmt::*; | ||
| 63 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 64 | use embassy_stm32::tsc::{self, *}; | ||
| 65 | use embassy_stm32::{bind_interrupts, mode, peripherals}; | ||
| 66 | use embassy_time::Timer; | ||
| 67 | use {defmt_rtt as _, panic_probe as _}; | ||
| 68 | |||
| 69 | bind_interrupts!(struct Irqs { | ||
| 70 | TSC => InterruptHandler<embassy_stm32::peripherals::TSC>; | ||
| 71 | }); | ||
| 72 | |||
| 73 | const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10; | ||
| 74 | |||
| 75 | async fn read_touch_values( | ||
| 76 | touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Async>, | ||
| 77 | tsc_acquisition_bank: &TscAcquisitionBank, | ||
| 78 | ) -> Option<TscAcquisitionBankReadings> { | ||
| 79 | for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS { | ||
| 80 | let status = touch_controller.get_acquisition_bank_status(tsc_acquisition_bank); | ||
| 81 | if status.all_complete() { | ||
| 82 | let r = touch_controller.get_acquisition_bank_values(tsc_acquisition_bank); | ||
| 83 | return Some(r); | ||
| 84 | } else { | ||
| 85 | info!("Acquisition still ongoing"); | ||
| 86 | Timer::after_millis(1).await; | ||
| 87 | } | ||
| 88 | } | ||
| 89 | info!("Acquisition failed after {} attempts", MAX_GROUP_STATUS_READ_ATTEMPTS); | ||
| 90 | None | ||
| 91 | } | ||
| 92 | |||
| 93 | const SENSOR_THRESHOLD: u16 = 20; | ||
| 94 | |||
| 95 | async fn acquire_sensors( | ||
| 96 | touch_controller: &mut Tsc<'static, peripherals::TSC, mode::Async>, | ||
| 97 | tsc_acquisition_bank: &TscAcquisitionBank, | ||
| 98 | ) { | ||
| 99 | touch_controller.set_active_channels_mask(tsc_acquisition_bank.mask()); | ||
| 100 | touch_controller.start(); | ||
| 101 | touch_controller.pend_for_acquisition().await; | ||
| 102 | touch_controller.discharge_io(true); | ||
| 103 | let discharge_delay = 1; // ms | ||
| 104 | Timer::after_millis(discharge_delay).await; | ||
| 105 | } | ||
| 106 | |||
| 107 | #[embassy_executor::main] | ||
| 108 | async fn main(_spawner: embassy_executor::Spawner) { | ||
| 109 | let device_config = embassy_stm32::Config::default(); | ||
| 110 | let context = embassy_stm32::init(device_config); | ||
| 111 | |||
| 112 | // ---------- initial configuration of TSC ---------- | ||
| 113 | let mut g1: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default(); | ||
| 114 | g1.set_io1::<tsc_pin_roles::Sample>(context.PB12); | ||
| 115 | let sensor0 = g1.set_io2::<tsc_pin_roles::Channel>(context.PB13); | ||
| 116 | |||
| 117 | let mut g2: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default(); | ||
| 118 | g2.set_io1::<tsc_pin_roles::Sample>(context.PB4); | ||
| 119 | let sensor1 = g2.set_io2(context.PB5); | ||
| 120 | let sensor2 = g2.set_io3(context.PB6); | ||
| 121 | |||
| 122 | let config = tsc::Config { | ||
| 123 | ct_pulse_high_length: ChargeTransferPulseCycle::_16, | ||
| 124 | ct_pulse_low_length: ChargeTransferPulseCycle::_16, | ||
| 125 | spread_spectrum: false, | ||
| 126 | spread_spectrum_deviation: SSDeviation::new(2).unwrap(), | ||
| 127 | spread_spectrum_prescaler: false, | ||
| 128 | pulse_generator_prescaler: PGPrescalerDivider::_16, | ||
| 129 | max_count_value: MaxCount::_255, | ||
| 130 | io_default_mode: false, | ||
| 131 | synchro_pin_polarity: false, | ||
| 132 | acquisition_mode: false, | ||
| 133 | max_count_interrupt: false, | ||
| 134 | }; | ||
| 135 | |||
| 136 | let pin_groups: PinGroups<peripherals::TSC> = PinGroups { | ||
| 137 | g1: Some(g1.pin_group), | ||
| 138 | g2: Some(g2.pin_group), | ||
| 139 | ..Default::default() | ||
| 140 | }; | ||
| 141 | |||
| 142 | let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, config, Irqs).unwrap(); | ||
| 143 | |||
| 144 | // ---------- setting up acquisition banks ---------- | ||
| 145 | // sensor0 and sensor1 belong to different TSC-groups, therefore we can acquire and | ||
| 146 | // read them both in one go. | ||
| 147 | let bank1 = touch_controller.create_acquisition_bank(TscAcquisitionBankPins { | ||
| 148 | g1_pin: Some(sensor0), | ||
| 149 | g2_pin: Some(sensor1), | ||
| 150 | ..Default::default() | ||
| 151 | }); | ||
| 152 | // `sensor1` and `sensor2` belongs to the same TSC-group, therefore we must make sure to | ||
| 153 | // acquire them one at the time. We do this by organizing them into different acquisition banks. | ||
| 154 | let bank2 = touch_controller.create_acquisition_bank(TscAcquisitionBankPins { | ||
| 155 | g2_pin: Some(sensor2), | ||
| 156 | ..Default::default() | ||
| 157 | }); | ||
| 158 | |||
| 159 | // Check if TSC is ready | ||
| 160 | if touch_controller.get_state() != State::Ready { | ||
| 161 | crate::panic!("TSC not ready!"); | ||
| 162 | } | ||
| 163 | |||
| 164 | info!("TSC initialized successfully"); | ||
| 165 | |||
| 166 | let mut led = Output::new(context.PB14, Level::High, Speed::Low); | ||
| 167 | |||
| 168 | let mut led_state = false; | ||
| 169 | |||
| 170 | loop { | ||
| 171 | acquire_sensors(&mut touch_controller, &bank1).await; | ||
| 172 | let readings1: TscAcquisitionBankReadings = read_touch_values(&mut touch_controller, &bank1) | ||
| 173 | .await | ||
| 174 | .expect("should be able to read values for bank 1"); | ||
| 175 | acquire_sensors(&mut touch_controller, &bank2).await; | ||
| 176 | let readings2: TscAcquisitionBankReadings = read_touch_values(&mut touch_controller, &bank2) | ||
| 177 | .await | ||
| 178 | .expect("should be able to read values for bank 2"); | ||
| 179 | |||
| 180 | let mut touched_sensors_count = 0; | ||
| 181 | for reading in readings1.iter().chain(readings2.iter()) { | ||
| 182 | info!("{}", reading); | ||
| 183 | if reading.sensor_value < SENSOR_THRESHOLD { | ||
| 184 | touched_sensors_count += 1; | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | match touched_sensors_count { | ||
| 189 | 0 => { | ||
| 190 | // No sensors touched, turn off the LED | ||
| 191 | led.set_low(); | ||
| 192 | led_state = false; | ||
| 193 | } | ||
| 194 | 1 => { | ||
| 195 | // One sensor touched, blink slowly | ||
| 196 | led_state = !led_state; | ||
| 197 | if led_state { | ||
| 198 | led.set_high(); | ||
| 199 | } else { | ||
| 200 | led.set_low(); | ||
| 201 | } | ||
| 202 | Timer::after_millis(200).await; | ||
| 203 | } | ||
| 204 | 2 => { | ||
| 205 | // Two sensors touched, blink faster | ||
| 206 | led_state = !led_state; | ||
| 207 | if led_state { | ||
| 208 | led.set_high(); | ||
| 209 | } else { | ||
| 210 | led.set_low(); | ||
| 211 | } | ||
| 212 | Timer::after_millis(50).await; | ||
| 213 | } | ||
| 214 | 3 => { | ||
| 215 | // All three sensors touched, LED constantly on | ||
| 216 | led.set_high(); | ||
| 217 | led_state = true; | ||
| 218 | } | ||
| 219 | _ => crate::unreachable!(), // This case should never occur with 3 sensors | ||
| 220 | } | ||
| 221 | } | ||
| 222 | } | ||
