aboutsummaryrefslogtreecommitdiff
path: root/examples/stm32l4
diff options
context:
space:
mode:
authorjrmoulton <[email protected]>2025-06-10 15:47:54 -0600
committerjrmoulton <[email protected]>2025-06-10 15:48:36 -0600
commitcfad9798ff99d4de0571a512d156b5fe1ef1d427 (patch)
treefc3bf670f82d139de19466cddad1e909db7f3d2e /examples/stm32l4
parentfc342915e6155dec7bafa3e135da7f37a9a07f5c (diff)
parent6186d111a5c150946ee5b7e9e68d987a38c1a463 (diff)
merge new embassy changes
Diffstat (limited to 'examples/stm32l4')
-rw-r--r--examples/stm32l4/.cargo/config.toml3
-rw-r--r--examples/stm32l4/Cargo.toml23
-rw-r--r--examples/stm32l4/README.md24
-rw-r--r--examples/stm32l4/src/bin/dac.rs3
-rw-r--r--examples/stm32l4/src/bin/dac_dma.rs8
-rw-r--r--examples/stm32l4/src/bin/spe_adin1110_http_server.rs14
-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/stm32l4/src/bin/usb_serial.rs7
10 files changed, 502 insertions, 33 deletions
diff --git a/examples/stm32l4/.cargo/config.toml b/examples/stm32l4/.cargo/config.toml
index 83fc6d6f8..d71fb1517 100644
--- a/examples/stm32l4/.cargo/config.toml
+++ b/examples/stm32l4/.cargo/config.toml
@@ -2,7 +2,8 @@
2# replace STM32F429ZITx with your chip as listed in `probe-rs chip list` 2# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
3#runner = "probe-rs run --chip STM32L475VGT6" 3#runner = "probe-rs run --chip STM32L475VGT6"
4#runner = "probe-rs run --chip STM32L475VG" 4#runner = "probe-rs run --chip STM32L475VG"
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 c5478b17b..7da09e5b0 100644
--- a/examples/stm32l4/Cargo.toml
+++ b/examples/stm32l4/Cargo.toml
@@ -6,20 +6,20 @@ 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.2.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4r5zi", "memory-x", "time-driver-any", "exti", "chrono", "dual-bank"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
12embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] } 12embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] }
13embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } 13embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" }
14embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] }
15embassy-net-adin1110 = { version = "0.2.0", path = "../../embassy-net-adin1110" } 15embassy-net-adin1110 = { version = "0.3.0", path = "../../embassy-net-adin1110" }
16embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "udp", "tcp", "dhcpv4", "medium-ethernet"] } 16embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "udp", "tcp", "dhcpv4", "medium-ethernet"] }
17embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 17embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
18embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } 18embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
19embedded-io = { version = "0.6.0", features = ["defmt-03"] } 19embedded-io = { version = "0.6.0", features = ["defmt-03"] }
20 20
21defmt = "0.3" 21defmt = "1.0.1"
22defmt-rtt = "0.4" 22defmt-rtt = "1.0.0"
23 23
24cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } 24cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
25cortex-m-rt = "0.7.0" 25cortex-m-rt = "0.7.0"
@@ -27,10 +27,9 @@ embedded-hal = "0.2.6"
27embedded-hal-1 = { package = "embedded-hal", version = "1.0" } 27embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
28embedded-hal-async = { version = "1.0" } 28embedded-hal-async = { version = "1.0" }
29embedded-hal-bus = { version = "0.1", features = ["async"] } 29embedded-hal-bus = { version = "0.1", features = ["async"] }
30panic-probe = { version = "0.3", features = ["print-defmt"] } 30panic-probe = { version = "1.0.0", features = ["print-defmt"] }
31heapless = { version = "0.8", default-features = false } 31heapless = { version = "0.8", default-features = false }
32chrono = { version = "^0.4", default-features = false } 32chrono = { version = "^0.4", default-features = false }
33rand = { version = "0.8.5", default-features = false }
34static_cell = "2" 33static_cell = "2"
35 34
36micromath = "2.0.0" 35micromath = "2.0.0"
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/dac.rs b/examples/stm32l4/src/bin/dac.rs
index fdbf1d374..50db0e082 100644
--- a/examples/stm32l4/src/bin/dac.rs
+++ b/examples/stm32l4/src/bin/dac.rs
@@ -3,7 +3,6 @@
3 3
4use defmt::*; 4use defmt::*;
5use embassy_stm32::dac::{DacCh1, Value}; 5use embassy_stm32::dac::{DacCh1, Value};
6use embassy_stm32::dma::NoDma;
7use {defmt_rtt as _, panic_probe as _}; 6use {defmt_rtt as _, panic_probe as _};
8 7
9#[cortex_m_rt::entry] 8#[cortex_m_rt::entry]
@@ -11,7 +10,7 @@ fn main() -> ! {
11 let p = embassy_stm32::init(Default::default()); 10 let p = embassy_stm32::init(Default::default());
12 info!("Hello World!"); 11 info!("Hello World!");
13 12
14 let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); 13 let mut dac = DacCh1::new_blocking(p.DAC1, p.PA4);
15 14
16 loop { 15 loop {
17 for v in 0..=255 { 16 for v in 0..=255 {
diff --git a/examples/stm32l4/src/bin/dac_dma.rs b/examples/stm32l4/src/bin/dac_dma.rs
index d01b016c0..cde24f411 100644
--- a/examples/stm32l4/src/bin/dac_dma.rs
+++ b/examples/stm32l4/src/bin/dac_dma.rs
@@ -4,11 +4,13 @@
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::dac::{DacCh1, DacCh2, ValueArray}; 6use embassy_stm32::dac::{DacCh1, DacCh2, ValueArray};
7use embassy_stm32::mode::Async;
7use embassy_stm32::pac::timer::vals::Mms; 8use embassy_stm32::pac::timer::vals::Mms;
8use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7}; 9use embassy_stm32::peripherals::{DAC1, TIM6, TIM7};
9use embassy_stm32::rcc::frequency; 10use embassy_stm32::rcc::frequency;
10use embassy_stm32::time::Hertz; 11use embassy_stm32::time::Hertz;
11use embassy_stm32::timer::low_level::Timer; 12use embassy_stm32::timer::low_level::Timer;
13use embassy_stm32::Peri;
12use micromath::F32Ext; 14use micromath::F32Ext;
13use {defmt_rtt as _, panic_probe as _}; 15use {defmt_rtt as _, panic_probe as _};
14 16
@@ -27,7 +29,7 @@ async fn main(spawner: Spawner) {
27} 29}
28 30
29#[embassy_executor::task] 31#[embassy_executor::task]
30async fn dac_task1(tim: TIM6, mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { 32async fn dac_task1(tim: Peri<'static, TIM6>, mut dac: DacCh1<'static, DAC1, Async>) {
31 let data: &[u8; 256] = &calculate_array::<256>(); 33 let data: &[u8; 256] = &calculate_array::<256>();
32 34
33 info!("TIM6 frequency is {}", frequency::<TIM6>()); 35 info!("TIM6 frequency is {}", frequency::<TIM6>());
@@ -70,7 +72,7 @@ async fn dac_task1(tim: TIM6, mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
70} 72}
71 73
72#[embassy_executor::task] 74#[embassy_executor::task]
73async fn dac_task2(tim: TIM7, mut dac: DacCh2<'static, DAC1, DMA1_CH4>) { 75async fn dac_task2(tim: Peri<'static, TIM7>, mut dac: DacCh2<'static, DAC1, Async>) {
74 let data: &[u8; 256] = &calculate_array::<256>(); 76 let data: &[u8; 256] = &calculate_array::<256>();
75 77
76 info!("TIM7 frequency is {}", frequency::<TIM7>()); 78 info!("TIM7 frequency is {}", frequency::<TIM7>());
diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs
index 8f23e4083..dc90a3b85 100644
--- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs
+++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs
@@ -38,7 +38,6 @@ use embedded_io::Write as bWrite;
38use embedded_io_async::Write; 38use embedded_io_async::Write;
39use heapless::Vec; 39use heapless::Vec;
40use panic_probe as _; 40use panic_probe as _;
41use rand::RngCore;
42use static_cell::StaticCell; 41use static_cell::StaticCell;
43 42
44bind_interrupts!(struct Irqs { 43bind_interrupts!(struct Irqs {
@@ -51,7 +50,7 @@ bind_interrupts!(struct Irqs {
51// MAC-address used by the adin1110 50// MAC-address used by the adin1110
52const MAC: [u8; 6] = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]; 51const MAC: [u8; 6] = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff];
53// Static IP settings 52// Static IP settings
54const IP_ADDRESS: Ipv4Cidr = Ipv4Cidr::new(Ipv4Address([192, 168, 1, 5]), 24); 53const IP_ADDRESS: Ipv4Cidr = Ipv4Cidr::new(Ipv4Address::new(192, 168, 1, 5), 24);
55// Listen port for the webserver 54// Listen port for the webserver
56const HTTP_LISTEN_PORT: u16 = 80; 55const HTTP_LISTEN_PORT: u16 = 80;
57 56
@@ -206,12 +205,11 @@ async fn main(spawner: Spawner) {
206 }; 205 };
207 206
208 // Init network stack 207 // Init network stack
209 static STACK: StaticCell<Stack<Device<'static>>> = StaticCell::new();
210 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); 208 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
211 let stack = &*STACK.init(Stack::new(device, ip_cfg, RESOURCES.init(StackResources::new()), seed)); 209 let (stack, runner) = embassy_net::new(device, ip_cfg, RESOURCES.init(StackResources::new()), seed);
212 210
213 // Launch network task 211 // Launch network task
214 unwrap!(spawner.spawn(net_task(stack))); 212 unwrap!(spawner.spawn(net_task(runner)));
215 213
216 let cfg = wait_for_config(stack).await; 214 let cfg = wait_for_config(stack).await;
217 let local_addr = cfg.address.address(); 215 let local_addr = cfg.address.address();
@@ -274,7 +272,7 @@ async fn main(spawner: Spawner) {
274 } 272 }
275} 273}
276 274
277async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { 275async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {
278 loop { 276 loop {
279 if let Some(config) = stack.config_v4() { 277 if let Some(config) = stack.config_v4() {
280 return config; 278 return config;
@@ -323,8 +321,8 @@ async fn ethernet_task(runner: Runner<'static, SpeSpiCs, SpeInt, SpeRst>) -> ! {
323} 321}
324 322
325#[embassy_executor::task] 323#[embassy_executor::task]
326async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { 324async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {
327 stack.run().await 325 runner.run().await
328} 326}
329 327
330// same panicking *behavior* as `panic-probe` but doesn't print a panic message 328// same panicking *behavior* as `panic-probe` but doesn't print a panic message
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/stm32l4/src/bin/usb_serial.rs b/examples/stm32l4/src/bin/usb_serial.rs
index c3b1211d8..af90e297e 100644
--- a/examples/stm32l4/src/bin/usb_serial.rs
+++ b/examples/stm32l4/src/bin/usb_serial.rs
@@ -62,13 +62,6 @@ async fn main(_spawner: Spawner) {
62 config.product = Some("USB-serial example"); 62 config.product = Some("USB-serial example");
63 config.serial_number = Some("12345678"); 63 config.serial_number = Some("12345678");
64 64
65 // Required for windows compatibility.
66 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
67 config.device_class = 0xEF;
68 config.device_sub_class = 0x02;
69 config.device_protocol = 0x01;
70 config.composite_with_iads = true;
71
72 // Create embassy-usb DeviceBuilder using the driver and config. 65 // Create embassy-usb DeviceBuilder using the driver and config.
73 // It needs some buffers for building the descriptors. 66 // It needs some buffers for building the descriptors.
74 let mut config_descriptor = [0; 256]; 67 let mut config_descriptor = [0; 256];