diff options
| author | jrmoulton <[email protected]> | 2025-06-10 15:47:54 -0600 |
|---|---|---|
| committer | jrmoulton <[email protected]> | 2025-06-10 15:48:36 -0600 |
| commit | cfad9798ff99d4de0571a512d156b5fe1ef1d427 (patch) | |
| tree | fc3bf670f82d139de19466cddad1e909db7f3d2e /examples/stm32l4 | |
| parent | fc342915e6155dec7bafa3e135da7f37a9a07f5c (diff) | |
| parent | 6186d111a5c150946ee5b7e9e68d987a38c1a463 (diff) | |
merge new embassy changes
Diffstat (limited to 'examples/stm32l4')
| -rw-r--r-- | examples/stm32l4/.cargo/config.toml | 3 | ||||
| -rw-r--r-- | examples/stm32l4/Cargo.toml | 23 | ||||
| -rw-r--r-- | examples/stm32l4/README.md | 24 | ||||
| -rw-r--r-- | examples/stm32l4/src/bin/dac.rs | 3 | ||||
| -rw-r--r-- | examples/stm32l4/src/bin/dac_dma.rs | 8 | ||||
| -rw-r--r-- | examples/stm32l4/src/bin/spe_adin1110_http_server.rs | 14 | ||||
| -rw-r--r-- | examples/stm32l4/src/bin/tsc_async.rs | 108 | ||||
| -rw-r--r-- | examples/stm32l4/src/bin/tsc_blocking.rs | 147 | ||||
| -rw-r--r-- | examples/stm32l4/src/bin/tsc_multipin.rs | 198 | ||||
| -rw-r--r-- | examples/stm32l4/src/bin/usb_serial.rs | 7 |
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" |
| 5 | runner = "probe-rs run --chip STM32L4S5QI" | 5 | #runner = "probe-rs run --chip STM32L4S5QI" |
| 6 | runner = "probe-rs run --chip STM32L4R5ZITxP" | ||
| 6 | 7 | ||
| 7 | [build] | 8 | [build] |
| 8 | target = "thumbv7em-none-eabi" | 9 | target = "thumbv7em-none-eabi" |
diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 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. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4s5qi", "memory-x", "time-driver-any", "exti", "chrono"] } | 9 | embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4r5zi", "memory-x", "time-driver-any", "exti", "chrono", "dual-bank"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] } |
| 13 | embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } |
| 14 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } |
| 15 | embassy-net-adin1110 = { version = "0.2.0", path = "../../embassy-net-adin1110" } | 15 | embassy-net-adin1110 = { version = "0.3.0", path = "../../embassy-net-adin1110" } |
| 16 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "udp", "tcp", "dhcpv4", "medium-ethernet"] } | 16 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "udp", "tcp", "dhcpv4", "medium-ethernet"] } |
| 17 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 17 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 18 | embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } | 18 | embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } |
| 19 | embedded-io = { version = "0.6.0", features = ["defmt-03"] } | 19 | embedded-io = { version = "0.6.0", features = ["defmt-03"] } |
| 20 | 20 | ||
| 21 | defmt = "0.3" | 21 | defmt = "1.0.1" |
| 22 | defmt-rtt = "0.4" | 22 | defmt-rtt = "1.0.0" |
| 23 | 23 | ||
| 24 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 24 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 25 | cortex-m-rt = "0.7.0" | 25 | cortex-m-rt = "0.7.0" |
| @@ -27,10 +27,9 @@ embedded-hal = "0.2.6" | |||
| 27 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | 27 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } |
| 28 | embedded-hal-async = { version = "1.0" } | 28 | embedded-hal-async = { version = "1.0" } |
| 29 | embedded-hal-bus = { version = "0.1", features = ["async"] } | 29 | embedded-hal-bus = { version = "0.1", features = ["async"] } |
| 30 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 30 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 31 | heapless = { version = "0.8", default-features = false } | 31 | heapless = { version = "0.8", default-features = false } |
| 32 | chrono = { version = "^0.4", default-features = false } | 32 | chrono = { version = "^0.4", default-features = false } |
| 33 | rand = { version = "0.8.5", default-features = false } | ||
| 34 | static_cell = "2" | 33 | static_cell = "2" |
| 35 | 34 | ||
| 36 | micromath = "2.0.0" | 35 | micromath = "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 | ||
| 2 | Run individual examples with | ||
| 3 | ``` | ||
| 4 | cargo run --bin <module-name> | ||
| 5 | ``` | ||
| 6 | for example | ||
| 7 | ``` | ||
| 8 | cargo run --bin blinky | ||
| 9 | ``` | ||
| 10 | |||
| 11 | ## Checklist before running examples | ||
| 12 | You might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using. | ||
| 13 | |||
| 14 | * [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L4R5ZI-P it should be `probe-rs run --chip STM32L4R5ZITxP`. (use `probe-rs chip list` to find your chip) | ||
| 15 | * [ ] Update Cargo.toml to have the correct `embassy-stm32` feature. For example for L4R5ZI-P it should be `stm32l4r5zi`. Look in the `Cargo.toml` file of the `embassy-stm32` project to find the correct feature flag for your chip. | ||
| 16 | * [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately. | ||
| 17 | * [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic | ||
| 18 | |||
| 19 | If you are unsure, please drop by the Embassy Matrix chat for support, and let us know: | ||
| 20 | |||
| 21 | * Which example you are trying to run | ||
| 22 | * Which chip and board you are using | ||
| 23 | |||
| 24 | Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org | ||
diff --git a/examples/stm32l4/src/bin/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 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_stm32::dac::{DacCh1, Value}; | 5 | use embassy_stm32::dac::{DacCh1, Value}; |
| 6 | use embassy_stm32::dma::NoDma; | ||
| 7 | use {defmt_rtt as _, panic_probe as _}; | 6 | use {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 @@ | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::dac::{DacCh1, DacCh2, ValueArray}; | 6 | use embassy_stm32::dac::{DacCh1, DacCh2, ValueArray}; |
| 7 | use embassy_stm32::mode::Async; | ||
| 7 | use embassy_stm32::pac::timer::vals::Mms; | 8 | use embassy_stm32::pac::timer::vals::Mms; |
| 8 | use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7}; | 9 | use embassy_stm32::peripherals::{DAC1, TIM6, TIM7}; |
| 9 | use embassy_stm32::rcc::frequency; | 10 | use embassy_stm32::rcc::frequency; |
| 10 | use embassy_stm32::time::Hertz; | 11 | use embassy_stm32::time::Hertz; |
| 11 | use embassy_stm32::timer::low_level::Timer; | 12 | use embassy_stm32::timer::low_level::Timer; |
| 13 | use embassy_stm32::Peri; | ||
| 12 | use micromath::F32Ext; | 14 | use micromath::F32Ext; |
| 13 | use {defmt_rtt as _, panic_probe as _}; | 15 | use {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] |
| 30 | async fn dac_task1(tim: TIM6, mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { | 32 | async 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] |
| 73 | async fn dac_task2(tim: TIM7, mut dac: DacCh2<'static, DAC1, DMA1_CH4>) { | 75 | async 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; | |||
| 38 | use embedded_io_async::Write; | 38 | use embedded_io_async::Write; |
| 39 | use heapless::Vec; | 39 | use heapless::Vec; |
| 40 | use panic_probe as _; | 40 | use panic_probe as _; |
| 41 | use rand::RngCore; | ||
| 42 | use static_cell::StaticCell; | 41 | use static_cell::StaticCell; |
| 43 | 42 | ||
| 44 | bind_interrupts!(struct Irqs { | 43 | bind_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 |
| 52 | const MAC: [u8; 6] = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]; | 51 | const MAC: [u8; 6] = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]; |
| 53 | // Static IP settings | 52 | // Static IP settings |
| 54 | const IP_ADDRESS: Ipv4Cidr = Ipv4Cidr::new(Ipv4Address([192, 168, 1, 5]), 24); | 53 | const IP_ADDRESS: Ipv4Cidr = Ipv4Cidr::new(Ipv4Address::new(192, 168, 1, 5), 24); |
| 55 | // Listen port for the webserver | 54 | // Listen port for the webserver |
| 56 | const HTTP_LISTEN_PORT: u16 = 80; | 55 | const 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 | ||
| 277 | async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { | 275 | async 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] |
| 326 | async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { | 324 | async 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 | |||
| 34 | use defmt::*; | ||
| 35 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 36 | use embassy_stm32::tsc::{self, *}; | ||
| 37 | use embassy_stm32::{bind_interrupts, peripherals}; | ||
| 38 | use embassy_time::Timer; | ||
| 39 | use {defmt_rtt as _, panic_probe as _}; | ||
| 40 | |||
| 41 | bind_interrupts!(struct Irqs { | ||
| 42 | TSC => InterruptHandler<embassy_stm32::peripherals::TSC>; | ||
| 43 | }); | ||
| 44 | const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup | ||
| 45 | |||
| 46 | #[embassy_executor::main] | ||
| 47 | async fn main(_spawner: embassy_executor::Spawner) { | ||
| 48 | let device_config = embassy_stm32::Config::default(); | ||
| 49 | let context = embassy_stm32::init(device_config); | ||
| 50 | |||
| 51 | let mut pin_group: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default(); | ||
| 52 | // D25 | ||
| 53 | pin_group.set_io1::<tsc::pin_roles::Sample>(context.PB4); | ||
| 54 | // D21 | ||
| 55 | let tsc_sensor = pin_group.set_io2::<tsc::pin_roles::Channel>(context.PB5); | ||
| 56 | |||
| 57 | let pin_groups: PinGroups<peripherals::TSC> = PinGroups { | ||
| 58 | g2: Some(pin_group.pin_group), | ||
| 59 | ..Default::default() | ||
| 60 | }; | ||
| 61 | |||
| 62 | let tsc_conf = Config { | ||
| 63 | ct_pulse_high_length: ChargeTransferPulseCycle::_4, | ||
| 64 | ct_pulse_low_length: ChargeTransferPulseCycle::_4, | ||
| 65 | spread_spectrum: false, | ||
| 66 | spread_spectrum_deviation: SSDeviation::new(2).unwrap(), | ||
| 67 | spread_spectrum_prescaler: false, | ||
| 68 | pulse_generator_prescaler: PGPrescalerDivider::_16, | ||
| 69 | max_count_value: MaxCount::_255, | ||
| 70 | io_default_mode: false, | ||
| 71 | synchro_pin_polarity: false, | ||
| 72 | acquisition_mode: false, | ||
| 73 | max_count_interrupt: false, | ||
| 74 | }; | ||
| 75 | |||
| 76 | let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, tsc_conf, Irqs).unwrap(); | ||
| 77 | |||
| 78 | // Check if TSC is ready | ||
| 79 | if touch_controller.get_state() != State::Ready { | ||
| 80 | info!("TSC not ready!"); | ||
| 81 | return; | ||
| 82 | } | ||
| 83 | info!("TSC initialized successfully"); | ||
| 84 | |||
| 85 | let mut led = Output::new(context.PB14, Level::High, Speed::Low); | ||
| 86 | |||
| 87 | let discharge_delay = 1; // ms | ||
| 88 | |||
| 89 | info!("Starting touch_controller interface"); | ||
| 90 | loop { | ||
| 91 | touch_controller.set_active_channels_mask(tsc_sensor.pin.into()); | ||
| 92 | touch_controller.start(); | ||
| 93 | touch_controller.pend_for_acquisition().await; | ||
| 94 | touch_controller.discharge_io(true); | ||
| 95 | Timer::after_millis(discharge_delay).await; | ||
| 96 | |||
| 97 | let group_val = touch_controller.group_get_value(tsc_sensor.pin.group()); | ||
| 98 | info!("Touch value: {}", group_val); | ||
| 99 | |||
| 100 | if group_val < SENSOR_THRESHOLD { | ||
| 101 | led.set_high(); | ||
| 102 | } else { | ||
| 103 | led.set_low(); | ||
| 104 | } | ||
| 105 | |||
| 106 | Timer::after_millis(100).await; | ||
| 107 | } | ||
| 108 | } | ||
diff --git a/examples/stm32l4/src/bin/tsc_blocking.rs b/examples/stm32l4/src/bin/tsc_blocking.rs new file mode 100644 index 000000000..12084f8e2 --- /dev/null +++ b/examples/stm32l4/src/bin/tsc_blocking.rs | |||
| @@ -0,0 +1,147 @@ | |||
| 1 | // # Example of blocking TSC (Touch Sensing Controller) that lights an LED when touch is detected | ||
| 2 | // | ||
| 3 | // This example demonstrates how to use the Touch Sensing Controller (TSC) in blocking mode on an STM32L4R5ZI-P board. | ||
| 4 | // | ||
| 5 | // ## This example demonstrates: | ||
| 6 | // | ||
| 7 | // 1. Configuring a single TSC channel pin | ||
| 8 | // 2. Using the blocking TSC interface with polling | ||
| 9 | // 3. Waiting for acquisition completion using `poll_for_acquisition` | ||
| 10 | // 4. Reading touch values and controlling an LED based on the results | ||
| 11 | // | ||
| 12 | // ## Suggested physical setup on STM32L4R5ZI-P board: | ||
| 13 | // | ||
| 14 | // - Connect a 1000pF capacitor between pin PB4 (D25) and GND. This is your sampling capacitor. | ||
| 15 | // - Connect one end of a 1K resistor to pin PB5 (D21) and leave the other end loose. | ||
| 16 | // The loose end will act as the touch sensor which will register your touch. | ||
| 17 | // | ||
| 18 | // ## Pin Configuration: | ||
| 19 | // | ||
| 20 | // The example uses two pins from Group 2 of the TSC: | ||
| 21 | // - PB4 (D25) as the sampling capacitor, TSC group 2 IO1 | ||
| 22 | // - PB5 (D21) as the channel pin, TSC group 2 IO2 | ||
| 23 | // | ||
| 24 | // ## Program Behavior: | ||
| 25 | // | ||
| 26 | // The program continuously reads the touch sensor value: | ||
| 27 | // - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value. | ||
| 28 | // - The LED (connected to PB14) is turned on when touch is detected (sensor value < SENSOR_THRESHOLD). | ||
| 29 | // - Touch values are logged to the console. | ||
| 30 | // | ||
| 31 | // ## Troubleshooting: | ||
| 32 | // | ||
| 33 | // - If touch is not detected, try adjusting the SENSOR_THRESHOLD value (currently set to 25). | ||
| 34 | // - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, | ||
| 35 | // pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity. | ||
| 36 | // - Be aware that for some boards, there might be overlapping concerns between some pins, | ||
| 37 | // such as UART connections for the programmer. No errors or warnings will be emitted if you | ||
| 38 | // try to use such a pin for TSC, but you may get strange sensor readings. | ||
| 39 | // | ||
| 40 | // Note: Configuration values and sampling capacitor value have been determined experimentally. | ||
| 41 | // Optimal values may vary based on your specific hardware setup. Refer to the official | ||
| 42 | // STM32L4R5ZI-P datasheet and user manuals for more information on pin configurations and TSC functionality. | ||
| 43 | |||
| 44 | #![no_std] | ||
| 45 | #![no_main] | ||
| 46 | |||
| 47 | use defmt::*; | ||
| 48 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 49 | use embassy_stm32::tsc::{self, *}; | ||
| 50 | use embassy_stm32::{mode, peripherals}; | ||
| 51 | use embassy_time::Timer; | ||
| 52 | use {defmt_rtt as _, panic_probe as _}; | ||
| 53 | |||
| 54 | const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup | ||
| 55 | |||
| 56 | #[embassy_executor::main] | ||
| 57 | async fn main(_spawner: embassy_executor::Spawner) { | ||
| 58 | let device_config = embassy_stm32::Config::default(); | ||
| 59 | let context = embassy_stm32::init(device_config); | ||
| 60 | |||
| 61 | let tsc_conf = Config { | ||
| 62 | ct_pulse_high_length: ChargeTransferPulseCycle::_4, | ||
| 63 | ct_pulse_low_length: ChargeTransferPulseCycle::_4, | ||
| 64 | spread_spectrum: false, | ||
| 65 | spread_spectrum_deviation: SSDeviation::new(2).unwrap(), | ||
| 66 | spread_spectrum_prescaler: false, | ||
| 67 | pulse_generator_prescaler: PGPrescalerDivider::_16, | ||
| 68 | max_count_value: MaxCount::_255, | ||
| 69 | io_default_mode: false, | ||
| 70 | synchro_pin_polarity: false, | ||
| 71 | acquisition_mode: false, | ||
| 72 | max_count_interrupt: false, | ||
| 73 | }; | ||
| 74 | |||
| 75 | let mut g2: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default(); | ||
| 76 | // D25 | ||
| 77 | g2.set_io1::<tsc::pin_roles::Sample>(context.PB4); | ||
| 78 | // D21 | ||
| 79 | let tsc_sensor = g2.set_io2::<tsc::pin_roles::Channel>(context.PB5); | ||
| 80 | |||
| 81 | let pin_groups: PinGroups<peripherals::TSC> = PinGroups { | ||
| 82 | g2: Some(g2.pin_group), | ||
| 83 | ..Default::default() | ||
| 84 | }; | ||
| 85 | |||
| 86 | let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, pin_groups, tsc_conf).unwrap(); | ||
| 87 | |||
| 88 | // Check if TSC is ready | ||
| 89 | if touch_controller.get_state() != State::Ready { | ||
| 90 | crate::panic!("TSC not ready!"); | ||
| 91 | } | ||
| 92 | info!("TSC initialized successfully"); | ||
| 93 | |||
| 94 | let mut led = Output::new(context.PB14, Level::High, Speed::Low); | ||
| 95 | |||
| 96 | // smaller sample capacitor discharge faster and can be used with shorter delay. | ||
| 97 | let discharge_delay = 5; // ms | ||
| 98 | |||
| 99 | // the interval at which the loop polls for new touch sensor values | ||
| 100 | let polling_interval = 100; // ms | ||
| 101 | |||
| 102 | info!("polling for touch"); | ||
| 103 | loop { | ||
| 104 | touch_controller.set_active_channels_mask(tsc_sensor.pin.into()); | ||
| 105 | touch_controller.start(); | ||
| 106 | touch_controller.poll_for_acquisition(); | ||
| 107 | touch_controller.discharge_io(true); | ||
| 108 | Timer::after_millis(discharge_delay).await; | ||
| 109 | |||
| 110 | match read_touch_value(&mut touch_controller, tsc_sensor.pin).await { | ||
| 111 | Some(v) => { | ||
| 112 | info!("sensor value {}", v); | ||
| 113 | if v < SENSOR_THRESHOLD { | ||
| 114 | led.set_high(); | ||
| 115 | } else { | ||
| 116 | led.set_low(); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | None => led.set_low(), | ||
| 120 | } | ||
| 121 | |||
| 122 | Timer::after_millis(polling_interval).await; | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10; | ||
| 127 | |||
| 128 | // attempt to read group status and delay when still ongoing | ||
| 129 | async fn read_touch_value( | ||
| 130 | touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Blocking>, | ||
| 131 | sensor_pin: tsc::IOPin, | ||
| 132 | ) -> Option<u16> { | ||
| 133 | for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS { | ||
| 134 | match touch_controller.group_get_status(sensor_pin.group()) { | ||
| 135 | GroupStatus::Complete => { | ||
| 136 | return Some(touch_controller.group_get_value(sensor_pin.group())); | ||
| 137 | } | ||
| 138 | GroupStatus::Ongoing => { | ||
| 139 | // if you end up here a lot, then you prob need to increase discharge_delay | ||
| 140 | // or consider changing the code to adjust the discharge_delay dynamically | ||
| 141 | info!("Acquisition still ongoing"); | ||
| 142 | Timer::after_millis(1).await; | ||
| 143 | } | ||
| 144 | } | ||
| 145 | } | ||
| 146 | None | ||
| 147 | } | ||
diff --git a/examples/stm32l4/src/bin/tsc_multipin.rs b/examples/stm32l4/src/bin/tsc_multipin.rs new file mode 100644 index 000000000..8fec5ddc4 --- /dev/null +++ b/examples/stm32l4/src/bin/tsc_multipin.rs | |||
| @@ -0,0 +1,198 @@ | |||
| 1 | // # Example of TSC (Touch Sensing Controller) using multiple pins from the same TSC group | ||
| 2 | // | ||
| 3 | // This example demonstrates how to use the Touch Sensing Controller (TSC) with multiple pins, including pins from the same TSC group, on an STM32L4R5ZI-P board. | ||
| 4 | // | ||
| 5 | // ## Key Concepts | ||
| 6 | // | ||
| 7 | // - Only one TSC pin for each TSC group can be acquired and read at a time. | ||
| 8 | // - To control which channel pins are acquired and read, we must write a mask before initiating an acquisition. | ||
| 9 | // - We organize channel pins into acquisition banks to manage this process efficiently. | ||
| 10 | // - Each acquisition bank can contain exactly one channel pin per TSC group and will contain the relevant mask. | ||
| 11 | // | ||
| 12 | // ## This example demonstrates how to: | ||
| 13 | // | ||
| 14 | // 1. Configure multiple channel pins within a single TSC group | ||
| 15 | // 2. Use the set_active_channels_bank method to switch between sets of different channels (acquisition banks) | ||
| 16 | // 3. Read and interpret touch values from multiple channels in the same group | ||
| 17 | // | ||
| 18 | // ## Suggested physical setup on STM32L4R5ZI-P board: | ||
| 19 | // | ||
| 20 | // - Connect a 1000pF capacitor between pin PB12 (D19) and GND. This is the sampling capacitor for TSC group 1. | ||
| 21 | // - Connect one end of a 1K resistor to pin PB13 (D18) and leave the other end loose. This will act as a touch sensor. | ||
| 22 | // - Connect a 1000pF capacitor between pin PB4 (D25) and GND. This is the sampling capacitor for TSC group 2. | ||
| 23 | // - Connect one end of a 1K resistor to pin PB5 (D22) and leave the other end loose. This will act as a touch sensor. | ||
| 24 | // - Connect one end of another 1K resistor to pin PB6 (D71) and leave the other end loose. This will act as a touch sensor. | ||
| 25 | // | ||
| 26 | // ## Pin Configuration: | ||
| 27 | // | ||
| 28 | // The example uses pins from two TSC groups: | ||
| 29 | // | ||
| 30 | // - Group 1: | ||
| 31 | // - PB12 (D19) as sampling capacitor (TSC group 1 IO1) | ||
| 32 | // - PB13 (D18) as channel (TSC group 1 IO2) | ||
| 33 | // - Group 2: | ||
| 34 | // - PB4 (D25) as sampling capacitor (TSC group 2 IO1) | ||
| 35 | // - PB5 (D22) as channel (TSC group 2 IO2) | ||
| 36 | // - PB6 (D71) as channel (TSC group 2 IO3) | ||
| 37 | // | ||
| 38 | // The pins have been chosen for their convenient locations on the STM32L4R5ZI-P board, making it easy to add capacitors and resistors directly to the board without special connectors, breadboards, or soldering. | ||
| 39 | // | ||
| 40 | // ## Program Behavior: | ||
| 41 | // | ||
| 42 | // The program reads the designated channel pins and adjusts the LED (connected to PB14) blinking pattern based on which sensor(s) are touched: | ||
| 43 | // | ||
| 44 | // - No touch: LED off | ||
| 45 | // - One sensor touched: Slow blinking | ||
| 46 | // - Two sensors touched: Fast blinking | ||
| 47 | // - Three sensors touched: LED constantly on | ||
| 48 | // | ||
| 49 | // ## Troubleshooting: | ||
| 50 | // | ||
| 51 | // - If touch is not detected, try adjusting the SENSOR_THRESHOLD value (currently set to 20). | ||
| 52 | // - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity. | ||
| 53 | // - Be aware that for some boards there will be overlapping concerns between some pins, for | ||
| 54 | // example UART connection for the programmer to the MCU and a TSC pin. No errors or warning will | ||
| 55 | // be emitted if you try to use such a pin for TSC, but you will get strange sensor readings. | ||
| 56 | // | ||
| 57 | // Note: Configuration values and sampling capacitor values have been determined experimentally. Optimal values may vary based on your specific hardware setup. Refer to the official STM32L4R5ZI-P datasheet and user manuals for more information on pin configurations and TSC functionality. | ||
| 58 | |||
| 59 | #![no_std] | ||
| 60 | #![no_main] | ||
| 61 | |||
| 62 | use defmt::*; | ||
| 63 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 64 | use embassy_stm32::tsc::{self, *}; | ||
| 65 | use embassy_stm32::{bind_interrupts, mode, peripherals}; | ||
| 66 | use embassy_time::Timer; | ||
| 67 | use {defmt_rtt as _, panic_probe as _}; | ||
| 68 | |||
| 69 | bind_interrupts!(struct Irqs { | ||
| 70 | TSC => InterruptHandler<embassy_stm32::peripherals::TSC>; | ||
| 71 | }); | ||
| 72 | |||
| 73 | const SENSOR_THRESHOLD: u16 = 20; | ||
| 74 | |||
| 75 | async fn acquire_sensors( | ||
| 76 | touch_controller: &mut Tsc<'static, peripherals::TSC, mode::Async>, | ||
| 77 | tsc_acquisition_bank: &AcquisitionBank, | ||
| 78 | ) { | ||
| 79 | touch_controller.set_active_channels_bank(tsc_acquisition_bank); | ||
| 80 | touch_controller.start(); | ||
| 81 | touch_controller.pend_for_acquisition().await; | ||
| 82 | touch_controller.discharge_io(true); | ||
| 83 | let discharge_delay = 1; // ms | ||
| 84 | Timer::after_millis(discharge_delay).await; | ||
| 85 | } | ||
| 86 | |||
| 87 | #[embassy_executor::main] | ||
| 88 | async fn main(_spawner: embassy_executor::Spawner) { | ||
| 89 | let device_config = embassy_stm32::Config::default(); | ||
| 90 | let context = embassy_stm32::init(device_config); | ||
| 91 | |||
| 92 | // ---------- initial configuration of TSC ---------- | ||
| 93 | let mut g1: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default(); | ||
| 94 | g1.set_io1::<tsc::pin_roles::Sample>(context.PB12); | ||
| 95 | let sensor0 = g1.set_io2::<tsc::pin_roles::Channel>(context.PB13); | ||
| 96 | |||
| 97 | let mut g2: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default(); | ||
| 98 | g2.set_io1::<tsc::pin_roles::Sample>(context.PB4); | ||
| 99 | let sensor1 = g2.set_io2(context.PB5); | ||
| 100 | let sensor2 = g2.set_io3(context.PB6); | ||
| 101 | |||
| 102 | let config = tsc::Config { | ||
| 103 | ct_pulse_high_length: ChargeTransferPulseCycle::_16, | ||
| 104 | ct_pulse_low_length: ChargeTransferPulseCycle::_16, | ||
| 105 | spread_spectrum: false, | ||
| 106 | spread_spectrum_deviation: SSDeviation::new(2).unwrap(), | ||
| 107 | spread_spectrum_prescaler: false, | ||
| 108 | pulse_generator_prescaler: PGPrescalerDivider::_16, | ||
| 109 | max_count_value: MaxCount::_255, | ||
| 110 | io_default_mode: false, | ||
| 111 | synchro_pin_polarity: false, | ||
| 112 | acquisition_mode: false, | ||
| 113 | max_count_interrupt: false, | ||
| 114 | }; | ||
| 115 | |||
| 116 | let pin_groups: PinGroups<peripherals::TSC> = PinGroups { | ||
| 117 | g1: Some(g1.pin_group), | ||
| 118 | g2: Some(g2.pin_group), | ||
| 119 | ..Default::default() | ||
| 120 | }; | ||
| 121 | |||
| 122 | let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, config, Irqs).unwrap(); | ||
| 123 | |||
| 124 | // ---------- setting up acquisition banks ---------- | ||
| 125 | // sensor0 and sensor1 belong to different TSC-groups, therefore we can acquire and | ||
| 126 | // read them both in one go. | ||
| 127 | let bank1 = touch_controller.create_acquisition_bank(AcquisitionBankPins { | ||
| 128 | g1_pin: Some(sensor0), | ||
| 129 | g2_pin: Some(sensor1), | ||
| 130 | ..Default::default() | ||
| 131 | }); | ||
| 132 | // `sensor1` and `sensor2` belongs to the same TSC-group, therefore we must make sure to | ||
| 133 | // acquire them one at the time. We do this by organizing them into different acquisition banks. | ||
| 134 | let bank2 = touch_controller.create_acquisition_bank(AcquisitionBankPins { | ||
| 135 | g2_pin: Some(sensor2), | ||
| 136 | ..Default::default() | ||
| 137 | }); | ||
| 138 | |||
| 139 | // Check if TSC is ready | ||
| 140 | if touch_controller.get_state() != State::Ready { | ||
| 141 | crate::panic!("TSC not ready!"); | ||
| 142 | } | ||
| 143 | |||
| 144 | info!("TSC initialized successfully"); | ||
| 145 | |||
| 146 | let mut led = Output::new(context.PB14, Level::High, Speed::Low); | ||
| 147 | |||
| 148 | let mut led_state = false; | ||
| 149 | |||
| 150 | loop { | ||
| 151 | acquire_sensors(&mut touch_controller, &bank1).await; | ||
| 152 | let readings1 = touch_controller.get_acquisition_bank_values(&bank1); | ||
| 153 | acquire_sensors(&mut touch_controller, &bank2).await; | ||
| 154 | let readings2 = touch_controller.get_acquisition_bank_values(&bank2); | ||
| 155 | |||
| 156 | let mut touched_sensors_count = 0; | ||
| 157 | for reading in readings1.iter().chain(readings2.iter()) { | ||
| 158 | info!("{}", reading); | ||
| 159 | if reading.sensor_value < SENSOR_THRESHOLD { | ||
| 160 | touched_sensors_count += 1; | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | match touched_sensors_count { | ||
| 165 | 0 => { | ||
| 166 | // No sensors touched, turn off the LED | ||
| 167 | led.set_low(); | ||
| 168 | led_state = false; | ||
| 169 | } | ||
| 170 | 1 => { | ||
| 171 | // One sensor touched, blink slowly | ||
| 172 | led_state = !led_state; | ||
| 173 | if led_state { | ||
| 174 | led.set_high(); | ||
| 175 | } else { | ||
| 176 | led.set_low(); | ||
| 177 | } | ||
| 178 | Timer::after_millis(200).await; | ||
| 179 | } | ||
| 180 | 2 => { | ||
| 181 | // Two sensors touched, blink faster | ||
| 182 | led_state = !led_state; | ||
| 183 | if led_state { | ||
| 184 | led.set_high(); | ||
| 185 | } else { | ||
| 186 | led.set_low(); | ||
| 187 | } | ||
| 188 | Timer::after_millis(50).await; | ||
| 189 | } | ||
| 190 | 3 => { | ||
| 191 | // All three sensors touched, LED constantly on | ||
| 192 | led.set_high(); | ||
| 193 | led_state = true; | ||
| 194 | } | ||
| 195 | _ => crate::unreachable!(), // This case should never occur with 3 sensors | ||
| 196 | } | ||
| 197 | } | ||
| 198 | } | ||
diff --git a/examples/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]; |
