diff options
| author | Dario Nieuwenhuis <[email protected]> | 2024-12-03 01:11:33 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-12-03 01:11:33 +0100 |
| commit | 4acc0f84b084235b576de3b9e1d12a3472a5274b (patch) | |
| tree | c9905c08383623af24865b761905585e0d7747d5 /examples | |
| parent | 86b53a2ce3d93d07e5c8e4f4122e9588b87aafb7 (diff) | |
| parent | dcd6284996c501b2d376f5c1d4af6fc5a0f00521 (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
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/stm32f3/README.md | 24 | ||||
| -rw-r--r-- | examples/stm32f3/src/bin/blocking-tsc.rs | 98 | ||||
| -rw-r--r-- | examples/stm32f3/src/bin/tsc_blocking.rs | 138 | ||||
| -rw-r--r-- | examples/stm32f3/src/bin/tsc_multipin.rs | 204 | ||||
| -rw-r--r-- | examples/stm32l0/.cargo/config.toml | 2 | ||||
| -rw-r--r-- | examples/stm32l0/Cargo.toml | 2 | ||||
| -rw-r--r-- | examples/stm32l0/README.md | 24 | ||||
| -rw-r--r-- | examples/stm32l0/src/bin/async-tsc.rs | 122 | ||||
| -rw-r--r-- | examples/stm32l0/src/bin/blocking-tsc.rs | 116 | ||||
| -rw-r--r-- | examples/stm32l0/src/bin/tsc_async.rs | 116 | ||||
| -rw-r--r-- | examples/stm32l0/src/bin/tsc_blocking.rs | 142 | ||||
| -rw-r--r-- | examples/stm32l0/src/bin/tsc_multipin.rs | 209 | ||||
| -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 | 198 | ||||
| -rw-r--r-- | examples/stm32u5/src/bin/tsc.rs | 75 |
19 files changed, 1371 insertions, 383 deletions
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 | ||
| 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 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 | |||
| 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/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 | |||
| 19 | use defmt::*; | ||
| 20 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 21 | use embassy_stm32::tsc::{self, *}; | ||
| 22 | use embassy_time::Timer; | ||
| 23 | use {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] | ||
| 43 | async 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 | |||
| 37 | use defmt::*; | ||
| 38 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 39 | use embassy_stm32::tsc::{self, *}; | ||
| 40 | use embassy_stm32::{mode, peripherals}; | ||
| 41 | use embassy_time::Timer; | ||
| 42 | use {defmt_rtt as _, panic_probe as _}; | ||
| 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 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 | |||
| 117 | const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10; | ||
| 118 | |||
| 119 | // attempt to read group status and delay when still ongoing | ||
| 120 | async 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 | |||
| 59 | use defmt::*; | ||
| 60 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 61 | use embassy_stm32::tsc::{self, *}; | ||
| 62 | use embassy_stm32::{mode, peripherals}; | ||
| 63 | use embassy_time::Timer; | ||
| 64 | use {defmt_rtt as _, panic_probe as _}; | ||
| 65 | |||
| 66 | const SENSOR_THRESHOLD: u16 = 10; | ||
| 67 | |||
| 68 | async 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] | ||
| 81 | async 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` |
| 3 | runner = "probe-rs run --chip STM32L053R8Tx" | 3 | runner = "probe-rs run --chip STM32L073RZTx" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv6m-none-eabi" | 6 | target = "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. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "unstable-pac", "time-driver-any", "exti", "memory-x"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l073rz", "unstable-pac", "time-driver-any", "exti", "memory-x"] } |
| 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 = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["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/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 | ||
| 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 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 | |||
| 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/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 | |||
| 19 | use defmt::*; | ||
| 20 | use embassy_stm32::bind_interrupts; | ||
| 21 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 22 | use embassy_stm32::tsc::{self, *}; | ||
| 23 | use embassy_time::Timer; | ||
| 24 | use {defmt_rtt as _, panic_probe as _}; | ||
| 25 | |||
| 26 | bind_interrupts!(struct Irqs { | ||
| 27 | TSC => InterruptHandler<embassy_stm32::peripherals::TSC>; | ||
| 28 | }); | ||
| 29 | |||
| 30 | #[cortex_m_rt::exception] | ||
| 31 | unsafe 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] | ||
| 53 | async 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 | |||
| 19 | use defmt::*; | ||
| 20 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 21 | use embassy_stm32::tsc::{self, *}; | ||
| 22 | use embassy_time::Timer; | ||
| 23 | use {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] | ||
| 43 | async 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 | |||
| 43 | use defmt::*; | ||
| 44 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 45 | use embassy_stm32::tsc::{self, *}; | ||
| 46 | use embassy_stm32::{bind_interrupts, peripherals}; | ||
| 47 | use embassy_time::Timer; | ||
| 48 | use {defmt_rtt as _, panic_probe as _}; | ||
| 49 | |||
| 50 | bind_interrupts!(struct Irqs { | ||
| 51 | TSC => InterruptHandler<embassy_stm32::peripherals::TSC>; | ||
| 52 | }); | ||
| 53 | const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup | ||
| 54 | |||
| 55 | #[embassy_executor::main] | ||
| 56 | async 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 | |||
| 43 | use defmt::*; | ||
| 44 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 45 | use embassy_stm32::tsc::{self, *}; | ||
| 46 | use embassy_stm32::{mode, peripherals}; | ||
| 47 | use embassy_time::Timer; | ||
| 48 | use {defmt_rtt as _, panic_probe as _}; | ||
| 49 | |||
| 50 | const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup | ||
| 51 | |||
| 52 | #[embassy_executor::main] | ||
| 53 | async 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 | |||
| 121 | const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10; | ||
| 122 | |||
| 123 | // attempt to read group status and delay when still ongoing | ||
| 124 | async 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 | |||
| 66 | use defmt::*; | ||
| 67 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 68 | use embassy_stm32::tsc::{self, *}; | ||
| 69 | use embassy_stm32::{bind_interrupts, mode, peripherals}; | ||
| 70 | use embassy_time::Timer; | ||
| 71 | use {defmt_rtt as _, panic_probe as _}; | ||
| 72 | |||
| 73 | bind_interrupts!(struct Irqs { | ||
| 74 | TSC => InterruptHandler<embassy_stm32::peripherals::TSC>; | ||
| 75 | }); | ||
| 76 | |||
| 77 | const SENSOR_THRESHOLD: u16 = 35; | ||
| 78 | |||
| 79 | async 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] | ||
| 92 | async 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" |
| 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..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 | |||
| 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..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 | |||
| 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: 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 | |||
| 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 SENSOR_THRESHOLD: u16 = 20; | ||
| 74 | |||
| 75 | async 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] | ||
| 88 | async 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 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_stm32::bind_interrupts; | ||
| 6 | use embassy_stm32::tsc::{self, *}; | 5 | use embassy_stm32::tsc::{self, *}; |
| 6 | use embassy_stm32::{bind_interrupts, peripherals}; | ||
| 7 | use embassy_time::Timer; | 7 | use embassy_time::Timer; |
| 8 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {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 | } |
