aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2024-12-03 01:11:33 +0100
committerGitHub <[email protected]>2024-12-03 01:11:33 +0100
commit4acc0f84b084235b576de3b9e1d12a3472a5274b (patch)
treec9905c08383623af24865b761905585e0d7747d5 /examples
parent86b53a2ce3d93d07e5c8e4f4122e9588b87aafb7 (diff)
parentdcd6284996c501b2d376f5c1d4af6fc5a0f00521 (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.md24
-rw-r--r--examples/stm32f3/src/bin/blocking-tsc.rs98
-rw-r--r--examples/stm32f3/src/bin/tsc_blocking.rs138
-rw-r--r--examples/stm32f3/src/bin/tsc_multipin.rs204
-rw-r--r--examples/stm32l0/.cargo/config.toml2
-rw-r--r--examples/stm32l0/Cargo.toml2
-rw-r--r--examples/stm32l0/README.md24
-rw-r--r--examples/stm32l0/src/bin/async-tsc.rs122
-rw-r--r--examples/stm32l0/src/bin/blocking-tsc.rs116
-rw-r--r--examples/stm32l0/src/bin/tsc_async.rs116
-rw-r--r--examples/stm32l0/src/bin/tsc_blocking.rs142
-rw-r--r--examples/stm32l0/src/bin/tsc_multipin.rs209
-rw-r--r--examples/stm32l4/.cargo/config.toml3
-rw-r--r--examples/stm32l4/Cargo.toml2
-rw-r--r--examples/stm32l4/README.md24
-rw-r--r--examples/stm32l4/src/bin/tsc_async.rs108
-rw-r--r--examples/stm32l4/src/bin/tsc_blocking.rs147
-rw-r--r--examples/stm32l4/src/bin/tsc_multipin.rs198
-rw-r--r--examples/stm32u5/src/bin/tsc.rs75
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
2Run individual examples with
3```
4cargo run --bin <module-name>
5```
6for example
7```
8cargo run --bin blinky
9```
10
11## Checklist before running examples
12You 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
19If 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
24Embassy 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
19use defmt::*;
20use embassy_stm32::gpio::{Level, Output, Speed};
21use embassy_stm32::tsc::{self, *};
22use embassy_time::Timer;
23use {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]
43async 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
37use defmt::*;
38use embassy_stm32::gpio::{Level, Output, Speed};
39use embassy_stm32::tsc::{self, *};
40use embassy_stm32::{mode, peripherals};
41use embassy_time::Timer;
42use {defmt_rtt as _, panic_probe as _};
43
44const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup
45
46#[embassy_executor::main]
47async 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
117const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10;
118
119// attempt to read group status and delay when still ongoing
120async 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
59use defmt::*;
60use embassy_stm32::gpio::{Level, Output, Speed};
61use embassy_stm32::tsc::{self, *};
62use embassy_stm32::{mode, peripherals};
63use embassy_time::Timer;
64use {defmt_rtt as _, panic_probe as _};
65
66const SENSOR_THRESHOLD: u16 = 10;
67
68async 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]
81async 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`
3runner = "probe-rs run --chip STM32L053R8Tx" 3runner = "probe-rs run --chip STM32L073RZTx"
4 4
5[build] 5[build]
6target = "thumbv6m-none-eabi" 6target = "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.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "unstable-pac", "time-driver-any", "exti", "memory-x"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l073rz", "unstable-pac", "time-driver-any", "exti", "memory-x"] }
10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-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
2Run individual examples with
3```
4cargo run --bin <module-name>
5```
6for example
7```
8cargo run --bin blinky
9```
10
11## Checklist before running examples
12You 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
19If 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
24Embassy 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
19use defmt::*;
20use embassy_stm32::bind_interrupts;
21use embassy_stm32::gpio::{Level, Output, Speed};
22use embassy_stm32::tsc::{self, *};
23use embassy_time::Timer;
24use {defmt_rtt as _, panic_probe as _};
25
26bind_interrupts!(struct Irqs {
27 TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;
28});
29
30#[cortex_m_rt::exception]
31unsafe 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]
53async 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
19use defmt::*;
20use embassy_stm32::gpio::{Level, Output, Speed};
21use embassy_stm32::tsc::{self, *};
22use embassy_time::Timer;
23use {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]
43async 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
43use defmt::*;
44use embassy_stm32::gpio::{Level, Output, Speed};
45use embassy_stm32::tsc::{self, *};
46use embassy_stm32::{bind_interrupts, peripherals};
47use embassy_time::Timer;
48use {defmt_rtt as _, panic_probe as _};
49
50bind_interrupts!(struct Irqs {
51 TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;
52});
53const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup
54
55#[embassy_executor::main]
56async 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
43use defmt::*;
44use embassy_stm32::gpio::{Level, Output, Speed};
45use embassy_stm32::tsc::{self, *};
46use embassy_stm32::{mode, peripherals};
47use embassy_time::Timer;
48use {defmt_rtt as _, panic_probe as _};
49
50const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup
51
52#[embassy_executor::main]
53async 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
121const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10;
122
123// attempt to read group status and delay when still ongoing
124async 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
66use defmt::*;
67use embassy_stm32::gpio::{Level, Output, Speed};
68use embassy_stm32::tsc::{self, *};
69use embassy_stm32::{bind_interrupts, mode, peripherals};
70use embassy_time::Timer;
71use {defmt_rtt as _, panic_probe as _};
72
73bind_interrupts!(struct Irqs {
74 TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;
75});
76
77const SENSOR_THRESHOLD: u16 = 35;
78
79async 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]
92async 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"
5runner = "probe-rs run --chip STM32L4S5QI" 5#runner = "probe-rs run --chip STM32L4S5QI"
6runner = "probe-rs run --chip STM32L4R5ZITxP"
6 7
7[build] 8[build]
8target = "thumbv7em-none-eabi" 9target = "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.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4s5qi", "memory-x", "time-driver-any", "exti", "chrono"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4r5zi", "memory-x", "time-driver-any", "exti", "chrono"] }
10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] } 12embassy-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
2Run individual examples with
3```
4cargo run --bin <module-name>
5```
6for example
7```
8cargo run --bin blinky
9```
10
11## Checklist before running examples
12You 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
19If 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
24Embassy 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
34use defmt::*;
35use embassy_stm32::gpio::{Level, Output, Speed};
36use embassy_stm32::tsc::{self, *};
37use embassy_stm32::{bind_interrupts, peripherals};
38use embassy_time::Timer;
39use {defmt_rtt as _, panic_probe as _};
40
41bind_interrupts!(struct Irqs {
42 TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;
43});
44const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup
45
46#[embassy_executor::main]
47async 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
47use defmt::*;
48use embassy_stm32::gpio::{Level, Output, Speed};
49use embassy_stm32::tsc::{self, *};
50use embassy_stm32::{mode, peripherals};
51use embassy_time::Timer;
52use {defmt_rtt as _, panic_probe as _};
53
54const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup
55
56#[embassy_executor::main]
57async 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
126const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10;
127
128// attempt to read group status and delay when still ongoing
129async 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
62use defmt::*;
63use embassy_stm32::gpio::{Level, Output, Speed};
64use embassy_stm32::tsc::{self, *};
65use embassy_stm32::{bind_interrupts, mode, peripherals};
66use embassy_time::Timer;
67use {defmt_rtt as _, panic_probe as _};
68
69bind_interrupts!(struct Irqs {
70 TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;
71});
72
73const SENSOR_THRESHOLD: u16 = 20;
74
75async 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]
88async 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
4use defmt::*; 4use defmt::*;
5use embassy_stm32::bind_interrupts;
6use embassy_stm32::tsc::{self, *}; 5use embassy_stm32::tsc::{self, *};
6use embassy_stm32::{bind_interrupts, peripherals};
7use embassy_time::Timer; 7use embassy_time::Timer;
8use {defmt_rtt as _, panic_probe as _}; 8use {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}