STM32-TSC: enable discriminating between pins within same TSC group and improve TSC library in general
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||
# replace your chip as listed in `probe-rs chip list`
|
||||
runner = "probe-rs run --chip STM32L053R8Tx"
|
||||
runner = "probe-rs run --chip STM32L073RZTx"
|
||||
|
||||
[build]
|
||||
target = "thumbv6m-none-eabi"
|
||||
|
||||
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
# Change stm32l072cz to your chip name, if necessary.
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "unstable-pac", "time-driver-any", "exti", "memory-x"] }
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l073rz", "unstable-pac", "time-driver-any", "exti", "memory-x"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
|
||||
24
examples/stm32l0/README.md
Normal file
24
examples/stm32l0/README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Examples for STM32L0 family
|
||||
Run individual examples with
|
||||
```
|
||||
cargo run --bin <module-name>
|
||||
```
|
||||
for example
|
||||
```
|
||||
cargo run --bin blinky
|
||||
```
|
||||
|
||||
## Checklist before running examples
|
||||
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.
|
||||
|
||||
* [ ] 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)
|
||||
* [ ] 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.
|
||||
* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.
|
||||
* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic
|
||||
|
||||
If you are unsure, please drop by the Embassy Matrix chat for support, and let us know:
|
||||
|
||||
* Which example you are trying to run
|
||||
* Which chip and board you are using
|
||||
|
||||
Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org
|
||||
@@ -1,122 +0,0 @@
|
||||
// Example of async TSC (Touch Sensing Controller) that lights an LED when touch is detected.
|
||||
//
|
||||
// Suggested physical setup on STM32L073RZ Nucleo board:
|
||||
// - Connect a 1000pF capacitor between pin A0 and GND. This is your sampling capacitor.
|
||||
// - Connect one end of a 1K resistor to pin A1 and leave the other end loose.
|
||||
// The loose end will act as touch sensor which will register your touch.
|
||||
//
|
||||
// Troubleshooting the setup:
|
||||
// - If no touch seems to be registered, then try to disconnect the sampling capacitor from GND momentarily,
|
||||
// now the led should light up. Next try using a different value for the sampling capacitor.
|
||||
// Also experiment with increasing the values for `ct_pulse_high_length`, `ct_pulse_low_length`, `pulse_generator_prescaler`, `max_count_value` and `discharge_delay`.
|
||||
//
|
||||
// All configuration values and sampling capacitor value have been determined experimentally.
|
||||
// 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.
|
||||
//
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_stm32::bind_interrupts;
|
||||
use embassy_stm32::gpio::{Level, Output, Speed};
|
||||
use embassy_stm32::tsc::{self, *};
|
||||
use embassy_time::Timer;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;
|
||||
});
|
||||
|
||||
#[cortex_m_rt::exception]
|
||||
unsafe fn HardFault(_: &cortex_m_rt::ExceptionFrame) -> ! {
|
||||
cortex_m::peripheral::SCB::sys_reset();
|
||||
}
|
||||
|
||||
/// This example is written for the nucleo-stm32l073rz, with a stm32l073rz chip.
|
||||
///
|
||||
/// Make sure you check/update the following (whether you use the L073RZ or another board):
|
||||
///
|
||||
/// * [ ] Update .cargo/config.toml with the correct `probe-rs run --chip STM32L073RZTx`chip name.
|
||||
/// * [ ] Update Cargo.toml to have the correct `embassy-stm32` feature, for L073RZ it should be `stm32l073rz`.
|
||||
/// * [ ] If your board has a special clock or power configuration, make sure that it is
|
||||
/// set up appropriately.
|
||||
/// * [ ] If your board has different pin mapping, update any pin numbers or peripherals
|
||||
/// to match your schematic
|
||||
///
|
||||
/// If you are unsure, please drop by the Embassy Matrix chat for support, and let us know:
|
||||
///
|
||||
/// * Which example you are trying to run
|
||||
/// * Which chip and board you are using
|
||||
///
|
||||
/// Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: embassy_executor::Spawner) {
|
||||
let device_config = embassy_stm32::Config::default();
|
||||
let context = embassy_stm32::init(device_config);
|
||||
|
||||
let config = tsc::Config {
|
||||
ct_pulse_high_length: ChargeTransferPulseCycle::_4,
|
||||
ct_pulse_low_length: ChargeTransferPulseCycle::_4,
|
||||
spread_spectrum: false,
|
||||
spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
|
||||
spread_spectrum_prescaler: false,
|
||||
pulse_generator_prescaler: PGPrescalerDivider::_16,
|
||||
max_count_value: MaxCount::_255,
|
||||
io_default_mode: false,
|
||||
synchro_pin_polarity: false,
|
||||
acquisition_mode: false,
|
||||
max_count_interrupt: false,
|
||||
channel_ios: TscIOPin::Group1Io1.into(),
|
||||
shield_ios: 0, // no shield
|
||||
sampling_ios: TscIOPin::Group1Io2.into(),
|
||||
};
|
||||
|
||||
let mut g1: PinGroup<embassy_stm32::peripherals::TSC, G1> = PinGroup::new();
|
||||
g1.set_io1(context.PA0, PinType::Sample);
|
||||
g1.set_io2(context.PA1, PinType::Channel);
|
||||
|
||||
let mut touch_controller = tsc::Tsc::new_async(
|
||||
context.TSC,
|
||||
Some(g1),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
config,
|
||||
Irqs,
|
||||
);
|
||||
|
||||
// Check if TSC is ready
|
||||
if touch_controller.get_state() != State::Ready {
|
||||
info!("TSC not ready!");
|
||||
loop {} // Halt execution
|
||||
}
|
||||
info!("TSC initialized successfully");
|
||||
|
||||
// LED2 on the STM32L073RZ nucleo-board (PA5)
|
||||
let mut led = Output::new(context.PA5, Level::High, Speed::Low);
|
||||
|
||||
// smaller sample capacitor discharge faster and can be used with shorter delay.
|
||||
let discharge_delay = 5; // ms
|
||||
|
||||
info!("Starting touch_controller interface");
|
||||
loop {
|
||||
touch_controller.start();
|
||||
touch_controller.pend_for_acquisition().await;
|
||||
touch_controller.discharge_io(true);
|
||||
Timer::after_millis(discharge_delay).await;
|
||||
|
||||
let grp1_status = touch_controller.group_get_status(Group::One);
|
||||
match grp1_status {
|
||||
GroupStatus::Complete => {
|
||||
let group_one_val = touch_controller.group_get_value(Group::One);
|
||||
info!("{}", group_one_val);
|
||||
led.set_high();
|
||||
}
|
||||
GroupStatus::Ongoing => led.set_low(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
// Example of polling TSC (Touch Sensing Controller) that lights an LED when touch is detected.
|
||||
//
|
||||
// Suggested physical setup on STM32L073RZ Nucleo board:
|
||||
// - Connect a 1000pF capacitor between pin A0 and GND. This is your sampling capacitor.
|
||||
// - Connect one end of a 1K resistor to pin A1 and leave the other end loose.
|
||||
// The loose end will act as touch sensor which will register your touch.
|
||||
//
|
||||
// Troubleshooting the setup:
|
||||
// - If no touch seems to be registered, then try to disconnect the sampling capacitor from GND momentarily,
|
||||
// now the led should light up. Next try using a different value for the sampling capacitor.
|
||||
// Also experiment with increasing the values for `ct_pulse_high_length`, `ct_pulse_low_length`, `pulse_generator_prescaler`, `max_count_value` and `discharge_delay`.
|
||||
//
|
||||
// All configuration values and sampling capacitor value have been determined experimentally.
|
||||
// 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.
|
||||
//
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_stm32::gpio::{Level, Output, Speed};
|
||||
use embassy_stm32::tsc::{self, *};
|
||||
use embassy_time::Timer;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
/// This example is written for the nucleo-stm32l073rz, with a stm32l073rz chip.
|
||||
///
|
||||
/// Make sure you check/update the following (whether you use the L073RZ or another board):
|
||||
///
|
||||
/// * [ ] Update .cargo/config.toml with the correct `probe-rs run --chip STM32L073RZTx`chip name.
|
||||
/// * [ ] Update Cargo.toml to have the correct `embassy-stm32` feature, for L073RZ it should be `stm32l073rz`.
|
||||
/// * [ ] If your board has a special clock or power configuration, make sure that it is
|
||||
/// set up appropriately.
|
||||
/// * [ ] If your board has different pin mapping, update any pin numbers or peripherals
|
||||
/// to match your schematic
|
||||
///
|
||||
/// If you are unsure, please drop by the Embassy Matrix chat for support, and let us know:
|
||||
///
|
||||
/// * Which example you are trying to run
|
||||
/// * Which chip and board you are using
|
||||
///
|
||||
/// Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: embassy_executor::Spawner) {
|
||||
let device_config = embassy_stm32::Config::default();
|
||||
let context = embassy_stm32::init(device_config);
|
||||
|
||||
let tsc_conf = Config {
|
||||
ct_pulse_high_length: ChargeTransferPulseCycle::_4,
|
||||
ct_pulse_low_length: ChargeTransferPulseCycle::_4,
|
||||
spread_spectrum: false,
|
||||
spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
|
||||
spread_spectrum_prescaler: false,
|
||||
pulse_generator_prescaler: PGPrescalerDivider::_16,
|
||||
max_count_value: MaxCount::_255,
|
||||
io_default_mode: false,
|
||||
synchro_pin_polarity: false,
|
||||
acquisition_mode: false,
|
||||
max_count_interrupt: false,
|
||||
channel_ios: TscIOPin::Group1Io1.into(),
|
||||
shield_ios: 0, // no shield
|
||||
sampling_ios: TscIOPin::Group1Io2.into(),
|
||||
};
|
||||
|
||||
let mut g1: PinGroup<embassy_stm32::peripherals::TSC, G1> = PinGroup::new();
|
||||
g1.set_io1(context.PA0, PinType::Sample);
|
||||
g1.set_io2(context.PA1, PinType::Channel);
|
||||
|
||||
let mut touch_controller = tsc::Tsc::new_blocking(
|
||||
context.TSC,
|
||||
Some(g1),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
tsc_conf,
|
||||
);
|
||||
|
||||
// Check if TSC is ready
|
||||
if touch_controller.get_state() != State::Ready {
|
||||
info!("TSC not ready!");
|
||||
loop {} // Halt execution
|
||||
}
|
||||
info!("TSC initialized successfully");
|
||||
|
||||
// LED2 on the STM32L073RZ nucleo-board (PA5)
|
||||
let mut led = Output::new(context.PA5, Level::High, Speed::Low);
|
||||
|
||||
// smaller sample capacitor discharge faster and can be used with shorter delay.
|
||||
let discharge_delay = 5; // ms
|
||||
|
||||
// the interval at which the loop polls for new touch sensor values
|
||||
let polling_interval = 100; // ms
|
||||
|
||||
info!("polling for touch");
|
||||
loop {
|
||||
touch_controller.start();
|
||||
touch_controller.poll_for_acquisition();
|
||||
touch_controller.discharge_io(true);
|
||||
Timer::after_millis(discharge_delay).await;
|
||||
|
||||
let grp1_status = touch_controller.group_get_status(Group::One);
|
||||
match grp1_status {
|
||||
GroupStatus::Complete => {
|
||||
let group_one_val = touch_controller.group_get_value(Group::One);
|
||||
info!("{}", group_one_val);
|
||||
led.set_high();
|
||||
}
|
||||
GroupStatus::Ongoing => led.set_low(),
|
||||
}
|
||||
|
||||
Timer::after_millis(polling_interval).await;
|
||||
}
|
||||
}
|
||||
116
examples/stm32l0/src/bin/tsc_async.rs
Normal file
116
examples/stm32l0/src/bin/tsc_async.rs
Normal file
@@ -0,0 +1,116 @@
|
||||
// Example of async TSC (Touch Sensing Controller) that lights an LED when touch is detected.
|
||||
//
|
||||
// This example demonstrates:
|
||||
// 1. Configuring a single TSC channel pin
|
||||
// 2. Using the blocking TSC interface with polling
|
||||
// 3. Waiting for acquisition completion using `poll_for_acquisition`
|
||||
// 4. Reading touch values and controlling an LED based on the results
|
||||
//
|
||||
// Suggested physical setup on STM32L073RZ Nucleo board:
|
||||
// - Connect a 1000pF capacitor between pin PA0 and GND. This is your sampling capacitor.
|
||||
// - Connect one end of a 1K resistor to pin PA1 and leave the other end loose.
|
||||
// The loose end will act as the touch sensor which will register your touch.
|
||||
//
|
||||
// The example uses two pins from Group 1 of the TSC on the STM32L073RZ Nucleo board:
|
||||
// - PA0 as the sampling capacitor, TSC group 1 IO1 (label A0)
|
||||
// - PA1 as the channel pin, TSC group 1 IO2 (label A1)
|
||||
//
|
||||
// The program continuously reads the touch sensor value:
|
||||
// - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value.
|
||||
// - The LED is turned on when touch is detected (sensor value < 25).
|
||||
// - Touch values are logged to the console.
|
||||
//
|
||||
// Troubleshooting:
|
||||
// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value.
|
||||
// - 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.
|
||||
//
|
||||
// Note: Configuration values and sampling capacitor value have been determined experimentally.
|
||||
// Optimal values may vary based on your specific hardware setup.
|
||||
// Pins have been chosen for their convenient locations on the STM32L073RZ board. Refer to the
|
||||
// official relevant STM32 datasheets and nucleo-board user manuals to find suitable
|
||||
// alternative pins.
|
||||
//
|
||||
// Beware for STM32L073RZ nucleo-board, that PA2 and PA3 is used for the uart connection to
|
||||
// the programmer chip. If you try to use these two pins for TSC, you will get strange
|
||||
// readings, unless you somehow reconfigure/re-wire your nucleo-board.
|
||||
// No errors or warnings will be emitted, they will just silently not work as expected.
|
||||
// (see nucleo user manual UM1724, Rev 14, page 25)
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_stm32::gpio::{Level, Output, Speed};
|
||||
use embassy_stm32::tsc::{self, *};
|
||||
use embassy_stm32::{bind_interrupts, peripherals};
|
||||
use embassy_time::Timer;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;
|
||||
});
|
||||
const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: embassy_executor::Spawner) {
|
||||
let device_config = embassy_stm32::Config::default();
|
||||
let context = embassy_stm32::init(device_config);
|
||||
|
||||
let mut pin_group: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default();
|
||||
pin_group.set_io1::<tsc_pin_roles::Sample>(context.PA0);
|
||||
let sensor = pin_group.set_io2::<tsc_pin_roles::Channel>(context.PA1);
|
||||
|
||||
let tsc_conf = Config {
|
||||
ct_pulse_high_length: ChargeTransferPulseCycle::_4,
|
||||
ct_pulse_low_length: ChargeTransferPulseCycle::_4,
|
||||
spread_spectrum: false,
|
||||
spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
|
||||
spread_spectrum_prescaler: false,
|
||||
pulse_generator_prescaler: PGPrescalerDivider::_16,
|
||||
max_count_value: MaxCount::_255,
|
||||
io_default_mode: false,
|
||||
synchro_pin_polarity: false,
|
||||
acquisition_mode: false,
|
||||
max_count_interrupt: false,
|
||||
};
|
||||
|
||||
let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
|
||||
g1: Some(pin_group.pin_group),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, tsc_conf, Irqs).unwrap();
|
||||
|
||||
// Check if TSC is ready
|
||||
if touch_controller.get_state() != State::Ready {
|
||||
info!("TSC not ready!");
|
||||
return;
|
||||
}
|
||||
info!("TSC initialized successfully");
|
||||
|
||||
// LED2 on the STM32L073RZ nucleo-board (PA5)
|
||||
let mut led = Output::new(context.PA5, Level::Low, Speed::Low);
|
||||
|
||||
let discharge_delay = 5; // ms
|
||||
|
||||
info!("Starting touch_controller interface");
|
||||
loop {
|
||||
touch_controller.set_active_channels_mask(sensor.pin.into());
|
||||
touch_controller.start();
|
||||
touch_controller.pend_for_acquisition().await;
|
||||
touch_controller.discharge_io(true);
|
||||
Timer::after_millis(discharge_delay).await;
|
||||
|
||||
let group_val = touch_controller.group_get_value(sensor.pin.group());
|
||||
info!("Touch value: {}", group_val);
|
||||
|
||||
if group_val < SENSOR_THRESHOLD {
|
||||
led.set_high();
|
||||
} else {
|
||||
led.set_low();
|
||||
}
|
||||
|
||||
Timer::after_millis(100).await;
|
||||
}
|
||||
}
|
||||
142
examples/stm32l0/src/bin/tsc_blocking.rs
Normal file
142
examples/stm32l0/src/bin/tsc_blocking.rs
Normal file
@@ -0,0 +1,142 @@
|
||||
// Example of blocking TSC (Touch Sensing Controller) that lights an LED when touch is detected.
|
||||
//
|
||||
// This example demonstrates:
|
||||
// 1. Configuring a single TSC channel pin
|
||||
// 2. Using the blocking TSC interface with polling
|
||||
// 3. Waiting for acquisition completion using `poll_for_acquisition`
|
||||
// 4. Reading touch values and controlling an LED based on the results
|
||||
//
|
||||
// Suggested physical setup on STM32L073RZ Nucleo board:
|
||||
// - Connect a 1000pF capacitor between pin PA0 and GND. This is your sampling capacitor.
|
||||
// - Connect one end of a 1K resistor to pin PA1 and leave the other end loose.
|
||||
// The loose end will act as the touch sensor which will register your touch.
|
||||
//
|
||||
// The example uses two pins from Group 1 of the TSC on the STM32L073RZ Nucleo board:
|
||||
// - PA0 as the sampling capacitor, TSC group 1 IO1 (label A0)
|
||||
// - PA1 as the channel pin, TSC group 1 IO2 (label A1)
|
||||
//
|
||||
// The program continuously reads the touch sensor value:
|
||||
// - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value.
|
||||
// - The LED is turned on when touch is detected (sensor value < 25).
|
||||
// - Touch values are logged to the console.
|
||||
//
|
||||
// Troubleshooting:
|
||||
// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value.
|
||||
// - 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.
|
||||
//
|
||||
// Note: Configuration values and sampling capacitor value have been determined experimentally.
|
||||
// Optimal values may vary based on your specific hardware setup.
|
||||
// Pins have been chosen for their convenient locations on the STM32L073RZ board. Refer to the
|
||||
// official relevant STM32 datasheets and nucleo-board user manuals to find suitable
|
||||
// alternative pins.
|
||||
//
|
||||
// Beware for STM32L073RZ nucleo-board, that PA2 and PA3 is used for the uart connection to
|
||||
// the programmer chip. If you try to use these two pins for TSC, you will get strange
|
||||
// readings, unless you somehow reconfigure/re-wire your nucleo-board.
|
||||
// No errors or warnings will be emitted, they will just silently not work as expected.
|
||||
// (see nucleo user manual UM1724, Rev 14, page 25)
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_stm32::gpio::{Level, Output, Speed};
|
||||
use embassy_stm32::tsc::{self, *};
|
||||
use embassy_stm32::{mode, peripherals};
|
||||
use embassy_time::Timer;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: embassy_executor::Spawner) {
|
||||
let device_config = embassy_stm32::Config::default();
|
||||
let context = embassy_stm32::init(device_config);
|
||||
|
||||
let tsc_conf = Config {
|
||||
ct_pulse_high_length: ChargeTransferPulseCycle::_4,
|
||||
ct_pulse_low_length: ChargeTransferPulseCycle::_4,
|
||||
spread_spectrum: false,
|
||||
spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
|
||||
spread_spectrum_prescaler: false,
|
||||
pulse_generator_prescaler: PGPrescalerDivider::_16,
|
||||
max_count_value: MaxCount::_255,
|
||||
io_default_mode: false,
|
||||
synchro_pin_polarity: false,
|
||||
acquisition_mode: false,
|
||||
max_count_interrupt: false,
|
||||
};
|
||||
|
||||
let mut g1: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default();
|
||||
g1.set_io1::<tsc_pin_roles::Sample>(context.PA0);
|
||||
let tsc_sensor = g1.set_io2::<tsc_pin_roles::Channel>(context.PA1);
|
||||
|
||||
let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
|
||||
g1: Some(g1.pin_group),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, pin_groups, tsc_conf).unwrap();
|
||||
|
||||
// Check if TSC is ready
|
||||
if touch_controller.get_state() != State::Ready {
|
||||
crate::panic!("TSC not ready!");
|
||||
}
|
||||
info!("TSC initialized successfully");
|
||||
|
||||
// LED2 on the STM32L073RZ nucleo-board (PA5)
|
||||
let mut led = Output::new(context.PA5, Level::High, Speed::Low);
|
||||
|
||||
// smaller sample capacitor discharge faster and can be used with shorter delay.
|
||||
let discharge_delay = 5; // ms
|
||||
|
||||
// the interval at which the loop polls for new touch sensor values
|
||||
let polling_interval = 100; // ms
|
||||
|
||||
info!("polling for touch");
|
||||
loop {
|
||||
touch_controller.set_active_channels_mask(tsc_sensor.pin.into());
|
||||
touch_controller.start();
|
||||
touch_controller.poll_for_acquisition();
|
||||
touch_controller.discharge_io(true);
|
||||
Timer::after_millis(discharge_delay).await;
|
||||
|
||||
match read_touch_value(&mut touch_controller, tsc_sensor.pin).await {
|
||||
Some(v) => {
|
||||
info!("sensor value {}", v);
|
||||
if v < SENSOR_THRESHOLD {
|
||||
led.set_high();
|
||||
} else {
|
||||
led.set_low();
|
||||
}
|
||||
}
|
||||
None => led.set_low(),
|
||||
}
|
||||
|
||||
Timer::after_millis(polling_interval).await;
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10;
|
||||
|
||||
// attempt to read group status and delay when still ongoing
|
||||
async fn read_touch_value(
|
||||
touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Blocking>,
|
||||
sensor_pin: TscIOPin,
|
||||
) -> Option<u16> {
|
||||
for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS {
|
||||
match touch_controller.group_get_status(sensor_pin.group()) {
|
||||
GroupStatus::Complete => {
|
||||
return Some(touch_controller.group_get_value(sensor_pin.group()));
|
||||
}
|
||||
GroupStatus::Ongoing => {
|
||||
// if you end up here a lot, then you prob need to increase discharge_delay
|
||||
// or consider changing the code to adjust the discharge_delay dynamically
|
||||
info!("Acquisition still ongoing");
|
||||
Timer::after_millis(1).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
233
examples/stm32l0/src/bin/tsc_multipin.rs
Normal file
233
examples/stm32l0/src/bin/tsc_multipin.rs
Normal file
@@ -0,0 +1,233 @@
|
||||
// Example of TSC (Touch Sensing Controller) using multiple pins from the same tsc-group.
|
||||
//
|
||||
// What is special about using multiple TSC pins as sensor channels from the same TSC group,
|
||||
// is that only one TSC pin for each TSC group can be acquired and read at the time.
|
||||
// To control which channel pins are acquired and read, we must write a mask before initiating an
|
||||
// acquisition. To help manage and abstract all this business away, we can organize our channel
|
||||
// pins into acquisition banks. Each acquisition bank can contain exactly one channel pin per TSC
|
||||
// group and it will contain the relevant mask.
|
||||
//
|
||||
// This example demonstrates how to:
|
||||
// 1. Configure multiple channel pins within a single TSC group
|
||||
// 2. Use the set_active_channels method to switch between different channels
|
||||
// 3. Read and interpret touch values from multiple channels in the same group
|
||||
//
|
||||
// Suggested physical setup on STM32L073RZ Nucleo board:
|
||||
// - Connect a 1000pF capacitor between pin PA0 (label A0) and GND. This is the sampling capacitor for TSC
|
||||
// group 1.
|
||||
// - Connect one end of a 1K resistor to pin PA1 (label A1) and leave the other end loose.
|
||||
// The loose end will act as a touch sensor.
|
||||
//
|
||||
// - Connect a 1000pF capacitor between pin PB3 (label D3) and GND. This is the sampling capacitor for TSC
|
||||
// group 5.
|
||||
// - Connect one end of another 1K resistor to pin PB4 and leave the other end loose.
|
||||
// The loose end will act as a touch sensor.
|
||||
// - Connect one end of another 1K resistor to pin PB6 and leave the other end loose.
|
||||
// The loose end will act as a touch sensor.
|
||||
//
|
||||
// The example uses pins from two TSC groups.
|
||||
// - PA0 as sampling capacitor, TSC group 1 IO1 (label A0)
|
||||
// - PA1 as channel, TSC group 1 IO2 (label A1)
|
||||
// - PB3 as sampling capacitor, TSC group 5 IO1 (label D3)
|
||||
// - PB4 as channel, TSC group 5 IO2 (label D3)
|
||||
// - PB6 as channel, TSC group 5 IO3 (label D5)
|
||||
//
|
||||
// The pins have been chosen to make it easy to simply add capacitors directly onto the board and
|
||||
// connect one leg to GND, and to easily add resistors to the board with no special connectors,
|
||||
// breadboards, special wires or soldering required. All you need is the capacitors and resistors.
|
||||
//
|
||||
// The program reads the designated channel pins and adjusts the LED blinking
|
||||
// pattern based on which sensor(s) are touched:
|
||||
// - No touch: LED off
|
||||
// - one sensor touched: Slow blinking
|
||||
// - two sensors touched: Fast blinking
|
||||
// - three sensors touched: LED constantly on
|
||||
//
|
||||
// Troubleshooting:
|
||||
// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value.
|
||||
// - 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.
|
||||
//
|
||||
// Note: Configuration values and sampling capacitor value have been determined experimentally.
|
||||
// Optimal values may vary based on your specific hardware setup.
|
||||
// Pins have been chosen for their convenient locations on the STM32L073RZ board. Refer to the
|
||||
// official relevant STM32 datasheets and nucleo-board user manuals to find suitable
|
||||
// alternative pins.
|
||||
//
|
||||
// Beware for STM32L073RZ nucleo-board, that PA2 and PA3 is used for the uart connection to
|
||||
// the programmer chip. If you try to use these two pins for TSC, you will get strange
|
||||
// readings, unless you somehow reconfigure/re-wire your nucleo-board.
|
||||
// No errors or warnings will be emitted, they will just silently not work as expected.
|
||||
// (see nucleo user manual UM1724, Rev 14, page 25)
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_stm32::gpio::{Level, Output, Speed};
|
||||
use embassy_stm32::tsc::{self, *};
|
||||
use embassy_stm32::{bind_interrupts, mode, peripherals};
|
||||
use embassy_time::Timer;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;
|
||||
});
|
||||
|
||||
const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10;
|
||||
|
||||
async fn read_touch_values(
|
||||
touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Async>,
|
||||
tsc_acquisition_bank: &TscAcquisitionBank,
|
||||
) -> Option<TscAcquisitionBankReadings> {
|
||||
for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS {
|
||||
let status = touch_controller.get_acquisition_bank_status(tsc_acquisition_bank);
|
||||
if status.all_complete() {
|
||||
let r = touch_controller.get_acquisition_bank_values(tsc_acquisition_bank);
|
||||
return Some(r);
|
||||
} else {
|
||||
info!("Acquisition still ongoing");
|
||||
Timer::after_millis(1).await;
|
||||
}
|
||||
}
|
||||
info!("Acquisition failed after {} attempts", MAX_GROUP_STATUS_READ_ATTEMPTS);
|
||||
None
|
||||
}
|
||||
|
||||
const SENSOR_THRESHOLD: u16 = 35;
|
||||
|
||||
async fn acquire_sensors(
|
||||
touch_controller: &mut Tsc<'static, peripherals::TSC, mode::Async>,
|
||||
tsc_acquisition_bank: &TscAcquisitionBank,
|
||||
) {
|
||||
touch_controller.set_active_channels_mask(tsc_acquisition_bank.mask());
|
||||
touch_controller.start();
|
||||
touch_controller.pend_for_acquisition().await;
|
||||
touch_controller.discharge_io(true);
|
||||
let discharge_delay = 5; // ms
|
||||
Timer::after_millis(discharge_delay).await;
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: embassy_executor::Spawner) {
|
||||
let device_config = embassy_stm32::Config::default();
|
||||
let context = embassy_stm32::init(device_config);
|
||||
|
||||
// ---------- initial configuration of TSC ----------
|
||||
let mut pin_group1: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default();
|
||||
pin_group1.set_io1::<tsc_pin_roles::Sample>(context.PA0);
|
||||
let tsc_sensor0 = pin_group1.set_io2(context.PA1);
|
||||
|
||||
let mut pin_group5: PinGroupWithRoles<peripherals::TSC, G5> = PinGroupWithRoles::default();
|
||||
pin_group5.set_io1::<tsc_pin_roles::Sample>(context.PB3);
|
||||
let tsc_sensor1 = pin_group5.set_io2(context.PB4);
|
||||
let tsc_sensor2 = pin_group5.set_io3(context.PB6);
|
||||
|
||||
let config = tsc::Config {
|
||||
ct_pulse_high_length: ChargeTransferPulseCycle::_16,
|
||||
ct_pulse_low_length: ChargeTransferPulseCycle::_16,
|
||||
spread_spectrum: false,
|
||||
spread_spectrum_deviation: SSDeviation::new(2).unwrap(),
|
||||
spread_spectrum_prescaler: false,
|
||||
pulse_generator_prescaler: PGPrescalerDivider::_16,
|
||||
max_count_value: MaxCount::_255,
|
||||
io_default_mode: false,
|
||||
synchro_pin_polarity: false,
|
||||
acquisition_mode: false,
|
||||
max_count_interrupt: false,
|
||||
};
|
||||
|
||||
let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
|
||||
g1: Some(pin_group1.pin_group),
|
||||
g5: Some(pin_group5.pin_group),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, config, Irqs).unwrap();
|
||||
|
||||
// ---------- setting up acquisition banks ----------
|
||||
// sensor0 and sensor1 in this example belong to different TSC-groups,
|
||||
// therefore we can acquire and read them both in one go.
|
||||
let bank1 = touch_controller.create_acquisition_bank(TscAcquisitionBankPins {
|
||||
g1_pin: Some(tsc_sensor0),
|
||||
g5_pin: Some(tsc_sensor1),
|
||||
..Default::default()
|
||||
});
|
||||
// `sensor1` and `sensor2` belongs to the same TSC-group, therefore we must make sure to
|
||||
// acquire them one at the time. Therefore, we organize them into different acquisition banks.
|
||||
let bank2 = touch_controller.create_acquisition_bank(TscAcquisitionBankPins {
|
||||
g5_pin: Some(tsc_sensor2),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
// Check if TSC is ready
|
||||
if touch_controller.get_state() != State::Ready {
|
||||
crate::panic!("TSC not ready!");
|
||||
}
|
||||
|
||||
info!("TSC initialized successfully");
|
||||
|
||||
// LED2 on the STM32L073RZ nucleo-board (PA5)
|
||||
let mut led = Output::new(context.PA5, Level::High, Speed::Low);
|
||||
|
||||
let mut led_state = false;
|
||||
|
||||
loop {
|
||||
acquire_sensors(&mut touch_controller, &bank1).await;
|
||||
let readings1: TscAcquisitionBankReadings = read_touch_values(&mut touch_controller, &bank1)
|
||||
.await
|
||||
.expect("should be able to read values for bank 1");
|
||||
acquire_sensors(&mut touch_controller, &bank2).await;
|
||||
let readings2: TscAcquisitionBankReadings = read_touch_values(&mut touch_controller, &bank2)
|
||||
.await
|
||||
.expect("should be able to read values for bank 2");
|
||||
|
||||
let mut touched_sensors_count = 0;
|
||||
for reading in readings1.iter() {
|
||||
info!("{}", reading);
|
||||
if reading.sensor_value < SENSOR_THRESHOLD {
|
||||
touched_sensors_count += 1;
|
||||
}
|
||||
}
|
||||
for reading in readings2.iter() {
|
||||
info!("{}", reading);
|
||||
if reading.sensor_value < SENSOR_THRESHOLD {
|
||||
touched_sensors_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
match touched_sensors_count {
|
||||
0 => {
|
||||
// No sensors touched, turn off the LED
|
||||
led.set_low();
|
||||
led_state = false;
|
||||
}
|
||||
1 => {
|
||||
// One sensor touched, blink slowly
|
||||
led_state = !led_state;
|
||||
if led_state {
|
||||
led.set_high();
|
||||
} else {
|
||||
led.set_low();
|
||||
}
|
||||
Timer::after_millis(200).await;
|
||||
}
|
||||
2 => {
|
||||
// Two sensors touched, blink faster
|
||||
led_state = !led_state;
|
||||
if led_state {
|
||||
led.set_high();
|
||||
} else {
|
||||
led.set_low();
|
||||
}
|
||||
Timer::after_millis(50).await;
|
||||
}
|
||||
3 => {
|
||||
// All three sensors touched, LED constantly on
|
||||
led.set_high();
|
||||
led_state = true;
|
||||
}
|
||||
_ => crate::unreachable!(), // This case should never occur with 3 sensors
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user