diff options
| author | Olof <[email protected]> | 2024-12-18 01:48:25 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-12-18 01:48:25 +0100 |
| commit | 7cf96e4730964d085015320648c870a05fbaf431 (patch) | |
| tree | 04072529b62082cb66443377b589fe08169f83be /examples | |
| parent | 8678911028a591d72fd1d8418407b5885ed4c417 (diff) | |
| parent | 341036a8b865609767fbf9015b482ea70ed4f23f (diff) | |
Merge branch 'embassy-rs:main' into u5_adc
Diffstat (limited to 'examples')
208 files changed, 5632 insertions, 3089 deletions
diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 93e49faef..45ad341fc 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml | |||
| @@ -5,8 +5,8 @@ version = "0.1.0" | |||
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } | 8 | embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } |
| 9 | embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] } | 9 | embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] } | 10 | embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] } |
| 11 | embassy-nrf = { version = "0.2.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } | 11 | embassy-nrf = { version = "0.2.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } |
| 12 | embassy-boot = { version = "0.3.0", path = "../../../../embassy-boot", features = [] } | 12 | embassy-boot = { version = "0.3.0", path = "../../../../embassy-boot", features = [] } |
diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index 8bb8afdfe..ec99f2605 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml | |||
| @@ -5,8 +5,8 @@ version = "0.1.0" | |||
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } | 8 | embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } |
| 9 | embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] } | 9 | embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] } | 10 | embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] } |
| 11 | embassy-rp = { version = "0.2.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] } | 11 | embassy-rp = { version = "0.2.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] } |
| 12 | embassy-boot-rp = { version = "0.3.0", path = "../../../../embassy-boot-rp", features = [] } | 12 | embassy-boot-rp = { version = "0.3.0", path = "../../../../embassy-boot-rp", features = [] } |
diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index 1c2934298..d2138db87 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml | |||
| @@ -5,8 +5,8 @@ version = "0.1.0" | |||
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } | 8 | embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } |
| 9 | embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } | 9 | embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32f303re", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32f303re", "time-driver-any", "exti"] } |
| 12 | embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32" } | 12 | embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32" } |
diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index 09e34c7df..b86c66f5d 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml | |||
| @@ -5,8 +5,8 @@ version = "0.1.0" | |||
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } | 8 | embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } |
| 9 | embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } | 9 | embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32f767zi", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32f767zi", "time-driver-any", "exti"] } |
| 12 | embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } | 12 | embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } |
diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index 5e7f4d5e7..e2e2fe711 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml | |||
| @@ -5,8 +5,8 @@ version = "0.1.0" | |||
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } | 8 | embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } |
| 9 | embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } | 9 | embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32h743zi", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32h743zi", "time-driver-any", "exti"] } |
| 12 | embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } | 12 | embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } |
diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 60fdcfafb..7e9c52ffa 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml | |||
| @@ -5,8 +5,8 @@ version = "0.1.0" | |||
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } | 8 | embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } |
| 9 | embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } | 9 | embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l072cz", "time-driver-any", "exti", "memory-x"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l072cz", "time-driver-any", "exti", "memory-x"] } |
| 12 | embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } | 12 | embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } |
diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index fe3ab2c04..42353a24c 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml | |||
| @@ -5,8 +5,8 @@ version = "0.1.0" | |||
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } | 8 | embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } |
| 9 | embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } | 9 | embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l151cb-a", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l151cb-a", "time-driver-any", "exti"] } |
| 12 | embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } | 12 | embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } |
diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 169856358..cf0b0242a 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml | |||
| @@ -5,8 +5,8 @@ version = "0.1.0" | |||
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } | 8 | embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } |
| 9 | embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } | 9 | embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l475vg", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l475vg", "time-driver-any", "exti"] } |
| 12 | embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } | 12 | embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } |
diff --git a/examples/boot/application/stm32wb-dfu/Cargo.toml b/examples/boot/application/stm32wb-dfu/Cargo.toml index 7cef8fe0d..ea2879fb5 100644 --- a/examples/boot/application/stm32wb-dfu/Cargo.toml +++ b/examples/boot/application/stm32wb-dfu/Cargo.toml | |||
| @@ -5,8 +5,8 @@ version = "0.1.0" | |||
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } | 8 | embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } |
| 9 | embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } | 9 | embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32wb55rg", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32wb55rg", "time-driver-any", "exti"] } |
| 12 | embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } | 12 | embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } |
diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index 860a835a9..6417b8430 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml | |||
| @@ -5,8 +5,8 @@ version = "0.1.0" | |||
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } | 8 | embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } |
| 9 | embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } | 9 | embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32wl55jc-cm4", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32wl55jc-cm4", "time-driver-any", "exti"] } |
| 12 | embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } | 12 | embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } |
diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml index 9d5d51a13..c2e8bbe53 100644 --- a/examples/boot/bootloader/nrf/Cargo.toml +++ b/examples/boot/bootloader/nrf/Cargo.toml | |||
| @@ -12,7 +12,7 @@ defmt-rtt = { version = "0.4", optional = true } | |||
| 12 | embassy-nrf = { path = "../../../../embassy-nrf", features = [] } | 12 | embassy-nrf = { path = "../../../../embassy-nrf", features = [] } |
| 13 | embassy-boot-nrf = { path = "../../../../embassy-boot-nrf" } | 13 | embassy-boot-nrf = { path = "../../../../embassy-boot-nrf" } |
| 14 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 14 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 15 | embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } | 15 | embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } |
| 16 | cortex-m-rt = { version = "0.7" } | 16 | cortex-m-rt = { version = "0.7" } |
| 17 | cfg-if = "1.0.0" | 17 | cfg-if = "1.0.0" |
| 18 | 18 | ||
diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs index 67c700437..b849a0df3 100644 --- a/examples/boot/bootloader/nrf/src/main.rs +++ b/examples/boot/bootloader/nrf/src/main.rs | |||
| @@ -8,7 +8,7 @@ use cortex_m_rt::{entry, exception}; | |||
| 8 | use defmt_rtt as _; | 8 | use defmt_rtt as _; |
| 9 | use embassy_boot_nrf::*; | 9 | use embassy_boot_nrf::*; |
| 10 | use embassy_nrf::nvmc::Nvmc; | 10 | use embassy_nrf::nvmc::Nvmc; |
| 11 | use embassy_nrf::wdt; | 11 | use embassy_nrf::wdt::{self, HaltConfig, SleepConfig}; |
| 12 | use embassy_sync::blocking_mutex::Mutex; | 12 | use embassy_sync::blocking_mutex::Mutex; |
| 13 | 13 | ||
| 14 | #[entry] | 14 | #[entry] |
| @@ -25,8 +25,8 @@ fn main() -> ! { | |||
| 25 | 25 | ||
| 26 | let mut wdt_config = wdt::Config::default(); | 26 | let mut wdt_config = wdt::Config::default(); |
| 27 | wdt_config.timeout_ticks = 32768 * 5; // timeout seconds | 27 | wdt_config.timeout_ticks = 32768 * 5; // timeout seconds |
| 28 | wdt_config.run_during_sleep = true; | 28 | wdt_config.action_during_sleep = SleepConfig::RUN; |
| 29 | wdt_config.run_during_debug_halt = false; | 29 | wdt_config.action_during_debug_halt = HaltConfig::PAUSE; |
| 30 | 30 | ||
| 31 | let flash = WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config); | 31 | let flash = WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config); |
| 32 | let flash = Mutex::new(RefCell::new(flash)); | 32 | let flash = Mutex::new(RefCell::new(flash)); |
diff --git a/examples/boot/bootloader/rp/Cargo.toml b/examples/boot/bootloader/rp/Cargo.toml index 9df396e5e..24df3da82 100644 --- a/examples/boot/bootloader/rp/Cargo.toml +++ b/examples/boot/bootloader/rp/Cargo.toml | |||
| @@ -11,7 +11,7 @@ defmt-rtt = { version = "0.4", optional = true } | |||
| 11 | 11 | ||
| 12 | embassy-rp = { path = "../../../../embassy-rp", features = ["rp2040"] } | 12 | embassy-rp = { path = "../../../../embassy-rp", features = ["rp2040"] } |
| 13 | embassy-boot-rp = { path = "../../../../embassy-boot-rp" } | 13 | embassy-boot-rp = { path = "../../../../embassy-boot-rp" } |
| 14 | embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } | 14 | embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } |
| 15 | embassy-time = { path = "../../../../embassy-time", features = [] } | 15 | embassy-time = { path = "../../../../embassy-time", features = [] } |
| 16 | 16 | ||
| 17 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 17 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
diff --git a/examples/boot/bootloader/stm32-dual-bank/Cargo.toml b/examples/boot/bootloader/stm32-dual-bank/Cargo.toml index b91b05412..81e0026c6 100644 --- a/examples/boot/bootloader/stm32-dual-bank/Cargo.toml +++ b/examples/boot/bootloader/stm32-dual-bank/Cargo.toml | |||
| @@ -15,7 +15,7 @@ cortex-m = { version = "0.7.6", features = [ | |||
| 15 | "inline-asm", | 15 | "inline-asm", |
| 16 | "critical-section-single-core", | 16 | "critical-section-single-core", |
| 17 | ] } | 17 | ] } |
| 18 | embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } | 18 | embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } |
| 19 | cortex-m-rt = { version = "0.7" } | 19 | cortex-m-rt = { version = "0.7" } |
| 20 | embedded-storage = "0.3.1" | 20 | embedded-storage = "0.3.1" |
| 21 | embedded-storage-async = "0.4.0" | 21 | embedded-storage-async = "0.4.0" |
diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml index 541186949..f35e4e713 100644 --- a/examples/boot/bootloader/stm32/Cargo.toml +++ b/examples/boot/bootloader/stm32/Cargo.toml | |||
| @@ -12,7 +12,7 @@ defmt-rtt = { version = "0.4", optional = true } | |||
| 12 | embassy-stm32 = { path = "../../../../embassy-stm32", features = [] } | 12 | embassy-stm32 = { path = "../../../../embassy-stm32", features = [] } |
| 13 | embassy-boot-stm32 = { path = "../../../../embassy-boot-stm32" } | 13 | embassy-boot-stm32 = { path = "../../../../embassy-boot-stm32" } |
| 14 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 14 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 15 | embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } | 15 | embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } |
| 16 | cortex-m-rt = { version = "0.7" } | 16 | cortex-m-rt = { version = "0.7" } |
| 17 | embedded-storage = "0.3.1" | 17 | embedded-storage = "0.3.1" |
| 18 | embedded-storage-async = "0.4.0" | 18 | embedded-storage-async = "0.4.0" |
diff --git a/examples/boot/bootloader/stm32wb-dfu/Cargo.toml b/examples/boot/bootloader/stm32wb-dfu/Cargo.toml index 050b672ce..1431e7cc3 100644 --- a/examples/boot/bootloader/stm32wb-dfu/Cargo.toml +++ b/examples/boot/bootloader/stm32wb-dfu/Cargo.toml | |||
| @@ -12,7 +12,7 @@ defmt-rtt = { version = "0.4", optional = true } | |||
| 12 | embassy-stm32 = { path = "../../../../embassy-stm32", features = [] } | 12 | embassy-stm32 = { path = "../../../../embassy-stm32", features = [] } |
| 13 | embassy-boot-stm32 = { path = "../../../../embassy-boot-stm32" } | 13 | embassy-boot-stm32 = { path = "../../../../embassy-boot-stm32" } |
| 14 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 14 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 15 | embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } | 15 | embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } |
| 16 | cortex-m-rt = { version = "0.7" } | 16 | cortex-m-rt = { version = "0.7" } |
| 17 | embedded-storage = "0.3.1" | 17 | embedded-storage = "0.3.1" |
| 18 | embedded-storage-async = "0.4.0" | 18 | embedded-storage-async = "0.4.0" |
diff --git a/examples/boot/bootloader/stm32wb-dfu/README.md b/examples/boot/bootloader/stm32wb-dfu/README.md index d5c6ea57c..3c5f268a0 100644 --- a/examples/boot/bootloader/stm32wb-dfu/README.md +++ b/examples/boot/bootloader/stm32wb-dfu/README.md | |||
| @@ -1,11 +1,37 @@ | |||
| 1 | # Bootloader for STM32 | 1 | # Bootloader for STM32 |
| 2 | 2 | ||
| 3 | The bootloader uses `embassy-boot` to interact with the flash. | 3 | This bootloader implementation uses `embassy-boot` and `embassy-usb-dfu` to manage firmware updates and interact with the flash memory on STM32WB55 devices. |
| 4 | 4 | ||
| 5 | # Usage | 5 | ## Prerequisites |
| 6 | 6 | ||
| 7 | Flash the bootloader | 7 | - Rust toolchain with `cargo` installed |
| 8 | - `cargo-flash` for flashing the bootloader | ||
| 9 | - `dfu-util` for firmware updates | ||
| 10 | - `cargo-binutils` for binary generation | ||
| 11 | |||
| 12 | ## Usage | ||
| 13 | |||
| 14 | ### 1. Flash the Bootloader | ||
| 15 | |||
| 16 | First, flash the bootloader to your device: | ||
| 8 | 17 | ||
| 9 | ``` | 18 | ``` |
| 10 | cargo flash --features embassy-stm32/stm32wb55rg --release --chip STM32WB55RGVx | 19 | cargo flash --features embassy-stm32/stm32wb55rg --release --chip STM32WB55RGVx |
| 11 | ``` | 20 | ``` |
| 21 | |||
| 22 | ### 2. Build and Flash Application | ||
| 23 | |||
| 24 | Generate your application binary and flash it using DFU: | ||
| 25 | |||
| 26 | ``` | ||
| 27 | cargo objcopy --release -- -O binary fw.bin | ||
| 28 | dfu-util -d c0de:cafe -w -D fw.bin | ||
| 29 | ``` | ||
| 30 | |||
| 31 | ## Troubleshooting | ||
| 32 | |||
| 33 | - Make sure your device is in DFU mode before flashing | ||
| 34 | - Verify the USB VID:PID matches your device (c0de:cafe) | ||
| 35 | - Check USB connections if the device is not detected | ||
| 36 | - Make sure the transfer size option of `dfu-util` matches the bootloader configuration. By default, `dfu-util` will use the transfer size reported by the device, but you can override it with the `-t` option if needed. | ||
| 37 | - Make sure `control_buf` size is larger than or equal to the `usb_dfu` `BLOCK_SIZE` parameter (in this example, both are set to 4096 bytes). | ||
diff --git a/examples/boot/bootloader/stm32wb-dfu/src/main.rs b/examples/boot/bootloader/stm32wb-dfu/src/main.rs index 093b39f9d..b09d53cf0 100644 --- a/examples/boot/bootloader/stm32wb-dfu/src/main.rs +++ b/examples/boot/bootloader/stm32wb-dfu/src/main.rs | |||
| @@ -12,7 +12,7 @@ use embassy_stm32::rcc::WPAN_DEFAULT; | |||
| 12 | use embassy_stm32::usb::Driver; | 12 | use embassy_stm32::usb::Driver; |
| 13 | use embassy_stm32::{bind_interrupts, peripherals, usb}; | 13 | use embassy_stm32::{bind_interrupts, peripherals, usb}; |
| 14 | use embassy_sync::blocking_mutex::Mutex; | 14 | use embassy_sync::blocking_mutex::Mutex; |
| 15 | use embassy_usb::Builder; | 15 | use embassy_usb::{msos, Builder}; |
| 16 | use embassy_usb_dfu::consts::DfuAttributes; | 16 | use embassy_usb_dfu::consts::DfuAttributes; |
| 17 | use embassy_usb_dfu::{usb_dfu, Control, ResetImmediate}; | 17 | use embassy_usb_dfu::{usb_dfu, Control, ResetImmediate}; |
| 18 | 18 | ||
| @@ -20,6 +20,9 @@ bind_interrupts!(struct Irqs { | |||
| 20 | USB_LP => usb::InterruptHandler<peripherals::USB>; | 20 | USB_LP => usb::InterruptHandler<peripherals::USB>; |
| 21 | }); | 21 | }); |
| 22 | 22 | ||
| 23 | // This is a randomly generated GUID to allow clients on Windows to find our device | ||
| 24 | const DEVICE_INTERFACE_GUIDS: &[&str] = &["{EAA9A5DC-30BA-44BC-9232-606CDC875321}"]; | ||
| 25 | |||
| 23 | #[entry] | 26 | #[entry] |
| 24 | fn main() -> ! { | 27 | fn main() -> ! { |
| 25 | let mut config = embassy_stm32::Config::default(); | 28 | let mut config = embassy_stm32::Config::default(); |
| @@ -62,6 +65,18 @@ fn main() -> ! { | |||
| 62 | &mut control_buf, | 65 | &mut control_buf, |
| 63 | ); | 66 | ); |
| 64 | 67 | ||
| 68 | // We add MSOS headers so that the device automatically gets assigned the WinUSB driver on Windows. | ||
| 69 | // Otherwise users need to do this manually using a tool like Zadig. | ||
| 70 | // | ||
| 71 | // It seems it is important for the DFU class that these headers be on the Device level. | ||
| 72 | // | ||
| 73 | builder.msos_descriptor(msos::windows_version::WIN8_1, 2); | ||
| 74 | builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); | ||
| 75 | builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new( | ||
| 76 | "DeviceInterfaceGUIDs", | ||
| 77 | msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS), | ||
| 78 | )); | ||
| 79 | |||
| 65 | usb_dfu::<_, _, _, ResetImmediate, 4096>(&mut builder, &mut state); | 80 | usb_dfu::<_, _, _, ResetImmediate, 4096>(&mut builder, &mut state); |
| 66 | 81 | ||
| 67 | let mut dev = builder.build(); | 82 | let mut dev = builder.build(); |
diff --git a/examples/lpc55s69/.cargo/config.toml b/examples/lpc55s69/.cargo/config.toml new file mode 100644 index 000000000..9556de72f --- /dev/null +++ b/examples/lpc55s69/.cargo/config.toml | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | ||
| 2 | runner = "probe-rs run --chip LPC55S69JBD100" | ||
| 3 | |||
| 4 | [build] | ||
| 5 | target = "thumbv8m.main-none-eabihf" | ||
| 6 | |||
| 7 | [env] | ||
| 8 | DEFMT_LOG = "debug" | ||
diff --git a/examples/lpc55s69/Cargo.toml b/examples/lpc55s69/Cargo.toml new file mode 100644 index 000000000..41a88f082 --- /dev/null +++ b/examples/lpc55s69/Cargo.toml | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-nxp-lpc55s69-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | |||
| 8 | [dependencies] | ||
| 9 | embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["rt"] } | ||
| 10 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt"] } | ||
| 11 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } | ||
| 12 | embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt"] } | ||
| 13 | panic-halt = "0.2.0" | ||
| 14 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||
| 15 | cortex-m-rt = {version = "0.7.0"} | ||
| 16 | defmt = "0.3" | ||
| 17 | defmt-rtt = "0.4" | ||
| 18 | panic-probe = { version = "0.3.2", features = ["print-defmt"] } | ||
| 19 | panic-semihosting = "0.6.0" | ||
| 20 | |||
| 21 | [profile.release] | ||
| 22 | debug = 2 | ||
diff --git a/examples/lpc55s69/build.rs b/examples/lpc55s69/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/examples/lpc55s69/build.rs | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | //! This build script copies the `memory.x` file from the crate root into | ||
| 2 | //! a directory where the linker can always find it at build time. | ||
| 3 | //! For many projects this is optional, as the linker always searches the | ||
| 4 | //! project root directory -- wherever `Cargo.toml` is. However, if you | ||
| 5 | //! are using a workspace or have a more complicated build setup, this | ||
| 6 | //! build script becomes required. Additionally, by requesting that | ||
| 7 | //! Cargo re-run the build script whenever `memory.x` is changed, | ||
| 8 | //! updating `memory.x` ensures a rebuild of the application with the | ||
| 9 | //! new memory settings. | ||
| 10 | |||
| 11 | use std::env; | ||
| 12 | use std::fs::File; | ||
| 13 | use std::io::Write; | ||
| 14 | use std::path::PathBuf; | ||
| 15 | |||
| 16 | fn main() { | ||
| 17 | // Put `memory.x` in our output directory and ensure it's | ||
| 18 | // on the linker search path. | ||
| 19 | let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); | ||
| 20 | File::create(out.join("memory.x")) | ||
| 21 | .unwrap() | ||
| 22 | .write_all(include_bytes!("memory.x")) | ||
| 23 | .unwrap(); | ||
| 24 | println!("cargo:rustc-link-search={}", out.display()); | ||
| 25 | |||
| 26 | // By default, Cargo will re-run a build script whenever | ||
| 27 | // any file in the project changes. By specifying `memory.x` | ||
| 28 | // here, we ensure the build script is only re-run when | ||
| 29 | // `memory.x` is changed. | ||
| 30 | println!("cargo:rerun-if-changed=memory.x"); | ||
| 31 | |||
| 32 | println!("cargo:rustc-link-arg-bins=--nmagic"); | ||
| 33 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); | ||
| 34 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | ||
| 35 | } | ||
diff --git a/examples/lpc55s69/memory.x b/examples/lpc55s69/memory.x new file mode 100644 index 000000000..1483b2fad --- /dev/null +++ b/examples/lpc55s69/memory.x | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | /* File originally from lpc55-hal repo: https://github.com/lpc55/lpc55-hal/blob/main/memory.x */ | ||
| 2 | MEMORY | ||
| 3 | { | ||
| 4 | FLASH : ORIGIN = 0x00000000, LENGTH = 512K | ||
| 5 | |||
| 6 | /* for use with standard link.x */ | ||
| 7 | RAM : ORIGIN = 0x20000000, LENGTH = 256K | ||
| 8 | |||
| 9 | /* would be used with proper link.x */ | ||
| 10 | /* needs changes to r0 (initialization code) */ | ||
| 11 | /* SRAM0 : ORIGIN = 0x20000000, LENGTH = 64K */ | ||
| 12 | /* SRAM1 : ORIGIN = 0x20010000, LENGTH = 64K */ | ||
| 13 | /* SRAM2 : ORIGIN = 0x20020000, LENGTH = 64K */ | ||
| 14 | /* SRAM3 : ORIGIN = 0x20030000, LENGTH = 64K */ | ||
| 15 | |||
| 16 | /* CASPER SRAM regions */ | ||
| 17 | /* SRAMX0: ORIGIN = 0x1400_0000, LENGTH = 4K /1* to 0x1400_0FFF *1/ */ | ||
| 18 | /* SRAMX1: ORIGIN = 0x1400_4000, LENGTH = 4K /1* to 0x1400_4FFF *1/ */ | ||
| 19 | |||
| 20 | /* USB1 SRAM regin */ | ||
| 21 | /* USB1_SRAM : ORIGIN = 0x40100000, LENGTH = 16K */ | ||
| 22 | |||
| 23 | /* To define our own USB RAM section in one regular */ | ||
| 24 | /* RAM, probably easiest to shorten length of RAM */ | ||
| 25 | /* above, and use this freed RAM section */ | ||
| 26 | |||
| 27 | } | ||
| 28 | |||
diff --git a/examples/lpc55s69/src/bin/blinky_nop.rs b/examples/lpc55s69/src/bin/blinky_nop.rs new file mode 100644 index 000000000..58e2d9808 --- /dev/null +++ b/examples/lpc55s69/src/bin/blinky_nop.rs | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | //! This example has been made with the LPCXpresso55S69 board in mind, which has a built-in LED on PIO1_6. | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | |||
| 6 | use cortex_m::asm::nop; | ||
| 7 | use defmt::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_nxp::gpio::{Level, Output}; | ||
| 10 | use {defmt_rtt as _, panic_halt as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_spawner: Spawner) { | ||
| 14 | let p = embassy_nxp::init(Default::default()); | ||
| 15 | |||
| 16 | let mut led = Output::new(p.PIO1_6, Level::Low); | ||
| 17 | |||
| 18 | loop { | ||
| 19 | info!("led off!"); | ||
| 20 | led.set_high(); | ||
| 21 | |||
| 22 | for _ in 0..200_000 { | ||
| 23 | nop(); | ||
| 24 | } | ||
| 25 | |||
| 26 | info!("led on!"); | ||
| 27 | led.set_low(); | ||
| 28 | |||
| 29 | for _ in 0..200_000 { | ||
| 30 | nop(); | ||
| 31 | } | ||
| 32 | } | ||
| 33 | } | ||
diff --git a/examples/lpc55s69/src/bin/button_executor.rs b/examples/lpc55s69/src/bin/button_executor.rs new file mode 100644 index 000000000..836b1c9eb --- /dev/null +++ b/examples/lpc55s69/src/bin/button_executor.rs | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | //! This example has been made with the LPCXpresso55S69 board in mind, which has a built-in LED on | ||
| 2 | //! PIO1_6 and a button (labeled "user") on PIO1_9. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | |||
| 7 | use defmt::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_nxp::gpio::{Input, Level, Output, Pull}; | ||
| 10 | use {defmt_rtt as _, panic_halt as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_spawner: Spawner) -> ! { | ||
| 14 | let p = embassy_nxp::init(Default::default()); | ||
| 15 | |||
| 16 | let mut led = Output::new(p.PIO1_6, Level::Low); | ||
| 17 | let mut button = Input::new(p.PIO1_9, Pull::Up); | ||
| 18 | |||
| 19 | info!("Entered main loop"); | ||
| 20 | loop { | ||
| 21 | button.wait_for_rising_edge().await; | ||
| 22 | info!("Button pressed"); | ||
| 23 | led.toggle(); | ||
| 24 | } | ||
| 25 | } | ||
diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index 98a678815..6d13d668a 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml | |||
| @@ -15,8 +15,8 @@ log = [ | |||
| 15 | ] | 15 | ] |
| 16 | 16 | ||
| 17 | [dependencies] | 17 | [dependencies] |
| 18 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync" } | 18 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync" } |
| 19 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "integrated-timers"] } | 19 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace"] } |
| 20 | embassy-time = { version = "0.3.2", path = "../../embassy-time" } | 20 | embassy-time = { version = "0.3.2", path = "../../embassy-time" } |
| 21 | embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } | 21 | embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } |
| 22 | 22 | ||
diff --git a/examples/nrf51/Cargo.toml b/examples/nrf51/Cargo.toml index 93a19bea7..8d995cfd8 100644 --- a/examples/nrf51/Cargo.toml +++ b/examples/nrf51/Cargo.toml | |||
| @@ -5,7 +5,7 @@ version = "0.1.0" | |||
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | 8 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 9 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 9 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 10 | embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] } | 10 | embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] } |
| 11 | 11 | ||
diff --git a/examples/nrf52810/Cargo.toml b/examples/nrf52810/Cargo.toml index 0e3e81c3f..fa2a27aaa 100644 --- a/examples/nrf52810/Cargo.toml +++ b/examples/nrf52810/Cargo.toml | |||
| @@ -6,8 +6,8 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 8 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 9 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 9 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 10 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | 10 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 11 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 11 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 12 | embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | 12 | embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } |
| 13 | 13 | ||
diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml index 7fae7aefc..6b15b24da 100644 --- a/examples/nrf52840-rtic/Cargo.toml +++ b/examples/nrf52840-rtic/Cargo.toml | |||
| @@ -8,8 +8,9 @@ license = "MIT OR Apache-2.0" | |||
| 8 | rtic = { version = "2", features = ["thumbv7-backend"] } | 8 | rtic = { version = "2", features = ["thumbv7-backend"] } |
| 9 | 9 | ||
| 10 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 10 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 11 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 11 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime", "generic-queue"] } | 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime"] } |
| 13 | embassy-time-queue-driver = { version = "0.1.0", path = "../../embassy-time-queue-driver", features = ["generic-queue-8"] } | ||
| 13 | embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | 14 | embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } |
| 14 | 15 | ||
| 15 | defmt = "0.3" | 16 | defmt = "0.3" |
diff --git a/examples/nrf52840-rtic/src/bin/blinky.rs b/examples/nrf52840-rtic/src/bin/blinky.rs index 060bb9ebc..5a074ea17 100644 --- a/examples/nrf52840-rtic/src/bin/blinky.rs +++ b/examples/nrf52840-rtic/src/bin/blinky.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | use {defmt_rtt as _, panic_probe as _}; | 5 | use {defmt_rtt as _, panic_probe as _}; |
| 6 | 6 | ||
| 7 | #[rtic::app(device = embassy_nrf, peripherals = false, dispatchers = [SWI0_EGU0, SWI1_EGU1])] | 7 | #[rtic::app(device = embassy_nrf, peripherals = false, dispatchers = [EGU0_SWI0, EGU1_SWI1])] |
| 8 | mod app { | 8 | mod app { |
| 9 | use defmt::info; | 9 | use defmt::info; |
| 10 | use embassy_nrf::gpio::{Level, Output, OutputDrive}; | 10 | use embassy_nrf::gpio::{Level, Output, OutputDrive}; |
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 17fa6234d..fa29d52b9 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml | |||
| @@ -6,11 +6,11 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 8 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 9 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 9 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 10 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | 10 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 11 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 11 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 12 | embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | 12 | embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } |
| 13 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } | 13 | embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } |
| 14 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } |
| 15 | embedded-io = { version = "0.6.0", features = ["defmt-03"] } | 15 | embedded-io = { version = "0.6.0", features = ["defmt-03"] } |
| 16 | embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } | 16 | embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } |
diff --git a/examples/nrf52840/src/bin/buffered_uart.rs b/examples/nrf52840/src/bin/buffered_uart.rs index 6ac72bcaf..77d017964 100644 --- a/examples/nrf52840/src/bin/buffered_uart.rs +++ b/examples/nrf52840/src/bin/buffered_uart.rs | |||
| @@ -9,7 +9,7 @@ use embedded_io_async::Write; | |||
| 9 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 10 | ||
| 11 | bind_interrupts!(struct Irqs { | 11 | bind_interrupts!(struct Irqs { |
| 12 | UARTE0_UART0 => buffered_uarte::InterruptHandler<peripherals::UARTE0>; | 12 | UARTE0 => buffered_uarte::InterruptHandler<peripherals::UARTE0>; |
| 13 | }); | 13 | }); |
| 14 | 14 | ||
| 15 | #[embassy_executor::main] | 15 | #[embassy_executor::main] |
diff --git a/examples/nrf52840/src/bin/multiprio.rs b/examples/nrf52840/src/bin/multiprio.rs index 797be93a7..d58613da4 100644 --- a/examples/nrf52840/src/bin/multiprio.rs +++ b/examples/nrf52840/src/bin/multiprio.rs | |||
| @@ -112,12 +112,12 @@ static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); | |||
| 112 | static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new(); | 112 | static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new(); |
| 113 | 113 | ||
| 114 | #[interrupt] | 114 | #[interrupt] |
| 115 | unsafe fn SWI1_EGU1() { | 115 | unsafe fn EGU1_SWI1() { |
| 116 | EXECUTOR_HIGH.on_interrupt() | 116 | EXECUTOR_HIGH.on_interrupt() |
| 117 | } | 117 | } |
| 118 | 118 | ||
| 119 | #[interrupt] | 119 | #[interrupt] |
| 120 | unsafe fn SWI0_EGU0() { | 120 | unsafe fn EGU0_SWI0() { |
| 121 | EXECUTOR_MED.on_interrupt() | 121 | EXECUTOR_MED.on_interrupt() |
| 122 | } | 122 | } |
| 123 | 123 | ||
| @@ -127,14 +127,14 @@ fn main() -> ! { | |||
| 127 | 127 | ||
| 128 | let _p = embassy_nrf::init(Default::default()); | 128 | let _p = embassy_nrf::init(Default::default()); |
| 129 | 129 | ||
| 130 | // High-priority executor: SWI1_EGU1, priority level 6 | 130 | // High-priority executor: EGU1_SWI1, priority level 6 |
| 131 | interrupt::SWI1_EGU1.set_priority(Priority::P6); | 131 | interrupt::EGU1_SWI1.set_priority(Priority::P6); |
| 132 | let spawner = EXECUTOR_HIGH.start(interrupt::SWI1_EGU1); | 132 | let spawner = EXECUTOR_HIGH.start(interrupt::EGU1_SWI1); |
| 133 | unwrap!(spawner.spawn(run_high())); | 133 | unwrap!(spawner.spawn(run_high())); |
| 134 | 134 | ||
| 135 | // Medium-priority executor: SWI0_EGU0, priority level 7 | 135 | // Medium-priority executor: EGU0_SWI0, priority level 7 |
| 136 | interrupt::SWI0_EGU0.set_priority(Priority::P7); | 136 | interrupt::EGU0_SWI0.set_priority(Priority::P7); |
| 137 | let spawner = EXECUTOR_MED.start(interrupt::SWI0_EGU0); | 137 | let spawner = EXECUTOR_MED.start(interrupt::EGU0_SWI0); |
| 138 | unwrap!(spawner.spawn(run_med())); | 138 | unwrap!(spawner.spawn(run_med())); |
| 139 | 139 | ||
| 140 | // Low priority executor: runs in thread mode, using WFE/SEV | 140 | // Low priority executor: runs in thread mode, using WFE/SEV |
diff --git a/examples/nrf52840/src/bin/nfct.rs b/examples/nrf52840/src/bin/nfct.rs new file mode 100644 index 000000000..d559d006a --- /dev/null +++ b/examples/nrf52840/src/bin/nfct.rs | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_nrf::config::HfclkSource; | ||
| 7 | use embassy_nrf::nfct::{Config as NfcConfig, NfcId, NfcT}; | ||
| 8 | use embassy_nrf::{bind_interrupts, nfct}; | ||
| 9 | use {defmt_rtt as _, embassy_nrf as _, panic_probe as _}; | ||
| 10 | |||
| 11 | bind_interrupts!(struct Irqs { | ||
| 12 | NFCT => nfct::InterruptHandler; | ||
| 13 | }); | ||
| 14 | |||
| 15 | #[embassy_executor::main] | ||
| 16 | async fn main(_spawner: Spawner) { | ||
| 17 | let mut config = embassy_nrf::config::Config::default(); | ||
| 18 | config.hfclk_source = HfclkSource::ExternalXtal; | ||
| 19 | let p = embassy_nrf::init(config); | ||
| 20 | |||
| 21 | dbg!("Setting up..."); | ||
| 22 | let config = NfcConfig { | ||
| 23 | nfcid1: NfcId::DoubleSize([0x04, 0x68, 0x95, 0x71, 0xFA, 0x5C, 0x64]), | ||
| 24 | sdd_pat: nfct::SddPat::SDD00100, | ||
| 25 | plat_conf: 0b0000, | ||
| 26 | protocol: nfct::SelResProtocol::Type4A, | ||
| 27 | }; | ||
| 28 | |||
| 29 | let mut nfc = NfcT::new(p.NFCT, Irqs, &config); | ||
| 30 | |||
| 31 | let mut buf = [0u8; 256]; | ||
| 32 | |||
| 33 | loop { | ||
| 34 | info!("activating"); | ||
| 35 | nfc.activate().await; | ||
| 36 | |||
| 37 | loop { | ||
| 38 | info!("rxing"); | ||
| 39 | let n = match nfc.receive(&mut buf).await { | ||
| 40 | Ok(n) => n, | ||
| 41 | Err(e) => { | ||
| 42 | error!("rx error {}", e); | ||
| 43 | break; | ||
| 44 | } | ||
| 45 | }; | ||
| 46 | let req = &buf[..n]; | ||
| 47 | info!("received frame {:02x}", req); | ||
| 48 | |||
| 49 | let mut deselect = false; | ||
| 50 | let resp = match req { | ||
| 51 | [0xe0, ..] => { | ||
| 52 | info!("Got RATS, tx'ing ATS"); | ||
| 53 | &[0x06, 0x77, 0x77, 0x81, 0x02, 0x80][..] | ||
| 54 | } | ||
| 55 | [0xc2] => { | ||
| 56 | info!("Got deselect!"); | ||
| 57 | deselect = true; | ||
| 58 | &[0xc2] | ||
| 59 | } | ||
| 60 | _ => { | ||
| 61 | info!("Got unknown command!"); | ||
| 62 | &[0xFF] | ||
| 63 | } | ||
| 64 | }; | ||
| 65 | |||
| 66 | match nfc.transmit(resp).await { | ||
| 67 | Ok(()) => {} | ||
| 68 | Err(e) => { | ||
| 69 | error!("tx error {}", e); | ||
| 70 | break; | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | if deselect { | ||
| 75 | break; | ||
| 76 | } | ||
| 77 | } | ||
| 78 | } | ||
| 79 | } | ||
diff --git a/examples/nrf52840/src/bin/spis.rs b/examples/nrf52840/src/bin/spis.rs index 613cd37ab..4f28da07e 100644 --- a/examples/nrf52840/src/bin/spis.rs +++ b/examples/nrf52840/src/bin/spis.rs | |||
| @@ -8,7 +8,7 @@ use embassy_nrf::{bind_interrupts, peripherals, spis}; | |||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 9 | ||
| 10 | bind_interrupts!(struct Irqs { | 10 | bind_interrupts!(struct Irqs { |
| 11 | SPIM2_SPIS2_SPI2 => spis::InterruptHandler<peripherals::SPI2>; | 11 | SPI2 => spis::InterruptHandler<peripherals::SPI2>; |
| 12 | }); | 12 | }); |
| 13 | 13 | ||
| 14 | #[embassy_executor::main] | 14 | #[embassy_executor::main] |
diff --git a/examples/nrf52840/src/bin/twim.rs b/examples/nrf52840/src/bin/twim.rs index a9a0765e8..ceaafd784 100644 --- a/examples/nrf52840/src/bin/twim.rs +++ b/examples/nrf52840/src/bin/twim.rs | |||
| @@ -14,7 +14,7 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 14 | const ADDRESS: u8 = 0x50; | 14 | const ADDRESS: u8 = 0x50; |
| 15 | 15 | ||
| 16 | bind_interrupts!(struct Irqs { | 16 | bind_interrupts!(struct Irqs { |
| 17 | SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twim::InterruptHandler<peripherals::TWISPI0>; | 17 | TWISPI0 => twim::InterruptHandler<peripherals::TWISPI0>; |
| 18 | }); | 18 | }); |
| 19 | 19 | ||
| 20 | #[embassy_executor::main] | 20 | #[embassy_executor::main] |
diff --git a/examples/nrf52840/src/bin/twim_lowpower.rs b/examples/nrf52840/src/bin/twim_lowpower.rs index c743614b8..e2efbdd8d 100644 --- a/examples/nrf52840/src/bin/twim_lowpower.rs +++ b/examples/nrf52840/src/bin/twim_lowpower.rs | |||
| @@ -19,7 +19,7 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 19 | const ADDRESS: u8 = 0x50; | 19 | const ADDRESS: u8 = 0x50; |
| 20 | 20 | ||
| 21 | bind_interrupts!(struct Irqs { | 21 | bind_interrupts!(struct Irqs { |
| 22 | SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twim::InterruptHandler<peripherals::TWISPI0>; | 22 | TWISPI0 => twim::InterruptHandler<peripherals::TWISPI0>; |
| 23 | }); | 23 | }); |
| 24 | 24 | ||
| 25 | #[embassy_executor::main] | 25 | #[embassy_executor::main] |
diff --git a/examples/nrf52840/src/bin/twis.rs b/examples/nrf52840/src/bin/twis.rs index 88bd4cceb..856b34140 100644 --- a/examples/nrf52840/src/bin/twis.rs +++ b/examples/nrf52840/src/bin/twis.rs | |||
| @@ -10,7 +10,7 @@ use embassy_nrf::{bind_interrupts, peripherals}; | |||
| 10 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 11 | ||
| 12 | bind_interrupts!(struct Irqs { | 12 | bind_interrupts!(struct Irqs { |
| 13 | SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twis::InterruptHandler<peripherals::TWISPI0>; | 13 | TWISPI0 => twis::InterruptHandler<peripherals::TWISPI0>; |
| 14 | }); | 14 | }); |
| 15 | 15 | ||
| 16 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
diff --git a/examples/nrf52840/src/bin/uart.rs b/examples/nrf52840/src/bin/uart.rs index accaccea1..23154672f 100644 --- a/examples/nrf52840/src/bin/uart.rs +++ b/examples/nrf52840/src/bin/uart.rs | |||
| @@ -7,7 +7,7 @@ use embassy_nrf::{bind_interrupts, peripherals, uarte}; | |||
| 7 | use {defmt_rtt as _, panic_probe as _}; | 7 | use {defmt_rtt as _, panic_probe as _}; |
| 8 | 8 | ||
| 9 | bind_interrupts!(struct Irqs { | 9 | bind_interrupts!(struct Irqs { |
| 10 | UARTE0_UART0 => uarte::InterruptHandler<peripherals::UARTE0>; | 10 | UARTE0 => uarte::InterruptHandler<peripherals::UARTE0>; |
| 11 | }); | 11 | }); |
| 12 | 12 | ||
| 13 | #[embassy_executor::main] | 13 | #[embassy_executor::main] |
diff --git a/examples/nrf52840/src/bin/uart_idle.rs b/examples/nrf52840/src/bin/uart_idle.rs index fa93bcf21..a42e84fa4 100644 --- a/examples/nrf52840/src/bin/uart_idle.rs +++ b/examples/nrf52840/src/bin/uart_idle.rs | |||
| @@ -8,7 +8,7 @@ use embassy_nrf::{bind_interrupts, uarte}; | |||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 9 | ||
| 10 | bind_interrupts!(struct Irqs { | 10 | bind_interrupts!(struct Irqs { |
| 11 | UARTE0_UART0 => uarte::InterruptHandler<UARTE0>; | 11 | UARTE0 => uarte::InterruptHandler<UARTE0>; |
| 12 | }); | 12 | }); |
| 13 | 13 | ||
| 14 | #[embassy_executor::main] | 14 | #[embassy_executor::main] |
diff --git a/examples/nrf52840/src/bin/uart_split.rs b/examples/nrf52840/src/bin/uart_split.rs index c7510a9a8..94af4be86 100644 --- a/examples/nrf52840/src/bin/uart_split.rs +++ b/examples/nrf52840/src/bin/uart_split.rs | |||
| @@ -13,7 +13,7 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 13 | static CHANNEL: Channel<ThreadModeRawMutex, [u8; 8], 1> = Channel::new(); | 13 | static CHANNEL: Channel<ThreadModeRawMutex, [u8; 8], 1> = Channel::new(); |
| 14 | 14 | ||
| 15 | bind_interrupts!(struct Irqs { | 15 | bind_interrupts!(struct Irqs { |
| 16 | UARTE0_UART0 => uarte::InterruptHandler<UARTE0>; | 16 | UARTE0 => uarte::InterruptHandler<UARTE0>; |
| 17 | }); | 17 | }); |
| 18 | 18 | ||
| 19 | #[embassy_executor::main] | 19 | #[embassy_executor::main] |
diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index b07adac1f..88314b749 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs | |||
| @@ -1,8 +1,6 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::mem; | ||
| 5 | |||
| 6 | use defmt::*; | 4 | use defmt::*; |
| 7 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 8 | use embassy_net::tcp::TcpSocket; | 6 | use embassy_net::tcp::TcpSocket; |
| @@ -20,7 +18,7 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 20 | 18 | ||
| 21 | bind_interrupts!(struct Irqs { | 19 | bind_interrupts!(struct Irqs { |
| 22 | USBD => usb::InterruptHandler<peripherals::USBD>; | 20 | USBD => usb::InterruptHandler<peripherals::USBD>; |
| 23 | POWER_CLOCK => usb::vbus_detect::InterruptHandler; | 21 | CLOCK_POWER => usb::vbus_detect::InterruptHandler; |
| 24 | RNG => rng::InterruptHandler<peripherals::RNG>; | 22 | RNG => rng::InterruptHandler<peripherals::RNG>; |
| 25 | }); | 23 | }); |
| 26 | 24 | ||
| @@ -46,11 +44,10 @@ async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static, MTU>> | |||
| 46 | #[embassy_executor::main] | 44 | #[embassy_executor::main] |
| 47 | async fn main(spawner: Spawner) { | 45 | async fn main(spawner: Spawner) { |
| 48 | let p = embassy_nrf::init(Default::default()); | 46 | let p = embassy_nrf::init(Default::default()); |
| 49 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 50 | 47 | ||
| 51 | info!("Enabling ext hfosc..."); | 48 | info!("Enabling ext hfosc..."); |
| 52 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | 49 | pac::CLOCK.tasks_hfclkstart().write_value(1); |
| 53 | while clock.events_hfclkstarted.read().bits() != 1 {} | 50 | while pac::CLOCK.events_hfclkstarted().read() != 1 {} |
| 54 | 51 | ||
| 55 | // Create the driver, from the HAL. | 52 | // Create the driver, from the HAL. |
| 56 | let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); | 53 | let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); |
diff --git a/examples/nrf52840/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs index e33ee5866..5a9dc90a2 100644 --- a/examples/nrf52840/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs | |||
| @@ -1,7 +1,6 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::mem; | ||
| 5 | use core::sync::atomic::{AtomicBool, Ordering}; | 4 | use core::sync::atomic::{AtomicBool, Ordering}; |
| 6 | 5 | ||
| 7 | use defmt::*; | 6 | use defmt::*; |
| @@ -22,7 +21,7 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 22 | 21 | ||
| 23 | bind_interrupts!(struct Irqs { | 22 | bind_interrupts!(struct Irqs { |
| 24 | USBD => usb::InterruptHandler<peripherals::USBD>; | 23 | USBD => usb::InterruptHandler<peripherals::USBD>; |
| 25 | POWER_CLOCK => usb::vbus_detect::InterruptHandler; | 24 | CLOCK_POWER => usb::vbus_detect::InterruptHandler; |
| 26 | }); | 25 | }); |
| 27 | 26 | ||
| 28 | static SUSPENDED: AtomicBool = AtomicBool::new(false); | 27 | static SUSPENDED: AtomicBool = AtomicBool::new(false); |
| @@ -30,11 +29,10 @@ static SUSPENDED: AtomicBool = AtomicBool::new(false); | |||
| 30 | #[embassy_executor::main] | 29 | #[embassy_executor::main] |
| 31 | async fn main(_spawner: Spawner) { | 30 | async fn main(_spawner: Spawner) { |
| 32 | let p = embassy_nrf::init(Default::default()); | 31 | let p = embassy_nrf::init(Default::default()); |
| 33 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 34 | 32 | ||
| 35 | info!("Enabling ext hfosc..."); | 33 | info!("Enabling ext hfosc..."); |
| 36 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | 34 | pac::CLOCK.tasks_hfclkstart().write_value(1); |
| 37 | while clock.events_hfclkstarted.read().bits() != 1 {} | 35 | while pac::CLOCK.events_hfclkstarted().read() != 1 {} |
| 38 | 36 | ||
| 39 | // Create the driver, from the HAL. | 37 | // Create the driver, from the HAL. |
| 40 | let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); | 38 | let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); |
diff --git a/examples/nrf52840/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs index 8076ac283..80cda70e3 100644 --- a/examples/nrf52840/src/bin/usb_hid_mouse.rs +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs | |||
| @@ -1,8 +1,6 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::mem; | ||
| 5 | |||
| 6 | use defmt::*; | 4 | use defmt::*; |
| 7 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 8 | use embassy_futures::join::join; | 6 | use embassy_futures::join::join; |
| @@ -18,17 +16,16 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 18 | 16 | ||
| 19 | bind_interrupts!(struct Irqs { | 17 | bind_interrupts!(struct Irqs { |
| 20 | USBD => usb::InterruptHandler<peripherals::USBD>; | 18 | USBD => usb::InterruptHandler<peripherals::USBD>; |
| 21 | POWER_CLOCK => usb::vbus_detect::InterruptHandler; | 19 | CLOCK_POWER => usb::vbus_detect::InterruptHandler; |
| 22 | }); | 20 | }); |
| 23 | 21 | ||
| 24 | #[embassy_executor::main] | 22 | #[embassy_executor::main] |
| 25 | async fn main(_spawner: Spawner) { | 23 | async fn main(_spawner: Spawner) { |
| 26 | let p = embassy_nrf::init(Default::default()); | 24 | let p = embassy_nrf::init(Default::default()); |
| 27 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 28 | 25 | ||
| 29 | info!("Enabling ext hfosc..."); | 26 | info!("Enabling ext hfosc..."); |
| 30 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | 27 | pac::CLOCK.tasks_hfclkstart().write_value(1); |
| 31 | while clock.events_hfclkstarted.read().bits() != 1 {} | 28 | while pac::CLOCK.events_hfclkstarted().read() != 1 {} |
| 32 | 29 | ||
| 33 | // Create the driver, from the HAL. | 30 | // Create the driver, from the HAL. |
| 34 | let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); | 31 | let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); |
diff --git a/examples/nrf52840/src/bin/usb_serial.rs b/examples/nrf52840/src/bin/usb_serial.rs index 02048e692..a534046d9 100644 --- a/examples/nrf52840/src/bin/usb_serial.rs +++ b/examples/nrf52840/src/bin/usb_serial.rs | |||
| @@ -1,8 +1,6 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::mem; | ||
| 5 | |||
| 6 | use defmt::{info, panic}; | 4 | use defmt::{info, panic}; |
| 7 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 8 | use embassy_futures::join::join; | 6 | use embassy_futures::join::join; |
| @@ -16,17 +14,16 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 16 | 14 | ||
| 17 | bind_interrupts!(struct Irqs { | 15 | bind_interrupts!(struct Irqs { |
| 18 | USBD => usb::InterruptHandler<peripherals::USBD>; | 16 | USBD => usb::InterruptHandler<peripherals::USBD>; |
| 19 | POWER_CLOCK => usb::vbus_detect::InterruptHandler; | 17 | CLOCK_POWER => usb::vbus_detect::InterruptHandler; |
| 20 | }); | 18 | }); |
| 21 | 19 | ||
| 22 | #[embassy_executor::main] | 20 | #[embassy_executor::main] |
| 23 | async fn main(_spawner: Spawner) { | 21 | async fn main(_spawner: Spawner) { |
| 24 | let p = embassy_nrf::init(Default::default()); | 22 | let p = embassy_nrf::init(Default::default()); |
| 25 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 26 | 23 | ||
| 27 | info!("Enabling ext hfosc..."); | 24 | info!("Enabling ext hfosc..."); |
| 28 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | 25 | pac::CLOCK.tasks_hfclkstart().write_value(1); |
| 29 | while clock.events_hfclkstarted.read().bits() != 1 {} | 26 | while pac::CLOCK.events_hfclkstarted().read() != 1 {} |
| 30 | 27 | ||
| 31 | // Create the driver, from the HAL. | 28 | // Create the driver, from the HAL. |
| 32 | let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); | 29 | let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); |
diff --git a/examples/nrf52840/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs index 895cca8b9..32fc5e094 100644 --- a/examples/nrf52840/src/bin/usb_serial_multitask.rs +++ b/examples/nrf52840/src/bin/usb_serial_multitask.rs | |||
| @@ -1,8 +1,6 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::mem; | ||
| 5 | |||
| 6 | use defmt::{info, panic, unwrap}; | 4 | use defmt::{info, panic, unwrap}; |
| 7 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 8 | use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; | 6 | use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; |
| @@ -16,7 +14,7 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 16 | 14 | ||
| 17 | bind_interrupts!(struct Irqs { | 15 | bind_interrupts!(struct Irqs { |
| 18 | USBD => usb::InterruptHandler<peripherals::USBD>; | 16 | USBD => usb::InterruptHandler<peripherals::USBD>; |
| 19 | POWER_CLOCK => usb::vbus_detect::InterruptHandler; | 17 | CLOCK_POWER => usb::vbus_detect::InterruptHandler; |
| 20 | }); | 18 | }); |
| 21 | 19 | ||
| 22 | type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; | 20 | type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; |
| @@ -39,11 +37,10 @@ async fn echo_task(mut class: CdcAcmClass<'static, MyDriver>) { | |||
| 39 | #[embassy_executor::main] | 37 | #[embassy_executor::main] |
| 40 | async fn main(spawner: Spawner) { | 38 | async fn main(spawner: Spawner) { |
| 41 | let p = embassy_nrf::init(Default::default()); | 39 | let p = embassy_nrf::init(Default::default()); |
| 42 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 43 | 40 | ||
| 44 | info!("Enabling ext hfosc..."); | 41 | info!("Enabling ext hfosc..."); |
| 45 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | 42 | pac::CLOCK.tasks_hfclkstart().write_value(1); |
| 46 | while clock.events_hfclkstarted.read().bits() != 1 {} | 43 | while pac::CLOCK.events_hfclkstarted().read() != 1 {} |
| 47 | 44 | ||
| 48 | // Create the driver, from the HAL. | 45 | // Create the driver, from the HAL. |
| 49 | let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); | 46 | let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); |
diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs index c6675a3d3..0352f9c66 100644 --- a/examples/nrf52840/src/bin/usb_serial_winusb.rs +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs | |||
| @@ -1,8 +1,6 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::mem; | ||
| 5 | |||
| 6 | use defmt::{info, panic}; | 4 | use defmt::{info, panic}; |
| 7 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 8 | use embassy_futures::join::join; | 6 | use embassy_futures::join::join; |
| @@ -18,7 +16,7 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 18 | 16 | ||
| 19 | bind_interrupts!(struct Irqs { | 17 | bind_interrupts!(struct Irqs { |
| 20 | USBD => usb::InterruptHandler<peripherals::USBD>; | 18 | USBD => usb::InterruptHandler<peripherals::USBD>; |
| 21 | POWER_CLOCK => usb::vbus_detect::InterruptHandler; | 19 | CLOCK_POWER => usb::vbus_detect::InterruptHandler; |
| 22 | }); | 20 | }); |
| 23 | 21 | ||
| 24 | // This is a randomly generated GUID to allow clients on Windows to find our device | 22 | // This is a randomly generated GUID to allow clients on Windows to find our device |
| @@ -27,11 +25,10 @@ const DEVICE_INTERFACE_GUIDS: &[&str] = &["{EAA9A5DC-30BA-44BC-9232-606CDC875321 | |||
| 27 | #[embassy_executor::main] | 25 | #[embassy_executor::main] |
| 28 | async fn main(_spawner: Spawner) { | 26 | async fn main(_spawner: Spawner) { |
| 29 | let p = embassy_nrf::init(Default::default()); | 27 | let p = embassy_nrf::init(Default::default()); |
| 30 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 31 | 28 | ||
| 32 | info!("Enabling ext hfosc..."); | 29 | info!("Enabling ext hfosc..."); |
| 33 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | 30 | pac::CLOCK.tasks_hfclkstart().write_value(1); |
| 34 | while clock.events_hfclkstarted.read().bits() != 1 {} | 31 | while pac::CLOCK.events_hfclkstarted().read() != 1 {} |
| 35 | 32 | ||
| 36 | // Create the driver, from the HAL. | 33 | // Create the driver, from the HAL. |
| 37 | let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); | 34 | let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); |
diff --git a/examples/nrf52840/src/bin/wdt.rs b/examples/nrf52840/src/bin/wdt.rs index ede88cc26..0d9ee3cf8 100644 --- a/examples/nrf52840/src/bin/wdt.rs +++ b/examples/nrf52840/src/bin/wdt.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_nrf::gpio::{Input, Pull}; | 6 | use embassy_nrf::gpio::{Input, Pull}; |
| 7 | use embassy_nrf::wdt::{Config, Watchdog}; | 7 | use embassy_nrf::wdt::{Config, HaltConfig, Watchdog}; |
| 8 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 9 | ||
| 10 | #[embassy_executor::main] | 10 | #[embassy_executor::main] |
| @@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) { | |||
| 17 | 17 | ||
| 18 | // This is needed for `probe-rs run` to be able to catch the panic message | 18 | // This is needed for `probe-rs run` to be able to catch the panic message |
| 19 | // in the WDT interrupt. The core resets 2 ticks after firing the interrupt. | 19 | // in the WDT interrupt. The core resets 2 ticks after firing the interrupt. |
| 20 | config.run_during_debug_halt = false; | 20 | config.action_during_debug_halt = HaltConfig::PAUSE; |
| 21 | 21 | ||
| 22 | let (_wdt, [mut handle]) = match Watchdog::try_new(p.WDT, config) { | 22 | let (_wdt, [mut handle]) = match Watchdog::try_new(p.WDT, config) { |
| 23 | Ok(x) => x, | 23 | Ok(x) => x, |
diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 0da85be07..1792b277c 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml | |||
| @@ -6,11 +6,11 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 8 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 9 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 9 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 10 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 10 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] } |
| 11 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 11 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 12 | embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } | 12 | embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } |
| 13 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } | 13 | embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } |
| 14 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } |
| 15 | embedded-io-async = { version = "0.6.1" } | 15 | embedded-io-async = { version = "0.6.1" } |
| 16 | 16 | ||
diff --git a/examples/nrf54l15/.cargo/config.toml b/examples/nrf54l15/.cargo/config.toml new file mode 100644 index 000000000..4a026ebbd --- /dev/null +++ b/examples/nrf54l15/.cargo/config.toml | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | ||
| 2 | # replace nRF82840_xxAA with your chip as listed in `probe-rs chip list` | ||
| 3 | runner = "../../sshprobe.sh" | ||
| 4 | |||
| 5 | [build] | ||
| 6 | target = "thumbv8m.main-none-eabihf" | ||
| 7 | |||
| 8 | [env] | ||
| 9 | DEFMT_LOG = "trace" | ||
diff --git a/examples/nrf54l15/Cargo.toml b/examples/nrf54l15/Cargo.toml new file mode 100644 index 000000000..7288ef6af --- /dev/null +++ b/examples/nrf54l15/Cargo.toml | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-nrf54l15-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | ||
| 9 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | ||
| 10 | embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | ||
| 11 | |||
| 12 | defmt = "0.3" | ||
| 13 | defmt-rtt = "0.4" | ||
| 14 | panic-probe = { version = "0.3", features = ["print-defmt"] } | ||
| 15 | |||
| 16 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||
| 17 | cortex-m-rt = "0.7.0" | ||
| 18 | |||
| 19 | [profile.release] | ||
| 20 | debug = 2 | ||
diff --git a/examples/nrf54l15/build.rs b/examples/nrf54l15/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/examples/nrf54l15/build.rs | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | //! This build script copies the `memory.x` file from the crate root into | ||
| 2 | //! a directory where the linker can always find it at build time. | ||
| 3 | //! For many projects this is optional, as the linker always searches the | ||
| 4 | //! project root directory -- wherever `Cargo.toml` is. However, if you | ||
| 5 | //! are using a workspace or have a more complicated build setup, this | ||
| 6 | //! build script becomes required. Additionally, by requesting that | ||
| 7 | //! Cargo re-run the build script whenever `memory.x` is changed, | ||
| 8 | //! updating `memory.x` ensures a rebuild of the application with the | ||
| 9 | //! new memory settings. | ||
| 10 | |||
| 11 | use std::env; | ||
| 12 | use std::fs::File; | ||
| 13 | use std::io::Write; | ||
| 14 | use std::path::PathBuf; | ||
| 15 | |||
| 16 | fn main() { | ||
| 17 | // Put `memory.x` in our output directory and ensure it's | ||
| 18 | // on the linker search path. | ||
| 19 | let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); | ||
| 20 | File::create(out.join("memory.x")) | ||
| 21 | .unwrap() | ||
| 22 | .write_all(include_bytes!("memory.x")) | ||
| 23 | .unwrap(); | ||
| 24 | println!("cargo:rustc-link-search={}", out.display()); | ||
| 25 | |||
| 26 | // By default, Cargo will re-run a build script whenever | ||
| 27 | // any file in the project changes. By specifying `memory.x` | ||
| 28 | // here, we ensure the build script is only re-run when | ||
| 29 | // `memory.x` is changed. | ||
| 30 | println!("cargo:rerun-if-changed=memory.x"); | ||
| 31 | |||
| 32 | println!("cargo:rustc-link-arg-bins=--nmagic"); | ||
| 33 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); | ||
| 34 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | ||
| 35 | } | ||
diff --git a/examples/nrf54l15/memory.x b/examples/nrf54l15/memory.x new file mode 100644 index 000000000..1064c8a5c --- /dev/null +++ b/examples/nrf54l15/memory.x | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | MEMORY | ||
| 2 | { | ||
| 3 | FLASH : ORIGIN = 0x00000000, LENGTH = 1536K | ||
| 4 | RAM : ORIGIN = 0x20000000, LENGTH = 256K | ||
| 5 | } | ||
diff --git a/examples/nrf54l15/src/bin/blinky.rs b/examples/nrf54l15/src/bin/blinky.rs new file mode 100644 index 000000000..71fcc461f --- /dev/null +++ b/examples/nrf54l15/src/bin/blinky.rs | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::info; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_nrf::gpio::{Level, Output, OutputDrive}; | ||
| 7 | use embassy_time::Timer; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::main] | ||
| 11 | async fn main(_spawner: Spawner) { | ||
| 12 | let p = embassy_nrf::init(Default::default()); | ||
| 13 | let mut led = Output::new(p.P2_09, Level::Low, OutputDrive::Standard); | ||
| 14 | |||
| 15 | loop { | ||
| 16 | info!("high!"); | ||
| 17 | led.set_high(); | ||
| 18 | Timer::after_millis(300).await; | ||
| 19 | info!("low!"); | ||
| 20 | led.set_low(); | ||
| 21 | Timer::after_millis(300).await; | ||
| 22 | } | ||
| 23 | } | ||
diff --git a/examples/nrf9151/ns/Cargo.toml b/examples/nrf9151/ns/Cargo.toml index 17fe27b67..0353cf598 100644 --- a/examples/nrf9151/ns/Cargo.toml +++ b/examples/nrf9151/ns/Cargo.toml | |||
| @@ -5,7 +5,7 @@ version = "0.1.0" | |||
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-executor = { version = "0.6.0", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | 8 | embassy-executor = { version = "0.6.3", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 9 | embassy-time = { version = "0.3.2", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 9 | embassy-time = { version = "0.3.2", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 10 | embassy-nrf = { version = "0.2.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | 10 | embassy-nrf = { version = "0.2.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } |
| 11 | 11 | ||
diff --git a/examples/nrf9151/ns/src/bin/uart.rs b/examples/nrf9151/ns/src/bin/uart.rs index 2220dccfb..234ff35f2 100644 --- a/examples/nrf9151/ns/src/bin/uart.rs +++ b/examples/nrf9151/ns/src/bin/uart.rs | |||
| @@ -7,7 +7,7 @@ use embassy_nrf::{bind_interrupts, peripherals, uarte}; | |||
| 7 | use {defmt_rtt as _, panic_probe as _}; | 7 | use {defmt_rtt as _, panic_probe as _}; |
| 8 | 8 | ||
| 9 | bind_interrupts!(struct Irqs { | 9 | bind_interrupts!(struct Irqs { |
| 10 | SPIM0_SPIS0_TWIM0_TWIS0_UARTE0 => uarte::InterruptHandler<peripherals::SERIAL0>; | 10 | SERIAL0 => uarte::InterruptHandler<peripherals::SERIAL0>; |
| 11 | }); | 11 | }); |
| 12 | 12 | ||
| 13 | #[embassy_executor::main] | 13 | #[embassy_executor::main] |
diff --git a/examples/nrf9151/s/Cargo.toml b/examples/nrf9151/s/Cargo.toml index 7253fc4be..5d2302574 100644 --- a/examples/nrf9151/s/Cargo.toml +++ b/examples/nrf9151/s/Cargo.toml | |||
| @@ -5,7 +5,7 @@ version = "0.1.0" | |||
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-executor = { version = "0.6.0", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | 8 | embassy-executor = { version = "0.6.3", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 9 | embassy-time = { version = "0.3.2", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 9 | embassy-time = { version = "0.3.2", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 10 | embassy-nrf = { version = "0.2.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | 10 | embassy-nrf = { version = "0.2.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } |
| 11 | 11 | ||
diff --git a/examples/nrf9160/Cargo.toml b/examples/nrf9160/Cargo.toml index 9aeb99317..b52cd4af0 100644 --- a/examples/nrf9160/Cargo.toml +++ b/examples/nrf9160/Cargo.toml | |||
| @@ -5,11 +5,11 @@ version = "0.1.0" | |||
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | 8 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 9 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 9 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 10 | embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | 10 | embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } |
| 11 | embassy-net-nrf91 = { version = "0.1.0", path = "../../embassy-net-nrf91", features = ["defmt"] } | 11 | embassy-net-nrf91 = { version = "0.1.0", path = "../../embassy-net-nrf91", features = ["defmt"] } |
| 12 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "proto-ipv4", "medium-ip"] } | 12 | embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "proto-ipv4", "medium-ip"] } |
| 13 | 13 | ||
| 14 | defmt = "0.3" | 14 | defmt = "0.3" |
| 15 | defmt-rtt = "0.4" | 15 | defmt-rtt = "0.4" |
diff --git a/examples/nrf9160/src/bin/modem_tcp_client.rs b/examples/nrf9160/src/bin/modem_tcp_client.rs index 929883884..35900cdd8 100644 --- a/examples/nrf9160/src/bin/modem_tcp_client.rs +++ b/examples/nrf9160/src/bin/modem_tcp_client.rs | |||
| @@ -9,7 +9,7 @@ use core::str::FromStr; | |||
| 9 | 9 | ||
| 10 | use defmt::{info, unwrap, warn}; | 10 | use defmt::{info, unwrap, warn}; |
| 11 | use embassy_executor::Spawner; | 11 | use embassy_executor::Spawner; |
| 12 | use embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources}; | 12 | use embassy_net::{Ipv4Cidr, Stack, StackResources}; |
| 13 | use embassy_net_nrf91::context::Status; | 13 | use embassy_net_nrf91::context::Status; |
| 14 | use embassy_net_nrf91::{context, Runner, State, TraceBuffer, TraceReader}; | 14 | use embassy_net_nrf91::{context, Runner, State, TraceBuffer, TraceReader}; |
| 15 | use embassy_nrf::buffered_uarte::{self, BufferedUarteTx}; | 15 | use embassy_nrf::buffered_uarte::{self, BufferedUarteTx}; |
| @@ -28,7 +28,7 @@ fn IPC() { | |||
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | bind_interrupts!(struct Irqs { | 30 | bind_interrupts!(struct Irqs { |
| 31 | UARTE0_SPIM0_SPIS0_TWIM0_TWIS0 => buffered_uarte::InterruptHandler<peripherals::SERIAL0>; | 31 | SERIAL0 => buffered_uarte::InterruptHandler<peripherals::SERIAL0>; |
| 32 | }); | 32 | }); |
| 33 | 33 | ||
| 34 | #[embassy_executor::task] | 34 | #[embassy_executor::task] |
| @@ -70,18 +70,16 @@ fn status_to_config(status: &Status) -> embassy_net::ConfigV4 { | |||
| 70 | let Some(IpAddr::V4(addr)) = status.ip else { | 70 | let Some(IpAddr::V4(addr)) = status.ip else { |
| 71 | panic!("Unexpected IP address"); | 71 | panic!("Unexpected IP address"); |
| 72 | }; | 72 | }; |
| 73 | let addr = Ipv4Address(addr.octets()); | ||
| 74 | 73 | ||
| 75 | let gateway = if let Some(IpAddr::V4(addr)) = status.gateway { | 74 | let gateway = match status.gateway { |
| 76 | Some(Ipv4Address(addr.octets())) | 75 | Some(IpAddr::V4(addr)) => Some(addr), |
| 77 | } else { | 76 | _ => None, |
| 78 | None | ||
| 79 | }; | 77 | }; |
| 80 | 78 | ||
| 81 | let mut dns_servers = Vec::new(); | 79 | let mut dns_servers = Vec::new(); |
| 82 | for dns in status.dns.iter() { | 80 | for dns in status.dns.iter() { |
| 83 | if let IpAddr::V4(ip) = dns { | 81 | if let IpAddr::V4(ip) = dns { |
| 84 | unwrap!(dns_servers.push(Ipv4Address(ip.octets()))); | 82 | unwrap!(dns_servers.push(*ip)); |
| 85 | } | 83 | } |
| 86 | } | 84 | } |
| 87 | 85 | ||
| @@ -163,6 +161,7 @@ async fn main(spawner: Spawner) { | |||
| 163 | apn: b"iot.nat.es", | 161 | apn: b"iot.nat.es", |
| 164 | auth_prot: context::AuthProt::Pap, | 162 | auth_prot: context::AuthProt::Pap, |
| 165 | auth: Some((b"orange", b"orange")), | 163 | auth: Some((b"orange", b"orange")), |
| 164 | pin: None, | ||
| 166 | }, | 165 | }, |
| 167 | stack | 166 | stack |
| 168 | ))); | 167 | ))); |
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 04b4c6317..ce812b2e0 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml | |||
| @@ -7,12 +7,12 @@ license = "MIT OR Apache-2.0" | |||
| 7 | 7 | ||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] } | 9 | embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 13 | embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] } | 13 | embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] } |
| 14 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } |
| 15 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] } | 15 | embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns", "proto-ipv4", "proto-ipv6", "multicast"] } |
| 16 | embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } | 16 | embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } |
| 17 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 17 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 18 | embassy-usb-logger = { version = "0.2.0", path = "../../embassy-usb-logger" } | 18 | embassy-usb-logger = { version = "0.2.0", path = "../../embassy-usb-logger" } |
| @@ -25,7 +25,7 @@ fixed = "1.23.1" | |||
| 25 | fixed-macro = "1.2" | 25 | fixed-macro = "1.2" |
| 26 | 26 | ||
| 27 | # for web request example | 27 | # for web request example |
| 28 | reqwless = { version = "0.12.0", features = ["defmt",]} | 28 | reqwless = { version = "0.13.0", features = ["defmt"] } |
| 29 | serde = { version = "1.0.203", default-features = false, features = ["derive"] } | 29 | serde = { version = "1.0.203", default-features = false, features = ["derive"] } |
| 30 | serde-json-core = "0.5.1" | 30 | serde-json-core = "0.5.1" |
| 31 | 31 | ||
| @@ -37,14 +37,15 @@ cortex-m = { version = "0.7.6", features = ["inline-asm"] } | |||
| 37 | cortex-m-rt = "0.7.0" | 37 | cortex-m-rt = "0.7.0" |
| 38 | critical-section = "1.1" | 38 | critical-section = "1.1" |
| 39 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 39 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 40 | display-interface-spi = "0.4.1" | 40 | display-interface-spi = "0.5.0" |
| 41 | embedded-graphics = "0.7.1" | 41 | embedded-graphics = "0.8.1" |
| 42 | st7789 = "0.6.1" | 42 | mipidsi = "0.8.0" |
| 43 | display-interface = "0.4.1" | 43 | display-interface = "0.5.0" |
| 44 | byte-slice-cast = { version = "1.2.0", default-features = false } | 44 | byte-slice-cast = { version = "1.2.0", default-features = false } |
| 45 | smart-leds = "0.3.0" | 45 | smart-leds = "0.4.0" |
| 46 | heapless = "0.8" | 46 | heapless = "0.8" |
| 47 | usbd-hid = "0.8.1" | 47 | usbd-hid = "0.8.1" |
| 48 | rand_core = "0.6.4" | ||
| 48 | 49 | ||
| 49 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | 50 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } |
| 50 | embedded-hal-async = "1.0" | 51 | embedded-hal-async = "1.0" |
diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 6c02630e0..164e6f8d3 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs | |||
| @@ -7,13 +7,11 @@ | |||
| 7 | use core::fmt::Write; | 7 | use core::fmt::Write; |
| 8 | 8 | ||
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_rp::dma::{AnyChannel, Channel}; | 10 | use embassy_rp::bind_interrupts; |
| 11 | use embassy_rp::peripherals::PIO0; | 11 | use embassy_rp::peripherals::PIO0; |
| 12 | use embassy_rp::pio::{ | 12 | use embassy_rp::pio::{InterruptHandler, Pio}; |
| 13 | Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, | 13 | use embassy_rp::pio_programs::hd44780::{PioHD44780, PioHD44780CommandSequenceProgram, PioHD44780CommandWordProgram}; |
| 14 | }; | ||
| 15 | use embassy_rp::pwm::{self, Pwm}; | 14 | use embassy_rp::pwm::{self, Pwm}; |
| 16 | use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef}; | ||
| 17 | use embassy_time::{Instant, Timer}; | 15 | use embassy_time::{Instant, Timer}; |
| 18 | use {defmt_rtt as _, panic_probe as _}; | 16 | use {defmt_rtt as _, panic_probe as _}; |
| 19 | 17 | ||
| @@ -43,8 +41,27 @@ async fn main(_spawner: Spawner) { | |||
| 43 | c | 41 | c |
| 44 | }); | 42 | }); |
| 45 | 43 | ||
| 46 | let mut hd = HD44780::new( | 44 | let Pio { |
| 47 | p.PIO0, Irqs, p.DMA_CH3, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, p.PIN_4, p.PIN_5, p.PIN_6, | 45 | mut common, sm0, irq0, .. |
| 46 | } = Pio::new(p.PIO0, Irqs); | ||
| 47 | |||
| 48 | let word_prg = PioHD44780CommandWordProgram::new(&mut common); | ||
| 49 | let seq_prg = PioHD44780CommandSequenceProgram::new(&mut common); | ||
| 50 | |||
| 51 | let mut hd = PioHD44780::new( | ||
| 52 | &mut common, | ||
| 53 | sm0, | ||
| 54 | irq0, | ||
| 55 | p.DMA_CH3, | ||
| 56 | p.PIN_0, | ||
| 57 | p.PIN_1, | ||
| 58 | p.PIN_2, | ||
| 59 | p.PIN_3, | ||
| 60 | p.PIN_4, | ||
| 61 | p.PIN_5, | ||
| 62 | p.PIN_6, | ||
| 63 | &word_prg, | ||
| 64 | &seq_prg, | ||
| 48 | ) | 65 | ) |
| 49 | .await; | 66 | .await; |
| 50 | 67 | ||
| @@ -68,173 +85,3 @@ async fn main(_spawner: Spawner) { | |||
| 68 | Timer::after_secs(1).await; | 85 | Timer::after_secs(1).await; |
| 69 | } | 86 | } |
| 70 | } | 87 | } |
| 71 | |||
| 72 | pub struct HD44780<'l> { | ||
| 73 | dma: PeripheralRef<'l, AnyChannel>, | ||
| 74 | sm: StateMachine<'l, PIO0, 0>, | ||
| 75 | |||
| 76 | buf: [u8; 40], | ||
| 77 | } | ||
| 78 | |||
| 79 | impl<'l> HD44780<'l> { | ||
| 80 | pub async fn new( | ||
| 81 | pio: impl Peripheral<P = PIO0> + 'l, | ||
| 82 | irq: Irqs, | ||
| 83 | dma: impl Peripheral<P = impl Channel> + 'l, | ||
| 84 | rs: impl PioPin, | ||
| 85 | rw: impl PioPin, | ||
| 86 | e: impl PioPin, | ||
| 87 | db4: impl PioPin, | ||
| 88 | db5: impl PioPin, | ||
| 89 | db6: impl PioPin, | ||
| 90 | db7: impl PioPin, | ||
| 91 | ) -> HD44780<'l> { | ||
| 92 | into_ref!(dma); | ||
| 93 | |||
| 94 | let Pio { | ||
| 95 | mut common, | ||
| 96 | mut irq0, | ||
| 97 | mut sm0, | ||
| 98 | .. | ||
| 99 | } = Pio::new(pio, irq); | ||
| 100 | |||
| 101 | // takes command words (<wait:24> <command:4> <0:4>) | ||
| 102 | let prg = pio_proc::pio_asm!( | ||
| 103 | r#" | ||
| 104 | .side_set 1 opt | ||
| 105 | .origin 20 | ||
| 106 | |||
| 107 | loop: | ||
| 108 | out x, 24 | ||
| 109 | delay: | ||
| 110 | jmp x--, delay | ||
| 111 | out pins, 4 side 1 | ||
| 112 | out null, 4 side 0 | ||
| 113 | jmp !osre, loop | ||
| 114 | irq 0 | ||
| 115 | "#, | ||
| 116 | ); | ||
| 117 | |||
| 118 | let rs = common.make_pio_pin(rs); | ||
| 119 | let rw = common.make_pio_pin(rw); | ||
| 120 | let e = common.make_pio_pin(e); | ||
| 121 | let db4 = common.make_pio_pin(db4); | ||
| 122 | let db5 = common.make_pio_pin(db5); | ||
| 123 | let db6 = common.make_pio_pin(db6); | ||
| 124 | let db7 = common.make_pio_pin(db7); | ||
| 125 | |||
| 126 | sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]); | ||
| 127 | |||
| 128 | let mut cfg = Config::default(); | ||
| 129 | cfg.use_program(&common.load_program(&prg.program), &[&e]); | ||
| 130 | cfg.clock_divider = 125u8.into(); | ||
| 131 | cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); | ||
| 132 | cfg.shift_out = ShiftConfig { | ||
| 133 | auto_fill: true, | ||
| 134 | direction: ShiftDirection::Left, | ||
| 135 | threshold: 32, | ||
| 136 | }; | ||
| 137 | cfg.fifo_join = FifoJoin::TxOnly; | ||
| 138 | sm0.set_config(&cfg); | ||
| 139 | |||
| 140 | sm0.set_enable(true); | ||
| 141 | // init to 8 bit thrice | ||
| 142 | sm0.tx().push((50000 << 8) | 0x30); | ||
| 143 | sm0.tx().push((5000 << 8) | 0x30); | ||
| 144 | sm0.tx().push((200 << 8) | 0x30); | ||
| 145 | // init 4 bit | ||
| 146 | sm0.tx().push((200 << 8) | 0x20); | ||
| 147 | // set font and lines | ||
| 148 | sm0.tx().push((50 << 8) | 0x20); | ||
| 149 | sm0.tx().push(0b1100_0000); | ||
| 150 | |||
| 151 | irq0.wait().await; | ||
| 152 | sm0.set_enable(false); | ||
| 153 | |||
| 154 | // takes command sequences (<rs:1> <count:7>, data...) | ||
| 155 | // many side sets are only there to free up a delay bit! | ||
| 156 | let prg = pio_proc::pio_asm!( | ||
| 157 | r#" | ||
| 158 | .origin 27 | ||
| 159 | .side_set 1 | ||
| 160 | |||
| 161 | .wrap_target | ||
| 162 | pull side 0 | ||
| 163 | out x 1 side 0 ; !rs | ||
| 164 | out y 7 side 0 ; #data - 1 | ||
| 165 | |||
| 166 | ; rs/rw to e: >= 60ns | ||
| 167 | ; e high time: >= 500ns | ||
| 168 | ; e low time: >= 500ns | ||
| 169 | ; read data valid after e falling: ~5ns | ||
| 170 | ; write data hold after e falling: ~10ns | ||
| 171 | |||
| 172 | loop: | ||
| 173 | pull side 0 | ||
| 174 | jmp !x data side 0 | ||
| 175 | command: | ||
| 176 | set pins 0b00 side 0 | ||
| 177 | jmp shift side 0 | ||
| 178 | data: | ||
| 179 | set pins 0b01 side 0 | ||
| 180 | shift: | ||
| 181 | out pins 4 side 1 [9] | ||
| 182 | nop side 0 [9] | ||
| 183 | out pins 4 side 1 [9] | ||
| 184 | mov osr null side 0 [7] | ||
| 185 | out pindirs 4 side 0 | ||
| 186 | set pins 0b10 side 0 | ||
| 187 | busy: | ||
| 188 | nop side 1 [9] | ||
| 189 | jmp pin more side 0 [9] | ||
| 190 | mov osr ~osr side 1 [9] | ||
| 191 | nop side 0 [4] | ||
| 192 | out pindirs 4 side 0 | ||
| 193 | jmp y-- loop side 0 | ||
| 194 | .wrap | ||
| 195 | more: | ||
| 196 | nop side 1 [9] | ||
| 197 | jmp busy side 0 [9] | ||
| 198 | "# | ||
| 199 | ); | ||
| 200 | |||
| 201 | let mut cfg = Config::default(); | ||
| 202 | cfg.use_program(&common.load_program(&prg.program), &[&e]); | ||
| 203 | cfg.clock_divider = 8u8.into(); // ~64ns/insn | ||
| 204 | cfg.set_jmp_pin(&db7); | ||
| 205 | cfg.set_set_pins(&[&rs, &rw]); | ||
| 206 | cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); | ||
| 207 | cfg.shift_out.direction = ShiftDirection::Left; | ||
| 208 | cfg.fifo_join = FifoJoin::TxOnly; | ||
| 209 | sm0.set_config(&cfg); | ||
| 210 | |||
| 211 | sm0.set_enable(true); | ||
| 212 | |||
| 213 | // display on and cursor on and blinking, reset display | ||
| 214 | sm0.tx().dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await; | ||
| 215 | |||
| 216 | Self { | ||
| 217 | dma: dma.map_into(), | ||
| 218 | sm: sm0, | ||
| 219 | buf: [0x20; 40], | ||
| 220 | } | ||
| 221 | } | ||
| 222 | |||
| 223 | pub async fn add_line(&mut self, s: &[u8]) { | ||
| 224 | // move cursor to 0:0, prepare 16 characters | ||
| 225 | self.buf[..3].copy_from_slice(&[0x80, 0x80, 15]); | ||
| 226 | // move line 2 up | ||
| 227 | self.buf.copy_within(22..38, 3); | ||
| 228 | // move cursor to 1:0, prepare 16 characters | ||
| 229 | self.buf[19..22].copy_from_slice(&[0x80, 0xc0, 15]); | ||
| 230 | // file line 2 with spaces | ||
| 231 | self.buf[22..38].fill(0x20); | ||
| 232 | // copy input line | ||
| 233 | let len = s.len().min(16); | ||
| 234 | self.buf[22..22 + len].copy_from_slice(&s[0..len]); | ||
| 235 | // set cursor to 1:15 | ||
| 236 | self.buf[38..].copy_from_slice(&[0x80, 0xcf]); | ||
| 237 | |||
| 238 | self.sm.tx().dma_push(self.dma.reborrow(), &self.buf).await; | ||
| 239 | } | ||
| 240 | } | ||
diff --git a/examples/rp/src/bin/pio_i2s.rs b/examples/rp/src/bin/pio_i2s.rs index cf60e5b30..447100ddf 100644 --- a/examples/rp/src/bin/pio_i2s.rs +++ b/examples/rp/src/bin/pio_i2s.rs | |||
| @@ -13,10 +13,10 @@ | |||
| 13 | use core::mem; | 13 | use core::mem; |
| 14 | 14 | ||
| 15 | use embassy_executor::Spawner; | 15 | use embassy_executor::Spawner; |
| 16 | use embassy_rp::bind_interrupts; | ||
| 16 | use embassy_rp::peripherals::PIO0; | 17 | use embassy_rp::peripherals::PIO0; |
| 17 | use embassy_rp::pio::{Config, FifoJoin, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; | 18 | use embassy_rp::pio::{InterruptHandler, Pio}; |
| 18 | use embassy_rp::{bind_interrupts, Peripheral}; | 19 | use embassy_rp::pio_programs::i2s::{PioI2sOut, PioI2sOutProgram}; |
| 19 | use fixed::traits::ToFixed; | ||
| 20 | use static_cell::StaticCell; | 20 | use static_cell::StaticCell; |
| 21 | use {defmt_rtt as _, panic_probe as _}; | 21 | use {defmt_rtt as _, panic_probe as _}; |
| 22 | 22 | ||
| @@ -25,61 +25,32 @@ bind_interrupts!(struct Irqs { | |||
| 25 | }); | 25 | }); |
| 26 | 26 | ||
| 27 | const SAMPLE_RATE: u32 = 48_000; | 27 | const SAMPLE_RATE: u32 = 48_000; |
| 28 | const BIT_DEPTH: u32 = 16; | ||
| 29 | const CHANNELS: u32 = 2; | ||
| 28 | 30 | ||
| 29 | #[embassy_executor::main] | 31 | #[embassy_executor::main] |
| 30 | async fn main(_spawner: Spawner) { | 32 | async fn main(_spawner: Spawner) { |
| 31 | let mut p = embassy_rp::init(Default::default()); | 33 | let mut p = embassy_rp::init(Default::default()); |
| 32 | 34 | ||
| 33 | // Setup pio state machine for i2s output | 35 | // Setup pio state machine for i2s output |
| 34 | let mut pio = Pio::new(p.PIO0, Irqs); | 36 | let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); |
| 35 | |||
| 36 | #[rustfmt::skip] | ||
| 37 | let pio_program = pio_proc::pio_asm!( | ||
| 38 | ".side_set 2", | ||
| 39 | " set x, 14 side 0b01", // side 0bWB - W = Word Clock, B = Bit Clock | ||
| 40 | "left_data:", | ||
| 41 | " out pins, 1 side 0b00", | ||
| 42 | " jmp x-- left_data side 0b01", | ||
| 43 | " out pins 1 side 0b10", | ||
| 44 | " set x, 14 side 0b11", | ||
| 45 | "right_data:", | ||
| 46 | " out pins 1 side 0b10", | ||
| 47 | " jmp x-- right_data side 0b11", | ||
| 48 | " out pins 1 side 0b00", | ||
| 49 | ); | ||
| 50 | 37 | ||
| 51 | let bit_clock_pin = p.PIN_18; | 38 | let bit_clock_pin = p.PIN_18; |
| 52 | let left_right_clock_pin = p.PIN_19; | 39 | let left_right_clock_pin = p.PIN_19; |
| 53 | let data_pin = p.PIN_20; | 40 | let data_pin = p.PIN_20; |
| 54 | 41 | ||
| 55 | let data_pin = pio.common.make_pio_pin(data_pin); | 42 | let program = PioI2sOutProgram::new(&mut common); |
| 56 | let bit_clock_pin = pio.common.make_pio_pin(bit_clock_pin); | 43 | let mut i2s = PioI2sOut::new( |
| 57 | let left_right_clock_pin = pio.common.make_pio_pin(left_right_clock_pin); | 44 | &mut common, |
| 58 | 45 | sm0, | |
| 59 | let cfg = { | 46 | p.DMA_CH0, |
| 60 | let mut cfg = Config::default(); | 47 | data_pin, |
| 61 | cfg.use_program( | 48 | bit_clock_pin, |
| 62 | &pio.common.load_program(&pio_program.program), | 49 | left_right_clock_pin, |
| 63 | &[&bit_clock_pin, &left_right_clock_pin], | 50 | SAMPLE_RATE, |
| 64 | ); | 51 | BIT_DEPTH, |
| 65 | cfg.set_out_pins(&[&data_pin]); | 52 | CHANNELS, |
| 66 | const BIT_DEPTH: u32 = 16; | 53 | &program, |
| 67 | const CHANNELS: u32 = 2; | ||
| 68 | let clock_frequency = SAMPLE_RATE * BIT_DEPTH * CHANNELS; | ||
| 69 | cfg.clock_divider = (125_000_000. / clock_frequency as f64 / 2.).to_fixed(); | ||
| 70 | cfg.shift_out = ShiftConfig { | ||
| 71 | threshold: 32, | ||
| 72 | direction: ShiftDirection::Left, | ||
| 73 | auto_fill: true, | ||
| 74 | }; | ||
| 75 | // join fifos to have twice the time to start the next dma transfer | ||
| 76 | cfg.fifo_join = FifoJoin::TxOnly; | ||
| 77 | cfg | ||
| 78 | }; | ||
| 79 | pio.sm0.set_config(&cfg); | ||
| 80 | pio.sm0.set_pin_dirs( | ||
| 81 | embassy_rp::pio::Direction::Out, | ||
| 82 | &[&data_pin, &left_right_clock_pin, &bit_clock_pin], | ||
| 83 | ); | 54 | ); |
| 84 | 55 | ||
| 85 | // create two audio buffers (back and front) which will take turns being | 56 | // create two audio buffers (back and front) which will take turns being |
| @@ -90,17 +61,13 @@ async fn main(_spawner: Spawner) { | |||
| 90 | let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE); | 61 | let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE); |
| 91 | 62 | ||
| 92 | // start pio state machine | 63 | // start pio state machine |
| 93 | pio.sm0.set_enable(true); | ||
| 94 | let tx = pio.sm0.tx(); | ||
| 95 | let mut dma_ref = p.DMA_CH0.into_ref(); | ||
| 96 | |||
| 97 | let mut fade_value: i32 = 0; | 64 | let mut fade_value: i32 = 0; |
| 98 | let mut phase: i32 = 0; | 65 | let mut phase: i32 = 0; |
| 99 | 66 | ||
| 100 | loop { | 67 | loop { |
| 101 | // trigger transfer of front buffer data to the pio fifo | 68 | // trigger transfer of front buffer data to the pio fifo |
| 102 | // but don't await the returned future, yet | 69 | // but don't await the returned future, yet |
| 103 | let dma_future = tx.dma_push(dma_ref.reborrow(), front_buffer); | 70 | let dma_future = i2s.write(front_buffer); |
| 104 | 71 | ||
| 105 | // fade in audio when bootsel is pressed | 72 | // fade in audio when bootsel is pressed |
| 106 | let fade_target = if p.BOOTSEL.is_pressed() { i32::MAX } else { 0 }; | 73 | let fade_target = if p.BOOTSEL.is_pressed() { i32::MAX } else { 0 }; |
diff --git a/examples/rp/src/bin/pio_onewire.rs b/examples/rp/src/bin/pio_onewire.rs index 5076101ec..991510851 100644 --- a/examples/rp/src/bin/pio_onewire.rs +++ b/examples/rp/src/bin/pio_onewire.rs | |||
| @@ -6,7 +6,8 @@ use defmt::*; | |||
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_rp::bind_interrupts; | 7 | use embassy_rp::bind_interrupts; |
| 8 | use embassy_rp::peripherals::PIO0; | 8 | use embassy_rp::peripherals::PIO0; |
| 9 | use embassy_rp::pio::{self, Common, Config, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; | 9 | use embassy_rp::pio::{self, InterruptHandler, Pio}; |
| 10 | use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram}; | ||
| 10 | use embassy_time::Timer; | 11 | use embassy_time::Timer; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 13 | ||
| @@ -18,7 +19,11 @@ bind_interrupts!(struct Irqs { | |||
| 18 | async fn main(_spawner: Spawner) { | 19 | async fn main(_spawner: Spawner) { |
| 19 | let p = embassy_rp::init(Default::default()); | 20 | let p = embassy_rp::init(Default::default()); |
| 20 | let mut pio = Pio::new(p.PIO0, Irqs); | 21 | let mut pio = Pio::new(p.PIO0, Irqs); |
| 21 | let mut sensor = Ds18b20::new(&mut pio.common, pio.sm0, p.PIN_2); | 22 | |
| 23 | let prg = PioOneWireProgram::new(&mut pio.common); | ||
| 24 | let onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg); | ||
| 25 | |||
| 26 | let mut sensor = Ds18b20::new(onewire); | ||
| 22 | 27 | ||
| 23 | loop { | 28 | loop { |
| 24 | sensor.start().await; // Start a new measurement | 29 | sensor.start().await; // Start a new measurement |
| @@ -33,89 +38,12 @@ async fn main(_spawner: Spawner) { | |||
| 33 | 38 | ||
| 34 | /// DS18B20 temperature sensor driver | 39 | /// DS18B20 temperature sensor driver |
| 35 | pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> { | 40 | pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> { |
| 36 | sm: StateMachine<'d, PIO, SM>, | 41 | wire: PioOneWire<'d, PIO, SM>, |
| 37 | } | 42 | } |
| 38 | 43 | ||
| 39 | impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> { | 44 | impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> { |
| 40 | /// Create a new instance the driver | 45 | pub fn new(wire: PioOneWire<'d, PIO, SM>) -> Self { |
| 41 | pub fn new(common: &mut Common<'d, PIO>, mut sm: StateMachine<'d, PIO, SM>, pin: impl PioPin) -> Self { | 46 | Self { wire } |
| 42 | let prg = pio_proc::pio_asm!( | ||
| 43 | r#" | ||
| 44 | .wrap_target | ||
| 45 | again: | ||
| 46 | pull block | ||
| 47 | mov x, osr | ||
| 48 | jmp !x, read | ||
| 49 | write: | ||
| 50 | set pindirs, 1 | ||
| 51 | set pins, 0 | ||
| 52 | loop1: | ||
| 53 | jmp x--,loop1 | ||
| 54 | set pindirs, 0 [31] | ||
| 55 | wait 1 pin 0 [31] | ||
| 56 | pull block | ||
| 57 | mov x, osr | ||
| 58 | bytes1: | ||
| 59 | pull block | ||
| 60 | set y, 7 | ||
| 61 | set pindirs, 1 | ||
| 62 | bit1: | ||
| 63 | set pins, 0 [1] | ||
| 64 | out pins,1 [31] | ||
| 65 | set pins, 1 [20] | ||
| 66 | jmp y--,bit1 | ||
| 67 | jmp x--,bytes1 | ||
| 68 | set pindirs, 0 [31] | ||
| 69 | jmp again | ||
| 70 | read: | ||
| 71 | pull block | ||
| 72 | mov x, osr | ||
| 73 | bytes2: | ||
| 74 | set y, 7 | ||
| 75 | bit2: | ||
| 76 | set pindirs, 1 | ||
| 77 | set pins, 0 [1] | ||
| 78 | set pindirs, 0 [5] | ||
| 79 | in pins,1 [10] | ||
| 80 | jmp y--,bit2 | ||
| 81 | jmp x--,bytes2 | ||
| 82 | .wrap | ||
| 83 | "#, | ||
| 84 | ); | ||
| 85 | |||
| 86 | let pin = common.make_pio_pin(pin); | ||
| 87 | let mut cfg = Config::default(); | ||
| 88 | cfg.use_program(&common.load_program(&prg.program), &[]); | ||
| 89 | cfg.set_out_pins(&[&pin]); | ||
| 90 | cfg.set_in_pins(&[&pin]); | ||
| 91 | cfg.set_set_pins(&[&pin]); | ||
| 92 | cfg.shift_in = ShiftConfig { | ||
| 93 | auto_fill: true, | ||
| 94 | direction: ShiftDirection::Right, | ||
| 95 | threshold: 8, | ||
| 96 | }; | ||
| 97 | cfg.clock_divider = 255_u8.into(); | ||
| 98 | sm.set_config(&cfg); | ||
| 99 | sm.set_enable(true); | ||
| 100 | Self { sm } | ||
| 101 | } | ||
| 102 | |||
| 103 | /// Write bytes over the wire | ||
| 104 | async fn write_bytes(&mut self, bytes: &[u8]) { | ||
| 105 | self.sm.tx().wait_push(250).await; | ||
| 106 | self.sm.tx().wait_push(bytes.len() as u32 - 1).await; | ||
| 107 | for b in bytes { | ||
| 108 | self.sm.tx().wait_push(*b as u32).await; | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | /// Read bytes from the wire | ||
| 113 | async fn read_bytes(&mut self, bytes: &mut [u8]) { | ||
| 114 | self.sm.tx().wait_push(0).await; | ||
| 115 | self.sm.tx().wait_push(bytes.len() as u32 - 1).await; | ||
| 116 | for b in bytes.iter_mut() { | ||
| 117 | *b = (self.sm.rx().wait_pull().await >> 24) as u8; | ||
| 118 | } | ||
| 119 | } | 47 | } |
| 120 | 48 | ||
| 121 | /// Calculate CRC8 of the data | 49 | /// Calculate CRC8 of the data |
| @@ -139,14 +67,14 @@ impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> { | |||
| 139 | 67 | ||
| 140 | /// Start a new measurement. Allow at least 1000ms before getting `temperature`. | 68 | /// Start a new measurement. Allow at least 1000ms before getting `temperature`. |
| 141 | pub async fn start(&mut self) { | 69 | pub async fn start(&mut self) { |
| 142 | self.write_bytes(&[0xCC, 0x44]).await; | 70 | self.wire.write_bytes(&[0xCC, 0x44]).await; |
| 143 | } | 71 | } |
| 144 | 72 | ||
| 145 | /// Read the temperature. Ensure >1000ms has passed since `start` before calling this. | 73 | /// Read the temperature. Ensure >1000ms has passed since `start` before calling this. |
| 146 | pub async fn temperature(&mut self) -> Result<f32, ()> { | 74 | pub async fn temperature(&mut self) -> Result<f32, ()> { |
| 147 | self.write_bytes(&[0xCC, 0xBE]).await; | 75 | self.wire.write_bytes(&[0xCC, 0xBE]).await; |
| 148 | let mut data = [0; 9]; | 76 | let mut data = [0; 9]; |
| 149 | self.read_bytes(&mut data).await; | 77 | self.wire.read_bytes(&mut data).await; |
| 150 | match Self::crc8(&data) == 0 { | 78 | match Self::crc8(&data) == 0 { |
| 151 | true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.), | 79 | true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.), |
| 152 | false => Err(()), | 80 | false => Err(()), |
diff --git a/examples/rp/src/bin/pio_pwm.rs b/examples/rp/src/bin/pio_pwm.rs index 23d63d435..7eabb2289 100644 --- a/examples/rp/src/bin/pio_pwm.rs +++ b/examples/rp/src/bin/pio_pwm.rs | |||
| @@ -5,12 +5,11 @@ | |||
| 5 | use core::time::Duration; | 5 | use core::time::Duration; |
| 6 | 6 | ||
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 8 | use embassy_rp::gpio::Level; | 8 | use embassy_rp::bind_interrupts; |
| 9 | use embassy_rp::peripherals::PIO0; | 9 | use embassy_rp::peripherals::PIO0; |
| 10 | use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine}; | 10 | use embassy_rp::pio::{InterruptHandler, Pio}; |
| 11 | use embassy_rp::{bind_interrupts, clocks}; | 11 | use embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram}; |
| 12 | use embassy_time::Timer; | 12 | use embassy_time::Timer; |
| 13 | use pio::InstructionOperands; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 15 | 14 | ||
| 16 | const REFRESH_INTERVAL: u64 = 20000; | 15 | const REFRESH_INTERVAL: u64 = 20000; |
| @@ -19,93 +18,14 @@ bind_interrupts!(struct Irqs { | |||
| 19 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | 18 | PIO0_IRQ_0 => InterruptHandler<PIO0>; |
| 20 | }); | 19 | }); |
| 21 | 20 | ||
| 22 | pub fn to_pio_cycles(duration: Duration) -> u32 { | ||
| 23 | (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow | ||
| 24 | } | ||
| 25 | |||
| 26 | pub struct PwmPio<'d, T: Instance, const SM: usize> { | ||
| 27 | sm: StateMachine<'d, T, SM>, | ||
| 28 | } | ||
| 29 | |||
| 30 | impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> { | ||
| 31 | pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self { | ||
| 32 | let prg = pio_proc::pio_asm!( | ||
| 33 | ".side_set 1 opt" | ||
| 34 | "pull noblock side 0" | ||
| 35 | "mov x, osr" | ||
| 36 | "mov y, isr" | ||
| 37 | "countloop:" | ||
| 38 | "jmp x!=y noset" | ||
| 39 | "jmp skip side 1" | ||
| 40 | "noset:" | ||
| 41 | "nop" | ||
| 42 | "skip:" | ||
| 43 | "jmp y-- countloop" | ||
| 44 | ); | ||
| 45 | |||
| 46 | pio.load_program(&prg.program); | ||
| 47 | let pin = pio.make_pio_pin(pin); | ||
| 48 | sm.set_pins(Level::High, &[&pin]); | ||
| 49 | sm.set_pin_dirs(Direction::Out, &[&pin]); | ||
| 50 | |||
| 51 | let mut cfg = Config::default(); | ||
| 52 | cfg.use_program(&pio.load_program(&prg.program), &[&pin]); | ||
| 53 | |||
| 54 | sm.set_config(&cfg); | ||
| 55 | |||
| 56 | Self { sm } | ||
| 57 | } | ||
| 58 | |||
| 59 | pub fn start(&mut self) { | ||
| 60 | self.sm.set_enable(true); | ||
| 61 | } | ||
| 62 | |||
| 63 | pub fn stop(&mut self) { | ||
| 64 | self.sm.set_enable(false); | ||
| 65 | } | ||
| 66 | |||
| 67 | pub fn set_period(&mut self, duration: Duration) { | ||
| 68 | let is_enabled = self.sm.is_enabled(); | ||
| 69 | while !self.sm.tx().empty() {} // Make sure that the queue is empty | ||
| 70 | self.sm.set_enable(false); | ||
| 71 | self.sm.tx().push(to_pio_cycles(duration)); | ||
| 72 | unsafe { | ||
| 73 | self.sm.exec_instr( | ||
| 74 | InstructionOperands::PULL { | ||
| 75 | if_empty: false, | ||
| 76 | block: false, | ||
| 77 | } | ||
| 78 | .encode(), | ||
| 79 | ); | ||
| 80 | self.sm.exec_instr( | ||
| 81 | InstructionOperands::OUT { | ||
| 82 | destination: ::pio::OutDestination::ISR, | ||
| 83 | bit_count: 32, | ||
| 84 | } | ||
| 85 | .encode(), | ||
| 86 | ); | ||
| 87 | }; | ||
| 88 | if is_enabled { | ||
| 89 | self.sm.set_enable(true) // Enable if previously enabled | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | pub fn set_level(&mut self, level: u32) { | ||
| 94 | self.sm.tx().push(level); | ||
| 95 | } | ||
| 96 | |||
| 97 | pub fn write(&mut self, duration: Duration) { | ||
| 98 | self.set_level(to_pio_cycles(duration)); | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | #[embassy_executor::main] | 21 | #[embassy_executor::main] |
| 103 | async fn main(_spawner: Spawner) { | 22 | async fn main(_spawner: Spawner) { |
| 104 | let p = embassy_rp::init(Default::default()); | 23 | let p = embassy_rp::init(Default::default()); |
| 105 | let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); | 24 | let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); |
| 106 | 25 | ||
| 107 | // Note that PIN_25 is the led pin on the Pico | 26 | // Note that PIN_25 is the led pin on the Pico |
| 108 | let mut pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_25); | 27 | let prg = PioPwmProgram::new(&mut common); |
| 28 | let mut pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_25, &prg); | ||
| 109 | pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL)); | 29 | pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL)); |
| 110 | pwm_pio.start(); | 30 | pwm_pio.start(); |
| 111 | 31 | ||
diff --git a/examples/rp/src/bin/pio_rotary_encoder.rs b/examples/rp/src/bin/pio_rotary_encoder.rs index 58bdadbc0..2750f61ae 100644 --- a/examples/rp/src/bin/pio_rotary_encoder.rs +++ b/examples/rp/src/bin/pio_rotary_encoder.rs | |||
| @@ -5,70 +5,30 @@ | |||
| 5 | 5 | ||
| 6 | use defmt::info; | 6 | use defmt::info; |
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 8 | use embassy_rp::gpio::Pull; | 8 | use embassy_rp::bind_interrupts; |
| 9 | use embassy_rp::peripherals::PIO0; | 9 | use embassy_rp::peripherals::PIO0; |
| 10 | use embassy_rp::{bind_interrupts, pio}; | 10 | use embassy_rp::pio::{InterruptHandler, Pio}; |
| 11 | use fixed::traits::ToFixed; | 11 | use embassy_rp::pio_programs::rotary_encoder::{Direction, PioEncoder, PioEncoderProgram}; |
| 12 | use pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftDirection, StateMachine}; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {defmt_rtt as _, panic_probe as _}; |
| 14 | 13 | ||
| 15 | bind_interrupts!(struct Irqs { | 14 | bind_interrupts!(struct Irqs { |
| 16 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | 15 | PIO0_IRQ_0 => InterruptHandler<PIO0>; |
| 17 | }); | 16 | }); |
| 18 | 17 | ||
| 19 | pub struct PioEncoder<'d, T: Instance, const SM: usize> { | 18 | #[embassy_executor::task] |
| 20 | sm: StateMachine<'d, T, SM>, | 19 | async fn encoder_0(mut encoder: PioEncoder<'static, PIO0, 0>) { |
| 21 | } | 20 | let mut count = 0; |
| 22 | 21 | loop { | |
| 23 | impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> { | 22 | info!("Count: {}", count); |
| 24 | pub fn new( | 23 | count += match encoder.read().await { |
| 25 | pio: &mut Common<'d, T>, | 24 | Direction::Clockwise => 1, |
| 26 | mut sm: StateMachine<'d, T, SM>, | 25 | Direction::CounterClockwise => -1, |
| 27 | pin_a: impl PioPin, | 26 | }; |
| 28 | pin_b: impl PioPin, | ||
| 29 | ) -> Self { | ||
| 30 | let mut pin_a = pio.make_pio_pin(pin_a); | ||
| 31 | let mut pin_b = pio.make_pio_pin(pin_b); | ||
| 32 | pin_a.set_pull(Pull::Up); | ||
| 33 | pin_b.set_pull(Pull::Up); | ||
| 34 | sm.set_pin_dirs(pio::Direction::In, &[&pin_a, &pin_b]); | ||
| 35 | |||
| 36 | let prg = pio_proc::pio_asm!("wait 1 pin 1", "wait 0 pin 1", "in pins, 2", "push",); | ||
| 37 | |||
| 38 | let mut cfg = Config::default(); | ||
| 39 | cfg.set_in_pins(&[&pin_a, &pin_b]); | ||
| 40 | cfg.fifo_join = FifoJoin::RxOnly; | ||
| 41 | cfg.shift_in.direction = ShiftDirection::Left; | ||
| 42 | cfg.clock_divider = 10_000.to_fixed(); | ||
| 43 | cfg.use_program(&pio.load_program(&prg.program), &[]); | ||
| 44 | sm.set_config(&cfg); | ||
| 45 | sm.set_enable(true); | ||
| 46 | Self { sm } | ||
| 47 | } | ||
| 48 | |||
| 49 | pub async fn read(&mut self) -> Direction { | ||
| 50 | loop { | ||
| 51 | match self.sm.rx().wait_pull().await { | ||
| 52 | 0 => return Direction::CounterClockwise, | ||
| 53 | 1 => return Direction::Clockwise, | ||
| 54 | _ => {} | ||
| 55 | } | ||
| 56 | } | ||
| 57 | } | 27 | } |
| 58 | } | 28 | } |
| 59 | 29 | ||
| 60 | pub enum Direction { | 30 | #[embassy_executor::task] |
| 61 | Clockwise, | 31 | async fn encoder_1(mut encoder: PioEncoder<'static, PIO0, 1>) { |
| 62 | CounterClockwise, | ||
| 63 | } | ||
| 64 | |||
| 65 | #[embassy_executor::main] | ||
| 66 | async fn main(_spawner: Spawner) { | ||
| 67 | let p = embassy_rp::init(Default::default()); | ||
| 68 | let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); | ||
| 69 | |||
| 70 | let mut encoder = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5); | ||
| 71 | |||
| 72 | let mut count = 0; | 32 | let mut count = 0; |
| 73 | loop { | 33 | loop { |
| 74 | info!("Count: {}", count); | 34 | info!("Count: {}", count); |
| @@ -78,3 +38,18 @@ async fn main(_spawner: Spawner) { | |||
| 78 | }; | 38 | }; |
| 79 | } | 39 | } |
| 80 | } | 40 | } |
| 41 | |||
| 42 | #[embassy_executor::main] | ||
| 43 | async fn main(spawner: Spawner) { | ||
| 44 | let p = embassy_rp::init(Default::default()); | ||
| 45 | let Pio { | ||
| 46 | mut common, sm0, sm1, .. | ||
| 47 | } = Pio::new(p.PIO0, Irqs); | ||
| 48 | |||
| 49 | let prg = PioEncoderProgram::new(&mut common); | ||
| 50 | let encoder0 = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5, &prg); | ||
| 51 | let encoder1 = PioEncoder::new(&mut common, sm1, p.PIN_6, p.PIN_7, &prg); | ||
| 52 | |||
| 53 | spawner.must_spawn(encoder_0(encoder0)); | ||
| 54 | spawner.must_spawn(encoder_1(encoder1)); | ||
| 55 | } | ||
diff --git a/examples/rp/src/bin/pio_servo.rs b/examples/rp/src/bin/pio_servo.rs index a79540479..c52ee7492 100644 --- a/examples/rp/src/bin/pio_servo.rs +++ b/examples/rp/src/bin/pio_servo.rs | |||
| @@ -5,12 +5,11 @@ | |||
| 5 | use core::time::Duration; | 5 | use core::time::Duration; |
| 6 | 6 | ||
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 8 | use embassy_rp::gpio::Level; | 8 | use embassy_rp::bind_interrupts; |
| 9 | use embassy_rp::peripherals::PIO0; | 9 | use embassy_rp::peripherals::PIO0; |
| 10 | use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine}; | 10 | use embassy_rp::pio::{Instance, InterruptHandler, Pio}; |
| 11 | use embassy_rp::{bind_interrupts, clocks}; | 11 | use embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram}; |
| 12 | use embassy_time::Timer; | 12 | use embassy_time::Timer; |
| 13 | use pio::InstructionOperands; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 15 | 14 | ||
| 16 | const DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo | 15 | const DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo |
| @@ -22,88 +21,8 @@ bind_interrupts!(struct Irqs { | |||
| 22 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | 21 | PIO0_IRQ_0 => InterruptHandler<PIO0>; |
| 23 | }); | 22 | }); |
| 24 | 23 | ||
| 25 | pub fn to_pio_cycles(duration: Duration) -> u32 { | ||
| 26 | (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow | ||
| 27 | } | ||
| 28 | |||
| 29 | pub struct PwmPio<'d, T: Instance, const SM: usize> { | ||
| 30 | sm: StateMachine<'d, T, SM>, | ||
| 31 | } | ||
| 32 | |||
| 33 | impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> { | ||
| 34 | pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self { | ||
| 35 | let prg = pio_proc::pio_asm!( | ||
| 36 | ".side_set 1 opt" | ||
| 37 | "pull noblock side 0" | ||
| 38 | "mov x, osr" | ||
| 39 | "mov y, isr" | ||
| 40 | "countloop:" | ||
| 41 | "jmp x!=y noset" | ||
| 42 | "jmp skip side 1" | ||
| 43 | "noset:" | ||
| 44 | "nop" | ||
| 45 | "skip:" | ||
| 46 | "jmp y-- countloop" | ||
| 47 | ); | ||
| 48 | |||
| 49 | pio.load_program(&prg.program); | ||
| 50 | let pin = pio.make_pio_pin(pin); | ||
| 51 | sm.set_pins(Level::High, &[&pin]); | ||
| 52 | sm.set_pin_dirs(Direction::Out, &[&pin]); | ||
| 53 | |||
| 54 | let mut cfg = Config::default(); | ||
| 55 | cfg.use_program(&pio.load_program(&prg.program), &[&pin]); | ||
| 56 | |||
| 57 | sm.set_config(&cfg); | ||
| 58 | |||
| 59 | Self { sm } | ||
| 60 | } | ||
| 61 | |||
| 62 | pub fn start(&mut self) { | ||
| 63 | self.sm.set_enable(true); | ||
| 64 | } | ||
| 65 | |||
| 66 | pub fn stop(&mut self) { | ||
| 67 | self.sm.set_enable(false); | ||
| 68 | } | ||
| 69 | |||
| 70 | pub fn set_period(&mut self, duration: Duration) { | ||
| 71 | let is_enabled = self.sm.is_enabled(); | ||
| 72 | while !self.sm.tx().empty() {} // Make sure that the queue is empty | ||
| 73 | self.sm.set_enable(false); | ||
| 74 | self.sm.tx().push(to_pio_cycles(duration)); | ||
| 75 | unsafe { | ||
| 76 | self.sm.exec_instr( | ||
| 77 | InstructionOperands::PULL { | ||
| 78 | if_empty: false, | ||
| 79 | block: false, | ||
| 80 | } | ||
| 81 | .encode(), | ||
| 82 | ); | ||
| 83 | self.sm.exec_instr( | ||
| 84 | InstructionOperands::OUT { | ||
| 85 | destination: ::pio::OutDestination::ISR, | ||
| 86 | bit_count: 32, | ||
| 87 | } | ||
| 88 | .encode(), | ||
| 89 | ); | ||
| 90 | }; | ||
| 91 | if is_enabled { | ||
| 92 | self.sm.set_enable(true) // Enable if previously enabled | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | pub fn set_level(&mut self, level: u32) { | ||
| 97 | self.sm.tx().push(level); | ||
| 98 | } | ||
| 99 | |||
| 100 | pub fn write(&mut self, duration: Duration) { | ||
| 101 | self.set_level(to_pio_cycles(duration)); | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | pub struct ServoBuilder<'d, T: Instance, const SM: usize> { | 24 | pub struct ServoBuilder<'d, T: Instance, const SM: usize> { |
| 106 | pwm: PwmPio<'d, T, SM>, | 25 | pwm: PioPwm<'d, T, SM>, |
| 107 | period: Duration, | 26 | period: Duration, |
| 108 | min_pulse_width: Duration, | 27 | min_pulse_width: Duration, |
| 109 | max_pulse_width: Duration, | 28 | max_pulse_width: Duration, |
| @@ -111,7 +30,7 @@ pub struct ServoBuilder<'d, T: Instance, const SM: usize> { | |||
| 111 | } | 30 | } |
| 112 | 31 | ||
| 113 | impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> { | 32 | impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> { |
| 114 | pub fn new(pwm: PwmPio<'d, T, SM>) -> Self { | 33 | pub fn new(pwm: PioPwm<'d, T, SM>) -> Self { |
| 115 | Self { | 34 | Self { |
| 116 | pwm, | 35 | pwm, |
| 117 | period: Duration::from_micros(REFRESH_INTERVAL), | 36 | period: Duration::from_micros(REFRESH_INTERVAL), |
| @@ -153,7 +72,7 @@ impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> { | |||
| 153 | } | 72 | } |
| 154 | 73 | ||
| 155 | pub struct Servo<'d, T: Instance, const SM: usize> { | 74 | pub struct Servo<'d, T: Instance, const SM: usize> { |
| 156 | pwm: PwmPio<'d, T, SM>, | 75 | pwm: PioPwm<'d, T, SM>, |
| 157 | min_pulse_width: Duration, | 76 | min_pulse_width: Duration, |
| 158 | max_pulse_width: Duration, | 77 | max_pulse_width: Duration, |
| 159 | max_degree_rotation: u64, | 78 | max_degree_rotation: u64, |
| @@ -190,7 +109,8 @@ async fn main(_spawner: Spawner) { | |||
| 190 | let p = embassy_rp::init(Default::default()); | 109 | let p = embassy_rp::init(Default::default()); |
| 191 | let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); | 110 | let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); |
| 192 | 111 | ||
| 193 | let pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_1); | 112 | let prg = PioPwmProgram::new(&mut common); |
| 113 | let pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_1, &prg); | ||
| 194 | let mut servo = ServoBuilder::new(pwm_pio) | 114 | let mut servo = ServoBuilder::new(pwm_pio) |
| 195 | .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo | 115 | .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo |
| 196 | .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment. | 116 | .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment. |
diff --git a/examples/rp/src/bin/pio_stepper.rs b/examples/rp/src/bin/pio_stepper.rs index 4952f4fbd..3862c248b 100644 --- a/examples/rp/src/bin/pio_stepper.rs +++ b/examples/rp/src/bin/pio_stepper.rs | |||
| @@ -3,143 +3,20 @@ | |||
| 3 | 3 | ||
| 4 | #![no_std] | 4 | #![no_std] |
| 5 | #![no_main] | 5 | #![no_main] |
| 6 | use core::mem::{self, MaybeUninit}; | ||
| 7 | 6 | ||
| 8 | use defmt::info; | 7 | use defmt::info; |
| 9 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 10 | use embassy_rp::bind_interrupts; | 9 | use embassy_rp::bind_interrupts; |
| 11 | use embassy_rp::peripherals::PIO0; | 10 | use embassy_rp::peripherals::PIO0; |
| 12 | use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Irq, Pio, PioPin, StateMachine}; | 11 | use embassy_rp::pio::{InterruptHandler, Pio}; |
| 12 | use embassy_rp::pio_programs::stepper::{PioStepper, PioStepperProgram}; | ||
| 13 | use embassy_time::{with_timeout, Duration, Timer}; | 13 | use embassy_time::{with_timeout, Duration, Timer}; |
| 14 | use fixed::traits::ToFixed; | ||
| 15 | use fixed::types::extra::U8; | ||
| 16 | use fixed::FixedU32; | ||
| 17 | use {defmt_rtt as _, panic_probe as _}; | 14 | use {defmt_rtt as _, panic_probe as _}; |
| 18 | 15 | ||
| 19 | bind_interrupts!(struct Irqs { | 16 | bind_interrupts!(struct Irqs { |
| 20 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | 17 | PIO0_IRQ_0 => InterruptHandler<PIO0>; |
| 21 | }); | 18 | }); |
| 22 | 19 | ||
| 23 | pub struct PioStepper<'d, T: Instance, const SM: usize> { | ||
| 24 | irq: Irq<'d, T, SM>, | ||
| 25 | sm: StateMachine<'d, T, SM>, | ||
| 26 | } | ||
| 27 | |||
| 28 | impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> { | ||
| 29 | pub fn new( | ||
| 30 | pio: &mut Common<'d, T>, | ||
| 31 | mut sm: StateMachine<'d, T, SM>, | ||
| 32 | irq: Irq<'d, T, SM>, | ||
| 33 | pin0: impl PioPin, | ||
| 34 | pin1: impl PioPin, | ||
| 35 | pin2: impl PioPin, | ||
| 36 | pin3: impl PioPin, | ||
| 37 | ) -> Self { | ||
| 38 | let prg = pio_proc::pio_asm!( | ||
| 39 | "pull block", | ||
| 40 | "mov x, osr", | ||
| 41 | "pull block", | ||
| 42 | "mov y, osr", | ||
| 43 | "jmp !x end", | ||
| 44 | "loop:", | ||
| 45 | "jmp !osre step", | ||
| 46 | "mov osr, y", | ||
| 47 | "step:", | ||
| 48 | "out pins, 4 [31]" | ||
| 49 | "jmp x-- loop", | ||
| 50 | "end:", | ||
| 51 | "irq 0 rel" | ||
| 52 | ); | ||
| 53 | let pin0 = pio.make_pio_pin(pin0); | ||
| 54 | let pin1 = pio.make_pio_pin(pin1); | ||
| 55 | let pin2 = pio.make_pio_pin(pin2); | ||
| 56 | let pin3 = pio.make_pio_pin(pin3); | ||
| 57 | sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]); | ||
| 58 | let mut cfg = Config::default(); | ||
| 59 | cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]); | ||
| 60 | cfg.clock_divider = (125_000_000 / (100 * 136)).to_fixed(); | ||
| 61 | cfg.use_program(&pio.load_program(&prg.program), &[]); | ||
| 62 | sm.set_config(&cfg); | ||
| 63 | sm.set_enable(true); | ||
| 64 | Self { irq, sm } | ||
| 65 | } | ||
| 66 | |||
| 67 | // Set pulse frequency | ||
| 68 | pub fn set_frequency(&mut self, freq: u32) { | ||
| 69 | let clock_divider: FixedU32<U8> = (125_000_000 / (freq * 136)).to_fixed(); | ||
| 70 | assert!(clock_divider <= 65536, "clkdiv must be <= 65536"); | ||
| 71 | assert!(clock_divider >= 1, "clkdiv must be >= 1"); | ||
| 72 | self.sm.set_clock_divider(clock_divider); | ||
| 73 | self.sm.clkdiv_restart(); | ||
| 74 | } | ||
| 75 | |||
| 76 | // Full step, one phase | ||
| 77 | pub async fn step(&mut self, steps: i32) { | ||
| 78 | if steps > 0 { | ||
| 79 | self.run(steps, 0b1000_0100_0010_0001_1000_0100_0010_0001).await | ||
| 80 | } else { | ||
| 81 | self.run(-steps, 0b0001_0010_0100_1000_0001_0010_0100_1000).await | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | // Full step, two phase | ||
| 86 | pub async fn step2(&mut self, steps: i32) { | ||
| 87 | if steps > 0 { | ||
| 88 | self.run(steps, 0b1001_1100_0110_0011_1001_1100_0110_0011).await | ||
| 89 | } else { | ||
| 90 | self.run(-steps, 0b0011_0110_1100_1001_0011_0110_1100_1001).await | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | // Half step | ||
| 95 | pub async fn step_half(&mut self, steps: i32) { | ||
| 96 | if steps > 0 { | ||
| 97 | self.run(steps, 0b1001_1000_1100_0100_0110_0010_0011_0001).await | ||
| 98 | } else { | ||
| 99 | self.run(-steps, 0b0001_0011_0010_0110_0100_1100_1000_1001).await | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | async fn run(&mut self, steps: i32, pattern: u32) { | ||
| 104 | self.sm.tx().wait_push(steps as u32).await; | ||
| 105 | self.sm.tx().wait_push(pattern).await; | ||
| 106 | let drop = OnDrop::new(|| { | ||
| 107 | self.sm.clear_fifos(); | ||
| 108 | unsafe { | ||
| 109 | self.sm.exec_instr( | ||
| 110 | pio::InstructionOperands::JMP { | ||
| 111 | address: 0, | ||
| 112 | condition: pio::JmpCondition::Always, | ||
| 113 | } | ||
| 114 | .encode(), | ||
| 115 | ); | ||
| 116 | } | ||
| 117 | }); | ||
| 118 | self.irq.wait().await; | ||
| 119 | drop.defuse(); | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | struct OnDrop<F: FnOnce()> { | ||
| 124 | f: MaybeUninit<F>, | ||
| 125 | } | ||
| 126 | |||
| 127 | impl<F: FnOnce()> OnDrop<F> { | ||
| 128 | pub fn new(f: F) -> Self { | ||
| 129 | Self { f: MaybeUninit::new(f) } | ||
| 130 | } | ||
| 131 | |||
| 132 | pub fn defuse(self) { | ||
| 133 | mem::forget(self) | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | impl<F: FnOnce()> Drop for OnDrop<F> { | ||
| 138 | fn drop(&mut self) { | ||
| 139 | unsafe { self.f.as_ptr().read()() } | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | #[embassy_executor::main] | 20 | #[embassy_executor::main] |
| 144 | async fn main(_spawner: Spawner) { | 21 | async fn main(_spawner: Spawner) { |
| 145 | let p = embassy_rp::init(Default::default()); | 22 | let p = embassy_rp::init(Default::default()); |
| @@ -147,14 +24,18 @@ async fn main(_spawner: Spawner) { | |||
| 147 | mut common, irq0, sm0, .. | 24 | mut common, irq0, sm0, .. |
| 148 | } = Pio::new(p.PIO0, Irqs); | 25 | } = Pio::new(p.PIO0, Irqs); |
| 149 | 26 | ||
| 150 | let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7); | 27 | let prg = PioStepperProgram::new(&mut common); |
| 28 | let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7, &prg); | ||
| 151 | stepper.set_frequency(120); | 29 | stepper.set_frequency(120); |
| 152 | loop { | 30 | loop { |
| 153 | info!("CW full steps"); | 31 | info!("CW full steps"); |
| 154 | stepper.step(1000).await; | 32 | stepper.step(1000).await; |
| 155 | 33 | ||
| 156 | info!("CCW full steps, drop after 1 sec"); | 34 | info!("CCW full steps, drop after 1 sec"); |
| 157 | if let Err(_) = with_timeout(Duration::from_secs(1), stepper.step(i32::MIN)).await { | 35 | if with_timeout(Duration::from_secs(1), stepper.step(-i32::MAX)) |
| 36 | .await | ||
| 37 | .is_err() | ||
| 38 | { | ||
| 158 | info!("Time's up!"); | 39 | info!("Time's up!"); |
| 159 | Timer::after(Duration::from_secs(1)).await; | 40 | Timer::after(Duration::from_secs(1)).await; |
| 160 | } | 41 | } |
diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs index 53b696309..aaf2a524f 100644 --- a/examples/rp/src/bin/pio_uart.rs +++ b/examples/rp/src/bin/pio_uart.rs | |||
| @@ -13,10 +13,10 @@ | |||
| 13 | use defmt::{info, panic, trace}; | 13 | use defmt::{info, panic, trace}; |
| 14 | use embassy_executor::Spawner; | 14 | use embassy_executor::Spawner; |
| 15 | use embassy_futures::join::{join, join3}; | 15 | use embassy_futures::join::{join, join3}; |
| 16 | use embassy_rp::bind_interrupts; | ||
| 17 | use embassy_rp::peripherals::{PIO0, USB}; | 16 | use embassy_rp::peripherals::{PIO0, USB}; |
| 18 | use embassy_rp::pio::InterruptHandler as PioInterruptHandler; | 17 | use embassy_rp::pio_programs::uart::{PioUartRx, PioUartRxProgram, PioUartTx, PioUartTxProgram}; |
| 19 | use embassy_rp::usb::{Driver, Instance, InterruptHandler}; | 18 | use embassy_rp::usb::{Driver, Instance, InterruptHandler}; |
| 19 | use embassy_rp::{bind_interrupts, pio}; | ||
| 20 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | 20 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; |
| 21 | use embassy_sync::pipe::Pipe; | 21 | use embassy_sync::pipe::Pipe; |
| 22 | use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State}; | 22 | use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State}; |
| @@ -25,13 +25,11 @@ use embassy_usb::{Builder, Config}; | |||
| 25 | use embedded_io_async::{Read, Write}; | 25 | use embedded_io_async::{Read, Write}; |
| 26 | use {defmt_rtt as _, panic_probe as _}; | 26 | use {defmt_rtt as _, panic_probe as _}; |
| 27 | 27 | ||
| 28 | use crate::uart::PioUart; | 28 | //use crate::uart::PioUart; |
| 29 | use crate::uart_rx::PioUartRx; | ||
| 30 | use crate::uart_tx::PioUartTx; | ||
| 31 | 29 | ||
| 32 | bind_interrupts!(struct Irqs { | 30 | bind_interrupts!(struct Irqs { |
| 33 | USBCTRL_IRQ => InterruptHandler<USB>; | 31 | USBCTRL_IRQ => InterruptHandler<USB>; |
| 34 | PIO0_IRQ_0 => PioInterruptHandler<PIO0>; | 32 | PIO0_IRQ_0 => pio::InterruptHandler<PIO0>; |
| 35 | }); | 33 | }); |
| 36 | 34 | ||
| 37 | #[embassy_executor::main] | 35 | #[embassy_executor::main] |
| @@ -85,8 +83,15 @@ async fn main(_spawner: Spawner) { | |||
| 85 | let usb_fut = usb.run(); | 83 | let usb_fut = usb.run(); |
| 86 | 84 | ||
| 87 | // PIO UART setup | 85 | // PIO UART setup |
| 88 | let uart = PioUart::new(9600, p.PIO0, p.PIN_4, p.PIN_5); | 86 | let pio::Pio { |
| 89 | let (mut uart_tx, mut uart_rx) = uart.split(); | 87 | mut common, sm0, sm1, .. |
| 88 | } = pio::Pio::new(p.PIO0, Irqs); | ||
| 89 | |||
| 90 | let tx_program = PioUartTxProgram::new(&mut common); | ||
| 91 | let mut uart_tx = PioUartTx::new(9600, &mut common, sm0, p.PIN_4, &tx_program); | ||
| 92 | |||
| 93 | let rx_program = PioUartRxProgram::new(&mut common); | ||
| 94 | let mut uart_rx = PioUartRx::new(9600, &mut common, sm1, p.PIN_5, &rx_program); | ||
| 90 | 95 | ||
| 91 | // Pipe setup | 96 | // Pipe setup |
| 92 | let mut usb_pipe: Pipe<NoopRawMutex, 20> = Pipe::new(); | 97 | let mut usb_pipe: Pipe<NoopRawMutex, 20> = Pipe::new(); |
| @@ -163,8 +168,8 @@ async fn usb_write<'d, T: Instance + 'd>( | |||
| 163 | } | 168 | } |
| 164 | 169 | ||
| 165 | /// Read from the UART and write it to the USB TX pipe | 170 | /// Read from the UART and write it to the USB TX pipe |
| 166 | async fn uart_read( | 171 | async fn uart_read<PIO: pio::Instance, const SM: usize>( |
| 167 | uart_rx: &mut PioUartRx<'_>, | 172 | uart_rx: &mut PioUartRx<'_, PIO, SM>, |
| 168 | usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>, | 173 | usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>, |
| 169 | ) -> ! { | 174 | ) -> ! { |
| 170 | let mut buf = [0; 64]; | 175 | let mut buf = [0; 64]; |
| @@ -180,8 +185,8 @@ async fn uart_read( | |||
| 180 | } | 185 | } |
| 181 | 186 | ||
| 182 | /// Read from the UART TX pipe and write it to the UART | 187 | /// Read from the UART TX pipe and write it to the UART |
| 183 | async fn uart_write( | 188 | async fn uart_write<PIO: pio::Instance, const SM: usize>( |
| 184 | uart_tx: &mut PioUartTx<'_>, | 189 | uart_tx: &mut PioUartTx<'_, PIO, SM>, |
| 185 | uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>, | 190 | uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>, |
| 186 | ) -> ! { | 191 | ) -> ! { |
| 187 | let mut buf = [0; 64]; | 192 | let mut buf = [0; 64]; |
| @@ -192,197 +197,3 @@ async fn uart_write( | |||
| 192 | let _ = uart_tx.write(&data).await; | 197 | let _ = uart_tx.write(&data).await; |
| 193 | } | 198 | } |
| 194 | } | 199 | } |
| 195 | |||
| 196 | mod uart { | ||
| 197 | use embassy_rp::peripherals::PIO0; | ||
| 198 | use embassy_rp::pio::{Pio, PioPin}; | ||
| 199 | use embassy_rp::Peripheral; | ||
| 200 | |||
| 201 | use crate::uart_rx::PioUartRx; | ||
| 202 | use crate::uart_tx::PioUartTx; | ||
| 203 | use crate::Irqs; | ||
| 204 | |||
| 205 | pub struct PioUart<'a> { | ||
| 206 | tx: PioUartTx<'a>, | ||
| 207 | rx: PioUartRx<'a>, | ||
| 208 | } | ||
| 209 | |||
| 210 | impl<'a> PioUart<'a> { | ||
| 211 | pub fn new( | ||
| 212 | baud: u64, | ||
| 213 | pio: impl Peripheral<P = PIO0> + 'a, | ||
| 214 | tx_pin: impl PioPin, | ||
| 215 | rx_pin: impl PioPin, | ||
| 216 | ) -> PioUart<'a> { | ||
| 217 | let Pio { | ||
| 218 | mut common, sm0, sm1, .. | ||
| 219 | } = Pio::new(pio, Irqs); | ||
| 220 | |||
| 221 | let tx = PioUartTx::new(&mut common, sm0, tx_pin, baud); | ||
| 222 | let rx = PioUartRx::new(&mut common, sm1, rx_pin, baud); | ||
| 223 | |||
| 224 | PioUart { tx, rx } | ||
| 225 | } | ||
| 226 | |||
| 227 | pub fn split(self) -> (PioUartTx<'a>, PioUartRx<'a>) { | ||
| 228 | (self.tx, self.rx) | ||
| 229 | } | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | mod uart_tx { | ||
| 234 | use core::convert::Infallible; | ||
| 235 | |||
| 236 | use embassy_rp::gpio::Level; | ||
| 237 | use embassy_rp::peripherals::PIO0; | ||
| 238 | use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; | ||
| 239 | use embedded_io_async::{ErrorType, Write}; | ||
| 240 | use fixed::traits::ToFixed; | ||
| 241 | use fixed_macro::types::U56F8; | ||
| 242 | |||
| 243 | pub struct PioUartTx<'a> { | ||
| 244 | sm_tx: StateMachine<'a, PIO0, 0>, | ||
| 245 | } | ||
| 246 | |||
| 247 | impl<'a> PioUartTx<'a> { | ||
| 248 | pub fn new( | ||
| 249 | common: &mut Common<'a, PIO0>, | ||
| 250 | mut sm_tx: StateMachine<'a, PIO0, 0>, | ||
| 251 | tx_pin: impl PioPin, | ||
| 252 | baud: u64, | ||
| 253 | ) -> Self { | ||
| 254 | let prg = pio_proc::pio_asm!( | ||
| 255 | r#" | ||
| 256 | .side_set 1 opt | ||
| 257 | |||
| 258 | ; An 8n1 UART transmit program. | ||
| 259 | ; OUT pin 0 and side-set pin 0 are both mapped to UART TX pin. | ||
| 260 | |||
| 261 | pull side 1 [7] ; Assert stop bit, or stall with line in idle state | ||
| 262 | set x, 7 side 0 [7] ; Preload bit counter, assert start bit for 8 clocks | ||
| 263 | bitloop: ; This loop will run 8 times (8n1 UART) | ||
| 264 | out pins, 1 ; Shift 1 bit from OSR to the first OUT pin | ||
| 265 | jmp x-- bitloop [6] ; Each loop iteration is 8 cycles. | ||
| 266 | "# | ||
| 267 | ); | ||
| 268 | let tx_pin = common.make_pio_pin(tx_pin); | ||
| 269 | sm_tx.set_pins(Level::High, &[&tx_pin]); | ||
| 270 | sm_tx.set_pin_dirs(Direction::Out, &[&tx_pin]); | ||
| 271 | |||
| 272 | let mut cfg = Config::default(); | ||
| 273 | |||
| 274 | cfg.set_out_pins(&[&tx_pin]); | ||
| 275 | cfg.use_program(&common.load_program(&prg.program), &[&tx_pin]); | ||
| 276 | cfg.shift_out.auto_fill = false; | ||
| 277 | cfg.shift_out.direction = ShiftDirection::Right; | ||
| 278 | cfg.fifo_join = FifoJoin::TxOnly; | ||
| 279 | cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); | ||
| 280 | sm_tx.set_config(&cfg); | ||
| 281 | sm_tx.set_enable(true); | ||
| 282 | |||
| 283 | Self { sm_tx } | ||
| 284 | } | ||
| 285 | |||
| 286 | pub async fn write_u8(&mut self, data: u8) { | ||
| 287 | self.sm_tx.tx().wait_push(data as u32).await; | ||
| 288 | } | ||
| 289 | } | ||
| 290 | |||
| 291 | impl ErrorType for PioUartTx<'_> { | ||
| 292 | type Error = Infallible; | ||
| 293 | } | ||
| 294 | |||
| 295 | impl Write for PioUartTx<'_> { | ||
| 296 | async fn write(&mut self, buf: &[u8]) -> Result<usize, Infallible> { | ||
| 297 | for byte in buf { | ||
| 298 | self.write_u8(*byte).await; | ||
| 299 | } | ||
| 300 | Ok(buf.len()) | ||
| 301 | } | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | mod uart_rx { | ||
| 306 | use core::convert::Infallible; | ||
| 307 | |||
| 308 | use embassy_rp::gpio::Level; | ||
| 309 | use embassy_rp::peripherals::PIO0; | ||
| 310 | use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; | ||
| 311 | use embedded_io_async::{ErrorType, Read}; | ||
| 312 | use fixed::traits::ToFixed; | ||
| 313 | use fixed_macro::types::U56F8; | ||
| 314 | |||
| 315 | pub struct PioUartRx<'a> { | ||
| 316 | sm_rx: StateMachine<'a, PIO0, 1>, | ||
| 317 | } | ||
| 318 | |||
| 319 | impl<'a> PioUartRx<'a> { | ||
| 320 | pub fn new( | ||
| 321 | common: &mut Common<'a, PIO0>, | ||
| 322 | mut sm_rx: StateMachine<'a, PIO0, 1>, | ||
| 323 | rx_pin: impl PioPin, | ||
| 324 | baud: u64, | ||
| 325 | ) -> Self { | ||
| 326 | let prg = pio_proc::pio_asm!( | ||
| 327 | r#" | ||
| 328 | ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and | ||
| 329 | ; break conditions more gracefully. | ||
| 330 | ; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX. | ||
| 331 | |||
| 332 | start: | ||
| 333 | wait 0 pin 0 ; Stall until start bit is asserted | ||
| 334 | set x, 7 [10] ; Preload bit counter, then delay until halfway through | ||
| 335 | rx_bitloop: ; the first data bit (12 cycles incl wait, set). | ||
| 336 | in pins, 1 ; Shift data bit into ISR | ||
| 337 | jmp x-- rx_bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles | ||
| 338 | jmp pin good_rx_stop ; Check stop bit (should be high) | ||
| 339 | |||
| 340 | irq 4 rel ; Either a framing error or a break. Set a sticky flag, | ||
| 341 | wait 1 pin 0 ; and wait for line to return to idle state. | ||
| 342 | jmp start ; Don't push data if we didn't see good framing. | ||
| 343 | |||
| 344 | good_rx_stop: ; No delay before returning to start; a little slack is | ||
| 345 | in null 24 | ||
| 346 | push ; important in case the TX clock is slightly too fast. | ||
| 347 | "# | ||
| 348 | ); | ||
| 349 | let mut cfg = Config::default(); | ||
| 350 | cfg.use_program(&common.load_program(&prg.program), &[]); | ||
| 351 | |||
| 352 | let rx_pin = common.make_pio_pin(rx_pin); | ||
| 353 | sm_rx.set_pins(Level::High, &[&rx_pin]); | ||
| 354 | cfg.set_in_pins(&[&rx_pin]); | ||
| 355 | cfg.set_jmp_pin(&rx_pin); | ||
| 356 | sm_rx.set_pin_dirs(Direction::In, &[&rx_pin]); | ||
| 357 | |||
| 358 | cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); | ||
| 359 | cfg.shift_in.auto_fill = false; | ||
| 360 | cfg.shift_in.direction = ShiftDirection::Right; | ||
| 361 | cfg.shift_in.threshold = 32; | ||
| 362 | cfg.fifo_join = FifoJoin::RxOnly; | ||
| 363 | sm_rx.set_config(&cfg); | ||
| 364 | sm_rx.set_enable(true); | ||
| 365 | |||
| 366 | Self { sm_rx } | ||
| 367 | } | ||
| 368 | |||
| 369 | pub async fn read_u8(&mut self) -> u8 { | ||
| 370 | self.sm_rx.rx().wait_pull().await as u8 | ||
| 371 | } | ||
| 372 | } | ||
| 373 | |||
| 374 | impl ErrorType for PioUartRx<'_> { | ||
| 375 | type Error = Infallible; | ||
| 376 | } | ||
| 377 | |||
| 378 | impl Read for PioUartRx<'_> { | ||
| 379 | async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Infallible> { | ||
| 380 | let mut i = 0; | ||
| 381 | while i < buf.len() { | ||
| 382 | buf[i] = self.read_u8().await; | ||
| 383 | i += 1; | ||
| 384 | } | ||
| 385 | Ok(i) | ||
| 386 | } | ||
| 387 | } | ||
| 388 | } | ||
diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs index ac145933c..d1fcfc471 100644 --- a/examples/rp/src/bin/pio_ws2812.rs +++ b/examples/rp/src/bin/pio_ws2812.rs | |||
| @@ -6,15 +6,11 @@ | |||
| 6 | 6 | ||
| 7 | use defmt::*; | 7 | use defmt::*; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_rp::dma::{AnyChannel, Channel}; | 9 | use embassy_rp::bind_interrupts; |
| 10 | use embassy_rp::peripherals::PIO0; | 10 | use embassy_rp::peripherals::PIO0; |
| 11 | use embassy_rp::pio::{ | 11 | use embassy_rp::pio::{InterruptHandler, Pio}; |
| 12 | Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, | 12 | use embassy_rp::pio_programs::ws2812::{PioWs2812, PioWs2812Program}; |
| 13 | }; | 13 | use embassy_time::{Duration, Ticker}; |
| 14 | use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef}; | ||
| 15 | use embassy_time::{Duration, Ticker, Timer}; | ||
| 16 | use fixed::types::U24F8; | ||
| 17 | use fixed_macro::fixed; | ||
| 18 | use smart_leds::RGB8; | 14 | use smart_leds::RGB8; |
| 19 | use {defmt_rtt as _, panic_probe as _}; | 15 | use {defmt_rtt as _, panic_probe as _}; |
| 20 | 16 | ||
| @@ -22,96 +18,6 @@ bind_interrupts!(struct Irqs { | |||
| 22 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | 18 | PIO0_IRQ_0 => InterruptHandler<PIO0>; |
| 23 | }); | 19 | }); |
| 24 | 20 | ||
| 25 | pub struct Ws2812<'d, P: Instance, const S: usize, const N: usize> { | ||
| 26 | dma: PeripheralRef<'d, AnyChannel>, | ||
| 27 | sm: StateMachine<'d, P, S>, | ||
| 28 | } | ||
| 29 | |||
| 30 | impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> { | ||
| 31 | pub fn new( | ||
| 32 | pio: &mut Common<'d, P>, | ||
| 33 | mut sm: StateMachine<'d, P, S>, | ||
| 34 | dma: impl Peripheral<P = impl Channel> + 'd, | ||
| 35 | pin: impl PioPin, | ||
| 36 | ) -> Self { | ||
| 37 | into_ref!(dma); | ||
| 38 | |||
| 39 | // Setup sm0 | ||
| 40 | |||
| 41 | // prepare the PIO program | ||
| 42 | let side_set = pio::SideSet::new(false, 1, false); | ||
| 43 | let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set); | ||
| 44 | |||
| 45 | const T1: u8 = 2; // start bit | ||
| 46 | const T2: u8 = 5; // data bit | ||
| 47 | const T3: u8 = 3; // stop bit | ||
| 48 | const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32; | ||
| 49 | |||
| 50 | let mut wrap_target = a.label(); | ||
| 51 | let mut wrap_source = a.label(); | ||
| 52 | let mut do_zero = a.label(); | ||
| 53 | a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0); | ||
| 54 | a.bind(&mut wrap_target); | ||
| 55 | // Do stop bit | ||
| 56 | a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0); | ||
| 57 | // Do start bit | ||
| 58 | a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1); | ||
| 59 | // Do data bit = 1 | ||
| 60 | a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1); | ||
| 61 | a.bind(&mut do_zero); | ||
| 62 | // Do data bit = 0 | ||
| 63 | a.nop_with_delay_and_side_set(T2 - 1, 0); | ||
| 64 | a.bind(&mut wrap_source); | ||
| 65 | |||
| 66 | let prg = a.assemble_with_wrap(wrap_source, wrap_target); | ||
| 67 | let mut cfg = Config::default(); | ||
| 68 | |||
| 69 | // Pin config | ||
| 70 | let out_pin = pio.make_pio_pin(pin); | ||
| 71 | cfg.set_out_pins(&[&out_pin]); | ||
| 72 | cfg.set_set_pins(&[&out_pin]); | ||
| 73 | |||
| 74 | cfg.use_program(&pio.load_program(&prg), &[&out_pin]); | ||
| 75 | |||
| 76 | // Clock config, measured in kHz to avoid overflows | ||
| 77 | // TODO CLOCK_FREQ should come from embassy_rp | ||
| 78 | let clock_freq = U24F8::from_num(clocks::clk_sys_freq() / 1000); | ||
| 79 | let ws2812_freq = fixed!(800: U24F8); | ||
| 80 | let bit_freq = ws2812_freq * CYCLES_PER_BIT; | ||
| 81 | cfg.clock_divider = clock_freq / bit_freq; | ||
| 82 | |||
| 83 | // FIFO config | ||
| 84 | cfg.fifo_join = FifoJoin::TxOnly; | ||
| 85 | cfg.shift_out = ShiftConfig { | ||
| 86 | auto_fill: true, | ||
| 87 | threshold: 24, | ||
| 88 | direction: ShiftDirection::Left, | ||
| 89 | }; | ||
| 90 | |||
| 91 | sm.set_config(&cfg); | ||
| 92 | sm.set_enable(true); | ||
| 93 | |||
| 94 | Self { | ||
| 95 | dma: dma.map_into(), | ||
| 96 | sm, | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | pub async fn write(&mut self, colors: &[RGB8; N]) { | ||
| 101 | // Precompute the word bytes from the colors | ||
| 102 | let mut words = [0u32; N]; | ||
| 103 | for i in 0..N { | ||
| 104 | let word = (u32::from(colors[i].g) << 24) | (u32::from(colors[i].r) << 16) | (u32::from(colors[i].b) << 8); | ||
| 105 | words[i] = word; | ||
| 106 | } | ||
| 107 | |||
| 108 | // DMA transfer | ||
| 109 | self.sm.tx().dma_push(self.dma.reborrow(), &words).await; | ||
| 110 | |||
| 111 | Timer::after_micros(55).await; | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | /// Input a value 0 to 255 to get a color value | 21 | /// Input a value 0 to 255 to get a color value |
| 116 | /// The colours are a transition r - g - b - back to r. | 22 | /// The colours are a transition r - g - b - back to r. |
| 117 | fn wheel(mut wheel_pos: u8) -> RGB8 { | 23 | fn wheel(mut wheel_pos: u8) -> RGB8 { |
| @@ -142,7 +48,8 @@ async fn main(_spawner: Spawner) { | |||
| 142 | // Common neopixel pins: | 48 | // Common neopixel pins: |
| 143 | // Thing plus: 8 | 49 | // Thing plus: 8 |
| 144 | // Adafruit Feather: 16; Adafruit Feather+RFM95: 4 | 50 | // Adafruit Feather: 16; Adafruit Feather+RFM95: 4 |
| 145 | let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16); | 51 | let program = PioWs2812Program::new(&mut common); |
| 52 | let mut ws2812 = PioWs2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16, &program); | ||
| 146 | 53 | ||
| 147 | // Loop forever making RGB values and pushing them out to the WS2812. | 54 | // Loop forever making RGB values and pushing them out to the WS2812. |
| 148 | let mut ticker = Ticker::every(Duration::from_millis(10)); | 55 | let mut ticker = Ticker::every(Duration::from_millis(10)); |
diff --git a/examples/rp/src/bin/pwm.rs b/examples/rp/src/bin/pwm.rs index 26e233260..2f5f94870 100644 --- a/examples/rp/src/bin/pwm.rs +++ b/examples/rp/src/bin/pwm.rs | |||
| @@ -1,24 +1,36 @@ | |||
| 1 | //! This example shows how to use PWM (Pulse Width Modulation) in the RP2040 chip. | 1 | //! This example shows how to use PWM (Pulse Width Modulation) in the RP2040 chip. |
| 2 | //! | 2 | //! |
| 3 | //! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin. | 3 | //! We demonstrate two ways of using PWM: |
| 4 | //! 1. Via config | ||
| 5 | //! 2. Via setting a duty cycle | ||
| 4 | 6 | ||
| 5 | #![no_std] | 7 | #![no_std] |
| 6 | #![no_main] | 8 | #![no_main] |
| 7 | 9 | ||
| 8 | use defmt::*; | 10 | use defmt::*; |
| 9 | use embassy_executor::Spawner; | 11 | use embassy_executor::Spawner; |
| 10 | use embassy_rp::pwm::{Config, Pwm}; | 12 | use embassy_rp::peripherals::{PIN_25, PIN_4, PWM_SLICE2, PWM_SLICE4}; |
| 13 | use embassy_rp::pwm::{Config, Pwm, SetDutyCycle}; | ||
| 11 | use embassy_time::Timer; | 14 | use embassy_time::Timer; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 15 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 16 | ||
| 14 | #[embassy_executor::main] | 17 | #[embassy_executor::main] |
| 15 | async fn main(_spawner: Spawner) { | 18 | async fn main(spawner: Spawner) { |
| 16 | let p = embassy_rp::init(Default::default()); | 19 | let p = embassy_rp::init(Default::default()); |
| 20 | spawner.spawn(pwm_set_config(p.PWM_SLICE4, p.PIN_25)).unwrap(); | ||
| 21 | spawner.spawn(pwm_set_dutycycle(p.PWM_SLICE2, p.PIN_4)).unwrap(); | ||
| 22 | } | ||
| 17 | 23 | ||
| 18 | let mut c: Config = Default::default(); | 24 | /// Demonstrate PWM by modifying & applying the config |
| 19 | c.top = 0x8000; | 25 | /// |
| 26 | /// Using the onboard led, if You are using a different Board than plain Pico2 (i.e. W variant) | ||
| 27 | /// you must use another slice & pin and an appropriate resistor. | ||
| 28 | #[embassy_executor::task] | ||
| 29 | async fn pwm_set_config(slice4: PWM_SLICE4, pin25: PIN_25) { | ||
| 30 | let mut c = Config::default(); | ||
| 31 | c.top = 32_768; | ||
| 20 | c.compare_b = 8; | 32 | c.compare_b = 8; |
| 21 | let mut pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, c.clone()); | 33 | let mut pwm = Pwm::new_output_b(slice4, pin25, c.clone()); |
| 22 | 34 | ||
| 23 | loop { | 35 | loop { |
| 24 | info!("current LED duty cycle: {}/32768", c.compare_b); | 36 | info!("current LED duty cycle: {}/32768", c.compare_b); |
| @@ -27,3 +39,41 @@ async fn main(_spawner: Spawner) { | |||
| 27 | pwm.set_config(&c); | 39 | pwm.set_config(&c); |
| 28 | } | 40 | } |
| 29 | } | 41 | } |
| 42 | |||
| 43 | /// Demonstrate PWM by setting duty cycle | ||
| 44 | /// | ||
| 45 | /// Using GP4 in Slice2, make sure to use an appropriate resistor. | ||
| 46 | #[embassy_executor::task] | ||
| 47 | async fn pwm_set_dutycycle(slice2: PWM_SLICE2, pin4: PIN_4) { | ||
| 48 | // If we aim for a specific frequency, here is how we can calculate the top value. | ||
| 49 | // The top value sets the period of the PWM cycle, so a counter goes from 0 to top and then wraps around to 0. | ||
| 50 | // Every such wraparound is one PWM cycle. So here is how we get 25KHz: | ||
| 51 | let desired_freq_hz = 25_000; | ||
| 52 | let clock_freq_hz = embassy_rp::clocks::clk_sys_freq(); | ||
| 53 | let divider = 16u8; | ||
| 54 | let period = (clock_freq_hz / (desired_freq_hz * divider as u32)) as u16 - 1; | ||
| 55 | |||
| 56 | let mut c = Config::default(); | ||
| 57 | c.top = period; | ||
| 58 | c.divider = divider.into(); | ||
| 59 | |||
| 60 | let mut pwm = Pwm::new_output_a(slice2, pin4, c.clone()); | ||
| 61 | |||
| 62 | loop { | ||
| 63 | // 100% duty cycle, fully on | ||
| 64 | pwm.set_duty_cycle_fully_on().unwrap(); | ||
| 65 | Timer::after_secs(1).await; | ||
| 66 | |||
| 67 | // 66% duty cycle. Expressed as simple percentage. | ||
| 68 | pwm.set_duty_cycle_percent(66).unwrap(); | ||
| 69 | Timer::after_secs(1).await; | ||
| 70 | |||
| 71 | // 25% duty cycle. Expressed as 32768/4 = 8192. | ||
| 72 | pwm.set_duty_cycle(c.top / 4).unwrap(); | ||
| 73 | Timer::after_secs(1).await; | ||
| 74 | |||
| 75 | // 0% duty cycle, fully off. | ||
| 76 | pwm.set_duty_cycle_fully_off().unwrap(); | ||
| 77 | Timer::after_secs(1).await; | ||
| 78 | } | ||
| 79 | } | ||
diff --git a/examples/rp/src/bin/spi_display.rs b/examples/rp/src/bin/spi_display.rs index e937b9d0a..dd114a4ae 100644 --- a/examples/rp/src/bin/spi_display.rs +++ b/examples/rp/src/bin/spi_display.rs | |||
| @@ -9,11 +9,12 @@ | |||
| 9 | use core::cell::RefCell; | 9 | use core::cell::RefCell; |
| 10 | 10 | ||
| 11 | use defmt::*; | 11 | use defmt::*; |
| 12 | use display_interface_spi::SPIInterface; | ||
| 12 | use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig; | 13 | use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig; |
| 13 | use embassy_executor::Spawner; | 14 | use embassy_executor::Spawner; |
| 14 | use embassy_rp::gpio::{Level, Output}; | 15 | use embassy_rp::gpio::{Level, Output}; |
| 15 | use embassy_rp::spi; | 16 | use embassy_rp::spi; |
| 16 | use embassy_rp::spi::{Blocking, Spi}; | 17 | use embassy_rp::spi::Spi; |
| 17 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | 18 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; |
| 18 | use embassy_sync::blocking_mutex::Mutex; | 19 | use embassy_sync::blocking_mutex::Mutex; |
| 19 | use embassy_time::Delay; | 20 | use embassy_time::Delay; |
| @@ -24,10 +25,11 @@ use embedded_graphics::pixelcolor::Rgb565; | |||
| 24 | use embedded_graphics::prelude::*; | 25 | use embedded_graphics::prelude::*; |
| 25 | use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle}; | 26 | use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle}; |
| 26 | use embedded_graphics::text::Text; | 27 | use embedded_graphics::text::Text; |
| 27 | use st7789::{Orientation, ST7789}; | 28 | use mipidsi::models::ST7789; |
| 29 | use mipidsi::options::{Orientation, Rotation}; | ||
| 30 | use mipidsi::Builder; | ||
| 28 | use {defmt_rtt as _, panic_probe as _}; | 31 | use {defmt_rtt as _, panic_probe as _}; |
| 29 | 32 | ||
| 30 | use crate::my_display_interface::SPIDeviceInterface; | ||
| 31 | use crate::touch::Touch; | 33 | use crate::touch::Touch; |
| 32 | 34 | ||
| 33 | const DISPLAY_FREQ: u32 = 64_000_000; | 35 | const DISPLAY_FREQ: u32 = 64_000_000; |
| @@ -58,7 +60,7 @@ async fn main(_spawner: Spawner) { | |||
| 58 | touch_config.phase = spi::Phase::CaptureOnSecondTransition; | 60 | touch_config.phase = spi::Phase::CaptureOnSecondTransition; |
| 59 | touch_config.polarity = spi::Polarity::IdleHigh; | 61 | touch_config.polarity = spi::Polarity::IdleHigh; |
| 60 | 62 | ||
| 61 | let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone()); | 63 | let spi = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone()); |
| 62 | let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi)); | 64 | let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi)); |
| 63 | 65 | ||
| 64 | let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config); | 66 | let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config); |
| @@ -74,17 +76,15 @@ async fn main(_spawner: Spawner) { | |||
| 74 | let _bl = Output::new(bl, Level::High); | 76 | let _bl = Output::new(bl, Level::High); |
| 75 | 77 | ||
| 76 | // display interface abstraction from SPI and DC | 78 | // display interface abstraction from SPI and DC |
| 77 | let di = SPIDeviceInterface::new(display_spi, dcx); | 79 | let di = SPIInterface::new(display_spi, dcx); |
| 78 | 80 | ||
| 79 | // create driver | 81 | // Define the display from the display interface and initialize it |
| 80 | let mut display = ST7789::new(di, rst, 240, 320); | 82 | let mut display = Builder::new(ST7789, di) |
| 81 | 83 | .display_size(240, 320) | |
| 82 | // initialize | 84 | .reset_pin(rst) |
| 83 | display.init(&mut Delay).unwrap(); | 85 | .orientation(Orientation::new().rotate(Rotation::Deg90)) |
| 84 | 86 | .init(&mut Delay) | |
| 85 | // set default orientation | 87 | .unwrap(); |
| 86 | display.set_orientation(Orientation::Landscape).unwrap(); | ||
| 87 | |||
| 88 | display.clear(Rgb565::BLACK).unwrap(); | 88 | display.clear(Rgb565::BLACK).unwrap(); |
| 89 | 89 | ||
| 90 | let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), 86); | 90 | let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), 86); |
| @@ -175,138 +175,3 @@ mod touch { | |||
| 175 | } | 175 | } |
| 176 | } | 176 | } |
| 177 | } | 177 | } |
| 178 | |||
| 179 | mod my_display_interface { | ||
| 180 | use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand}; | ||
| 181 | use embedded_hal_1::digital::OutputPin; | ||
| 182 | use embedded_hal_1::spi::SpiDevice; | ||
| 183 | |||
| 184 | /// SPI display interface. | ||
| 185 | /// | ||
| 186 | /// This combines the SPI peripheral and a data/command pin | ||
| 187 | pub struct SPIDeviceInterface<SPI, DC> { | ||
| 188 | spi: SPI, | ||
| 189 | dc: DC, | ||
| 190 | } | ||
| 191 | |||
| 192 | impl<SPI, DC> SPIDeviceInterface<SPI, DC> | ||
| 193 | where | ||
| 194 | SPI: SpiDevice, | ||
| 195 | DC: OutputPin, | ||
| 196 | { | ||
| 197 | /// Create new SPI interface for communciation with a display driver | ||
| 198 | pub fn new(spi: SPI, dc: DC) -> Self { | ||
| 199 | Self { spi, dc } | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | impl<SPI, DC> WriteOnlyDataCommand for SPIDeviceInterface<SPI, DC> | ||
| 204 | where | ||
| 205 | SPI: SpiDevice, | ||
| 206 | DC: OutputPin, | ||
| 207 | { | ||
| 208 | fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> { | ||
| 209 | // 1 = data, 0 = command | ||
| 210 | self.dc.set_low().map_err(|_| DisplayError::DCError)?; | ||
| 211 | |||
| 212 | send_u8(&mut self.spi, cmds).map_err(|_| DisplayError::BusWriteError)?; | ||
| 213 | Ok(()) | ||
| 214 | } | ||
| 215 | |||
| 216 | fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> { | ||
| 217 | // 1 = data, 0 = command | ||
| 218 | self.dc.set_high().map_err(|_| DisplayError::DCError)?; | ||
| 219 | |||
| 220 | send_u8(&mut self.spi, buf).map_err(|_| DisplayError::BusWriteError)?; | ||
| 221 | Ok(()) | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | fn send_u8<T: SpiDevice>(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> { | ||
| 226 | match words { | ||
| 227 | DataFormat::U8(slice) => spi.write(slice), | ||
| 228 | DataFormat::U16(slice) => { | ||
| 229 | use byte_slice_cast::*; | ||
| 230 | spi.write(slice.as_byte_slice()) | ||
| 231 | } | ||
| 232 | DataFormat::U16LE(slice) => { | ||
| 233 | use byte_slice_cast::*; | ||
| 234 | for v in slice.as_mut() { | ||
| 235 | *v = v.to_le(); | ||
| 236 | } | ||
| 237 | spi.write(slice.as_byte_slice()) | ||
| 238 | } | ||
| 239 | DataFormat::U16BE(slice) => { | ||
| 240 | use byte_slice_cast::*; | ||
| 241 | for v in slice.as_mut() { | ||
| 242 | *v = v.to_be(); | ||
| 243 | } | ||
| 244 | spi.write(slice.as_byte_slice()) | ||
| 245 | } | ||
| 246 | DataFormat::U8Iter(iter) => { | ||
| 247 | let mut buf = [0; 32]; | ||
| 248 | let mut i = 0; | ||
| 249 | |||
| 250 | for v in iter.into_iter() { | ||
| 251 | buf[i] = v; | ||
| 252 | i += 1; | ||
| 253 | |||
| 254 | if i == buf.len() { | ||
| 255 | spi.write(&buf)?; | ||
| 256 | i = 0; | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | if i > 0 { | ||
| 261 | spi.write(&buf[..i])?; | ||
| 262 | } | ||
| 263 | |||
| 264 | Ok(()) | ||
| 265 | } | ||
| 266 | DataFormat::U16LEIter(iter) => { | ||
| 267 | use byte_slice_cast::*; | ||
| 268 | let mut buf = [0; 32]; | ||
| 269 | let mut i = 0; | ||
| 270 | |||
| 271 | for v in iter.map(u16::to_le) { | ||
| 272 | buf[i] = v; | ||
| 273 | i += 1; | ||
| 274 | |||
| 275 | if i == buf.len() { | ||
| 276 | spi.write(&buf.as_byte_slice())?; | ||
| 277 | i = 0; | ||
| 278 | } | ||
| 279 | } | ||
| 280 | |||
| 281 | if i > 0 { | ||
| 282 | spi.write(&buf[..i].as_byte_slice())?; | ||
| 283 | } | ||
| 284 | |||
| 285 | Ok(()) | ||
| 286 | } | ||
| 287 | DataFormat::U16BEIter(iter) => { | ||
| 288 | use byte_slice_cast::*; | ||
| 289 | let mut buf = [0; 64]; | ||
| 290 | let mut i = 0; | ||
| 291 | let len = buf.len(); | ||
| 292 | |||
| 293 | for v in iter.map(u16::to_be) { | ||
| 294 | buf[i] = v; | ||
| 295 | i += 1; | ||
| 296 | |||
| 297 | if i == len { | ||
| 298 | spi.write(&buf.as_byte_slice())?; | ||
| 299 | i = 0; | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | if i > 0 { | ||
| 304 | spi.write(&buf[..i].as_byte_slice())?; | ||
| 305 | } | ||
| 306 | |||
| 307 | Ok(()) | ||
| 308 | } | ||
| 309 | _ => unimplemented!(), | ||
| 310 | } | ||
| 311 | } | ||
| 312 | } | ||
diff --git a/examples/rp/src/bin/spi_gc9a01.rs b/examples/rp/src/bin/spi_gc9a01.rs new file mode 100644 index 000000000..30afc253d --- /dev/null +++ b/examples/rp/src/bin/spi_gc9a01.rs | |||
| @@ -0,0 +1,126 @@ | |||
| 1 | //! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip. | ||
| 2 | //! | ||
| 3 | //! Example written for a display using the GC9A01 chip. Possibly the Waveshare RP2040-LCD-1.28 | ||
| 4 | //! (https://www.waveshare.com/wiki/RP2040-LCD-1.28) | ||
| 5 | |||
| 6 | #![no_std] | ||
| 7 | #![no_main] | ||
| 8 | |||
| 9 | use core::cell::RefCell; | ||
| 10 | |||
| 11 | use defmt::*; | ||
| 12 | use display_interface_spi::SPIInterface; | ||
| 13 | use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig; | ||
| 14 | use embassy_executor::Spawner; | ||
| 15 | use embassy_rp::clocks::RoscRng; | ||
| 16 | use embassy_rp::gpio::{Level, Output}; | ||
| 17 | use embassy_rp::spi; | ||
| 18 | use embassy_rp::spi::{Blocking, Spi}; | ||
| 19 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
| 20 | use embassy_sync::blocking_mutex::Mutex; | ||
| 21 | use embassy_time::{Delay, Duration, Timer}; | ||
| 22 | use embedded_graphics::image::{Image, ImageRawLE}; | ||
| 23 | use embedded_graphics::pixelcolor::Rgb565; | ||
| 24 | use embedded_graphics::prelude::*; | ||
| 25 | use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle}; | ||
| 26 | use mipidsi::models::GC9A01; | ||
| 27 | use mipidsi::options::{ColorInversion, ColorOrder}; | ||
| 28 | use mipidsi::Builder; | ||
| 29 | use rand_core::RngCore; | ||
| 30 | use {defmt_rtt as _, panic_probe as _}; | ||
| 31 | |||
| 32 | const DISPLAY_FREQ: u32 = 64_000_000; | ||
| 33 | const LCD_X_RES: i32 = 240; | ||
| 34 | const LCD_Y_RES: i32 = 240; | ||
| 35 | const FERRIS_WIDTH: u32 = 86; | ||
| 36 | const FERRIS_HEIGHT: u32 = 64; | ||
| 37 | |||
| 38 | #[embassy_executor::main] | ||
| 39 | async fn main(_spawner: Spawner) { | ||
| 40 | let p = embassy_rp::init(Default::default()); | ||
| 41 | let mut rng = RoscRng; | ||
| 42 | |||
| 43 | info!("Hello World!"); | ||
| 44 | |||
| 45 | let bl = p.PIN_25; | ||
| 46 | let rst = p.PIN_12; | ||
| 47 | let display_cs = p.PIN_9; | ||
| 48 | let dcx = p.PIN_8; | ||
| 49 | let mosi = p.PIN_11; | ||
| 50 | let clk = p.PIN_10; | ||
| 51 | |||
| 52 | // create SPI | ||
| 53 | let mut display_config = spi::Config::default(); | ||
| 54 | display_config.frequency = DISPLAY_FREQ; | ||
| 55 | display_config.phase = spi::Phase::CaptureOnSecondTransition; | ||
| 56 | display_config.polarity = spi::Polarity::IdleHigh; | ||
| 57 | |||
| 58 | let spi: Spi<'_, _, Blocking> = Spi::new_blocking_txonly(p.SPI1, clk, mosi, display_config.clone()); | ||
| 59 | let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi)); | ||
| 60 | |||
| 61 | let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config); | ||
| 62 | let dcx = Output::new(dcx, Level::Low); | ||
| 63 | let rst = Output::new(rst, Level::Low); | ||
| 64 | // dcx: 0 = command, 1 = data | ||
| 65 | |||
| 66 | // Enable LCD backlight | ||
| 67 | let _bl = Output::new(bl, Level::High); | ||
| 68 | |||
| 69 | // display interface abstraction from SPI and DC | ||
| 70 | let di = SPIInterface::new(display_spi, dcx); | ||
| 71 | |||
| 72 | // Define the display from the display interface and initialize it | ||
| 73 | let mut display = Builder::new(GC9A01, di) | ||
| 74 | .display_size(240, 240) | ||
| 75 | .reset_pin(rst) | ||
| 76 | .color_order(ColorOrder::Bgr) | ||
| 77 | .invert_colors(ColorInversion::Inverted) | ||
| 78 | .init(&mut Delay) | ||
| 79 | .unwrap(); | ||
| 80 | display.clear(Rgb565::BLACK).unwrap(); | ||
| 81 | |||
| 82 | let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), FERRIS_WIDTH); | ||
| 83 | let mut ferris = Image::new(&raw_image_data, Point::zero()); | ||
| 84 | |||
| 85 | let r = rng.next_u32(); | ||
| 86 | let mut delta = Point { | ||
| 87 | x: ((r % 10) + 5) as i32, | ||
| 88 | y: (((r >> 8) % 10) + 5) as i32, | ||
| 89 | }; | ||
| 90 | loop { | ||
| 91 | // Move Ferris | ||
| 92 | let bb = ferris.bounding_box(); | ||
| 93 | let tl = bb.top_left; | ||
| 94 | let br = bb.bottom_right().unwrap(); | ||
| 95 | if tl.x < 0 || br.x > LCD_X_RES { | ||
| 96 | delta.x = -delta.x; | ||
| 97 | } | ||
| 98 | if tl.y < 0 || br.y > LCD_Y_RES { | ||
| 99 | delta.y = -delta.y; | ||
| 100 | } | ||
| 101 | |||
| 102 | // Erase ghosting | ||
| 103 | let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::BLACK).build(); | ||
| 104 | let mut off = Point { x: 0, y: 0 }; | ||
| 105 | if delta.x < 0 { | ||
| 106 | off.x = FERRIS_WIDTH as i32; | ||
| 107 | } | ||
| 108 | Rectangle::new(tl + off, Size::new(delta.x as u32, FERRIS_HEIGHT)) | ||
| 109 | .into_styled(style) | ||
| 110 | .draw(&mut display) | ||
| 111 | .unwrap(); | ||
| 112 | off = Point { x: 0, y: 0 }; | ||
| 113 | if delta.y < 0 { | ||
| 114 | off.y = FERRIS_HEIGHT as i32; | ||
| 115 | } | ||
| 116 | Rectangle::new(tl + off, Size::new(FERRIS_WIDTH, delta.y as u32)) | ||
| 117 | .into_styled(style) | ||
| 118 | .draw(&mut display) | ||
| 119 | .unwrap(); | ||
| 120 | // Translate Ferris | ||
| 121 | ferris.translate_mut(delta); | ||
| 122 | // Display the image | ||
| 123 | ferris.draw(&mut display).unwrap(); | ||
| 124 | Timer::after(Duration::from_millis(50)).await; | ||
| 125 | } | ||
| 126 | } | ||
diff --git a/examples/rp/src/bin/spi_sdmmc.rs b/examples/rp/src/bin/spi_sdmmc.rs index 4cbc82f7b..a60850d0f 100644 --- a/examples/rp/src/bin/spi_sdmmc.rs +++ b/examples/rp/src/bin/spi_sdmmc.rs | |||
| @@ -7,7 +7,6 @@ | |||
| 7 | #![no_main] | 7 | #![no_main] |
| 8 | 8 | ||
| 9 | use defmt::*; | 9 | use defmt::*; |
| 10 | use embassy_embedded_hal::SetConfig; | ||
| 11 | use embassy_executor::Spawner; | 10 | use embassy_executor::Spawner; |
| 12 | use embassy_rp::spi::Spi; | 11 | use embassy_rp::spi::Spi; |
| 13 | use embassy_rp::{gpio, spi}; | 12 | use embassy_rp::{gpio, spi}; |
| @@ -51,7 +50,7 @@ async fn main(_spawner: Spawner) { | |||
| 51 | // Now that the card is initialized, the SPI clock can go faster | 50 | // Now that the card is initialized, the SPI clock can go faster |
| 52 | let mut config = spi::Config::default(); | 51 | let mut config = spi::Config::default(); |
| 53 | config.frequency = 16_000_000; | 52 | config.frequency = 16_000_000; |
| 54 | sdcard.spi(|dev| dev.bus_mut().set_config(&config)).ok(); | 53 | sdcard.spi(|dev| dev.bus_mut().set_config(&config)); |
| 55 | 54 | ||
| 56 | // Now let's look for volumes (also known as partitions) on our block device. | 55 | // Now let's look for volumes (also known as partitions) on our block device. |
| 57 | // To do this we need a Volume Manager. It will take ownership of the block device. | 56 | // To do this we need a Volume Manager. It will take ownership of the block device. |
diff --git a/examples/rp/src/bin/usb_serial_with_handler.rs b/examples/rp/src/bin/usb_serial_with_handler.rs new file mode 100644 index 000000000..a9e65be70 --- /dev/null +++ b/examples/rp/src/bin/usb_serial_with_handler.rs | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | //! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip. | ||
| 2 | //! | ||
| 3 | //! This creates the possibility to send log::info/warn/error/debug! to USB serial port. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | |||
| 8 | use core::str; | ||
| 9 | |||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_rp::bind_interrupts; | ||
| 12 | use embassy_rp::peripherals::USB; | ||
| 13 | use embassy_rp::rom_data::reset_to_usb_boot; | ||
| 14 | use embassy_rp::usb::{Driver, InterruptHandler}; | ||
| 15 | use embassy_time::Timer; | ||
| 16 | use embassy_usb_logger::ReceiverHandler; | ||
| 17 | use {defmt_rtt as _, panic_probe as _}; | ||
| 18 | |||
| 19 | bind_interrupts!(struct Irqs { | ||
| 20 | USBCTRL_IRQ => InterruptHandler<USB>; | ||
| 21 | }); | ||
| 22 | |||
| 23 | struct Handler; | ||
| 24 | |||
| 25 | impl ReceiverHandler for Handler { | ||
| 26 | async fn handle_data(&self, data: &[u8]) { | ||
| 27 | if let Ok(data) = str::from_utf8(data) { | ||
| 28 | let data = data.trim(); | ||
| 29 | |||
| 30 | // If you are using elf2uf2-term with the '-t' flag, then when closing the serial monitor, | ||
| 31 | // this will automatically put the pico into boot mode | ||
| 32 | if data == "q" || data == "elf2uf2-term" { | ||
| 33 | reset_to_usb_boot(0, 0); // Restart the chip | ||
| 34 | } else if data.eq_ignore_ascii_case("hello") { | ||
| 35 | log::info!("World!"); | ||
| 36 | } else { | ||
| 37 | log::info!("Recieved: {:?}", data); | ||
| 38 | } | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | fn new() -> Self { | ||
| 43 | Self | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | #[embassy_executor::task] | ||
| 48 | async fn logger_task(driver: Driver<'static, USB>) { | ||
| 49 | embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver, Handler); | ||
| 50 | } | ||
| 51 | |||
| 52 | #[embassy_executor::main] | ||
| 53 | async fn main(spawner: Spawner) { | ||
| 54 | let p = embassy_rp::init(Default::default()); | ||
| 55 | let driver = Driver::new(p.USB, Irqs); | ||
| 56 | spawner.spawn(logger_task(driver)).unwrap(); | ||
| 57 | |||
| 58 | let mut counter = 0; | ||
| 59 | loop { | ||
| 60 | counter += 1; | ||
| 61 | log::info!("Tick {}", counter); | ||
| 62 | Timer::after_secs(1).await; | ||
| 63 | } | ||
| 64 | } | ||
diff --git a/examples/rp/src/bin/wifi_blinky.rs b/examples/rp/src/bin/wifi_blinky.rs index 04a61bbd5..0107a2326 100644 --- a/examples/rp/src/bin/wifi_blinky.rs +++ b/examples/rp/src/bin/wifi_blinky.rs | |||
| @@ -33,8 +33,8 @@ async fn main(spawner: Spawner) { | |||
| 33 | 33 | ||
| 34 | // To make flashing faster for development, you may want to flash the firmwares independently | 34 | // To make flashing faster for development, you may want to flash the firmwares independently |
| 35 | // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: | 35 | // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: |
| 36 | // probe-rs download 43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000 | 36 | // probe-rs download ../../cyw43-firmware/43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000 |
| 37 | // probe-rs download 43439A0_clm.bin --binary-format bin --chip RP2040 --base-address 0x10140000 | 37 | // probe-rs download ../../cyw43-firmware/43439A0_clm.bin --binary-format bin --chip RP2040 --base-address 0x10140000 |
| 38 | //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) }; | 38 | //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) }; |
| 39 | //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; | 39 | //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; |
| 40 | 40 | ||
diff --git a/examples/rp/src/bin/wifi_scan.rs b/examples/rp/src/bin/wifi_scan.rs index 434f0074c..2ef899080 100644 --- a/examples/rp/src/bin/wifi_scan.rs +++ b/examples/rp/src/bin/wifi_scan.rs | |||
| @@ -26,11 +26,6 @@ async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'stat | |||
| 26 | runner.run().await | 26 | runner.run().await |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | #[embassy_executor::task] | ||
| 30 | async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! { | ||
| 31 | runner.run().await | ||
| 32 | } | ||
| 33 | |||
| 34 | #[embassy_executor::main] | 29 | #[embassy_executor::main] |
| 35 | async fn main(spawner: Spawner) { | 30 | async fn main(spawner: Spawner) { |
| 36 | info!("Hello World!"); | 31 | info!("Hello World!"); |
diff --git a/examples/rp23/Cargo.toml b/examples/rp23/Cargo.toml index 087f6fd69..72eef222d 100644 --- a/examples/rp23/Cargo.toml +++ b/examples/rp23/Cargo.toml | |||
| @@ -7,12 +7,12 @@ license = "MIT OR Apache-2.0" | |||
| 7 | 7 | ||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] } | 9 | embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 13 | embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] } | 13 | embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] } |
| 14 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } |
| 15 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] } | 15 | embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] } |
| 16 | embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } | 16 | embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } |
| 17 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 17 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 18 | embassy-usb-logger = { version = "0.2.0", path = "../../embassy-usb-logger" } | 18 | embassy-usb-logger = { version = "0.2.0", path = "../../embassy-usb-logger" } |
| @@ -24,23 +24,24 @@ defmt-rtt = "0.4" | |||
| 24 | fixed = "1.23.1" | 24 | fixed = "1.23.1" |
| 25 | fixed-macro = "1.2" | 25 | fixed-macro = "1.2" |
| 26 | 26 | ||
| 27 | # for web request example | ||
| 28 | reqwless = { version = "0.12.0", features = ["defmt",]} | ||
| 29 | serde = { version = "1.0.203", default-features = false, features = ["derive"] } | 27 | serde = { version = "1.0.203", default-features = false, features = ["derive"] } |
| 30 | serde-json-core = "0.5.1" | 28 | serde-json-core = "0.5.1" |
| 31 | 29 | ||
| 32 | # for assign resources example | 30 | # for assign resources example |
| 33 | assign-resources = { git = "https://github.com/adamgreig/assign-resources", rev = "94ad10e2729afdf0fd5a77cd12e68409a982f58a" } | 31 | assign-resources = { git = "https://github.com/adamgreig/assign-resources", rev = "94ad10e2729afdf0fd5a77cd12e68409a982f58a" } |
| 34 | 32 | ||
| 33 | # for TB6612FNG example | ||
| 34 | tb6612fng = "1.0.0" | ||
| 35 | |||
| 35 | #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 36 | #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 36 | cortex-m = { version = "0.7.6", features = ["inline-asm"] } | 37 | cortex-m = { version = "0.7.6", features = ["inline-asm"] } |
| 37 | cortex-m-rt = "0.7.0" | 38 | cortex-m-rt = "0.7.0" |
| 38 | critical-section = "1.1" | 39 | critical-section = "1.1" |
| 39 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 40 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 40 | display-interface-spi = "0.4.1" | 41 | display-interface-spi = "0.5.0" |
| 41 | embedded-graphics = "0.7.1" | 42 | embedded-graphics = "0.8.1" |
| 42 | st7789 = "0.6.1" | 43 | mipidsi = "0.8.0" |
| 43 | display-interface = "0.4.1" | 44 | display-interface = "0.5.0" |
| 44 | byte-slice-cast = { version = "1.2.0", default-features = false } | 45 | byte-slice-cast = { version = "1.2.0", default-features = false } |
| 45 | smart-leds = "0.3.0" | 46 | smart-leds = "0.3.0" |
| 46 | heapless = "0.8" | 47 | heapless = "0.8" |
diff --git a/examples/rp23/memory.x b/examples/rp23/memory.x index 777492062..c803896f6 100644 --- a/examples/rp23/memory.x +++ b/examples/rp23/memory.x | |||
| @@ -31,6 +31,7 @@ SECTIONS { | |||
| 31 | { | 31 | { |
| 32 | __start_block_addr = .; | 32 | __start_block_addr = .; |
| 33 | KEEP(*(.start_block)); | 33 | KEEP(*(.start_block)); |
| 34 | KEEP(*(.boot_info)); | ||
| 34 | } > FLASH | 35 | } > FLASH |
| 35 | 36 | ||
| 36 | } INSERT AFTER .vector_table; | 37 | } INSERT AFTER .vector_table; |
diff --git a/examples/rp23/src/bin/adc.rs b/examples/rp23/src/bin/adc.rs index d1f053d39..f7db9653a 100644 --- a/examples/rp23/src/bin/adc.rs +++ b/examples/rp23/src/bin/adc.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | //! This example test the ADC (Analog to Digital Conversion) of the RS2040 pin 26, 27 and 28. | 1 | //! This example test the ADC (Analog to Digital Conversion) of the RP2350A pins 26, 27 and 28. |
| 2 | //! It also reads the temperature sensor in the chip. | 2 | //! It also reads the temperature sensor in the chip. |
| 3 | 3 | ||
| 4 | #![no_std] | 4 | #![no_std] |
| @@ -17,16 +17,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 17 | #[used] | 17 | #[used] |
| 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 19 | 19 | ||
| 20 | // Program metadata for `picotool info` | ||
| 21 | #[link_section = ".bi_entries"] | ||
| 22 | #[used] | ||
| 23 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 24 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 25 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 26 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 27 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 28 | ]; | ||
| 29 | |||
| 30 | bind_interrupts!(struct Irqs { | 20 | bind_interrupts!(struct Irqs { |
| 31 | ADC_IRQ_FIFO => InterruptHandler; | 21 | ADC_IRQ_FIFO => InterruptHandler; |
| 32 | }); | 22 | }); |
diff --git a/examples/rp23/src/bin/adc_dma.rs b/examples/rp23/src/bin/adc_dma.rs index 5046e5530..a6814c23a 100644 --- a/examples/rp23/src/bin/adc_dma.rs +++ b/examples/rp23/src/bin/adc_dma.rs | |||
| @@ -17,16 +17,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 17 | #[used] | 17 | #[used] |
| 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 19 | 19 | ||
| 20 | // Program metadata for `picotool info` | ||
| 21 | #[link_section = ".bi_entries"] | ||
| 22 | #[used] | ||
| 23 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 24 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 25 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 26 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 27 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 28 | ]; | ||
| 29 | |||
| 30 | bind_interrupts!(struct Irqs { | 20 | bind_interrupts!(struct Irqs { |
| 31 | ADC_IRQ_FIFO => InterruptHandler; | 21 | ADC_IRQ_FIFO => InterruptHandler; |
| 32 | }); | 22 | }); |
diff --git a/examples/rp23/src/bin/assign_resources.rs b/examples/rp23/src/bin/assign_resources.rs index 2f9783917..0d4ad8dc3 100644 --- a/examples/rp23/src/bin/assign_resources.rs +++ b/examples/rp23/src/bin/assign_resources.rs | |||
| @@ -24,16 +24,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 24 | #[used] | 24 | #[used] |
| 25 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 25 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 26 | 26 | ||
| 27 | // Program metadata for `picotool info` | ||
| 28 | #[link_section = ".bi_entries"] | ||
| 29 | #[used] | ||
| 30 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 31 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 32 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 33 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 34 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 35 | ]; | ||
| 36 | |||
| 37 | #[embassy_executor::main] | 27 | #[embassy_executor::main] |
| 38 | async fn main(spawner: Spawner) { | 28 | async fn main(spawner: Spawner) { |
| 39 | // initialize the peripherals | 29 | // initialize the peripherals |
diff --git a/examples/rp23/src/bin/blinky.rs b/examples/rp23/src/bin/blinky.rs index 9e45679c8..c1ddbb7d2 100644 --- a/examples/rp23/src/bin/blinky.rs +++ b/examples/rp23/src/bin/blinky.rs | |||
| @@ -17,20 +17,23 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 17 | #[used] | 17 | #[used] |
| 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 19 | 19 | ||
| 20 | // Program metadata for `picotool info` | 20 | // Program metadata for `picotool info`. |
| 21 | // This isn't needed, but it's recomended to have these minimal entries. | ||
| 21 | #[link_section = ".bi_entries"] | 22 | #[link_section = ".bi_entries"] |
| 22 | #[used] | 23 | #[used] |
| 23 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | 24 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ |
| 24 | embassy_rp::binary_info::rp_program_name!(c"example"), | 25 | embassy_rp::binary_info::rp_program_name!(c"Blinky Example"), |
| 26 | embassy_rp::binary_info::rp_program_description!( | ||
| 27 | c"This example tests the RP Pico on board LED, connected to gpio 25" | ||
| 28 | ), | ||
| 25 | embassy_rp::binary_info::rp_cargo_version!(), | 29 | embassy_rp::binary_info::rp_cargo_version!(), |
| 26 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 27 | embassy_rp::binary_info::rp_program_build_attribute!(), | 30 | embassy_rp::binary_info::rp_program_build_attribute!(), |
| 28 | ]; | 31 | ]; |
| 29 | 32 | ||
| 30 | #[embassy_executor::main] | 33 | #[embassy_executor::main] |
| 31 | async fn main(_spawner: Spawner) { | 34 | async fn main(_spawner: Spawner) { |
| 32 | let p = embassy_rp::init(Default::default()); | 35 | let p = embassy_rp::init(Default::default()); |
| 33 | let mut led = Output::new(p.PIN_2, Level::Low); | 36 | let mut led = Output::new(p.PIN_25, Level::Low); |
| 34 | 37 | ||
| 35 | loop { | 38 | loop { |
| 36 | info!("led on!"); | 39 | info!("led on!"); |
diff --git a/examples/rp23/src/bin/blinky_two_channels.rs b/examples/rp23/src/bin/blinky_two_channels.rs index 87fc58bbc..ce482858e 100644 --- a/examples/rp23/src/bin/blinky_two_channels.rs +++ b/examples/rp23/src/bin/blinky_two_channels.rs | |||
| @@ -19,16 +19,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 19 | #[used] | 19 | #[used] |
| 20 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 20 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 21 | 21 | ||
| 22 | // Program metadata for `picotool info` | ||
| 23 | #[link_section = ".bi_entries"] | ||
| 24 | #[used] | ||
| 25 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 26 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 27 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 28 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 29 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 30 | ]; | ||
| 31 | |||
| 32 | enum LedState { | 22 | enum LedState { |
| 33 | Toggle, | 23 | Toggle, |
| 34 | } | 24 | } |
diff --git a/examples/rp23/src/bin/blinky_two_tasks.rs b/examples/rp23/src/bin/blinky_two_tasks.rs index 40236c53b..5dc62245d 100644 --- a/examples/rp23/src/bin/blinky_two_tasks.rs +++ b/examples/rp23/src/bin/blinky_two_tasks.rs | |||
| @@ -19,16 +19,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 19 | #[used] | 19 | #[used] |
| 20 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 20 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 21 | 21 | ||
| 22 | // Program metadata for `picotool info` | ||
| 23 | #[link_section = ".bi_entries"] | ||
| 24 | #[used] | ||
| 25 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 26 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 27 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 28 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 29 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 30 | ]; | ||
| 31 | |||
| 32 | type LedType = Mutex<ThreadModeRawMutex, Option<Output<'static>>>; | 22 | type LedType = Mutex<ThreadModeRawMutex, Option<Output<'static>>>; |
| 33 | static LED: LedType = Mutex::new(None); | 23 | static LED: LedType = Mutex::new(None); |
| 34 | 24 | ||
diff --git a/examples/rp23/src/bin/button.rs b/examples/rp23/src/bin/button.rs index fb067a370..85f1bcae3 100644 --- a/examples/rp23/src/bin/button.rs +++ b/examples/rp23/src/bin/button.rs | |||
| @@ -14,16 +14,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 14 | #[used] | 14 | #[used] |
| 15 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 15 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 16 | 16 | ||
| 17 | // Program metadata for `picotool info` | ||
| 18 | #[link_section = ".bi_entries"] | ||
| 19 | #[used] | ||
| 20 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 21 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 22 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 23 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 24 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 25 | ]; | ||
| 26 | |||
| 27 | #[embassy_executor::main] | 17 | #[embassy_executor::main] |
| 28 | async fn main(_spawner: Spawner) { | 18 | async fn main(_spawner: Spawner) { |
| 29 | let p = embassy_rp::init(Default::default()); | 19 | let p = embassy_rp::init(Default::default()); |
diff --git a/examples/rp23/src/bin/debounce.rs b/examples/rp23/src/bin/debounce.rs index e672521ec..4c8b80d92 100644 --- a/examples/rp23/src/bin/debounce.rs +++ b/examples/rp23/src/bin/debounce.rs | |||
| @@ -15,16 +15,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 15 | #[used] | 15 | #[used] |
| 16 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 16 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 17 | 17 | ||
| 18 | // Program metadata for `picotool info` | ||
| 19 | #[link_section = ".bi_entries"] | ||
| 20 | #[used] | ||
| 21 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 22 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 23 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 24 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 25 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 26 | ]; | ||
| 27 | |||
| 28 | pub struct Debouncer<'a> { | 18 | pub struct Debouncer<'a> { |
| 29 | input: Input<'a>, | 19 | input: Input<'a>, |
| 30 | debounce: Duration, | 20 | debounce: Duration, |
diff --git a/examples/rp23/src/bin/flash.rs b/examples/rp23/src/bin/flash.rs index 84011e394..28dec24c9 100644 --- a/examples/rp23/src/bin/flash.rs +++ b/examples/rp23/src/bin/flash.rs | |||
| @@ -15,16 +15,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 15 | #[used] | 15 | #[used] |
| 16 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 16 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 17 | 17 | ||
| 18 | // Program metadata for `picotool info` | ||
| 19 | #[link_section = ".bi_entries"] | ||
| 20 | #[used] | ||
| 21 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 22 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 23 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 24 | embassy_rp::binary_info::rp_program_description!(c"Flash"), | ||
| 25 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 26 | ]; | ||
| 27 | |||
| 28 | const ADDR_OFFSET: u32 = 0x100000; | 18 | const ADDR_OFFSET: u32 = 0x100000; |
| 29 | const FLASH_SIZE: usize = 2 * 1024 * 1024; | 19 | const FLASH_SIZE: usize = 2 * 1024 * 1024; |
| 30 | 20 | ||
diff --git a/examples/rp23/src/bin/gpio_async.rs b/examples/rp23/src/bin/gpio_async.rs index ff12367bf..bfb9a3f95 100644 --- a/examples/rp23/src/bin/gpio_async.rs +++ b/examples/rp23/src/bin/gpio_async.rs | |||
| @@ -17,16 +17,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 17 | #[used] | 17 | #[used] |
| 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 19 | 19 | ||
| 20 | // Program metadata for `picotool info` | ||
| 21 | #[link_section = ".bi_entries"] | ||
| 22 | #[used] | ||
| 23 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 24 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 25 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 26 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 27 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 28 | ]; | ||
| 29 | |||
| 30 | /// It requires an external signal to be manually triggered on PIN 16. For | 20 | /// It requires an external signal to be manually triggered on PIN 16. For |
| 31 | /// example, this could be accomplished using an external power source with a | 21 | /// example, this could be accomplished using an external power source with a |
| 32 | /// button so that it is possible to toggle the signal from low to high. | 22 | /// button so that it is possible to toggle the signal from low to high. |
diff --git a/examples/rp23/src/bin/gpout.rs b/examples/rp23/src/bin/gpout.rs index d2ee55197..3cc2ea938 100644 --- a/examples/rp23/src/bin/gpout.rs +++ b/examples/rp23/src/bin/gpout.rs | |||
| @@ -16,16 +16,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 16 | #[used] | 16 | #[used] |
| 17 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 17 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 18 | 18 | ||
| 19 | // Program metadata for `picotool info` | ||
| 20 | #[link_section = ".bi_entries"] | ||
| 21 | #[used] | ||
| 22 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 23 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 24 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 25 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 26 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 27 | ]; | ||
| 28 | |||
| 29 | #[embassy_executor::main] | 19 | #[embassy_executor::main] |
| 30 | async fn main(_spawner: Spawner) { | 20 | async fn main(_spawner: Spawner) { |
| 31 | let p = embassy_rp::init(Default::default()); | 21 | let p = embassy_rp::init(Default::default()); |
diff --git a/examples/rp23/src/bin/i2c_async.rs b/examples/rp23/src/bin/i2c_async.rs index c8d918b56..b30088bae 100644 --- a/examples/rp23/src/bin/i2c_async.rs +++ b/examples/rp23/src/bin/i2c_async.rs | |||
| @@ -20,16 +20,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 20 | #[used] | 20 | #[used] |
| 21 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 21 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 22 | 22 | ||
| 23 | // Program metadata for `picotool info` | ||
| 24 | #[link_section = ".bi_entries"] | ||
| 25 | #[used] | ||
| 26 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 27 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 28 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 29 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 30 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 31 | ]; | ||
| 32 | |||
| 33 | bind_interrupts!(struct Irqs { | 23 | bind_interrupts!(struct Irqs { |
| 34 | I2C1_IRQ => InterruptHandler<I2C1>; | 24 | I2C1_IRQ => InterruptHandler<I2C1>; |
| 35 | }); | 25 | }); |
diff --git a/examples/rp23/src/bin/i2c_async_embassy.rs b/examples/rp23/src/bin/i2c_async_embassy.rs index cce0abcde..c783a80c5 100644 --- a/examples/rp23/src/bin/i2c_async_embassy.rs +++ b/examples/rp23/src/bin/i2c_async_embassy.rs | |||
| @@ -15,16 +15,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 15 | #[used] | 15 | #[used] |
| 16 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 16 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 17 | 17 | ||
| 18 | // Program metadata for `picotool info` | ||
| 19 | #[link_section = ".bi_entries"] | ||
| 20 | #[used] | ||
| 21 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 22 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 23 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 24 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 25 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 26 | ]; | ||
| 27 | |||
| 28 | // Our anonymous hypotetical temperature sensor could be: | 18 | // Our anonymous hypotetical temperature sensor could be: |
| 29 | // a 12-bit sensor, with 100ms startup time, range of -40*C - 125*C, and precision 0.25*C | 19 | // a 12-bit sensor, with 100ms startup time, range of -40*C - 125*C, and precision 0.25*C |
| 30 | // It requires no configuration or calibration, works with all i2c bus speeds, | 20 | // It requires no configuration or calibration, works with all i2c bus speeds, |
diff --git a/examples/rp23/src/bin/i2c_blocking.rs b/examples/rp23/src/bin/i2c_blocking.rs index 85c33bf0d..a68677311 100644 --- a/examples/rp23/src/bin/i2c_blocking.rs +++ b/examples/rp23/src/bin/i2c_blocking.rs | |||
| @@ -18,16 +18,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 18 | #[used] | 18 | #[used] |
| 19 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 19 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 20 | 20 | ||
| 21 | // Program metadata for `picotool info` | ||
| 22 | #[link_section = ".bi_entries"] | ||
| 23 | #[used] | ||
| 24 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 25 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 26 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 27 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 28 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 29 | ]; | ||
| 30 | |||
| 31 | #[allow(dead_code)] | 21 | #[allow(dead_code)] |
| 32 | mod mcp23017 { | 22 | mod mcp23017 { |
| 33 | pub const ADDR: u8 = 0x20; // default addr | 23 | pub const ADDR: u8 = 0x20; // default addr |
diff --git a/examples/rp23/src/bin/i2c_slave.rs b/examples/rp23/src/bin/i2c_slave.rs index fb5f3cda1..8817538c0 100644 --- a/examples/rp23/src/bin/i2c_slave.rs +++ b/examples/rp23/src/bin/i2c_slave.rs | |||
| @@ -15,16 +15,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 15 | #[used] | 15 | #[used] |
| 16 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 16 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 17 | 17 | ||
| 18 | // Program metadata for `picotool info` | ||
| 19 | #[link_section = ".bi_entries"] | ||
| 20 | #[used] | ||
| 21 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 22 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 23 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 24 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 25 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 26 | ]; | ||
| 27 | |||
| 28 | bind_interrupts!(struct Irqs { | 18 | bind_interrupts!(struct Irqs { |
| 29 | I2C0_IRQ => i2c::InterruptHandler<I2C0>; | 19 | I2C0_IRQ => i2c::InterruptHandler<I2C0>; |
| 30 | I2C1_IRQ => i2c::InterruptHandler<I2C1>; | 20 | I2C1_IRQ => i2c::InterruptHandler<I2C1>; |
diff --git a/examples/rp23/src/bin/interrupt.rs b/examples/rp23/src/bin/interrupt.rs index ee3d9bfe7..d9b662253 100644 --- a/examples/rp23/src/bin/interrupt.rs +++ b/examples/rp23/src/bin/interrupt.rs | |||
| @@ -29,16 +29,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 29 | #[used] | 29 | #[used] |
| 30 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 30 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 31 | 31 | ||
| 32 | // Program metadata for `picotool info` | ||
| 33 | #[link_section = ".bi_entries"] | ||
| 34 | #[used] | ||
| 35 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 36 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 37 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 38 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 39 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 40 | ]; | ||
| 41 | |||
| 42 | static COUNTER: AtomicU32 = AtomicU32::new(0); | 32 | static COUNTER: AtomicU32 = AtomicU32::new(0); |
| 43 | static PWM: Mutex<CriticalSectionRawMutex, RefCell<Option<Pwm>>> = Mutex::new(RefCell::new(None)); | 33 | static PWM: Mutex<CriticalSectionRawMutex, RefCell<Option<Pwm>>> = Mutex::new(RefCell::new(None)); |
| 44 | static ADC: Mutex<CriticalSectionRawMutex, RefCell<Option<(Adc<Blocking>, adc::Channel)>>> = | 34 | static ADC: Mutex<CriticalSectionRawMutex, RefCell<Option<(Adc<Blocking>, adc::Channel)>>> = |
diff --git a/examples/rp23/src/bin/multicore.rs b/examples/rp23/src/bin/multicore.rs index 9ab43d7a5..d4d470fa2 100644 --- a/examples/rp23/src/bin/multicore.rs +++ b/examples/rp23/src/bin/multicore.rs | |||
| @@ -20,16 +20,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 20 | #[used] | 20 | #[used] |
| 21 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 21 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 22 | 22 | ||
| 23 | // Program metadata for `picotool info` | ||
| 24 | #[link_section = ".bi_entries"] | ||
| 25 | #[used] | ||
| 26 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 27 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 28 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 29 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 30 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 31 | ]; | ||
| 32 | |||
| 33 | static mut CORE1_STACK: Stack<4096> = Stack::new(); | 23 | static mut CORE1_STACK: Stack<4096> = Stack::new(); |
| 34 | static EXECUTOR0: StaticCell<Executor> = StaticCell::new(); | 24 | static EXECUTOR0: StaticCell<Executor> = StaticCell::new(); |
| 35 | static EXECUTOR1: StaticCell<Executor> = StaticCell::new(); | 25 | static EXECUTOR1: StaticCell<Executor> = StaticCell::new(); |
diff --git a/examples/rp23/src/bin/multiprio.rs b/examples/rp23/src/bin/multiprio.rs index 27cd3656e..787854aa9 100644 --- a/examples/rp23/src/bin/multiprio.rs +++ b/examples/rp23/src/bin/multiprio.rs | |||
| @@ -70,16 +70,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 70 | #[used] | 70 | #[used] |
| 71 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 71 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 72 | 72 | ||
| 73 | // Program metadata for `picotool info` | ||
| 74 | #[link_section = ".bi_entries"] | ||
| 75 | #[used] | ||
| 76 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 77 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 78 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 79 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 80 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 81 | ]; | ||
| 82 | |||
| 83 | #[embassy_executor::task] | 73 | #[embassy_executor::task] |
| 84 | async fn run_high() { | 74 | async fn run_high() { |
| 85 | loop { | 75 | loop { |
diff --git a/examples/rp23/src/bin/otp.rs b/examples/rp23/src/bin/otp.rs index 106e514ca..c67c9821a 100644 --- a/examples/rp23/src/bin/otp.rs +++ b/examples/rp23/src/bin/otp.rs | |||
| @@ -14,16 +14,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 14 | #[used] | 14 | #[used] |
| 15 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 15 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 16 | 16 | ||
| 17 | // Program metadata for `picotool info` | ||
| 18 | #[link_section = ".bi_entries"] | ||
| 19 | #[used] | ||
| 20 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 21 | embassy_rp::binary_info::rp_program_name!(c"OTP Read Example"), | ||
| 22 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 23 | embassy_rp::binary_info::rp_program_description!(c"OTP Read Example"), | ||
| 24 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 25 | ]; | ||
| 26 | |||
| 27 | #[embassy_executor::main] | 17 | #[embassy_executor::main] |
| 28 | async fn main(_spawner: Spawner) { | 18 | async fn main(_spawner: Spawner) { |
| 29 | let _ = embassy_rp::init(Default::default()); | 19 | let _ = embassy_rp::init(Default::default()); |
diff --git a/examples/rp23/src/bin/pio_async.rs b/examples/rp23/src/bin/pio_async.rs index 231afc80e..896447e28 100644 --- a/examples/rp23/src/bin/pio_async.rs +++ b/examples/rp23/src/bin/pio_async.rs | |||
| @@ -16,16 +16,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 16 | #[used] | 16 | #[used] |
| 17 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 17 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 18 | 18 | ||
| 19 | // Program metadata for `picotool info` | ||
| 20 | #[link_section = ".bi_entries"] | ||
| 21 | #[used] | ||
| 22 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 23 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 24 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 25 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 26 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 27 | ]; | ||
| 28 | |||
| 29 | bind_interrupts!(struct Irqs { | 19 | bind_interrupts!(struct Irqs { |
| 30 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | 20 | PIO0_IRQ_0 => InterruptHandler<PIO0>; |
| 31 | }); | 21 | }); |
diff --git a/examples/rp23/src/bin/pio_dma.rs b/examples/rp23/src/bin/pio_dma.rs index 60fbcb83a..b5f754798 100644 --- a/examples/rp23/src/bin/pio_dma.rs +++ b/examples/rp23/src/bin/pio_dma.rs | |||
| @@ -17,16 +17,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 17 | #[used] | 17 | #[used] |
| 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 19 | 19 | ||
| 20 | // Program metadata for `picotool info` | ||
| 21 | #[link_section = ".bi_entries"] | ||
| 22 | #[used] | ||
| 23 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 24 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 25 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 26 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 27 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 28 | ]; | ||
| 29 | |||
| 30 | bind_interrupts!(struct Irqs { | 20 | bind_interrupts!(struct Irqs { |
| 31 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | 21 | PIO0_IRQ_0 => InterruptHandler<PIO0>; |
| 32 | }); | 22 | }); |
diff --git a/examples/rp23/src/bin/pio_hd44780.rs b/examples/rp23/src/bin/pio_hd44780.rs index 92aa858f9..c6f5f6db0 100644 --- a/examples/rp23/src/bin/pio_hd44780.rs +++ b/examples/rp23/src/bin/pio_hd44780.rs | |||
| @@ -7,14 +7,12 @@ | |||
| 7 | use core::fmt::Write; | 7 | use core::fmt::Write; |
| 8 | 8 | ||
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_rp::bind_interrupts; | ||
| 10 | use embassy_rp::block::ImageDef; | 11 | use embassy_rp::block::ImageDef; |
| 11 | use embassy_rp::dma::{AnyChannel, Channel}; | ||
| 12 | use embassy_rp::peripherals::PIO0; | 12 | use embassy_rp::peripherals::PIO0; |
| 13 | use embassy_rp::pio::{ | 13 | use embassy_rp::pio::{InterruptHandler, Pio}; |
| 14 | Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, | 14 | use embassy_rp::pio_programs::hd44780::{PioHD44780, PioHD44780CommandSequenceProgram, PioHD44780CommandWordProgram}; |
| 15 | }; | ||
| 16 | use embassy_rp::pwm::{self, Pwm}; | 15 | use embassy_rp::pwm::{self, Pwm}; |
| 17 | use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef}; | ||
| 18 | use embassy_time::{Instant, Timer}; | 16 | use embassy_time::{Instant, Timer}; |
| 19 | use {defmt_rtt as _, panic_probe as _}; | 17 | use {defmt_rtt as _, panic_probe as _}; |
| 20 | 18 | ||
| @@ -22,16 +20,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 22 | #[used] | 20 | #[used] |
| 23 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 21 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 24 | 22 | ||
| 25 | // Program metadata for `picotool info` | ||
| 26 | #[link_section = ".bi_entries"] | ||
| 27 | #[used] | ||
| 28 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 29 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 30 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 31 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 32 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 33 | ]; | ||
| 34 | |||
| 35 | bind_interrupts!(pub struct Irqs { | 23 | bind_interrupts!(pub struct Irqs { |
| 36 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | 24 | PIO0_IRQ_0 => InterruptHandler<PIO0>; |
| 37 | }); | 25 | }); |
| @@ -58,8 +46,27 @@ async fn main(_spawner: Spawner) { | |||
| 58 | c | 46 | c |
| 59 | }); | 47 | }); |
| 60 | 48 | ||
| 61 | let mut hd = HD44780::new( | 49 | let Pio { |
| 62 | p.PIO0, Irqs, p.DMA_CH3, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, p.PIN_4, p.PIN_5, p.PIN_6, | 50 | mut common, sm0, irq0, .. |
| 51 | } = Pio::new(p.PIO0, Irqs); | ||
| 52 | |||
| 53 | let word_prg = PioHD44780CommandWordProgram::new(&mut common); | ||
| 54 | let seq_prg = PioHD44780CommandSequenceProgram::new(&mut common); | ||
| 55 | |||
| 56 | let mut hd = PioHD44780::new( | ||
| 57 | &mut common, | ||
| 58 | sm0, | ||
| 59 | irq0, | ||
| 60 | p.DMA_CH3, | ||
| 61 | p.PIN_0, | ||
| 62 | p.PIN_1, | ||
| 63 | p.PIN_2, | ||
| 64 | p.PIN_3, | ||
| 65 | p.PIN_4, | ||
| 66 | p.PIN_5, | ||
| 67 | p.PIN_6, | ||
| 68 | &word_prg, | ||
| 69 | &seq_prg, | ||
| 63 | ) | 70 | ) |
| 64 | .await; | 71 | .await; |
| 65 | 72 | ||
| @@ -83,173 +90,3 @@ async fn main(_spawner: Spawner) { | |||
| 83 | Timer::after_secs(1).await; | 90 | Timer::after_secs(1).await; |
| 84 | } | 91 | } |
| 85 | } | 92 | } |
| 86 | |||
| 87 | pub struct HD44780<'l> { | ||
| 88 | dma: PeripheralRef<'l, AnyChannel>, | ||
| 89 | sm: StateMachine<'l, PIO0, 0>, | ||
| 90 | |||
| 91 | buf: [u8; 40], | ||
| 92 | } | ||
| 93 | |||
| 94 | impl<'l> HD44780<'l> { | ||
| 95 | pub async fn new( | ||
| 96 | pio: impl Peripheral<P = PIO0> + 'l, | ||
| 97 | irq: Irqs, | ||
| 98 | dma: impl Peripheral<P = impl Channel> + 'l, | ||
| 99 | rs: impl PioPin, | ||
| 100 | rw: impl PioPin, | ||
| 101 | e: impl PioPin, | ||
| 102 | db4: impl PioPin, | ||
| 103 | db5: impl PioPin, | ||
| 104 | db6: impl PioPin, | ||
| 105 | db7: impl PioPin, | ||
| 106 | ) -> HD44780<'l> { | ||
| 107 | into_ref!(dma); | ||
| 108 | |||
| 109 | let Pio { | ||
| 110 | mut common, | ||
| 111 | mut irq0, | ||
| 112 | mut sm0, | ||
| 113 | .. | ||
| 114 | } = Pio::new(pio, irq); | ||
| 115 | |||
| 116 | // takes command words (<wait:24> <command:4> <0:4>) | ||
| 117 | let prg = pio_proc::pio_asm!( | ||
| 118 | r#" | ||
| 119 | .side_set 1 opt | ||
| 120 | .origin 20 | ||
| 121 | |||
| 122 | loop: | ||
| 123 | out x, 24 | ||
| 124 | delay: | ||
| 125 | jmp x--, delay | ||
| 126 | out pins, 4 side 1 | ||
| 127 | out null, 4 side 0 | ||
| 128 | jmp !osre, loop | ||
| 129 | irq 0 | ||
| 130 | "#, | ||
| 131 | ); | ||
| 132 | |||
| 133 | let rs = common.make_pio_pin(rs); | ||
| 134 | let rw = common.make_pio_pin(rw); | ||
| 135 | let e = common.make_pio_pin(e); | ||
| 136 | let db4 = common.make_pio_pin(db4); | ||
| 137 | let db5 = common.make_pio_pin(db5); | ||
| 138 | let db6 = common.make_pio_pin(db6); | ||
| 139 | let db7 = common.make_pio_pin(db7); | ||
| 140 | |||
| 141 | sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]); | ||
| 142 | |||
| 143 | let mut cfg = Config::default(); | ||
| 144 | cfg.use_program(&common.load_program(&prg.program), &[&e]); | ||
| 145 | cfg.clock_divider = 125u8.into(); | ||
| 146 | cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); | ||
| 147 | cfg.shift_out = ShiftConfig { | ||
| 148 | auto_fill: true, | ||
| 149 | direction: ShiftDirection::Left, | ||
| 150 | threshold: 32, | ||
| 151 | }; | ||
| 152 | cfg.fifo_join = FifoJoin::TxOnly; | ||
| 153 | sm0.set_config(&cfg); | ||
| 154 | |||
| 155 | sm0.set_enable(true); | ||
| 156 | // init to 8 bit thrice | ||
| 157 | sm0.tx().push((50000 << 8) | 0x30); | ||
| 158 | sm0.tx().push((5000 << 8) | 0x30); | ||
| 159 | sm0.tx().push((200 << 8) | 0x30); | ||
| 160 | // init 4 bit | ||
| 161 | sm0.tx().push((200 << 8) | 0x20); | ||
| 162 | // set font and lines | ||
| 163 | sm0.tx().push((50 << 8) | 0x20); | ||
| 164 | sm0.tx().push(0b1100_0000); | ||
| 165 | |||
| 166 | irq0.wait().await; | ||
| 167 | sm0.set_enable(false); | ||
| 168 | |||
| 169 | // takes command sequences (<rs:1> <count:7>, data...) | ||
| 170 | // many side sets are only there to free up a delay bit! | ||
| 171 | let prg = pio_proc::pio_asm!( | ||
| 172 | r#" | ||
| 173 | .origin 27 | ||
| 174 | .side_set 1 | ||
| 175 | |||
| 176 | .wrap_target | ||
| 177 | pull side 0 | ||
| 178 | out x 1 side 0 ; !rs | ||
| 179 | out y 7 side 0 ; #data - 1 | ||
| 180 | |||
| 181 | ; rs/rw to e: >= 60ns | ||
| 182 | ; e high time: >= 500ns | ||
| 183 | ; e low time: >= 500ns | ||
| 184 | ; read data valid after e falling: ~5ns | ||
| 185 | ; write data hold after e falling: ~10ns | ||
| 186 | |||
| 187 | loop: | ||
| 188 | pull side 0 | ||
| 189 | jmp !x data side 0 | ||
| 190 | command: | ||
| 191 | set pins 0b00 side 0 | ||
| 192 | jmp shift side 0 | ||
| 193 | data: | ||
| 194 | set pins 0b01 side 0 | ||
| 195 | shift: | ||
| 196 | out pins 4 side 1 [9] | ||
| 197 | nop side 0 [9] | ||
| 198 | out pins 4 side 1 [9] | ||
| 199 | mov osr null side 0 [7] | ||
| 200 | out pindirs 4 side 0 | ||
| 201 | set pins 0b10 side 0 | ||
| 202 | busy: | ||
| 203 | nop side 1 [9] | ||
| 204 | jmp pin more side 0 [9] | ||
| 205 | mov osr ~osr side 1 [9] | ||
| 206 | nop side 0 [4] | ||
| 207 | out pindirs 4 side 0 | ||
| 208 | jmp y-- loop side 0 | ||
| 209 | .wrap | ||
| 210 | more: | ||
| 211 | nop side 1 [9] | ||
| 212 | jmp busy side 0 [9] | ||
| 213 | "# | ||
| 214 | ); | ||
| 215 | |||
| 216 | let mut cfg = Config::default(); | ||
| 217 | cfg.use_program(&common.load_program(&prg.program), &[&e]); | ||
| 218 | cfg.clock_divider = 8u8.into(); // ~64ns/insn | ||
| 219 | cfg.set_jmp_pin(&db7); | ||
| 220 | cfg.set_set_pins(&[&rs, &rw]); | ||
| 221 | cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); | ||
| 222 | cfg.shift_out.direction = ShiftDirection::Left; | ||
| 223 | cfg.fifo_join = FifoJoin::TxOnly; | ||
| 224 | sm0.set_config(&cfg); | ||
| 225 | |||
| 226 | sm0.set_enable(true); | ||
| 227 | |||
| 228 | // display on and cursor on and blinking, reset display | ||
| 229 | sm0.tx().dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await; | ||
| 230 | |||
| 231 | Self { | ||
| 232 | dma: dma.map_into(), | ||
| 233 | sm: sm0, | ||
| 234 | buf: [0x20; 40], | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | pub async fn add_line(&mut self, s: &[u8]) { | ||
| 239 | // move cursor to 0:0, prepare 16 characters | ||
| 240 | self.buf[..3].copy_from_slice(&[0x80, 0x80, 15]); | ||
| 241 | // move line 2 up | ||
| 242 | self.buf.copy_within(22..38, 3); | ||
| 243 | // move cursor to 1:0, prepare 16 characters | ||
| 244 | self.buf[19..22].copy_from_slice(&[0x80, 0xc0, 15]); | ||
| 245 | // file line 2 with spaces | ||
| 246 | self.buf[22..38].fill(0x20); | ||
| 247 | // copy input line | ||
| 248 | let len = s.len().min(16); | ||
| 249 | self.buf[22..22 + len].copy_from_slice(&s[0..len]); | ||
| 250 | // set cursor to 1:15 | ||
| 251 | self.buf[38..].copy_from_slice(&[0x80, 0xcf]); | ||
| 252 | |||
| 253 | self.sm.tx().dma_push(self.dma.reborrow(), &self.buf).await; | ||
| 254 | } | ||
| 255 | } | ||
diff --git a/examples/rp23/src/bin/pio_i2s.rs b/examples/rp23/src/bin/pio_i2s.rs index d6d2d0ade..1fd34357b 100644 --- a/examples/rp23/src/bin/pio_i2s.rs +++ b/examples/rp23/src/bin/pio_i2s.rs | |||
| @@ -13,11 +13,12 @@ | |||
| 13 | use core::mem; | 13 | use core::mem; |
| 14 | 14 | ||
| 15 | use embassy_executor::Spawner; | 15 | use embassy_executor::Spawner; |
| 16 | use embassy_rp::bind_interrupts; | ||
| 16 | use embassy_rp::block::ImageDef; | 17 | use embassy_rp::block::ImageDef; |
| 18 | use embassy_rp::gpio::{Input, Pull}; | ||
| 17 | use embassy_rp::peripherals::PIO0; | 19 | use embassy_rp::peripherals::PIO0; |
| 18 | use embassy_rp::pio::{Config, FifoJoin, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; | 20 | use embassy_rp::pio::{InterruptHandler, Pio}; |
| 19 | use embassy_rp::{bind_interrupts, Peripheral}; | 21 | use embassy_rp::pio_programs::i2s::{PioI2sOut, PioI2sOutProgram}; |
| 20 | use fixed::traits::ToFixed; | ||
| 21 | use static_cell::StaticCell; | 22 | use static_cell::StaticCell; |
| 22 | use {defmt_rtt as _, panic_probe as _}; | 23 | use {defmt_rtt as _, panic_probe as _}; |
| 23 | 24 | ||
| @@ -25,78 +26,41 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 25 | #[used] | 26 | #[used] |
| 26 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 27 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 27 | 28 | ||
| 28 | // Program metadata for `picotool info` | ||
| 29 | #[link_section = ".bi_entries"] | ||
| 30 | #[used] | ||
| 31 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 32 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 33 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 34 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 35 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 36 | ]; | ||
| 37 | |||
| 38 | bind_interrupts!(struct Irqs { | 29 | bind_interrupts!(struct Irqs { |
| 39 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | 30 | PIO0_IRQ_0 => InterruptHandler<PIO0>; |
| 40 | }); | 31 | }); |
| 41 | 32 | ||
| 42 | const SAMPLE_RATE: u32 = 48_000; | 33 | const SAMPLE_RATE: u32 = 48_000; |
| 34 | const BIT_DEPTH: u32 = 16; | ||
| 35 | const CHANNELS: u32 = 2; | ||
| 43 | 36 | ||
| 44 | #[embassy_executor::main] | 37 | #[embassy_executor::main] |
| 45 | async fn main(_spawner: Spawner) { | 38 | async fn main(_spawner: Spawner) { |
| 46 | let p = embassy_rp::init(Default::default()); | 39 | let p = embassy_rp::init(Default::default()); |
| 47 | 40 | ||
| 48 | // Setup pio state machine for i2s output | 41 | // Setup pio state machine for i2s output |
| 49 | let mut pio = Pio::new(p.PIO0, Irqs); | 42 | let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); |
| 50 | |||
| 51 | #[rustfmt::skip] | ||
| 52 | let pio_program = pio_proc::pio_asm!( | ||
| 53 | ".side_set 2", | ||
| 54 | " set x, 14 side 0b01", // side 0bWB - W = Word Clock, B = Bit Clock | ||
| 55 | "left_data:", | ||
| 56 | " out pins, 1 side 0b00", | ||
| 57 | " jmp x-- left_data side 0b01", | ||
| 58 | " out pins 1 side 0b10", | ||
| 59 | " set x, 14 side 0b11", | ||
| 60 | "right_data:", | ||
| 61 | " out pins 1 side 0b10", | ||
| 62 | " jmp x-- right_data side 0b11", | ||
| 63 | " out pins 1 side 0b00", | ||
| 64 | ); | ||
| 65 | 43 | ||
| 66 | let bit_clock_pin = p.PIN_18; | 44 | let bit_clock_pin = p.PIN_18; |
| 67 | let left_right_clock_pin = p.PIN_19; | 45 | let left_right_clock_pin = p.PIN_19; |
| 68 | let data_pin = p.PIN_20; | 46 | let data_pin = p.PIN_20; |
| 69 | 47 | ||
| 70 | let data_pin = pio.common.make_pio_pin(data_pin); | 48 | let program = PioI2sOutProgram::new(&mut common); |
| 71 | let bit_clock_pin = pio.common.make_pio_pin(bit_clock_pin); | 49 | let mut i2s = PioI2sOut::new( |
| 72 | let left_right_clock_pin = pio.common.make_pio_pin(left_right_clock_pin); | 50 | &mut common, |
| 73 | 51 | sm0, | |
| 74 | let cfg = { | 52 | p.DMA_CH0, |
| 75 | let mut cfg = Config::default(); | 53 | data_pin, |
| 76 | cfg.use_program( | 54 | bit_clock_pin, |
| 77 | &pio.common.load_program(&pio_program.program), | 55 | left_right_clock_pin, |
| 78 | &[&bit_clock_pin, &left_right_clock_pin], | 56 | SAMPLE_RATE, |
| 79 | ); | 57 | BIT_DEPTH, |
| 80 | cfg.set_out_pins(&[&data_pin]); | 58 | CHANNELS, |
| 81 | const BIT_DEPTH: u32 = 16; | 59 | &program, |
| 82 | const CHANNELS: u32 = 2; | ||
| 83 | let clock_frequency = SAMPLE_RATE * BIT_DEPTH * CHANNELS; | ||
| 84 | cfg.clock_divider = (125_000_000. / clock_frequency as f64 / 2.).to_fixed(); | ||
| 85 | cfg.shift_out = ShiftConfig { | ||
| 86 | threshold: 32, | ||
| 87 | direction: ShiftDirection::Left, | ||
| 88 | auto_fill: true, | ||
| 89 | }; | ||
| 90 | // join fifos to have twice the time to start the next dma transfer | ||
| 91 | cfg.fifo_join = FifoJoin::TxOnly; | ||
| 92 | cfg | ||
| 93 | }; | ||
| 94 | pio.sm0.set_config(&cfg); | ||
| 95 | pio.sm0.set_pin_dirs( | ||
| 96 | embassy_rp::pio::Direction::Out, | ||
| 97 | &[&data_pin, &left_right_clock_pin, &bit_clock_pin], | ||
| 98 | ); | 60 | ); |
| 99 | 61 | ||
| 62 | let fade_input = Input::new(p.PIN_0, Pull::Up); | ||
| 63 | |||
| 100 | // create two audio buffers (back and front) which will take turns being | 64 | // create two audio buffers (back and front) which will take turns being |
| 101 | // filled with new audio data and being sent to the pio fifo using dma | 65 | // filled with new audio data and being sent to the pio fifo using dma |
| 102 | const BUFFER_SIZE: usize = 960; | 66 | const BUFFER_SIZE: usize = 960; |
| @@ -105,20 +69,16 @@ async fn main(_spawner: Spawner) { | |||
| 105 | let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE); | 69 | let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE); |
| 106 | 70 | ||
| 107 | // start pio state machine | 71 | // start pio state machine |
| 108 | pio.sm0.set_enable(true); | ||
| 109 | let tx = pio.sm0.tx(); | ||
| 110 | let mut dma_ref = p.DMA_CH0.into_ref(); | ||
| 111 | |||
| 112 | let mut fade_value: i32 = 0; | 72 | let mut fade_value: i32 = 0; |
| 113 | let mut phase: i32 = 0; | 73 | let mut phase: i32 = 0; |
| 114 | 74 | ||
| 115 | loop { | 75 | loop { |
| 116 | // trigger transfer of front buffer data to the pio fifo | 76 | // trigger transfer of front buffer data to the pio fifo |
| 117 | // but don't await the returned future, yet | 77 | // but don't await the returned future, yet |
| 118 | let dma_future = tx.dma_push(dma_ref.reborrow(), front_buffer); | 78 | let dma_future = i2s.write(front_buffer); |
| 119 | 79 | ||
| 120 | // fade in audio | 80 | // fade in audio when bootsel is pressed |
| 121 | let fade_target = i32::MAX; | 81 | let fade_target = if fade_input.is_low() { i32::MAX } else { 0 }; |
| 122 | 82 | ||
| 123 | // fill back buffer with fresh audio samples before awaiting the dma future | 83 | // fill back buffer with fresh audio samples before awaiting the dma future |
| 124 | for s in back_buffer.iter_mut() { | 84 | for s in back_buffer.iter_mut() { |
diff --git a/examples/rp23/src/bin/pio_onewire.rs b/examples/rp23/src/bin/pio_onewire.rs new file mode 100644 index 000000000..7f227d04b --- /dev/null +++ b/examples/rp23/src/bin/pio_onewire.rs | |||
| @@ -0,0 +1,88 @@ | |||
| 1 | //! This example shows how you can use PIO to read a `DS18B20` one-wire temperature sensor. | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_rp::bind_interrupts; | ||
| 8 | use embassy_rp::block::ImageDef; | ||
| 9 | use embassy_rp::peripherals::PIO0; | ||
| 10 | use embassy_rp::pio::{self, InterruptHandler, Pio}; | ||
| 11 | use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram}; | ||
| 12 | use embassy_time::Timer; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | #[link_section = ".start_block"] | ||
| 16 | #[used] | ||
| 17 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 18 | |||
| 19 | bind_interrupts!(struct Irqs { | ||
| 20 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 21 | }); | ||
| 22 | |||
| 23 | #[embassy_executor::main] | ||
| 24 | async fn main(_spawner: Spawner) { | ||
| 25 | let p = embassy_rp::init(Default::default()); | ||
| 26 | let mut pio = Pio::new(p.PIO0, Irqs); | ||
| 27 | |||
| 28 | let prg = PioOneWireProgram::new(&mut pio.common); | ||
| 29 | let onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg); | ||
| 30 | |||
| 31 | let mut sensor = Ds18b20::new(onewire); | ||
| 32 | |||
| 33 | loop { | ||
| 34 | sensor.start().await; // Start a new measurement | ||
| 35 | Timer::after_secs(1).await; // Allow 1s for the measurement to finish | ||
| 36 | match sensor.temperature().await { | ||
| 37 | Ok(temp) => info!("temp = {:?} deg C", temp), | ||
| 38 | _ => error!("sensor error"), | ||
| 39 | } | ||
| 40 | Timer::after_secs(1).await; | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | /// DS18B20 temperature sensor driver | ||
| 45 | pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> { | ||
| 46 | wire: PioOneWire<'d, PIO, SM>, | ||
| 47 | } | ||
| 48 | |||
| 49 | impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> { | ||
| 50 | pub fn new(wire: PioOneWire<'d, PIO, SM>) -> Self { | ||
| 51 | Self { wire } | ||
| 52 | } | ||
| 53 | |||
| 54 | /// Calculate CRC8 of the data | ||
| 55 | fn crc8(data: &[u8]) -> u8 { | ||
| 56 | let mut temp; | ||
| 57 | let mut data_byte; | ||
| 58 | let mut crc = 0; | ||
| 59 | for b in data { | ||
| 60 | data_byte = *b; | ||
| 61 | for _ in 0..8 { | ||
| 62 | temp = (crc ^ data_byte) & 0x01; | ||
| 63 | crc >>= 1; | ||
| 64 | if temp != 0 { | ||
| 65 | crc ^= 0x8C; | ||
| 66 | } | ||
| 67 | data_byte >>= 1; | ||
| 68 | } | ||
| 69 | } | ||
| 70 | crc | ||
| 71 | } | ||
| 72 | |||
| 73 | /// Start a new measurement. Allow at least 1000ms before getting `temperature`. | ||
| 74 | pub async fn start(&mut self) { | ||
| 75 | self.wire.write_bytes(&[0xCC, 0x44]).await; | ||
| 76 | } | ||
| 77 | |||
| 78 | /// Read the temperature. Ensure >1000ms has passed since `start` before calling this. | ||
| 79 | pub async fn temperature(&mut self) -> Result<f32, ()> { | ||
| 80 | self.wire.write_bytes(&[0xCC, 0xBE]).await; | ||
| 81 | let mut data = [0; 9]; | ||
| 82 | self.wire.read_bytes(&mut data).await; | ||
| 83 | match Self::crc8(&data) == 0 { | ||
| 84 | true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.), | ||
| 85 | false => Err(()), | ||
| 86 | } | ||
| 87 | } | ||
| 88 | } | ||
diff --git a/examples/rp23/src/bin/pio_pwm.rs b/examples/rp23/src/bin/pio_pwm.rs index 587f91ac3..11af62a7a 100644 --- a/examples/rp23/src/bin/pio_pwm.rs +++ b/examples/rp23/src/bin/pio_pwm.rs | |||
| @@ -5,122 +5,32 @@ | |||
| 5 | use core::time::Duration; | 5 | use core::time::Duration; |
| 6 | 6 | ||
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 8 | use embassy_rp::bind_interrupts; | ||
| 8 | use embassy_rp::block::ImageDef; | 9 | use embassy_rp::block::ImageDef; |
| 9 | use embassy_rp::gpio::Level; | ||
| 10 | use embassy_rp::peripherals::PIO0; | 10 | use embassy_rp::peripherals::PIO0; |
| 11 | use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine}; | 11 | use embassy_rp::pio::{InterruptHandler, Pio}; |
| 12 | use embassy_rp::{bind_interrupts, clocks}; | 12 | use embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram}; |
| 13 | use embassy_time::Timer; | 13 | use embassy_time::Timer; |
| 14 | use pio::InstructionOperands; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | 14 | use {defmt_rtt as _, panic_probe as _}; |
| 16 | 15 | ||
| 17 | #[link_section = ".start_block"] | 16 | #[link_section = ".start_block"] |
| 18 | #[used] | 17 | #[used] |
| 19 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 20 | 19 | ||
| 21 | // Program metadata for `picotool info` | ||
| 22 | #[link_section = ".bi_entries"] | ||
| 23 | #[used] | ||
| 24 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 25 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 26 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 27 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 28 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 29 | ]; | ||
| 30 | |||
| 31 | const REFRESH_INTERVAL: u64 = 20000; | 20 | const REFRESH_INTERVAL: u64 = 20000; |
| 32 | 21 | ||
| 33 | bind_interrupts!(struct Irqs { | 22 | bind_interrupts!(struct Irqs { |
| 34 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | 23 | PIO0_IRQ_0 => InterruptHandler<PIO0>; |
| 35 | }); | 24 | }); |
| 36 | 25 | ||
| 37 | pub fn to_pio_cycles(duration: Duration) -> u32 { | ||
| 38 | (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow | ||
| 39 | } | ||
| 40 | |||
| 41 | pub struct PwmPio<'d, T: Instance, const SM: usize> { | ||
| 42 | sm: StateMachine<'d, T, SM>, | ||
| 43 | } | ||
| 44 | |||
| 45 | impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> { | ||
| 46 | pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self { | ||
| 47 | let prg = pio_proc::pio_asm!( | ||
| 48 | ".side_set 1 opt" | ||
| 49 | "pull noblock side 0" | ||
| 50 | "mov x, osr" | ||
| 51 | "mov y, isr" | ||
| 52 | "countloop:" | ||
| 53 | "jmp x!=y noset" | ||
| 54 | "jmp skip side 1" | ||
| 55 | "noset:" | ||
| 56 | "nop" | ||
| 57 | "skip:" | ||
| 58 | "jmp y-- countloop" | ||
| 59 | ); | ||
| 60 | |||
| 61 | pio.load_program(&prg.program); | ||
| 62 | let pin = pio.make_pio_pin(pin); | ||
| 63 | sm.set_pins(Level::High, &[&pin]); | ||
| 64 | sm.set_pin_dirs(Direction::Out, &[&pin]); | ||
| 65 | |||
| 66 | let mut cfg = Config::default(); | ||
| 67 | cfg.use_program(&pio.load_program(&prg.program), &[&pin]); | ||
| 68 | |||
| 69 | sm.set_config(&cfg); | ||
| 70 | |||
| 71 | Self { sm } | ||
| 72 | } | ||
| 73 | |||
| 74 | pub fn start(&mut self) { | ||
| 75 | self.sm.set_enable(true); | ||
| 76 | } | ||
| 77 | |||
| 78 | pub fn stop(&mut self) { | ||
| 79 | self.sm.set_enable(false); | ||
| 80 | } | ||
| 81 | |||
| 82 | pub fn set_period(&mut self, duration: Duration) { | ||
| 83 | let is_enabled = self.sm.is_enabled(); | ||
| 84 | while !self.sm.tx().empty() {} // Make sure that the queue is empty | ||
| 85 | self.sm.set_enable(false); | ||
| 86 | self.sm.tx().push(to_pio_cycles(duration)); | ||
| 87 | unsafe { | ||
| 88 | self.sm.exec_instr( | ||
| 89 | InstructionOperands::PULL { | ||
| 90 | if_empty: false, | ||
| 91 | block: false, | ||
| 92 | } | ||
| 93 | .encode(), | ||
| 94 | ); | ||
| 95 | self.sm.exec_instr( | ||
| 96 | InstructionOperands::OUT { | ||
| 97 | destination: ::pio::OutDestination::ISR, | ||
| 98 | bit_count: 32, | ||
| 99 | } | ||
| 100 | .encode(), | ||
| 101 | ); | ||
| 102 | }; | ||
| 103 | if is_enabled { | ||
| 104 | self.sm.set_enable(true) // Enable if previously enabled | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | pub fn set_level(&mut self, level: u32) { | ||
| 109 | self.sm.tx().push(level); | ||
| 110 | } | ||
| 111 | |||
| 112 | pub fn write(&mut self, duration: Duration) { | ||
| 113 | self.set_level(to_pio_cycles(duration)); | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | #[embassy_executor::main] | 26 | #[embassy_executor::main] |
| 118 | async fn main(_spawner: Spawner) { | 27 | async fn main(_spawner: Spawner) { |
| 119 | let p = embassy_rp::init(Default::default()); | 28 | let p = embassy_rp::init(Default::default()); |
| 120 | let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); | 29 | let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); |
| 121 | 30 | ||
| 122 | // Note that PIN_25 is the led pin on the Pico | 31 | // Note that PIN_25 is the led pin on the Pico |
| 123 | let mut pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_25); | 32 | let prg = PioPwmProgram::new(&mut common); |
| 33 | let mut pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_25, &prg); | ||
| 124 | pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL)); | 34 | pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL)); |
| 125 | pwm_pio.start(); | 35 | pwm_pio.start(); |
| 126 | 36 | ||
diff --git a/examples/rp23/src/bin/pio_rotary_encoder.rs b/examples/rp23/src/bin/pio_rotary_encoder.rs index c147351e8..2bb0e67f9 100644 --- a/examples/rp23/src/bin/pio_rotary_encoder.rs +++ b/examples/rp23/src/bin/pio_rotary_encoder.rs | |||
| @@ -5,85 +5,35 @@ | |||
| 5 | 5 | ||
| 6 | use defmt::info; | 6 | use defmt::info; |
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 8 | use embassy_rp::bind_interrupts; | ||
| 8 | use embassy_rp::block::ImageDef; | 9 | use embassy_rp::block::ImageDef; |
| 9 | use embassy_rp::gpio::Pull; | ||
| 10 | use embassy_rp::peripherals::PIO0; | 10 | use embassy_rp::peripherals::PIO0; |
| 11 | use embassy_rp::{bind_interrupts, pio}; | 11 | use embassy_rp::pio::{InterruptHandler, Pio}; |
| 12 | use fixed::traits::ToFixed; | 12 | use embassy_rp::pio_programs::rotary_encoder::{Direction, PioEncoder, PioEncoderProgram}; |
| 13 | use pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftDirection, StateMachine}; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 15 | 14 | ||
| 16 | #[link_section = ".start_block"] | 15 | #[link_section = ".start_block"] |
| 17 | #[used] | 16 | #[used] |
| 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 17 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 19 | 18 | ||
| 20 | // Program metadata for `picotool info` | ||
| 21 | #[link_section = ".bi_entries"] | ||
| 22 | #[used] | ||
| 23 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 24 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 25 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 26 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 27 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 28 | ]; | ||
| 29 | |||
| 30 | bind_interrupts!(struct Irqs { | 19 | bind_interrupts!(struct Irqs { |
| 31 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | 20 | PIO0_IRQ_0 => InterruptHandler<PIO0>; |
| 32 | }); | 21 | }); |
| 33 | 22 | ||
| 34 | pub struct PioEncoder<'d, T: Instance, const SM: usize> { | 23 | #[embassy_executor::task] |
| 35 | sm: StateMachine<'d, T, SM>, | 24 | async fn encoder_0(mut encoder: PioEncoder<'static, PIO0, 0>) { |
| 36 | } | 25 | let mut count = 0; |
| 37 | 26 | loop { | |
| 38 | impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> { | 27 | info!("Count: {}", count); |
| 39 | pub fn new( | 28 | count += match encoder.read().await { |
| 40 | pio: &mut Common<'d, T>, | 29 | Direction::Clockwise => 1, |
| 41 | mut sm: StateMachine<'d, T, SM>, | 30 | Direction::CounterClockwise => -1, |
| 42 | pin_a: impl PioPin, | 31 | }; |
| 43 | pin_b: impl PioPin, | ||
| 44 | ) -> Self { | ||
| 45 | let mut pin_a = pio.make_pio_pin(pin_a); | ||
| 46 | let mut pin_b = pio.make_pio_pin(pin_b); | ||
| 47 | pin_a.set_pull(Pull::Up); | ||
| 48 | pin_b.set_pull(Pull::Up); | ||
| 49 | sm.set_pin_dirs(pio::Direction::In, &[&pin_a, &pin_b]); | ||
| 50 | |||
| 51 | let prg = pio_proc::pio_asm!("wait 1 pin 1", "wait 0 pin 1", "in pins, 2", "push",); | ||
| 52 | |||
| 53 | let mut cfg = Config::default(); | ||
| 54 | cfg.set_in_pins(&[&pin_a, &pin_b]); | ||
| 55 | cfg.fifo_join = FifoJoin::RxOnly; | ||
| 56 | cfg.shift_in.direction = ShiftDirection::Left; | ||
| 57 | cfg.clock_divider = 10_000.to_fixed(); | ||
| 58 | cfg.use_program(&pio.load_program(&prg.program), &[]); | ||
| 59 | sm.set_config(&cfg); | ||
| 60 | sm.set_enable(true); | ||
| 61 | Self { sm } | ||
| 62 | } | ||
| 63 | |||
| 64 | pub async fn read(&mut self) -> Direction { | ||
| 65 | loop { | ||
| 66 | match self.sm.rx().wait_pull().await { | ||
| 67 | 0 => return Direction::CounterClockwise, | ||
| 68 | 1 => return Direction::Clockwise, | ||
| 69 | _ => {} | ||
| 70 | } | ||
| 71 | } | ||
| 72 | } | 32 | } |
| 73 | } | 33 | } |
| 74 | 34 | ||
| 75 | pub enum Direction { | 35 | #[embassy_executor::task] |
| 76 | Clockwise, | 36 | async fn encoder_1(mut encoder: PioEncoder<'static, PIO0, 1>) { |
| 77 | CounterClockwise, | ||
| 78 | } | ||
| 79 | |||
| 80 | #[embassy_executor::main] | ||
| 81 | async fn main(_spawner: Spawner) { | ||
| 82 | let p = embassy_rp::init(Default::default()); | ||
| 83 | let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); | ||
| 84 | |||
| 85 | let mut encoder = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5); | ||
| 86 | |||
| 87 | let mut count = 0; | 37 | let mut count = 0; |
| 88 | loop { | 38 | loop { |
| 89 | info!("Count: {}", count); | 39 | info!("Count: {}", count); |
| @@ -93,3 +43,18 @@ async fn main(_spawner: Spawner) { | |||
| 93 | }; | 43 | }; |
| 94 | } | 44 | } |
| 95 | } | 45 | } |
| 46 | |||
| 47 | #[embassy_executor::main] | ||
| 48 | async fn main(spawner: Spawner) { | ||
| 49 | let p = embassy_rp::init(Default::default()); | ||
| 50 | let Pio { | ||
| 51 | mut common, sm0, sm1, .. | ||
| 52 | } = Pio::new(p.PIO0, Irqs); | ||
| 53 | |||
| 54 | let prg = PioEncoderProgram::new(&mut common); | ||
| 55 | let encoder0 = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5, &prg); | ||
| 56 | let encoder1 = PioEncoder::new(&mut common, sm1, p.PIN_6, p.PIN_7, &prg); | ||
| 57 | |||
| 58 | spawner.must_spawn(encoder_0(encoder0)); | ||
| 59 | spawner.must_spawn(encoder_1(encoder1)); | ||
| 60 | } | ||
diff --git a/examples/rp23/src/bin/pio_servo.rs b/examples/rp23/src/bin/pio_servo.rs index 5e8714178..4e94103f1 100644 --- a/examples/rp23/src/bin/pio_servo.rs +++ b/examples/rp23/src/bin/pio_servo.rs | |||
| @@ -5,29 +5,18 @@ | |||
| 5 | use core::time::Duration; | 5 | use core::time::Duration; |
| 6 | 6 | ||
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 8 | use embassy_rp::bind_interrupts; | ||
| 8 | use embassy_rp::block::ImageDef; | 9 | use embassy_rp::block::ImageDef; |
| 9 | use embassy_rp::gpio::Level; | ||
| 10 | use embassy_rp::peripherals::PIO0; | 10 | use embassy_rp::peripherals::PIO0; |
| 11 | use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine}; | 11 | use embassy_rp::pio::{Instance, InterruptHandler, Pio}; |
| 12 | use embassy_rp::{bind_interrupts, clocks}; | 12 | use embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram}; |
| 13 | use embassy_time::Timer; | 13 | use embassy_time::Timer; |
| 14 | use pio::InstructionOperands; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | 14 | use {defmt_rtt as _, panic_probe as _}; |
| 16 | 15 | ||
| 17 | #[link_section = ".start_block"] | 16 | #[link_section = ".start_block"] |
| 18 | #[used] | 17 | #[used] |
| 19 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 20 | 19 | ||
| 21 | // Program metadata for `picotool info` | ||
| 22 | #[link_section = ".bi_entries"] | ||
| 23 | #[used] | ||
| 24 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 25 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 26 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 27 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 28 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 29 | ]; | ||
| 30 | |||
| 31 | const DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo | 20 | const DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo |
| 32 | const DEFAULT_MAX_PULSE_WIDTH: u64 = 2000; // uncalibrated default, the longest duty cycle sent to a servo | 21 | const DEFAULT_MAX_PULSE_WIDTH: u64 = 2000; // uncalibrated default, the longest duty cycle sent to a servo |
| 33 | const DEFAULT_MAX_DEGREE_ROTATION: u64 = 160; // 160 degrees is typical | 22 | const DEFAULT_MAX_DEGREE_ROTATION: u64 = 160; // 160 degrees is typical |
| @@ -37,88 +26,8 @@ bind_interrupts!(struct Irqs { | |||
| 37 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | 26 | PIO0_IRQ_0 => InterruptHandler<PIO0>; |
| 38 | }); | 27 | }); |
| 39 | 28 | ||
| 40 | pub fn to_pio_cycles(duration: Duration) -> u32 { | ||
| 41 | (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow | ||
| 42 | } | ||
| 43 | |||
| 44 | pub struct PwmPio<'d, T: Instance, const SM: usize> { | ||
| 45 | sm: StateMachine<'d, T, SM>, | ||
| 46 | } | ||
| 47 | |||
| 48 | impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> { | ||
| 49 | pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self { | ||
| 50 | let prg = pio_proc::pio_asm!( | ||
| 51 | ".side_set 1 opt" | ||
| 52 | "pull noblock side 0" | ||
| 53 | "mov x, osr" | ||
| 54 | "mov y, isr" | ||
| 55 | "countloop:" | ||
| 56 | "jmp x!=y noset" | ||
| 57 | "jmp skip side 1" | ||
| 58 | "noset:" | ||
| 59 | "nop" | ||
| 60 | "skip:" | ||
| 61 | "jmp y-- countloop" | ||
| 62 | ); | ||
| 63 | |||
| 64 | pio.load_program(&prg.program); | ||
| 65 | let pin = pio.make_pio_pin(pin); | ||
| 66 | sm.set_pins(Level::High, &[&pin]); | ||
| 67 | sm.set_pin_dirs(Direction::Out, &[&pin]); | ||
| 68 | |||
| 69 | let mut cfg = Config::default(); | ||
| 70 | cfg.use_program(&pio.load_program(&prg.program), &[&pin]); | ||
| 71 | |||
| 72 | sm.set_config(&cfg); | ||
| 73 | |||
| 74 | Self { sm } | ||
| 75 | } | ||
| 76 | |||
| 77 | pub fn start(&mut self) { | ||
| 78 | self.sm.set_enable(true); | ||
| 79 | } | ||
| 80 | |||
| 81 | pub fn stop(&mut self) { | ||
| 82 | self.sm.set_enable(false); | ||
| 83 | } | ||
| 84 | |||
| 85 | pub fn set_period(&mut self, duration: Duration) { | ||
| 86 | let is_enabled = self.sm.is_enabled(); | ||
| 87 | while !self.sm.tx().empty() {} // Make sure that the queue is empty | ||
| 88 | self.sm.set_enable(false); | ||
| 89 | self.sm.tx().push(to_pio_cycles(duration)); | ||
| 90 | unsafe { | ||
| 91 | self.sm.exec_instr( | ||
| 92 | InstructionOperands::PULL { | ||
| 93 | if_empty: false, | ||
| 94 | block: false, | ||
| 95 | } | ||
| 96 | .encode(), | ||
| 97 | ); | ||
| 98 | self.sm.exec_instr( | ||
| 99 | InstructionOperands::OUT { | ||
| 100 | destination: ::pio::OutDestination::ISR, | ||
| 101 | bit_count: 32, | ||
| 102 | } | ||
| 103 | .encode(), | ||
| 104 | ); | ||
| 105 | }; | ||
| 106 | if is_enabled { | ||
| 107 | self.sm.set_enable(true) // Enable if previously enabled | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | pub fn set_level(&mut self, level: u32) { | ||
| 112 | self.sm.tx().push(level); | ||
| 113 | } | ||
| 114 | |||
| 115 | pub fn write(&mut self, duration: Duration) { | ||
| 116 | self.set_level(to_pio_cycles(duration)); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | pub struct ServoBuilder<'d, T: Instance, const SM: usize> { | 29 | pub struct ServoBuilder<'d, T: Instance, const SM: usize> { |
| 121 | pwm: PwmPio<'d, T, SM>, | 30 | pwm: PioPwm<'d, T, SM>, |
| 122 | period: Duration, | 31 | period: Duration, |
| 123 | min_pulse_width: Duration, | 32 | min_pulse_width: Duration, |
| 124 | max_pulse_width: Duration, | 33 | max_pulse_width: Duration, |
| @@ -126,7 +35,7 @@ pub struct ServoBuilder<'d, T: Instance, const SM: usize> { | |||
| 126 | } | 35 | } |
| 127 | 36 | ||
| 128 | impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> { | 37 | impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> { |
| 129 | pub fn new(pwm: PwmPio<'d, T, SM>) -> Self { | 38 | pub fn new(pwm: PioPwm<'d, T, SM>) -> Self { |
| 130 | Self { | 39 | Self { |
| 131 | pwm, | 40 | pwm, |
| 132 | period: Duration::from_micros(REFRESH_INTERVAL), | 41 | period: Duration::from_micros(REFRESH_INTERVAL), |
| @@ -168,7 +77,7 @@ impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> { | |||
| 168 | } | 77 | } |
| 169 | 78 | ||
| 170 | pub struct Servo<'d, T: Instance, const SM: usize> { | 79 | pub struct Servo<'d, T: Instance, const SM: usize> { |
| 171 | pwm: PwmPio<'d, T, SM>, | 80 | pwm: PioPwm<'d, T, SM>, |
| 172 | min_pulse_width: Duration, | 81 | min_pulse_width: Duration, |
| 173 | max_pulse_width: Duration, | 82 | max_pulse_width: Duration, |
| 174 | max_degree_rotation: u64, | 83 | max_degree_rotation: u64, |
| @@ -205,7 +114,8 @@ async fn main(_spawner: Spawner) { | |||
| 205 | let p = embassy_rp::init(Default::default()); | 114 | let p = embassy_rp::init(Default::default()); |
| 206 | let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); | 115 | let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); |
| 207 | 116 | ||
| 208 | let pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_1); | 117 | let prg = PioPwmProgram::new(&mut common); |
| 118 | let pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_1, &prg); | ||
| 209 | let mut servo = ServoBuilder::new(pwm_pio) | 119 | let mut servo = ServoBuilder::new(pwm_pio) |
| 210 | .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo | 120 | .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo |
| 211 | .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment. | 121 | .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment. |
diff --git a/examples/rp23/src/bin/pio_stepper.rs b/examples/rp23/src/bin/pio_stepper.rs index 24785443b..4fabe78ca 100644 --- a/examples/rp23/src/bin/pio_stepper.rs +++ b/examples/rp23/src/bin/pio_stepper.rs | |||
| @@ -3,158 +3,25 @@ | |||
| 3 | 3 | ||
| 4 | #![no_std] | 4 | #![no_std] |
| 5 | #![no_main] | 5 | #![no_main] |
| 6 | use core::mem::{self, MaybeUninit}; | ||
| 7 | 6 | ||
| 8 | use defmt::info; | 7 | use defmt::info; |
| 9 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 10 | use embassy_rp::bind_interrupts; | 9 | use embassy_rp::bind_interrupts; |
| 11 | use embassy_rp::block::ImageDef; | 10 | use embassy_rp::block::ImageDef; |
| 12 | use embassy_rp::peripherals::PIO0; | 11 | use embassy_rp::peripherals::PIO0; |
| 13 | use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Irq, Pio, PioPin, StateMachine}; | 12 | use embassy_rp::pio::{InterruptHandler, Pio}; |
| 13 | use embassy_rp::pio_programs::stepper::{PioStepper, PioStepperProgram}; | ||
| 14 | use embassy_time::{with_timeout, Duration, Timer}; | 14 | use embassy_time::{with_timeout, Duration, Timer}; |
| 15 | use fixed::traits::ToFixed; | ||
| 16 | use fixed::types::extra::U8; | ||
| 17 | use fixed::FixedU32; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | 15 | use {defmt_rtt as _, panic_probe as _}; |
| 19 | 16 | ||
| 20 | #[link_section = ".start_block"] | 17 | #[link_section = ".start_block"] |
| 21 | #[used] | 18 | #[used] |
| 22 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 19 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 23 | 20 | ||
| 24 | // Program metadata for `picotool info` | ||
| 25 | #[link_section = ".bi_entries"] | ||
| 26 | #[used] | ||
| 27 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 28 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 29 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 30 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 31 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 32 | ]; | ||
| 33 | |||
| 34 | bind_interrupts!(struct Irqs { | 21 | bind_interrupts!(struct Irqs { |
| 35 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | 22 | PIO0_IRQ_0 => InterruptHandler<PIO0>; |
| 36 | }); | 23 | }); |
| 37 | 24 | ||
| 38 | pub struct PioStepper<'d, T: Instance, const SM: usize> { | ||
| 39 | irq: Irq<'d, T, SM>, | ||
| 40 | sm: StateMachine<'d, T, SM>, | ||
| 41 | } | ||
| 42 | |||
| 43 | impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> { | ||
| 44 | pub fn new( | ||
| 45 | pio: &mut Common<'d, T>, | ||
| 46 | mut sm: StateMachine<'d, T, SM>, | ||
| 47 | irq: Irq<'d, T, SM>, | ||
| 48 | pin0: impl PioPin, | ||
| 49 | pin1: impl PioPin, | ||
| 50 | pin2: impl PioPin, | ||
| 51 | pin3: impl PioPin, | ||
| 52 | ) -> Self { | ||
| 53 | let prg = pio_proc::pio_asm!( | ||
| 54 | "pull block", | ||
| 55 | "mov x, osr", | ||
| 56 | "pull block", | ||
| 57 | "mov y, osr", | ||
| 58 | "jmp !x end", | ||
| 59 | "loop:", | ||
| 60 | "jmp !osre step", | ||
| 61 | "mov osr, y", | ||
| 62 | "step:", | ||
| 63 | "out pins, 4 [31]" | ||
| 64 | "jmp x-- loop", | ||
| 65 | "end:", | ||
| 66 | "irq 0 rel" | ||
| 67 | ); | ||
| 68 | let pin0 = pio.make_pio_pin(pin0); | ||
| 69 | let pin1 = pio.make_pio_pin(pin1); | ||
| 70 | let pin2 = pio.make_pio_pin(pin2); | ||
| 71 | let pin3 = pio.make_pio_pin(pin3); | ||
| 72 | sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]); | ||
| 73 | let mut cfg = Config::default(); | ||
| 74 | cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]); | ||
| 75 | cfg.clock_divider = (125_000_000 / (100 * 136)).to_fixed(); | ||
| 76 | cfg.use_program(&pio.load_program(&prg.program), &[]); | ||
| 77 | sm.set_config(&cfg); | ||
| 78 | sm.set_enable(true); | ||
| 79 | Self { irq, sm } | ||
| 80 | } | ||
| 81 | |||
| 82 | // Set pulse frequency | ||
| 83 | pub fn set_frequency(&mut self, freq: u32) { | ||
| 84 | let clock_divider: FixedU32<U8> = (125_000_000 / (freq * 136)).to_fixed(); | ||
| 85 | assert!(clock_divider <= 65536, "clkdiv must be <= 65536"); | ||
| 86 | assert!(clock_divider >= 1, "clkdiv must be >= 1"); | ||
| 87 | self.sm.set_clock_divider(clock_divider); | ||
| 88 | self.sm.clkdiv_restart(); | ||
| 89 | } | ||
| 90 | |||
| 91 | // Full step, one phase | ||
| 92 | pub async fn step(&mut self, steps: i32) { | ||
| 93 | if steps > 0 { | ||
| 94 | self.run(steps, 0b1000_0100_0010_0001_1000_0100_0010_0001).await | ||
| 95 | } else { | ||
| 96 | self.run(-steps, 0b0001_0010_0100_1000_0001_0010_0100_1000).await | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | // Full step, two phase | ||
| 101 | pub async fn step2(&mut self, steps: i32) { | ||
| 102 | if steps > 0 { | ||
| 103 | self.run(steps, 0b1001_1100_0110_0011_1001_1100_0110_0011).await | ||
| 104 | } else { | ||
| 105 | self.run(-steps, 0b0011_0110_1100_1001_0011_0110_1100_1001).await | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | // Half step | ||
| 110 | pub async fn step_half(&mut self, steps: i32) { | ||
| 111 | if steps > 0 { | ||
| 112 | self.run(steps, 0b1001_1000_1100_0100_0110_0010_0011_0001).await | ||
| 113 | } else { | ||
| 114 | self.run(-steps, 0b0001_0011_0010_0110_0100_1100_1000_1001).await | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | async fn run(&mut self, steps: i32, pattern: u32) { | ||
| 119 | self.sm.tx().wait_push(steps as u32).await; | ||
| 120 | self.sm.tx().wait_push(pattern).await; | ||
| 121 | let drop = OnDrop::new(|| { | ||
| 122 | self.sm.clear_fifos(); | ||
| 123 | unsafe { | ||
| 124 | self.sm.exec_instr( | ||
| 125 | pio::InstructionOperands::JMP { | ||
| 126 | address: 0, | ||
| 127 | condition: pio::JmpCondition::Always, | ||
| 128 | } | ||
| 129 | .encode(), | ||
| 130 | ); | ||
| 131 | } | ||
| 132 | }); | ||
| 133 | self.irq.wait().await; | ||
| 134 | drop.defuse(); | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | struct OnDrop<F: FnOnce()> { | ||
| 139 | f: MaybeUninit<F>, | ||
| 140 | } | ||
| 141 | |||
| 142 | impl<F: FnOnce()> OnDrop<F> { | ||
| 143 | pub fn new(f: F) -> Self { | ||
| 144 | Self { f: MaybeUninit::new(f) } | ||
| 145 | } | ||
| 146 | |||
| 147 | pub fn defuse(self) { | ||
| 148 | mem::forget(self) | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | impl<F: FnOnce()> Drop for OnDrop<F> { | ||
| 153 | fn drop(&mut self) { | ||
| 154 | unsafe { self.f.as_ptr().read()() } | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | #[embassy_executor::main] | 25 | #[embassy_executor::main] |
| 159 | async fn main(_spawner: Spawner) { | 26 | async fn main(_spawner: Spawner) { |
| 160 | let p = embassy_rp::init(Default::default()); | 27 | let p = embassy_rp::init(Default::default()); |
| @@ -162,14 +29,18 @@ async fn main(_spawner: Spawner) { | |||
| 162 | mut common, irq0, sm0, .. | 29 | mut common, irq0, sm0, .. |
| 163 | } = Pio::new(p.PIO0, Irqs); | 30 | } = Pio::new(p.PIO0, Irqs); |
| 164 | 31 | ||
| 165 | let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7); | 32 | let prg = PioStepperProgram::new(&mut common); |
| 33 | let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7, &prg); | ||
| 166 | stepper.set_frequency(120); | 34 | stepper.set_frequency(120); |
| 167 | loop { | 35 | loop { |
| 168 | info!("CW full steps"); | 36 | info!("CW full steps"); |
| 169 | stepper.step(1000).await; | 37 | stepper.step(1000).await; |
| 170 | 38 | ||
| 171 | info!("CCW full steps, drop after 1 sec"); | 39 | info!("CCW full steps, drop after 1 sec"); |
| 172 | if let Err(_) = with_timeout(Duration::from_secs(1), stepper.step(i32::MIN)).await { | 40 | if with_timeout(Duration::from_secs(1), stepper.step(-i32::MAX)) |
| 41 | .await | ||
| 42 | .is_err() | ||
| 43 | { | ||
| 173 | info!("Time's up!"); | 44 | info!("Time's up!"); |
| 174 | Timer::after(Duration::from_secs(1)).await; | 45 | Timer::after(Duration::from_secs(1)).await; |
| 175 | } | 46 | } |
diff --git a/examples/rp23/src/bin/pio_uart.rs b/examples/rp23/src/bin/pio_uart.rs new file mode 100644 index 000000000..f8398c22a --- /dev/null +++ b/examples/rp23/src/bin/pio_uart.rs | |||
| @@ -0,0 +1,202 @@ | |||
| 1 | //! This example shows how to use the PIO module in the RP2040 chip to implement a duplex UART. | ||
| 2 | //! The PIO module is a very powerful peripheral that can be used to implement many different | ||
| 3 | //! protocols. It is a very flexible state machine that can be programmed to do almost anything. | ||
| 4 | //! | ||
| 5 | //! This example opens up a USB device that implements a CDC ACM serial port. It then uses the | ||
| 6 | //! PIO module to implement a UART that is connected to the USB serial port. This allows you to | ||
| 7 | //! communicate with a device connected to the RP2040 over USB serial. | ||
| 8 | |||
| 9 | #![no_std] | ||
| 10 | #![no_main] | ||
| 11 | #![allow(async_fn_in_trait)] | ||
| 12 | |||
| 13 | use defmt::{info, panic, trace}; | ||
| 14 | use embassy_executor::Spawner; | ||
| 15 | use embassy_futures::join::{join, join3}; | ||
| 16 | use embassy_rp::block::ImageDef; | ||
| 17 | use embassy_rp::peripherals::{PIO0, USB}; | ||
| 18 | use embassy_rp::pio_programs::uart::{PioUartRx, PioUartRxProgram, PioUartTx, PioUartTxProgram}; | ||
| 19 | use embassy_rp::usb::{Driver, Instance, InterruptHandler}; | ||
| 20 | use embassy_rp::{bind_interrupts, pio}; | ||
| 21 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
| 22 | use embassy_sync::pipe::Pipe; | ||
| 23 | use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State}; | ||
| 24 | use embassy_usb::driver::EndpointError; | ||
| 25 | use embassy_usb::{Builder, Config}; | ||
| 26 | use embedded_io_async::{Read, Write}; | ||
| 27 | use {defmt_rtt as _, panic_probe as _}; | ||
| 28 | |||
| 29 | #[link_section = ".start_block"] | ||
| 30 | #[used] | ||
| 31 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 32 | |||
| 33 | bind_interrupts!(struct Irqs { | ||
| 34 | USBCTRL_IRQ => InterruptHandler<USB>; | ||
| 35 | PIO0_IRQ_0 => pio::InterruptHandler<PIO0>; | ||
| 36 | }); | ||
| 37 | |||
| 38 | #[embassy_executor::main] | ||
| 39 | async fn main(_spawner: Spawner) { | ||
| 40 | info!("Hello there!"); | ||
| 41 | |||
| 42 | let p = embassy_rp::init(Default::default()); | ||
| 43 | |||
| 44 | // Create the driver, from the HAL. | ||
| 45 | let driver = Driver::new(p.USB, Irqs); | ||
| 46 | |||
| 47 | // Create embassy-usb Config | ||
| 48 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 49 | config.manufacturer = Some("Embassy"); | ||
| 50 | config.product = Some("PIO UART example"); | ||
| 51 | config.serial_number = Some("12345678"); | ||
| 52 | config.max_power = 100; | ||
| 53 | config.max_packet_size_0 = 64; | ||
| 54 | |||
| 55 | // Required for windows compatibility. | ||
| 56 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 57 | config.device_class = 0xEF; | ||
| 58 | config.device_sub_class = 0x02; | ||
| 59 | config.device_protocol = 0x01; | ||
| 60 | config.composite_with_iads = true; | ||
| 61 | |||
| 62 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 63 | // It needs some buffers for building the descriptors. | ||
| 64 | let mut config_descriptor = [0; 256]; | ||
| 65 | let mut bos_descriptor = [0; 256]; | ||
| 66 | let mut control_buf = [0; 64]; | ||
| 67 | |||
| 68 | let mut state = State::new(); | ||
| 69 | |||
| 70 | let mut builder = Builder::new( | ||
| 71 | driver, | ||
| 72 | config, | ||
| 73 | &mut config_descriptor, | ||
| 74 | &mut bos_descriptor, | ||
| 75 | &mut [], // no msos descriptors | ||
| 76 | &mut control_buf, | ||
| 77 | ); | ||
| 78 | |||
| 79 | // Create classes on the builder. | ||
| 80 | let class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 81 | |||
| 82 | // Build the builder. | ||
| 83 | let mut usb = builder.build(); | ||
| 84 | |||
| 85 | // Run the USB device. | ||
| 86 | let usb_fut = usb.run(); | ||
| 87 | |||
| 88 | // PIO UART setup | ||
| 89 | let pio::Pio { | ||
| 90 | mut common, sm0, sm1, .. | ||
| 91 | } = pio::Pio::new(p.PIO0, Irqs); | ||
| 92 | |||
| 93 | let tx_program = PioUartTxProgram::new(&mut common); | ||
| 94 | let mut uart_tx = PioUartTx::new(9600, &mut common, sm0, p.PIN_4, &tx_program); | ||
| 95 | |||
| 96 | let rx_program = PioUartRxProgram::new(&mut common); | ||
| 97 | let mut uart_rx = PioUartRx::new(9600, &mut common, sm1, p.PIN_5, &rx_program); | ||
| 98 | |||
| 99 | // Pipe setup | ||
| 100 | let mut usb_pipe: Pipe<NoopRawMutex, 20> = Pipe::new(); | ||
| 101 | let (mut usb_pipe_reader, mut usb_pipe_writer) = usb_pipe.split(); | ||
| 102 | |||
| 103 | let mut uart_pipe: Pipe<NoopRawMutex, 20> = Pipe::new(); | ||
| 104 | let (mut uart_pipe_reader, mut uart_pipe_writer) = uart_pipe.split(); | ||
| 105 | |||
| 106 | let (mut usb_tx, mut usb_rx) = class.split(); | ||
| 107 | |||
| 108 | // Read + write from USB | ||
| 109 | let usb_future = async { | ||
| 110 | loop { | ||
| 111 | info!("Wait for USB connection"); | ||
| 112 | usb_rx.wait_connection().await; | ||
| 113 | info!("Connected"); | ||
| 114 | let _ = join( | ||
| 115 | usb_read(&mut usb_rx, &mut uart_pipe_writer), | ||
| 116 | usb_write(&mut usb_tx, &mut usb_pipe_reader), | ||
| 117 | ) | ||
| 118 | .await; | ||
| 119 | info!("Disconnected"); | ||
| 120 | } | ||
| 121 | }; | ||
| 122 | |||
| 123 | // Read + write from UART | ||
| 124 | let uart_future = join( | ||
| 125 | uart_read(&mut uart_rx, &mut usb_pipe_writer), | ||
| 126 | uart_write(&mut uart_tx, &mut uart_pipe_reader), | ||
| 127 | ); | ||
| 128 | |||
| 129 | // Run everything concurrently. | ||
| 130 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 131 | join3(usb_fut, usb_future, uart_future).await; | ||
| 132 | } | ||
| 133 | |||
| 134 | struct Disconnected {} | ||
| 135 | |||
| 136 | impl From<EndpointError> for Disconnected { | ||
| 137 | fn from(val: EndpointError) -> Self { | ||
| 138 | match val { | ||
| 139 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 140 | EndpointError::Disabled => Disconnected {}, | ||
| 141 | } | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | /// Read from the USB and write it to the UART TX pipe | ||
| 146 | async fn usb_read<'d, T: Instance + 'd>( | ||
| 147 | usb_rx: &mut Receiver<'d, Driver<'d, T>>, | ||
| 148 | uart_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>, | ||
| 149 | ) -> Result<(), Disconnected> { | ||
| 150 | let mut buf = [0; 64]; | ||
| 151 | loop { | ||
| 152 | let n = usb_rx.read_packet(&mut buf).await?; | ||
| 153 | let data = &buf[..n]; | ||
| 154 | trace!("USB IN: {:x}", data); | ||
| 155 | (*uart_pipe_writer).write(data).await; | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | /// Read from the USB TX pipe and write it to the USB | ||
| 160 | async fn usb_write<'d, T: Instance + 'd>( | ||
| 161 | usb_tx: &mut Sender<'d, Driver<'d, T>>, | ||
| 162 | usb_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>, | ||
| 163 | ) -> Result<(), Disconnected> { | ||
| 164 | let mut buf = [0; 64]; | ||
| 165 | loop { | ||
| 166 | let n = (*usb_pipe_reader).read(&mut buf).await; | ||
| 167 | let data = &buf[..n]; | ||
| 168 | trace!("USB OUT: {:x}", data); | ||
| 169 | usb_tx.write_packet(&data).await?; | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | /// Read from the UART and write it to the USB TX pipe | ||
| 174 | async fn uart_read<PIO: pio::Instance, const SM: usize>( | ||
| 175 | uart_rx: &mut PioUartRx<'_, PIO, SM>, | ||
| 176 | usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>, | ||
| 177 | ) -> ! { | ||
| 178 | let mut buf = [0; 64]; | ||
| 179 | loop { | ||
| 180 | let n = uart_rx.read(&mut buf).await.expect("UART read error"); | ||
| 181 | if n == 0 { | ||
| 182 | continue; | ||
| 183 | } | ||
| 184 | let data = &buf[..n]; | ||
| 185 | trace!("UART IN: {:x}", buf); | ||
| 186 | (*usb_pipe_writer).write(data).await; | ||
| 187 | } | ||
| 188 | } | ||
| 189 | |||
| 190 | /// Read from the UART TX pipe and write it to the UART | ||
| 191 | async fn uart_write<PIO: pio::Instance, const SM: usize>( | ||
| 192 | uart_tx: &mut PioUartTx<'_, PIO, SM>, | ||
| 193 | uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>, | ||
| 194 | ) -> ! { | ||
| 195 | let mut buf = [0; 64]; | ||
| 196 | loop { | ||
| 197 | let n = (*uart_pipe_reader).read(&mut buf).await; | ||
| 198 | let data = &buf[..n]; | ||
| 199 | trace!("UART OUT: {:x}", data); | ||
| 200 | let _ = uart_tx.write(&data).await; | ||
| 201 | } | ||
| 202 | } | ||
diff --git a/examples/rp23/src/bin/pio_ws2812.rs b/examples/rp23/src/bin/pio_ws2812.rs index 00fe5e396..4d258234e 100644 --- a/examples/rp23/src/bin/pio_ws2812.rs +++ b/examples/rp23/src/bin/pio_ws2812.rs | |||
| @@ -6,16 +6,12 @@ | |||
| 6 | 6 | ||
| 7 | use defmt::*; | 7 | use defmt::*; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_rp::bind_interrupts; | ||
| 9 | use embassy_rp::block::ImageDef; | 10 | use embassy_rp::block::ImageDef; |
| 10 | use embassy_rp::dma::{AnyChannel, Channel}; | ||
| 11 | use embassy_rp::peripherals::PIO0; | 11 | use embassy_rp::peripherals::PIO0; |
| 12 | use embassy_rp::pio::{ | 12 | use embassy_rp::pio::{InterruptHandler, Pio}; |
| 13 | Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, | 13 | use embassy_rp::pio_programs::ws2812::{PioWs2812, PioWs2812Program}; |
| 14 | }; | 14 | use embassy_time::{Duration, Ticker}; |
| 15 | use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef}; | ||
| 16 | use embassy_time::{Duration, Ticker, Timer}; | ||
| 17 | use fixed::types::U24F8; | ||
| 18 | use fixed_macro::fixed; | ||
| 19 | use smart_leds::RGB8; | 15 | use smart_leds::RGB8; |
| 20 | use {defmt_rtt as _, panic_probe as _}; | 16 | use {defmt_rtt as _, panic_probe as _}; |
| 21 | 17 | ||
| @@ -23,110 +19,10 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 23 | #[used] | 19 | #[used] |
| 24 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 20 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 25 | 21 | ||
| 26 | // Program metadata for `picotool info` | ||
| 27 | #[link_section = ".bi_entries"] | ||
| 28 | #[used] | ||
| 29 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 30 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 31 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 32 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 33 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 34 | ]; | ||
| 35 | |||
| 36 | bind_interrupts!(struct Irqs { | 22 | bind_interrupts!(struct Irqs { |
| 37 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | 23 | PIO0_IRQ_0 => InterruptHandler<PIO0>; |
| 38 | }); | 24 | }); |
| 39 | 25 | ||
| 40 | pub struct Ws2812<'d, P: Instance, const S: usize, const N: usize> { | ||
| 41 | dma: PeripheralRef<'d, AnyChannel>, | ||
| 42 | sm: StateMachine<'d, P, S>, | ||
| 43 | } | ||
| 44 | |||
| 45 | impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> { | ||
| 46 | pub fn new( | ||
| 47 | pio: &mut Common<'d, P>, | ||
| 48 | mut sm: StateMachine<'d, P, S>, | ||
| 49 | dma: impl Peripheral<P = impl Channel> + 'd, | ||
| 50 | pin: impl PioPin, | ||
| 51 | ) -> Self { | ||
| 52 | into_ref!(dma); | ||
| 53 | |||
| 54 | // Setup sm0 | ||
| 55 | |||
| 56 | // prepare the PIO program | ||
| 57 | let side_set = pio::SideSet::new(false, 1, false); | ||
| 58 | let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set); | ||
| 59 | |||
| 60 | const T1: u8 = 2; // start bit | ||
| 61 | const T2: u8 = 5; // data bit | ||
| 62 | const T3: u8 = 3; // stop bit | ||
| 63 | const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32; | ||
| 64 | |||
| 65 | let mut wrap_target = a.label(); | ||
| 66 | let mut wrap_source = a.label(); | ||
| 67 | let mut do_zero = a.label(); | ||
| 68 | a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0); | ||
| 69 | a.bind(&mut wrap_target); | ||
| 70 | // Do stop bit | ||
| 71 | a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0); | ||
| 72 | // Do start bit | ||
| 73 | a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1); | ||
| 74 | // Do data bit = 1 | ||
| 75 | a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1); | ||
| 76 | a.bind(&mut do_zero); | ||
| 77 | // Do data bit = 0 | ||
| 78 | a.nop_with_delay_and_side_set(T2 - 1, 0); | ||
| 79 | a.bind(&mut wrap_source); | ||
| 80 | |||
| 81 | let prg = a.assemble_with_wrap(wrap_source, wrap_target); | ||
| 82 | let mut cfg = Config::default(); | ||
| 83 | |||
| 84 | // Pin config | ||
| 85 | let out_pin = pio.make_pio_pin(pin); | ||
| 86 | cfg.set_out_pins(&[&out_pin]); | ||
| 87 | cfg.set_set_pins(&[&out_pin]); | ||
| 88 | |||
| 89 | cfg.use_program(&pio.load_program(&prg), &[&out_pin]); | ||
| 90 | |||
| 91 | // Clock config, measured in kHz to avoid overflows | ||
| 92 | // TODO CLOCK_FREQ should come from embassy_rp | ||
| 93 | let clock_freq = U24F8::from_num(clocks::clk_sys_freq() / 1000); | ||
| 94 | let ws2812_freq = fixed!(800: U24F8); | ||
| 95 | let bit_freq = ws2812_freq * CYCLES_PER_BIT; | ||
| 96 | cfg.clock_divider = clock_freq / bit_freq; | ||
| 97 | |||
| 98 | // FIFO config | ||
| 99 | cfg.fifo_join = FifoJoin::TxOnly; | ||
| 100 | cfg.shift_out = ShiftConfig { | ||
| 101 | auto_fill: true, | ||
| 102 | threshold: 24, | ||
| 103 | direction: ShiftDirection::Left, | ||
| 104 | }; | ||
| 105 | |||
| 106 | sm.set_config(&cfg); | ||
| 107 | sm.set_enable(true); | ||
| 108 | |||
| 109 | Self { | ||
| 110 | dma: dma.map_into(), | ||
| 111 | sm, | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | pub async fn write(&mut self, colors: &[RGB8; N]) { | ||
| 116 | // Precompute the word bytes from the colors | ||
| 117 | let mut words = [0u32; N]; | ||
| 118 | for i in 0..N { | ||
| 119 | let word = (u32::from(colors[i].g) << 24) | (u32::from(colors[i].r) << 16) | (u32::from(colors[i].b) << 8); | ||
| 120 | words[i] = word; | ||
| 121 | } | ||
| 122 | |||
| 123 | // DMA transfer | ||
| 124 | self.sm.tx().dma_push(self.dma.reborrow(), &words).await; | ||
| 125 | |||
| 126 | Timer::after_micros(55).await; | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | /// Input a value 0 to 255 to get a color value | 26 | /// Input a value 0 to 255 to get a color value |
| 131 | /// The colours are a transition r - g - b - back to r. | 27 | /// The colours are a transition r - g - b - back to r. |
| 132 | fn wheel(mut wheel_pos: u8) -> RGB8 { | 28 | fn wheel(mut wheel_pos: u8) -> RGB8 { |
| @@ -157,7 +53,8 @@ async fn main(_spawner: Spawner) { | |||
| 157 | // Common neopixel pins: | 53 | // Common neopixel pins: |
| 158 | // Thing plus: 8 | 54 | // Thing plus: 8 |
| 159 | // Adafruit Feather: 16; Adafruit Feather+RFM95: 4 | 55 | // Adafruit Feather: 16; Adafruit Feather+RFM95: 4 |
| 160 | let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16); | 56 | let program = PioWs2812Program::new(&mut common); |
| 57 | let mut ws2812 = PioWs2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16, &program); | ||
| 161 | 58 | ||
| 162 | // Loop forever making RGB values and pushing them out to the WS2812. | 59 | // Loop forever making RGB values and pushing them out to the WS2812. |
| 163 | let mut ticker = Ticker::every(Duration::from_millis(10)); | 60 | let mut ticker = Ticker::every(Duration::from_millis(10)); |
diff --git a/examples/rp23/src/bin/pwm.rs b/examples/rp23/src/bin/pwm.rs index bfc2c6f67..ed3c94f15 100644 --- a/examples/rp23/src/bin/pwm.rs +++ b/examples/rp23/src/bin/pwm.rs | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | //! This example shows how to use PWM (Pulse Width Modulation) in the RP2040 chip. | 1 | //! This example shows how to use PWM (Pulse Width Modulation) in the RP235x chip. |
| 2 | //! | 2 | //! |
| 3 | //! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin. | 3 | //! We demonstrate two ways of using PWM: |
| 4 | //! 1. Via config | ||
| 5 | //! 2. Via setting a duty cycle | ||
| 4 | 6 | ||
| 5 | #![no_std] | 7 | #![no_std] |
| 6 | #![no_main] | 8 | #![no_main] |
| @@ -8,7 +10,8 @@ | |||
| 8 | use defmt::*; | 10 | use defmt::*; |
| 9 | use embassy_executor::Spawner; | 11 | use embassy_executor::Spawner; |
| 10 | use embassy_rp::block::ImageDef; | 12 | use embassy_rp::block::ImageDef; |
| 11 | use embassy_rp::pwm::{Config, Pwm}; | 13 | use embassy_rp::peripherals::{PIN_25, PIN_4, PWM_SLICE2, PWM_SLICE4}; |
| 14 | use embassy_rp::pwm::{Config, Pwm, SetDutyCycle}; | ||
| 12 | use embassy_time::Timer; | 15 | use embassy_time::Timer; |
| 13 | use {defmt_rtt as _, panic_probe as _}; | 16 | use {defmt_rtt as _, panic_probe as _}; |
| 14 | 17 | ||
| @@ -16,24 +19,23 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 16 | #[used] | 19 | #[used] |
| 17 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 20 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 18 | 21 | ||
| 19 | // Program metadata for `picotool info` | ||
| 20 | #[link_section = ".bi_entries"] | ||
| 21 | #[used] | ||
| 22 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 23 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 24 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 25 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 26 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 27 | ]; | ||
| 28 | |||
| 29 | #[embassy_executor::main] | 22 | #[embassy_executor::main] |
| 30 | async fn main(_spawner: Spawner) { | 23 | async fn main(spawner: Spawner) { |
| 31 | let p = embassy_rp::init(Default::default()); | 24 | let p = embassy_rp::init(Default::default()); |
| 25 | spawner.spawn(pwm_set_config(p.PWM_SLICE4, p.PIN_25)).unwrap(); | ||
| 26 | spawner.spawn(pwm_set_dutycycle(p.PWM_SLICE2, p.PIN_4)).unwrap(); | ||
| 27 | } | ||
| 32 | 28 | ||
| 33 | let mut c: Config = Default::default(); | 29 | /// Demonstrate PWM by modifying & applying the config |
| 34 | c.top = 0x8000; | 30 | /// |
| 31 | /// Using the onboard led, if You are using a different Board than plain Pico2 (i.e. W variant) | ||
| 32 | /// you must use another slice & pin and an appropriate resistor. | ||
| 33 | #[embassy_executor::task] | ||
| 34 | async fn pwm_set_config(slice4: PWM_SLICE4, pin25: PIN_25) { | ||
| 35 | let mut c = Config::default(); | ||
| 36 | c.top = 32_768; | ||
| 35 | c.compare_b = 8; | 37 | c.compare_b = 8; |
| 36 | let mut pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, c.clone()); | 38 | let mut pwm = Pwm::new_output_b(slice4, pin25, c.clone()); |
| 37 | 39 | ||
| 38 | loop { | 40 | loop { |
| 39 | info!("current LED duty cycle: {}/32768", c.compare_b); | 41 | info!("current LED duty cycle: {}/32768", c.compare_b); |
| @@ -42,3 +44,41 @@ async fn main(_spawner: Spawner) { | |||
| 42 | pwm.set_config(&c); | 44 | pwm.set_config(&c); |
| 43 | } | 45 | } |
| 44 | } | 46 | } |
| 47 | |||
| 48 | /// Demonstrate PWM by setting duty cycle | ||
| 49 | /// | ||
| 50 | /// Using GP4 in Slice2, make sure to use an appropriate resistor. | ||
| 51 | #[embassy_executor::task] | ||
| 52 | async fn pwm_set_dutycycle(slice2: PWM_SLICE2, pin4: PIN_4) { | ||
| 53 | // If we aim for a specific frequency, here is how we can calculate the top value. | ||
| 54 | // The top value sets the period of the PWM cycle, so a counter goes from 0 to top and then wraps around to 0. | ||
| 55 | // Every such wraparound is one PWM cycle. So here is how we get 25KHz: | ||
| 56 | let desired_freq_hz = 25_000; | ||
| 57 | let clock_freq_hz = embassy_rp::clocks::clk_sys_freq(); | ||
| 58 | let divider = 16u8; | ||
| 59 | let period = (clock_freq_hz / (desired_freq_hz * divider as u32)) as u16 - 1; | ||
| 60 | |||
| 61 | let mut c = Config::default(); | ||
| 62 | c.top = period; | ||
| 63 | c.divider = divider.into(); | ||
| 64 | |||
| 65 | let mut pwm = Pwm::new_output_a(slice2, pin4, c.clone()); | ||
| 66 | |||
| 67 | loop { | ||
| 68 | // 100% duty cycle, fully on | ||
| 69 | pwm.set_duty_cycle_fully_on().unwrap(); | ||
| 70 | Timer::after_secs(1).await; | ||
| 71 | |||
| 72 | // 66% duty cycle. Expressed as simple percentage. | ||
| 73 | pwm.set_duty_cycle_percent(66).unwrap(); | ||
| 74 | Timer::after_secs(1).await; | ||
| 75 | |||
| 76 | // 25% duty cycle. Expressed as 32768/4 = 8192. | ||
| 77 | pwm.set_duty_cycle(c.top / 4).unwrap(); | ||
| 78 | Timer::after_secs(1).await; | ||
| 79 | |||
| 80 | // 0% duty cycle, fully off. | ||
| 81 | pwm.set_duty_cycle_fully_off().unwrap(); | ||
| 82 | Timer::after_secs(1).await; | ||
| 83 | } | ||
| 84 | } | ||
diff --git a/examples/rp23/src/bin/pwm_input.rs b/examples/rp23/src/bin/pwm_input.rs index b65f2778b..ef87fe8b5 100644 --- a/examples/rp23/src/bin/pwm_input.rs +++ b/examples/rp23/src/bin/pwm_input.rs | |||
| @@ -15,16 +15,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 15 | #[used] | 15 | #[used] |
| 16 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 16 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 17 | 17 | ||
| 18 | // Program metadata for `picotool info` | ||
| 19 | #[link_section = ".bi_entries"] | ||
| 20 | #[used] | ||
| 21 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 22 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 23 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 24 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 25 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 26 | ]; | ||
| 27 | |||
| 28 | #[embassy_executor::main] | 18 | #[embassy_executor::main] |
| 29 | async fn main(_spawner: Spawner) { | 19 | async fn main(_spawner: Spawner) { |
| 30 | let p = embassy_rp::init(Default::default()); | 20 | let p = embassy_rp::init(Default::default()); |
diff --git a/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs b/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs new file mode 100644 index 000000000..0682888e8 --- /dev/null +++ b/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs | |||
| @@ -0,0 +1,110 @@ | |||
| 1 | //! # PWM TB6612FNG motor driver | ||
| 2 | //! | ||
| 3 | //! This example shows the use of a TB6612FNG motor driver. The driver is built on top of embedded_hal and the example demonstrates how embassy_rp can be used to interact with ist. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | |||
| 8 | use assign_resources::assign_resources; | ||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_rp::block::ImageDef; | ||
| 12 | use embassy_rp::config::Config; | ||
| 13 | use embassy_rp::gpio::Output; | ||
| 14 | use embassy_rp::{gpio, peripherals, pwm}; | ||
| 15 | use embassy_time::{Duration, Timer}; | ||
| 16 | use tb6612fng::{DriveCommand, Motor, Tb6612fng}; | ||
| 17 | use {defmt_rtt as _, panic_probe as _}; | ||
| 18 | |||
| 19 | #[link_section = ".start_block"] | ||
| 20 | #[used] | ||
| 21 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 22 | |||
| 23 | assign_resources! { | ||
| 24 | motor: MotorResources { | ||
| 25 | standby_pin: PIN_22, | ||
| 26 | left_slice: PWM_SLICE6, | ||
| 27 | left_pwm_pin: PIN_28, | ||
| 28 | left_forward_pin: PIN_21, | ||
| 29 | left_backward_pin: PIN_20, | ||
| 30 | right_slice: PWM_SLICE5, | ||
| 31 | right_pwm_pin: PIN_27, | ||
| 32 | right_forward_pin: PIN_19, | ||
| 33 | right_backward_pin: PIN_18, | ||
| 34 | }, | ||
| 35 | } | ||
| 36 | |||
| 37 | #[embassy_executor::main] | ||
| 38 | async fn main(_spawner: Spawner) { | ||
| 39 | let p = embassy_rp::init(Config::default()); | ||
| 40 | let s = split_resources!(p); | ||
| 41 | let r = s.motor; | ||
| 42 | |||
| 43 | // we want a PWM frequency of 10KHz, especially cheaper motors do not respond well to higher frequencies | ||
| 44 | let desired_freq_hz = 10_000; | ||
| 45 | let clock_freq_hz = embassy_rp::clocks::clk_sys_freq(); | ||
| 46 | let divider = 16u8; | ||
| 47 | let period = (clock_freq_hz / (desired_freq_hz * divider as u32)) as u16 - 1; | ||
| 48 | |||
| 49 | // we need a standby output and two motors to construct a full TB6612FNG | ||
| 50 | |||
| 51 | // standby pin | ||
| 52 | let stby = Output::new(r.standby_pin, gpio::Level::Low); | ||
| 53 | |||
| 54 | // motor A, here defined to be the left motor | ||
| 55 | let left_fwd = gpio::Output::new(r.left_forward_pin, gpio::Level::Low); | ||
| 56 | let left_bckw = gpio::Output::new(r.left_backward_pin, gpio::Level::Low); | ||
| 57 | let mut left_speed = pwm::Config::default(); | ||
| 58 | left_speed.top = period; | ||
| 59 | left_speed.divider = divider.into(); | ||
| 60 | let left_pwm = pwm::Pwm::new_output_a(r.left_slice, r.left_pwm_pin, left_speed); | ||
| 61 | let left_motor = Motor::new(left_fwd, left_bckw, left_pwm).unwrap(); | ||
| 62 | |||
| 63 | // motor B, here defined to be the right motor | ||
| 64 | let right_fwd = gpio::Output::new(r.right_forward_pin, gpio::Level::Low); | ||
| 65 | let right_bckw = gpio::Output::new(r.right_backward_pin, gpio::Level::Low); | ||
| 66 | let mut right_speed = pwm::Config::default(); | ||
| 67 | right_speed.top = period; | ||
| 68 | right_speed.divider = divider.into(); | ||
| 69 | let right_pwm = pwm::Pwm::new_output_b(r.right_slice, r.right_pwm_pin, right_speed); | ||
| 70 | let right_motor = Motor::new(right_fwd, right_bckw, right_pwm).unwrap(); | ||
| 71 | |||
| 72 | // construct the motor driver | ||
| 73 | let mut control = Tb6612fng::new(left_motor, right_motor, stby).unwrap(); | ||
| 74 | |||
| 75 | loop { | ||
| 76 | // wake up the motor driver | ||
| 77 | info!("end standby"); | ||
| 78 | control.disable_standby().unwrap(); | ||
| 79 | Timer::after(Duration::from_millis(100)).await; | ||
| 80 | |||
| 81 | // drive a straight line forward at 20% speed for 5s | ||
| 82 | info!("drive straight"); | ||
| 83 | control.motor_a.drive(DriveCommand::Forward(80)).unwrap(); | ||
| 84 | control.motor_b.drive(DriveCommand::Forward(80)).unwrap(); | ||
| 85 | Timer::after(Duration::from_secs(5)).await; | ||
| 86 | |||
| 87 | // coast for 2s | ||
| 88 | info!("coast"); | ||
| 89 | control.motor_a.drive(DriveCommand::Stop).unwrap(); | ||
| 90 | control.motor_b.drive(DriveCommand::Stop).unwrap(); | ||
| 91 | Timer::after(Duration::from_secs(2)).await; | ||
| 92 | |||
| 93 | // actively brake | ||
| 94 | info!("brake"); | ||
| 95 | control.motor_a.drive(DriveCommand::Brake).unwrap(); | ||
| 96 | control.motor_b.drive(DriveCommand::Brake).unwrap(); | ||
| 97 | Timer::after(Duration::from_secs(1)).await; | ||
| 98 | |||
| 99 | // slowly turn for 3s | ||
| 100 | info!("turn"); | ||
| 101 | control.motor_a.drive(DriveCommand::Backward(50)).unwrap(); | ||
| 102 | control.motor_b.drive(DriveCommand::Forward(50)).unwrap(); | ||
| 103 | Timer::after(Duration::from_secs(3)).await; | ||
| 104 | |||
| 105 | // and put the driver in standby mode and wait for 5s | ||
| 106 | info!("standby"); | ||
| 107 | control.enable_standby().unwrap(); | ||
| 108 | Timer::after(Duration::from_secs(5)).await; | ||
| 109 | } | ||
| 110 | } | ||
diff --git a/examples/rp23/src/bin/rosc.rs b/examples/rp23/src/bin/rosc.rs index f65b236b1..a096f0b7a 100644 --- a/examples/rp23/src/bin/rosc.rs +++ b/examples/rp23/src/bin/rosc.rs | |||
| @@ -17,16 +17,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 17 | #[used] | 17 | #[used] |
| 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 19 | 19 | ||
| 20 | // Program metadata for `picotool info` | ||
| 21 | #[link_section = ".bi_entries"] | ||
| 22 | #[used] | ||
| 23 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 24 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 25 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 26 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 27 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 28 | ]; | ||
| 29 | |||
| 30 | #[embassy_executor::main] | 20 | #[embassy_executor::main] |
| 31 | async fn main(_spawner: Spawner) { | 21 | async fn main(_spawner: Spawner) { |
| 32 | let mut config = embassy_rp::config::Config::default(); | 22 | let mut config = embassy_rp::config::Config::default(); |
diff --git a/examples/rp23/src/bin/shared_bus.rs b/examples/rp23/src/bin/shared_bus.rs index b3fde13e3..2151ccb56 100644 --- a/examples/rp23/src/bin/shared_bus.rs +++ b/examples/rp23/src/bin/shared_bus.rs | |||
| @@ -23,16 +23,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 23 | #[used] | 23 | #[used] |
| 24 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 24 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 25 | 25 | ||
| 26 | // Program metadata for `picotool info` | ||
| 27 | #[link_section = ".bi_entries"] | ||
| 28 | #[used] | ||
| 29 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 30 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 31 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 32 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 33 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 34 | ]; | ||
| 35 | |||
| 36 | type Spi1Bus = Mutex<NoopRawMutex, Spi<'static, SPI1, spi::Async>>; | 26 | type Spi1Bus = Mutex<NoopRawMutex, Spi<'static, SPI1, spi::Async>>; |
| 37 | type I2c1Bus = Mutex<NoopRawMutex, I2c<'static, I2C1, i2c::Async>>; | 27 | type I2c1Bus = Mutex<NoopRawMutex, I2c<'static, I2C1, i2c::Async>>; |
| 38 | 28 | ||
diff --git a/examples/rp23/src/bin/sharing.rs b/examples/rp23/src/bin/sharing.rs index 4a3301cfd..68eb5d133 100644 --- a/examples/rp23/src/bin/sharing.rs +++ b/examples/rp23/src/bin/sharing.rs | |||
| @@ -36,16 +36,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 36 | #[used] | 36 | #[used] |
| 37 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 37 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 38 | 38 | ||
| 39 | // Program metadata for `picotool info` | ||
| 40 | #[link_section = ".bi_entries"] | ||
| 41 | #[used] | ||
| 42 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 43 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 44 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 45 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 46 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 47 | ]; | ||
| 48 | |||
| 49 | type UartAsyncMutex = mutex::Mutex<CriticalSectionRawMutex, UartTx<'static, UART0, uart::Async>>; | 39 | type UartAsyncMutex = mutex::Mutex<CriticalSectionRawMutex, UartTx<'static, UART0, uart::Async>>; |
| 50 | 40 | ||
| 51 | struct MyType { | 41 | struct MyType { |
diff --git a/examples/rp23/src/bin/spi.rs b/examples/rp23/src/bin/spi.rs index 924873e60..aacb8c7db 100644 --- a/examples/rp23/src/bin/spi.rs +++ b/examples/rp23/src/bin/spi.rs | |||
| @@ -17,16 +17,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 17 | #[used] | 17 | #[used] |
| 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 19 | 19 | ||
| 20 | // Program metadata for `picotool info` | ||
| 21 | #[link_section = ".bi_entries"] | ||
| 22 | #[used] | ||
| 23 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 24 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 25 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 26 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 27 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 28 | ]; | ||
| 29 | |||
| 30 | #[embassy_executor::main] | 20 | #[embassy_executor::main] |
| 31 | async fn main(_spawner: Spawner) { | 21 | async fn main(_spawner: Spawner) { |
| 32 | let p = embassy_rp::init(Default::default()); | 22 | let p = embassy_rp::init(Default::default()); |
diff --git a/examples/rp23/src/bin/spi_async.rs b/examples/rp23/src/bin/spi_async.rs index 4a74f991c..ac7f02fa8 100644 --- a/examples/rp23/src/bin/spi_async.rs +++ b/examples/rp23/src/bin/spi_async.rs | |||
| @@ -15,16 +15,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 15 | #[used] | 15 | #[used] |
| 16 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 16 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 17 | 17 | ||
| 18 | // Program metadata for `picotool info` | ||
| 19 | #[link_section = ".bi_entries"] | ||
| 20 | #[used] | ||
| 21 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 22 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 23 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 24 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 25 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 26 | ]; | ||
| 27 | |||
| 28 | #[embassy_executor::main] | 18 | #[embassy_executor::main] |
| 29 | async fn main(_spawner: Spawner) { | 19 | async fn main(_spawner: Spawner) { |
| 30 | let p = embassy_rp::init(Default::default()); | 20 | let p = embassy_rp::init(Default::default()); |
diff --git a/examples/rp23/src/bin/spi_display.rs b/examples/rp23/src/bin/spi_display.rs index 71dd84658..6b7c0781f 100644 --- a/examples/rp23/src/bin/spi_display.rs +++ b/examples/rp23/src/bin/spi_display.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | //! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip. | 1 | //! This example shows how to use SPI (Serial Peripheral Interface) in the RP2350 chip. |
| 2 | //! | 2 | //! |
| 3 | //! Example written for a display using the ST7789 chip. Possibly the Waveshare Pico-ResTouch | 3 | //! Example written for a display using the ST7789 chip. Possibly the Waveshare Pico-ResTouch |
| 4 | //! (https://www.waveshare.com/wiki/Pico-ResTouch-LCD-2.8) | 4 | //! (https://www.waveshare.com/wiki/Pico-ResTouch-LCD-2.8) |
| @@ -9,6 +9,7 @@ | |||
| 9 | use core::cell::RefCell; | 9 | use core::cell::RefCell; |
| 10 | 10 | ||
| 11 | use defmt::*; | 11 | use defmt::*; |
| 12 | use display_interface_spi::SPIInterface; | ||
| 12 | use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig; | 13 | use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig; |
| 13 | use embassy_executor::Spawner; | 14 | use embassy_executor::Spawner; |
| 14 | use embassy_rp::block::ImageDef; | 15 | use embassy_rp::block::ImageDef; |
| @@ -25,24 +26,15 @@ use embedded_graphics::pixelcolor::Rgb565; | |||
| 25 | use embedded_graphics::prelude::*; | 26 | use embedded_graphics::prelude::*; |
| 26 | use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle}; | 27 | use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle}; |
| 27 | use embedded_graphics::text::Text; | 28 | use embedded_graphics::text::Text; |
| 28 | use st7789::{Orientation, ST7789}; | 29 | use mipidsi::models::ST7789; |
| 30 | use mipidsi::options::{Orientation, Rotation}; | ||
| 31 | use mipidsi::Builder; | ||
| 29 | use {defmt_rtt as _, panic_probe as _}; | 32 | use {defmt_rtt as _, panic_probe as _}; |
| 30 | 33 | ||
| 31 | #[link_section = ".start_block"] | 34 | #[link_section = ".start_block"] |
| 32 | #[used] | 35 | #[used] |
| 33 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 36 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 34 | 37 | ||
| 35 | // Program metadata for `picotool info` | ||
| 36 | #[link_section = ".bi_entries"] | ||
| 37 | #[used] | ||
| 38 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 39 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 40 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 41 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 42 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 43 | ]; | ||
| 44 | |||
| 45 | use crate::my_display_interface::SPIDeviceInterface; | ||
| 46 | use crate::touch::Touch; | 38 | use crate::touch::Touch; |
| 47 | 39 | ||
| 48 | const DISPLAY_FREQ: u32 = 64_000_000; | 40 | const DISPLAY_FREQ: u32 = 64_000_000; |
| @@ -89,17 +81,15 @@ async fn main(_spawner: Spawner) { | |||
| 89 | let _bl = Output::new(bl, Level::High); | 81 | let _bl = Output::new(bl, Level::High); |
| 90 | 82 | ||
| 91 | // display interface abstraction from SPI and DC | 83 | // display interface abstraction from SPI and DC |
| 92 | let di = SPIDeviceInterface::new(display_spi, dcx); | 84 | let di = SPIInterface::new(display_spi, dcx); |
| 93 | 85 | ||
| 94 | // create driver | 86 | // Define the display from the display interface and initialize it |
| 95 | let mut display = ST7789::new(di, rst, 240, 320); | 87 | let mut display = Builder::new(ST7789, di) |
| 96 | 88 | .display_size(240, 320) | |
| 97 | // initialize | 89 | .reset_pin(rst) |
| 98 | display.init(&mut Delay).unwrap(); | 90 | .orientation(Orientation::new().rotate(Rotation::Deg90)) |
| 99 | 91 | .init(&mut Delay) | |
| 100 | // set default orientation | 92 | .unwrap(); |
| 101 | display.set_orientation(Orientation::Landscape).unwrap(); | ||
| 102 | |||
| 103 | display.clear(Rgb565::BLACK).unwrap(); | 93 | display.clear(Rgb565::BLACK).unwrap(); |
| 104 | 94 | ||
| 105 | let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), 86); | 95 | let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), 86); |
| @@ -190,138 +180,3 @@ mod touch { | |||
| 190 | } | 180 | } |
| 191 | } | 181 | } |
| 192 | } | 182 | } |
| 193 | |||
| 194 | mod my_display_interface { | ||
| 195 | use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand}; | ||
| 196 | use embedded_hal_1::digital::OutputPin; | ||
| 197 | use embedded_hal_1::spi::SpiDevice; | ||
| 198 | |||
| 199 | /// SPI display interface. | ||
| 200 | /// | ||
| 201 | /// This combines the SPI peripheral and a data/command pin | ||
| 202 | pub struct SPIDeviceInterface<SPI, DC> { | ||
| 203 | spi: SPI, | ||
| 204 | dc: DC, | ||
| 205 | } | ||
| 206 | |||
| 207 | impl<SPI, DC> SPIDeviceInterface<SPI, DC> | ||
| 208 | where | ||
| 209 | SPI: SpiDevice, | ||
| 210 | DC: OutputPin, | ||
| 211 | { | ||
| 212 | /// Create new SPI interface for communciation with a display driver | ||
| 213 | pub fn new(spi: SPI, dc: DC) -> Self { | ||
| 214 | Self { spi, dc } | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | impl<SPI, DC> WriteOnlyDataCommand for SPIDeviceInterface<SPI, DC> | ||
| 219 | where | ||
| 220 | SPI: SpiDevice, | ||
| 221 | DC: OutputPin, | ||
| 222 | { | ||
| 223 | fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> { | ||
| 224 | // 1 = data, 0 = command | ||
| 225 | self.dc.set_low().map_err(|_| DisplayError::DCError)?; | ||
| 226 | |||
| 227 | send_u8(&mut self.spi, cmds).map_err(|_| DisplayError::BusWriteError)?; | ||
| 228 | Ok(()) | ||
| 229 | } | ||
| 230 | |||
| 231 | fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> { | ||
| 232 | // 1 = data, 0 = command | ||
| 233 | self.dc.set_high().map_err(|_| DisplayError::DCError)?; | ||
| 234 | |||
| 235 | send_u8(&mut self.spi, buf).map_err(|_| DisplayError::BusWriteError)?; | ||
| 236 | Ok(()) | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 240 | fn send_u8<T: SpiDevice>(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> { | ||
| 241 | match words { | ||
| 242 | DataFormat::U8(slice) => spi.write(slice), | ||
| 243 | DataFormat::U16(slice) => { | ||
| 244 | use byte_slice_cast::*; | ||
| 245 | spi.write(slice.as_byte_slice()) | ||
| 246 | } | ||
| 247 | DataFormat::U16LE(slice) => { | ||
| 248 | use byte_slice_cast::*; | ||
| 249 | for v in slice.as_mut() { | ||
| 250 | *v = v.to_le(); | ||
| 251 | } | ||
| 252 | spi.write(slice.as_byte_slice()) | ||
| 253 | } | ||
| 254 | DataFormat::U16BE(slice) => { | ||
| 255 | use byte_slice_cast::*; | ||
| 256 | for v in slice.as_mut() { | ||
| 257 | *v = v.to_be(); | ||
| 258 | } | ||
| 259 | spi.write(slice.as_byte_slice()) | ||
| 260 | } | ||
| 261 | DataFormat::U8Iter(iter) => { | ||
| 262 | let mut buf = [0; 32]; | ||
| 263 | let mut i = 0; | ||
| 264 | |||
| 265 | for v in iter.into_iter() { | ||
| 266 | buf[i] = v; | ||
| 267 | i += 1; | ||
| 268 | |||
| 269 | if i == buf.len() { | ||
| 270 | spi.write(&buf)?; | ||
| 271 | i = 0; | ||
| 272 | } | ||
| 273 | } | ||
| 274 | |||
| 275 | if i > 0 { | ||
| 276 | spi.write(&buf[..i])?; | ||
| 277 | } | ||
| 278 | |||
| 279 | Ok(()) | ||
| 280 | } | ||
| 281 | DataFormat::U16LEIter(iter) => { | ||
| 282 | use byte_slice_cast::*; | ||
| 283 | let mut buf = [0; 32]; | ||
| 284 | let mut i = 0; | ||
| 285 | |||
| 286 | for v in iter.map(u16::to_le) { | ||
| 287 | buf[i] = v; | ||
| 288 | i += 1; | ||
| 289 | |||
| 290 | if i == buf.len() { | ||
| 291 | spi.write(&buf.as_byte_slice())?; | ||
| 292 | i = 0; | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | if i > 0 { | ||
| 297 | spi.write(&buf[..i].as_byte_slice())?; | ||
| 298 | } | ||
| 299 | |||
| 300 | Ok(()) | ||
| 301 | } | ||
| 302 | DataFormat::U16BEIter(iter) => { | ||
| 303 | use byte_slice_cast::*; | ||
| 304 | let mut buf = [0; 64]; | ||
| 305 | let mut i = 0; | ||
| 306 | let len = buf.len(); | ||
| 307 | |||
| 308 | for v in iter.map(u16::to_be) { | ||
| 309 | buf[i] = v; | ||
| 310 | i += 1; | ||
| 311 | |||
| 312 | if i == len { | ||
| 313 | spi.write(&buf.as_byte_slice())?; | ||
| 314 | i = 0; | ||
| 315 | } | ||
| 316 | } | ||
| 317 | |||
| 318 | if i > 0 { | ||
| 319 | spi.write(&buf[..i].as_byte_slice())?; | ||
| 320 | } | ||
| 321 | |||
| 322 | Ok(()) | ||
| 323 | } | ||
| 324 | _ => unimplemented!(), | ||
| 325 | } | ||
| 326 | } | ||
| 327 | } | ||
diff --git a/examples/rp23/src/bin/spi_sdmmc.rs b/examples/rp23/src/bin/spi_sdmmc.rs index dabf41ab8..cfc38dfd9 100644 --- a/examples/rp23/src/bin/spi_sdmmc.rs +++ b/examples/rp23/src/bin/spi_sdmmc.rs | |||
| @@ -21,16 +21,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 21 | #[used] | 21 | #[used] |
| 22 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 22 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 23 | 23 | ||
| 24 | // Program metadata for `picotool info` | ||
| 25 | #[link_section = ".bi_entries"] | ||
| 26 | #[used] | ||
| 27 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 28 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 29 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 30 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 31 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 32 | ]; | ||
| 33 | |||
| 34 | struct DummyTimesource(); | 24 | struct DummyTimesource(); |
| 35 | 25 | ||
| 36 | impl embedded_sdmmc::TimeSource for DummyTimesource { | 26 | impl embedded_sdmmc::TimeSource for DummyTimesource { |
| @@ -66,7 +56,7 @@ async fn main(_spawner: Spawner) { | |||
| 66 | // Now that the card is initialized, the SPI clock can go faster | 56 | // Now that the card is initialized, the SPI clock can go faster |
| 67 | let mut config = spi::Config::default(); | 57 | let mut config = spi::Config::default(); |
| 68 | config.frequency = 16_000_000; | 58 | config.frequency = 16_000_000; |
| 69 | sdcard.spi(|dev| dev.bus_mut().set_config(&config)).ok(); | 59 | sdcard.spi(|dev| SetConfig::set_config(dev.bus_mut(), &config)).ok(); |
| 70 | 60 | ||
| 71 | // Now let's look for volumes (also known as partitions) on our block device. | 61 | // Now let's look for volumes (also known as partitions) on our block device. |
| 72 | // To do this we need a Volume Manager. It will take ownership of the block device. | 62 | // To do this we need a Volume Manager. It will take ownership of the block device. |
diff --git a/examples/rp23/src/bin/trng.rs b/examples/rp23/src/bin/trng.rs index e146baa2e..8251ebd8b 100644 --- a/examples/rp23/src/bin/trng.rs +++ b/examples/rp23/src/bin/trng.rs | |||
| @@ -18,16 +18,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 18 | #[used] | 18 | #[used] |
| 19 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 19 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 20 | 20 | ||
| 21 | // Program metadata for `picotool info` | ||
| 22 | #[link_section = ".bi_entries"] | ||
| 23 | #[used] | ||
| 24 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 25 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 26 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 27 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 28 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 29 | ]; | ||
| 30 | |||
| 31 | bind_interrupts!(struct Irqs { | 21 | bind_interrupts!(struct Irqs { |
| 32 | TRNG_IRQ => embassy_rp::trng::InterruptHandler<TRNG>; | 22 | TRNG_IRQ => embassy_rp::trng::InterruptHandler<TRNG>; |
| 33 | }); | 23 | }); |
diff --git a/examples/rp23/src/bin/uart.rs b/examples/rp23/src/bin/uart.rs index 0ffe0b293..fe28bb046 100644 --- a/examples/rp23/src/bin/uart.rs +++ b/examples/rp23/src/bin/uart.rs | |||
| @@ -16,16 +16,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 16 | #[used] | 16 | #[used] |
| 17 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 17 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 18 | 18 | ||
| 19 | // Program metadata for `picotool info` | ||
| 20 | #[link_section = ".bi_entries"] | ||
| 21 | #[used] | ||
| 22 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 23 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 24 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 25 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 26 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 27 | ]; | ||
| 28 | |||
| 29 | #[embassy_executor::main] | 19 | #[embassy_executor::main] |
| 30 | async fn main(_spawner: Spawner) { | 20 | async fn main(_spawner: Spawner) { |
| 31 | let p = embassy_rp::init(Default::default()); | 21 | let p = embassy_rp::init(Default::default()); |
diff --git a/examples/rp23/src/bin/uart_buffered_split.rs b/examples/rp23/src/bin/uart_buffered_split.rs index 4e69a20c4..9ed130727 100644 --- a/examples/rp23/src/bin/uart_buffered_split.rs +++ b/examples/rp23/src/bin/uart_buffered_split.rs | |||
| @@ -22,16 +22,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 22 | #[used] | 22 | #[used] |
| 23 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 23 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 24 | 24 | ||
| 25 | // Program metadata for `picotool info` | ||
| 26 | #[link_section = ".bi_entries"] | ||
| 27 | #[used] | ||
| 28 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 29 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 30 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 31 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 32 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 33 | ]; | ||
| 34 | |||
| 35 | bind_interrupts!(struct Irqs { | 25 | bind_interrupts!(struct Irqs { |
| 36 | UART0_IRQ => BufferedInterruptHandler<UART0>; | 26 | UART0_IRQ => BufferedInterruptHandler<UART0>; |
| 37 | }); | 27 | }); |
diff --git a/examples/rp23/src/bin/uart_r503.rs b/examples/rp23/src/bin/uart_r503.rs index 5ac8839e3..9aed42785 100644 --- a/examples/rp23/src/bin/uart_r503.rs +++ b/examples/rp23/src/bin/uart_r503.rs | |||
| @@ -15,16 +15,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 15 | #[used] | 15 | #[used] |
| 16 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 16 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 17 | 17 | ||
| 18 | // Program metadata for `picotool info` | ||
| 19 | #[link_section = ".bi_entries"] | ||
| 20 | #[used] | ||
| 21 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 22 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 23 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 24 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 25 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 26 | ]; | ||
| 27 | |||
| 28 | bind_interrupts!(pub struct Irqs { | 18 | bind_interrupts!(pub struct Irqs { |
| 29 | UART0_IRQ => UARTInterruptHandler<UART0>; | 19 | UART0_IRQ => UARTInterruptHandler<UART0>; |
| 30 | }); | 20 | }); |
diff --git a/examples/rp23/src/bin/uart_unidir.rs b/examples/rp23/src/bin/uart_unidir.rs index 988e44a79..12214c4c2 100644 --- a/examples/rp23/src/bin/uart_unidir.rs +++ b/examples/rp23/src/bin/uart_unidir.rs | |||
| @@ -21,16 +21,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 21 | #[used] | 21 | #[used] |
| 22 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 22 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 23 | 23 | ||
| 24 | // Program metadata for `picotool info` | ||
| 25 | #[link_section = ".bi_entries"] | ||
| 26 | #[used] | ||
| 27 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 28 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 29 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 30 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 31 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 32 | ]; | ||
| 33 | |||
| 34 | bind_interrupts!(struct Irqs { | 24 | bind_interrupts!(struct Irqs { |
| 35 | UART1_IRQ => InterruptHandler<UART1>; | 25 | UART1_IRQ => InterruptHandler<UART1>; |
| 36 | }); | 26 | }); |
diff --git a/examples/rp23/src/bin/usb_hid_keyboard.rs b/examples/rp23/src/bin/usb_hid_keyboard.rs new file mode 100644 index 000000000..ec1e88746 --- /dev/null +++ b/examples/rp23/src/bin/usb_hid_keyboard.rs | |||
| @@ -0,0 +1,193 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 5 | |||
| 6 | use defmt::*; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_futures::join::join; | ||
| 9 | use embassy_rp::bind_interrupts; | ||
| 10 | use embassy_rp::block::ImageDef; | ||
| 11 | use embassy_rp::gpio::{Input, Pull}; | ||
| 12 | use embassy_rp::peripherals::USB; | ||
| 13 | use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler}; | ||
| 14 | use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State as HidState}; | ||
| 15 | use embassy_usb::control::OutResponse; | ||
| 16 | use embassy_usb::{Builder, Config, Handler}; | ||
| 17 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | #[link_section = ".start_block"] | ||
| 21 | #[used] | ||
| 22 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 23 | |||
| 24 | bind_interrupts!(struct Irqs { | ||
| 25 | USBCTRL_IRQ => InterruptHandler<USB>; | ||
| 26 | }); | ||
| 27 | |||
| 28 | #[embassy_executor::main] | ||
| 29 | async fn main(_spawner: Spawner) { | ||
| 30 | let p = embassy_rp::init(Default::default()); | ||
| 31 | // Create the driver, from the HAL. | ||
| 32 | let driver = UsbDriver::new(p.USB, Irqs); | ||
| 33 | |||
| 34 | // Create embassy-usb Config | ||
| 35 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 36 | config.manufacturer = Some("Embassy"); | ||
| 37 | config.product = Some("HID keyboard example"); | ||
| 38 | config.serial_number = Some("12345678"); | ||
| 39 | config.max_power = 100; | ||
| 40 | config.max_packet_size_0 = 64; | ||
| 41 | |||
| 42 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 43 | // It needs some buffers for building the descriptors. | ||
| 44 | let mut config_descriptor = [0; 256]; | ||
| 45 | let mut bos_descriptor = [0; 256]; | ||
| 46 | // You can also add a Microsoft OS descriptor. | ||
| 47 | let mut msos_descriptor = [0; 256]; | ||
| 48 | let mut control_buf = [0; 64]; | ||
| 49 | let mut request_handler = MyRequestHandler {}; | ||
| 50 | let mut device_handler = MyDeviceHandler::new(); | ||
| 51 | |||
| 52 | let mut state = HidState::new(); | ||
| 53 | |||
| 54 | let mut builder = Builder::new( | ||
| 55 | driver, | ||
| 56 | config, | ||
| 57 | &mut config_descriptor, | ||
| 58 | &mut bos_descriptor, | ||
| 59 | &mut msos_descriptor, | ||
| 60 | &mut control_buf, | ||
| 61 | ); | ||
| 62 | |||
| 63 | builder.handler(&mut device_handler); | ||
| 64 | |||
| 65 | // Create classes on the builder. | ||
| 66 | let config = embassy_usb::class::hid::Config { | ||
| 67 | report_descriptor: KeyboardReport::desc(), | ||
| 68 | request_handler: None, | ||
| 69 | poll_ms: 60, | ||
| 70 | max_packet_size: 64, | ||
| 71 | }; | ||
| 72 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); | ||
| 73 | |||
| 74 | // Build the builder. | ||
| 75 | let mut usb = builder.build(); | ||
| 76 | |||
| 77 | // Run the USB device. | ||
| 78 | let usb_fut = usb.run(); | ||
| 79 | |||
| 80 | // Set up the signal pin that will be used to trigger the keyboard. | ||
| 81 | let mut signal_pin = Input::new(p.PIN_16, Pull::None); | ||
| 82 | |||
| 83 | // Enable the schmitt trigger to slightly debounce. | ||
| 84 | signal_pin.set_schmitt(true); | ||
| 85 | |||
| 86 | let (reader, mut writer) = hid.split(); | ||
| 87 | |||
| 88 | // Do stuff with the class! | ||
| 89 | let in_fut = async { | ||
| 90 | loop { | ||
| 91 | info!("Waiting for HIGH on pin 16"); | ||
| 92 | signal_pin.wait_for_high().await; | ||
| 93 | info!("HIGH DETECTED"); | ||
| 94 | // Create a report with the A key pressed. (no shift modifier) | ||
| 95 | let report = KeyboardReport { | ||
| 96 | keycodes: [4, 0, 0, 0, 0, 0], | ||
| 97 | leds: 0, | ||
| 98 | modifier: 0, | ||
| 99 | reserved: 0, | ||
| 100 | }; | ||
| 101 | // Send the report. | ||
| 102 | match writer.write_serialize(&report).await { | ||
| 103 | Ok(()) => {} | ||
| 104 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 105 | }; | ||
| 106 | signal_pin.wait_for_low().await; | ||
| 107 | info!("LOW DETECTED"); | ||
| 108 | let report = KeyboardReport { | ||
| 109 | keycodes: [0, 0, 0, 0, 0, 0], | ||
| 110 | leds: 0, | ||
| 111 | modifier: 0, | ||
| 112 | reserved: 0, | ||
| 113 | }; | ||
| 114 | match writer.write_serialize(&report).await { | ||
| 115 | Ok(()) => {} | ||
| 116 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 117 | }; | ||
| 118 | } | ||
| 119 | }; | ||
| 120 | |||
| 121 | let out_fut = async { | ||
| 122 | reader.run(false, &mut request_handler).await; | ||
| 123 | }; | ||
| 124 | |||
| 125 | // Run everything concurrently. | ||
| 126 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 127 | join(usb_fut, join(in_fut, out_fut)).await; | ||
| 128 | } | ||
| 129 | |||
| 130 | struct MyRequestHandler {} | ||
| 131 | |||
| 132 | impl RequestHandler for MyRequestHandler { | ||
| 133 | fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option<usize> { | ||
| 134 | info!("Get report for {:?}", id); | ||
| 135 | None | ||
| 136 | } | ||
| 137 | |||
| 138 | fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse { | ||
| 139 | info!("Set report for {:?}: {=[u8]}", id, data); | ||
| 140 | OutResponse::Accepted | ||
| 141 | } | ||
| 142 | |||
| 143 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { | ||
| 144 | info!("Set idle rate for {:?} to {:?}", id, dur); | ||
| 145 | } | ||
| 146 | |||
| 147 | fn get_idle_ms(&mut self, id: Option<ReportId>) -> Option<u32> { | ||
| 148 | info!("Get idle rate for {:?}", id); | ||
| 149 | None | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | struct MyDeviceHandler { | ||
| 154 | configured: AtomicBool, | ||
| 155 | } | ||
| 156 | |||
| 157 | impl MyDeviceHandler { | ||
| 158 | fn new() -> Self { | ||
| 159 | MyDeviceHandler { | ||
| 160 | configured: AtomicBool::new(false), | ||
| 161 | } | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | impl Handler for MyDeviceHandler { | ||
| 166 | fn enabled(&mut self, enabled: bool) { | ||
| 167 | self.configured.store(false, Ordering::Relaxed); | ||
| 168 | if enabled { | ||
| 169 | info!("Device enabled"); | ||
| 170 | } else { | ||
| 171 | info!("Device disabled"); | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | fn reset(&mut self) { | ||
| 176 | self.configured.store(false, Ordering::Relaxed); | ||
| 177 | info!("Bus reset, the Vbus current limit is 100mA"); | ||
| 178 | } | ||
| 179 | |||
| 180 | fn addressed(&mut self, addr: u8) { | ||
| 181 | self.configured.store(false, Ordering::Relaxed); | ||
| 182 | info!("USB address set to: {}", addr); | ||
| 183 | } | ||
| 184 | |||
| 185 | fn configured(&mut self, configured: bool) { | ||
| 186 | self.configured.store(configured, Ordering::Relaxed); | ||
| 187 | if configured { | ||
| 188 | info!("Device configured, it may now draw up to the configured current limit from Vbus.") | ||
| 189 | } else { | ||
| 190 | info!("Device is no longer configured, the Vbus current limit is 100mA."); | ||
| 191 | } | ||
| 192 | } | ||
| 193 | } | ||
diff --git a/examples/rp23/src/bin/usb_webusb.rs b/examples/rp23/src/bin/usb_webusb.rs index 3ade2226b..15279cabc 100644 --- a/examples/rp23/src/bin/usb_webusb.rs +++ b/examples/rp23/src/bin/usb_webusb.rs | |||
| @@ -34,16 +34,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 34 | #[used] | 34 | #[used] |
| 35 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 35 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 36 | 36 | ||
| 37 | // Program metadata for `picotool info` | ||
| 38 | #[link_section = ".bi_entries"] | ||
| 39 | #[used] | ||
| 40 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 41 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 42 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 43 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 44 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 45 | ]; | ||
| 46 | |||
| 47 | bind_interrupts!(struct Irqs { | 37 | bind_interrupts!(struct Irqs { |
| 48 | USBCTRL_IRQ => InterruptHandler<USB>; | 38 | USBCTRL_IRQ => InterruptHandler<USB>; |
| 49 | }); | 39 | }); |
diff --git a/examples/rp23/src/bin/watchdog.rs b/examples/rp23/src/bin/watchdog.rs index a901c1164..efc24c4e3 100644 --- a/examples/rp23/src/bin/watchdog.rs +++ b/examples/rp23/src/bin/watchdog.rs | |||
| @@ -18,16 +18,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 18 | #[used] | 18 | #[used] |
| 19 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 19 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 20 | 20 | ||
| 21 | // Program metadata for `picotool info` | ||
| 22 | #[link_section = ".bi_entries"] | ||
| 23 | #[used] | ||
| 24 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 25 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 26 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 27 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 28 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 29 | ]; | ||
| 30 | |||
| 31 | #[embassy_executor::main] | 21 | #[embassy_executor::main] |
| 32 | async fn main(_spawner: Spawner) { | 22 | async fn main(_spawner: Spawner) { |
| 33 | let p = embassy_rp::init(Default::default()); | 23 | let p = embassy_rp::init(Default::default()); |
diff --git a/examples/rp23/src/bin/zerocopy.rs b/examples/rp23/src/bin/zerocopy.rs index 86fca6f12..d317c4b56 100644 --- a/examples/rp23/src/bin/zerocopy.rs +++ b/examples/rp23/src/bin/zerocopy.rs | |||
| @@ -23,16 +23,6 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 23 | #[used] | 23 | #[used] |
| 24 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 24 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 25 | 25 | ||
| 26 | // Program metadata for `picotool info` | ||
| 27 | #[link_section = ".bi_entries"] | ||
| 28 | #[used] | ||
| 29 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 30 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 31 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 32 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 33 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 34 | ]; | ||
| 35 | |||
| 36 | type SampleBuffer = [u16; 512]; | 26 | type SampleBuffer = [u16; 512]; |
| 37 | 27 | ||
| 38 | bind_interrupts!(struct Irqs { | 28 | bind_interrupts!(struct Irqs { |
diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 87491b1d2..e43fd77c8 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml | |||
| @@ -5,10 +5,10 @@ version = "0.1.0" | |||
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["log"] } | 8 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["log"] } |
| 9 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-std", "executor-thread", "log", "integrated-timers"] } | 9 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-std", "executor-thread", "log"] } |
| 10 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["log", "std", ] } | 10 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["log", "std", ] } |
| 11 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features=[ "std", "log", "medium-ethernet", "medium-ip", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6"] } | 11 | embassy-net = { version = "0.5.0", path = "../../embassy-net", features=[ "std", "log", "medium-ethernet", "medium-ip", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6"] } |
| 12 | embassy-net-tuntap = { version = "0.1.0", path = "../../embassy-net-tuntap" } | 12 | embassy-net-tuntap = { version = "0.1.0", path = "../../embassy-net-tuntap" } |
| 13 | embassy-net-ppp = { version = "0.1.0", path = "../../embassy-net-ppp", features = ["log"]} | 13 | embassy-net-ppp = { version = "0.1.0", path = "../../embassy-net-ppp", features = ["log"]} |
| 14 | embedded-io-async = { version = "0.6.1" } | 14 | embedded-io-async = { version = "0.6.1" } |
diff --git a/examples/std/src/bin/net_ppp.rs b/examples/std/src/bin/net_ppp.rs index 7d0f1327f..ea3fbebef 100644 --- a/examples/std/src/bin/net_ppp.rs +++ b/examples/std/src/bin/net_ppp.rs | |||
| @@ -16,7 +16,7 @@ use async_io::Async; | |||
| 16 | use clap::Parser; | 16 | use clap::Parser; |
| 17 | use embassy_executor::{Executor, Spawner}; | 17 | use embassy_executor::{Executor, Spawner}; |
| 18 | use embassy_net::tcp::TcpSocket; | 18 | use embassy_net::tcp::TcpSocket; |
| 19 | use embassy_net::{Config, ConfigV4, Ipv4Address, Ipv4Cidr, Stack, StackResources}; | 19 | use embassy_net::{Config, ConfigV4, Ipv4Cidr, Stack, StackResources}; |
| 20 | use embassy_net_ppp::Runner; | 20 | use embassy_net_ppp::Runner; |
| 21 | use embedded_io_async::Write; | 21 | use embedded_io_async::Write; |
| 22 | use futures::io::BufReader; | 22 | use futures::io::BufReader; |
| @@ -60,10 +60,10 @@ async fn ppp_task(stack: Stack<'static>, mut runner: Runner<'static>, port: Seri | |||
| 60 | }; | 60 | }; |
| 61 | let mut dns_servers = Vec::new(); | 61 | let mut dns_servers = Vec::new(); |
| 62 | for s in ipv4.dns_servers.iter().flatten() { | 62 | for s in ipv4.dns_servers.iter().flatten() { |
| 63 | let _ = dns_servers.push(Ipv4Address::from_bytes(&s.0)); | 63 | let _ = dns_servers.push(*s); |
| 64 | } | 64 | } |
| 65 | let config = ConfigV4::Static(embassy_net::StaticConfigV4 { | 65 | let config = ConfigV4::Static(embassy_net::StaticConfigV4 { |
| 66 | address: Ipv4Cidr::new(Ipv4Address::from_bytes(&addr.0), 0), | 66 | address: Ipv4Cidr::new(addr, 0), |
| 67 | gateway: None, | 67 | gateway: None, |
| 68 | dns_servers, | 68 | dns_servers, |
| 69 | }); | 69 | }); |
diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index 9102467eb..5ac3018e1 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml | |||
| @@ -7,8 +7,8 @@ license = "MIT OR Apache-2.0" | |||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32c031c6 to your chip name, if necessary. | 8 | # Change stm32c031c6 to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | 13 | ||
| 14 | defmt = "0.3" | 14 | defmt = "0.3" |
diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index 724efdaff..af3ef7abb 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml | |||
| @@ -6,14 +6,14 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32f091rc to your chip name, if necessary. | 8 | # Change stm32f091rc to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "memory-x", "stm32f091rc", "time-driver-tim2", "exti", "unstable-pac"] } |
| 10 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 10 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 11 | cortex-m-rt = "0.7.0" | 11 | cortex-m-rt = "0.7.0" |
| 12 | defmt = "0.3" | 12 | defmt = "0.3" |
| 13 | defmt-rtt = "0.4" | 13 | defmt-rtt = "0.4" |
| 14 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 14 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 15 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 15 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 16 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | 16 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 17 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 17 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 18 | static_cell = "2" | 18 | static_cell = "2" |
| 19 | portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] } | 19 | portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] } |
diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 0084651a3..538e95dfb 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml | |||
| @@ -7,8 +7,8 @@ license = "MIT OR Apache-2.0" | |||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32f103c8 to your chip name, if necessary. | 8 | # Change stm32f103c8 to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any" ] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any" ] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 13 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } |
| 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index 60eb0eb93..48d524b90 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml | |||
| @@ -7,8 +7,8 @@ license = "MIT OR Apache-2.0" | |||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32f207zg to your chip name, if necessary. | 8 | # Change stm32f207zg to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | 13 | ||
| 14 | defmt = "0.3" | 14 | defmt = "0.3" |
diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 7fda410d9..66fb34223 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml | |||
| @@ -6,9 +6,9 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32f303ze to your chip name, if necessary. | 8 | # Change stm32f303ze to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-tim2", "exti"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "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.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 13 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } |
| 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
diff --git a/examples/stm32f3/README.md b/examples/stm32f3/README.md new file mode 100644 index 000000000..0a85c4858 --- /dev/null +++ b/examples/stm32f3/README.md | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | # Examples for STM32F3 family | ||
| 2 | Run individual examples with | ||
| 3 | ``` | ||
| 4 | cargo run --bin <module-name> | ||
| 5 | ``` | ||
| 6 | for example | ||
| 7 | ``` | ||
| 8 | cargo run --bin blinky | ||
| 9 | ``` | ||
| 10 | |||
| 11 | ## Checklist before running examples | ||
| 12 | You might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using. | ||
| 13 | |||
| 14 | * [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for F303ZE it should be `probe-rs run --chip STM32F303ZETx`. (use `probe-rs chip list` to find your chip) | ||
| 15 | * [ ] Update Cargo.toml to have the correct `embassy-stm32` feature. For example for F303ZE it should be `stm32f303ze`. Look in the `Cargo.toml` file of the `embassy-stm32` project to find the correct feature flag for your chip. | ||
| 16 | * [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately. | ||
| 17 | * [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic | ||
| 18 | |||
| 19 | If you are unsure, please drop by the Embassy Matrix chat for support, and let us know: | ||
| 20 | |||
| 21 | * Which example you are trying to run | ||
| 22 | * Which chip and board you are using | ||
| 23 | |||
| 24 | Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org | ||
diff --git a/examples/stm32f3/src/bin/blocking-tsc.rs b/examples/stm32f3/src/bin/blocking-tsc.rs deleted file mode 100644 index 5c8dac94f..000000000 --- a/examples/stm32f3/src/bin/blocking-tsc.rs +++ /dev/null | |||
| @@ -1,98 +0,0 @@ | |||
| 1 | // Example of polling TSC (Touch Sensing Controller) that lights an LED when touch is detected. | ||
| 2 | // | ||
| 3 | // Suggested physical setup on STM32F303ZE Nucleo board: | ||
| 4 | // - Connect a 1000pF capacitor between pin A0 and GND. This is your sampling capacitor. | ||
| 5 | // - Connect one end of a 1K resistor to pin A1 and leave the other end loose. | ||
| 6 | // The loose end will act as touch sensor which will register your touch. | ||
| 7 | // | ||
| 8 | // Troubleshooting the setup: | ||
| 9 | // - If no touch seems to be registered, then try to disconnect the sampling capacitor from GND momentarily, | ||
| 10 | // now the led should light up. Next try using a different value for the sampling capacitor. | ||
| 11 | // Also experiment with increasing the values for `ct_pulse_high_length`, `ct_pulse_low_length`, `pulse_generator_prescaler`, `max_count_value` and `discharge_delay`. | ||
| 12 | // | ||
| 13 | // All configuration values and sampling capacitor value have been determined experimentally. | ||
| 14 | // Suitable configuration and discharge delay values are highly dependent on the value of the sample capacitor. For example, a shorter discharge delay can be used with smaller capacitor values. | ||
| 15 | // | ||
| 16 | #![no_std] | ||
| 17 | #![no_main] | ||
| 18 | |||
| 19 | use defmt::*; | ||
| 20 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 21 | use embassy_stm32::tsc::{self, *}; | ||
| 22 | use embassy_time::Timer; | ||
| 23 | use {defmt_rtt as _, panic_probe as _}; | ||
| 24 | |||
| 25 | /// This example is written for the nucleo-stm32f303ze, with a stm32f303ze chip. | ||
| 26 | /// | ||
| 27 | /// Make sure you check/update the following (whether you use the F303ZE or another board): | ||
| 28 | /// | ||
| 29 | /// * [ ] Update .cargo/config.toml with the correct `probe-rs run --chip STM32F303ZETx`chip name. | ||
| 30 | /// * [ ] Update Cargo.toml to have the correct `embassy-stm32` feature, for F303ZE it should be `stm32f303ze`. | ||
| 31 | /// * [ ] If your board has a special clock or power configuration, make sure that it is | ||
| 32 | /// set up appropriately. | ||
| 33 | /// * [ ] If your board has different pin mapping, update any pin numbers or peripherals | ||
| 34 | /// to match your schematic | ||
| 35 | /// | ||
| 36 | /// If you are unsure, please drop by the Embassy Matrix chat for support, and let us know: | ||
| 37 | /// | ||
| 38 | /// * Which example you are trying to run | ||
| 39 | /// * Which chip and board you are using | ||
| 40 | /// | ||
| 41 | /// Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org | ||
| 42 | #[embassy_executor::main] | ||
| 43 | async fn main(_spawner: embassy_executor::Spawner) { | ||
| 44 | let device_config = embassy_stm32::Config::default(); | ||
| 45 | let context = embassy_stm32::init(device_config); | ||
| 46 | |||
| 47 | let tsc_conf = Config { | ||
| 48 | ct_pulse_high_length: ChargeTransferPulseCycle::_8, | ||
| 49 | ct_pulse_low_length: ChargeTransferPulseCycle::_8, | ||
| 50 | spread_spectrum: false, | ||
| 51 | spread_spectrum_deviation: SSDeviation::new(2).unwrap(), | ||
| 52 | spread_spectrum_prescaler: false, | ||
| 53 | pulse_generator_prescaler: PGPrescalerDivider::_32, | ||
| 54 | max_count_value: MaxCount::_255, | ||
| 55 | io_default_mode: false, | ||
| 56 | synchro_pin_polarity: false, | ||
| 57 | acquisition_mode: false, | ||
| 58 | max_count_interrupt: false, | ||
| 59 | channel_ios: TscIOPin::Group1Io1.into(), | ||
| 60 | shield_ios: 0, // no shield | ||
| 61 | sampling_ios: TscIOPin::Group1Io2.into(), | ||
| 62 | }; | ||
| 63 | |||
| 64 | let mut g1: PinGroup<embassy_stm32::peripherals::TSC, G1> = PinGroup::new(); | ||
| 65 | g1.set_io1(context.PA0, PinType::Sample); | ||
| 66 | g1.set_io2(context.PA1, PinType::Channel); | ||
| 67 | |||
| 68 | let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, Some(g1), None, None, None, None, None, tsc_conf); | ||
| 69 | |||
| 70 | // LED2 on the STM32F303ZE nucleo-board | ||
| 71 | let mut led = Output::new(context.PB7, Level::High, Speed::Low); | ||
| 72 | |||
| 73 | // smaller sample capacitor discharge faster and can be used with shorter delay. | ||
| 74 | let discharge_delay = 5; // ms | ||
| 75 | |||
| 76 | // the interval at which the loop polls for new touch sensor values | ||
| 77 | let polling_interval = 100; // ms | ||
| 78 | |||
| 79 | info!("polling for touch"); | ||
| 80 | loop { | ||
| 81 | touch_controller.start(); | ||
| 82 | touch_controller.poll_for_acquisition(); | ||
| 83 | touch_controller.discharge_io(true); | ||
| 84 | Timer::after_millis(discharge_delay).await; | ||
| 85 | |||
| 86 | let grp1_status = touch_controller.group_get_status(Group::One); | ||
| 87 | match grp1_status { | ||
| 88 | GroupStatus::Complete => { | ||
| 89 | let group_one_val = touch_controller.group_get_value(Group::One); | ||
| 90 | info!("{}", group_one_val); | ||
| 91 | led.set_high(); | ||
| 92 | } | ||
| 93 | GroupStatus::Ongoing => led.set_low(), | ||
| 94 | } | ||
| 95 | |||
| 96 | Timer::after_millis(polling_interval).await; | ||
| 97 | } | ||
| 98 | } | ||
diff --git a/examples/stm32f3/src/bin/tsc_blocking.rs b/examples/stm32f3/src/bin/tsc_blocking.rs new file mode 100644 index 000000000..2c33838e5 --- /dev/null +++ b/examples/stm32f3/src/bin/tsc_blocking.rs | |||
| @@ -0,0 +1,138 @@ | |||
| 1 | // Example of blocking TSC (Touch Sensing Controller) that lights an LED when touch is detected. | ||
| 2 | // | ||
| 3 | // This example demonstrates: | ||
| 4 | // 1. Configuring a single TSC channel pin | ||
| 5 | // 2. Using the blocking TSC interface with polling | ||
| 6 | // 3. Waiting for acquisition completion using `poll_for_acquisition` | ||
| 7 | // 4. Reading touch values and controlling an LED based on the results | ||
| 8 | // | ||
| 9 | // Suggested physical setup on STM32F303ZE Nucleo board: | ||
| 10 | // - Connect a 1000pF capacitor between pin PA10 and GND. This is your sampling capacitor. | ||
| 11 | // - Connect one end of a 1K resistor to pin PA9 and leave the other end loose. | ||
| 12 | // The loose end will act as the touch sensor which will register your touch. | ||
| 13 | // | ||
| 14 | // The example uses two pins from Group 4 of the TSC: | ||
| 15 | // - PA10 as the sampling capacitor, TSC group 4 IO2 (D68 on the STM32F303ZE nucleo-board) | ||
| 16 | // - PA9 as the channel pin, TSC group 4 IO1 (D69 on the STM32F303ZE nucleo-board) | ||
| 17 | // | ||
| 18 | // The program continuously reads the touch sensor value: | ||
| 19 | // - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value. | ||
| 20 | // - The LED is turned on when touch is detected (sensor value < 40). | ||
| 21 | // - Touch values are logged to the console. | ||
| 22 | // | ||
| 23 | // Troubleshooting: | ||
| 24 | // - If touch is not detected, try adjusting the SENSOR_THRESHOLD value. | ||
| 25 | // - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, | ||
| 26 | // pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity. | ||
| 27 | // | ||
| 28 | // Note: Configuration values and sampling capacitor value have been determined experimentally. | ||
| 29 | // Optimal values may vary based on your specific hardware setup. | ||
| 30 | // Pins have been chosen for their convenient locations on the STM32F303ZE board. Refer to the | ||
| 31 | // official relevant STM32 datasheets and user nucleo-board user manuals to find suitable | ||
| 32 | // alternative pins. | ||
| 33 | |||
| 34 | #![no_std] | ||
| 35 | #![no_main] | ||
| 36 | |||
| 37 | use defmt::*; | ||
| 38 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 39 | use embassy_stm32::tsc::{self, *}; | ||
| 40 | use embassy_stm32::{mode, peripherals}; | ||
| 41 | use embassy_time::Timer; | ||
| 42 | use {defmt_rtt as _, panic_probe as _}; | ||
| 43 | |||
| 44 | const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup | ||
| 45 | |||
| 46 | #[embassy_executor::main] | ||
| 47 | async fn main(_spawner: embassy_executor::Spawner) { | ||
| 48 | let device_config = embassy_stm32::Config::default(); | ||
| 49 | let context = embassy_stm32::init(device_config); | ||
| 50 | |||
| 51 | let tsc_conf = Config { | ||
| 52 | ct_pulse_high_length: ChargeTransferPulseCycle::_4, | ||
| 53 | ct_pulse_low_length: ChargeTransferPulseCycle::_4, | ||
| 54 | spread_spectrum: false, | ||
| 55 | spread_spectrum_deviation: SSDeviation::new(2).unwrap(), | ||
| 56 | spread_spectrum_prescaler: false, | ||
| 57 | pulse_generator_prescaler: PGPrescalerDivider::_16, | ||
| 58 | max_count_value: MaxCount::_255, | ||
| 59 | io_default_mode: false, | ||
| 60 | synchro_pin_polarity: false, | ||
| 61 | acquisition_mode: false, | ||
| 62 | max_count_interrupt: false, | ||
| 63 | }; | ||
| 64 | |||
| 65 | let mut g: PinGroupWithRoles<peripherals::TSC, G4> = PinGroupWithRoles::default(); | ||
| 66 | // D68 on the STM32F303ZE nucleo-board | ||
| 67 | g.set_io2::<tsc::pin_roles::Sample>(context.PA10); | ||
| 68 | // D69 on the STM32F303ZE nucleo-board | ||
| 69 | let tsc_sensor = g.set_io1::<tsc::pin_roles::Channel>(context.PA9); | ||
| 70 | |||
| 71 | let pin_groups: PinGroups<peripherals::TSC> = PinGroups { | ||
| 72 | g4: Some(g.pin_group), | ||
| 73 | ..Default::default() | ||
| 74 | }; | ||
| 75 | |||
| 76 | let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, pin_groups, tsc_conf).unwrap(); | ||
| 77 | |||
| 78 | // Check if TSC is ready | ||
| 79 | if touch_controller.get_state() != State::Ready { | ||
| 80 | crate::panic!("TSC not ready!"); | ||
| 81 | } | ||
| 82 | info!("TSC initialized successfully"); | ||
| 83 | |||
| 84 | // LED2 on the STM32F303ZE nucleo-board | ||
| 85 | let mut led = Output::new(context.PB7, Level::High, Speed::Low); | ||
| 86 | |||
| 87 | // smaller sample capacitor discharge faster and can be used with shorter delay. | ||
| 88 | let discharge_delay = 5; // ms | ||
| 89 | |||
| 90 | // the interval at which the loop polls for new touch sensor values | ||
| 91 | let polling_interval = 100; // ms | ||
| 92 | |||
| 93 | info!("polling for touch"); | ||
| 94 | loop { | ||
| 95 | touch_controller.set_active_channels_mask(tsc_sensor.pin.into()); | ||
| 96 | touch_controller.start(); | ||
| 97 | touch_controller.poll_for_acquisition(); | ||
| 98 | touch_controller.discharge_io(true); | ||
| 99 | Timer::after_millis(discharge_delay).await; | ||
| 100 | |||
| 101 | match read_touch_value(&mut touch_controller, tsc_sensor.pin).await { | ||
| 102 | Some(v) => { | ||
| 103 | info!("sensor value {}", v); | ||
| 104 | if v < SENSOR_THRESHOLD { | ||
| 105 | led.set_high(); | ||
| 106 | } else { | ||
| 107 | led.set_low(); | ||
| 108 | } | ||
| 109 | } | ||
| 110 | None => led.set_low(), | ||
| 111 | } | ||
| 112 | |||
| 113 | Timer::after_millis(polling_interval).await; | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10; | ||
| 118 | |||
| 119 | // attempt to read group status and delay when still ongoing | ||
| 120 | async fn read_touch_value( | ||
| 121 | touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Blocking>, | ||
| 122 | sensor_pin: tsc::IOPin, | ||
| 123 | ) -> Option<u16> { | ||
| 124 | for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS { | ||
| 125 | match touch_controller.group_get_status(sensor_pin.group()) { | ||
| 126 | GroupStatus::Complete => { | ||
| 127 | return Some(touch_controller.group_get_value(sensor_pin.group())); | ||
| 128 | } | ||
| 129 | GroupStatus::Ongoing => { | ||
| 130 | // if you end up here a lot, then you prob need to increase discharge_delay | ||
| 131 | // or consider changing the code to adjust the discharge_delay dynamically | ||
| 132 | info!("Acquisition still ongoing"); | ||
| 133 | Timer::after_millis(1).await; | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | ||
| 137 | None | ||
| 138 | } | ||
diff --git a/examples/stm32f3/src/bin/tsc_multipin.rs b/examples/stm32f3/src/bin/tsc_multipin.rs new file mode 100644 index 000000000..c524c3760 --- /dev/null +++ b/examples/stm32f3/src/bin/tsc_multipin.rs | |||
| @@ -0,0 +1,204 @@ | |||
| 1 | // Example of TSC (Touch Sensing Controller) using multiple pins from the same tsc-group. | ||
| 2 | // | ||
| 3 | // What is special about using multiple TSC pins as sensor channels from the same TSC group, | ||
| 4 | // is that only one TSC pin for each TSC group can be acquired and read at the time. | ||
| 5 | // To control which channel pins are acquired and read, we must write a mask before initiating an | ||
| 6 | // acquisition. To help manage and abstract all this business away, we can organize our channel | ||
| 7 | // pins into acquisition banks. Each acquisition bank can contain exactly one channel pin per TSC | ||
| 8 | // group and it will contain the relevant mask. | ||
| 9 | // | ||
| 10 | // This example demonstrates how to: | ||
| 11 | // 1. Configure multiple channel pins within a single TSC group | ||
| 12 | // 2. Use the set_active_channels_bank method to switch between sets of different channels (acquisition banks) | ||
| 13 | // 3. Read and interpret touch values from multiple channels in the same group | ||
| 14 | // | ||
| 15 | // Suggested physical setup on STM32F303ZE Nucleo board: | ||
| 16 | // - Connect a 1000pF capacitor between pin PA10 and GND. This is the sampling capacitor for TSC | ||
| 17 | // group 4. | ||
| 18 | // - Connect one end of a 1K resistor to pin PA9 and leave the other end loose. | ||
| 19 | // The loose end will act as a touch sensor. | ||
| 20 | // | ||
| 21 | // - Connect a 1000pF capacitor between pin PA7 and GND. This is the sampling capacitor for TSC | ||
| 22 | // group 2. | ||
| 23 | // - Connect one end of another 1K resistor to pin PA6 and leave the other end loose. | ||
| 24 | // The loose end will act as a touch sensor. | ||
| 25 | // - Connect one end of another 1K resistor to pin PA5 and leave the other end loose. | ||
| 26 | // The loose end will act as a touch sensor. | ||
| 27 | // | ||
| 28 | // The example uses pins from two TSC groups. | ||
| 29 | // - PA10 as sampling capacitor, TSC group 4 IO2 | ||
| 30 | // - PA9 as channel, TSC group 4 IO1 | ||
| 31 | // - PA7 as sampling capacitor, TSC group 2 IO4 | ||
| 32 | // - PA6 as channel, TSC group 2 IO3 | ||
| 33 | // - PA5 as channel, TSC group 2 IO2 | ||
| 34 | // | ||
| 35 | // The pins have been chosen to make it easy to simply add capacitors directly onto the board and | ||
| 36 | // connect one leg to GND, and to easily add resistors to the board with no special connectors, | ||
| 37 | // breadboards, special wires or soldering required. All you need is the capacitors and resistors. | ||
| 38 | // | ||
| 39 | // The program reads the designated channel pins and adjusts the LED blinking | ||
| 40 | // pattern based on which sensor(s) are touched: | ||
| 41 | // - No touch: LED off | ||
| 42 | // - one sensor touched: Slow blinking | ||
| 43 | // - two sensors touched: Fast blinking | ||
| 44 | // - three sensors touched: LED constantly on | ||
| 45 | // | ||
| 46 | // ## Troubleshooting: | ||
| 47 | // | ||
| 48 | // - If touch is not detected, try adjusting the SENSOR_THRESHOLD value (currently set to 20). | ||
| 49 | // - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity. | ||
| 50 | // - Be aware that for some boards there will be overlapping concerns between some pins, for | ||
| 51 | // example UART connection for the programmer to the MCU and a TSC pin. No errors or warning will | ||
| 52 | // be emitted if you try to use such a pin for TSC, but you will get strange sensor readings. | ||
| 53 | // | ||
| 54 | // Note: Configuration values and sampling capacitor values have been determined experimentally. Optimal values may vary based on your specific hardware setup. Refer to the official STM32 datasheet and user manuals for more information on pin configurations and TSC functionality. | ||
| 55 | |||
| 56 | #![no_std] | ||
| 57 | #![no_main] | ||
| 58 | |||
| 59 | use defmt::*; | ||
| 60 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 61 | use embassy_stm32::tsc::{self, *}; | ||
| 62 | use embassy_stm32::{mode, peripherals}; | ||
| 63 | use embassy_time::Timer; | ||
| 64 | use {defmt_rtt as _, panic_probe as _}; | ||
| 65 | |||
| 66 | const SENSOR_THRESHOLD: u16 = 10; | ||
| 67 | |||
| 68 | async fn acquire_sensors( | ||
| 69 | touch_controller: &mut Tsc<'static, peripherals::TSC, mode::Blocking>, | ||
| 70 | tsc_acquisition_bank: &AcquisitionBank, | ||
| 71 | ) { | ||
| 72 | touch_controller.set_active_channels_bank(tsc_acquisition_bank); | ||
| 73 | touch_controller.start(); | ||
| 74 | touch_controller.poll_for_acquisition(); | ||
| 75 | touch_controller.discharge_io(true); | ||
| 76 | let discharge_delay = 5; // ms | ||
| 77 | Timer::after_millis(discharge_delay).await; | ||
| 78 | } | ||
| 79 | |||
| 80 | #[embassy_executor::main] | ||
| 81 | async fn main(_spawner: embassy_executor::Spawner) { | ||
| 82 | let device_config = embassy_stm32::Config::default(); | ||
| 83 | let context = embassy_stm32::init(device_config); | ||
| 84 | |||
| 85 | // ---------- initial configuration of TSC ---------- | ||
| 86 | // | ||
| 87 | let mut pin_group4: PinGroupWithRoles<peripherals::TSC, G4> = PinGroupWithRoles::default(); | ||
| 88 | // D68 on the STM32F303ZE nucleo-board | ||
| 89 | pin_group4.set_io2::<tsc::pin_roles::Sample>(context.PA10); | ||
| 90 | // D69 on the STM32F303ZE nucleo-board | ||
| 91 | let tsc_sensor0 = pin_group4.set_io1(context.PA9); | ||
| 92 | |||
| 93 | let mut pin_group2: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default(); | ||
| 94 | // D11 on the STM32F303ZE nucleo-board | ||
| 95 | pin_group2.set_io4::<tsc::pin_roles::Sample>(context.PA7); | ||
| 96 | // D12 on the STM32F303ZE nucleo-board | ||
| 97 | let tsc_sensor1 = pin_group2.set_io3(context.PA6); | ||
| 98 | // D13 on the STM32F303ZE nucleo-board | ||
| 99 | let tsc_sensor2 = pin_group2.set_io2(context.PA5); | ||
| 100 | |||
| 101 | let config = Config { | ||
| 102 | ct_pulse_high_length: ChargeTransferPulseCycle::_4, | ||
| 103 | ct_pulse_low_length: ChargeTransferPulseCycle::_4, | ||
| 104 | spread_spectrum: false, | ||
| 105 | spread_spectrum_deviation: SSDeviation::new(2).unwrap(), | ||
| 106 | spread_spectrum_prescaler: false, | ||
| 107 | pulse_generator_prescaler: PGPrescalerDivider::_16, | ||
| 108 | max_count_value: MaxCount::_255, | ||
| 109 | io_default_mode: false, | ||
| 110 | synchro_pin_polarity: false, | ||
| 111 | acquisition_mode: false, | ||
| 112 | max_count_interrupt: false, | ||
| 113 | }; | ||
| 114 | |||
| 115 | let pin_groups: PinGroups<peripherals::TSC> = PinGroups { | ||
| 116 | g4: Some(pin_group4.pin_group), | ||
| 117 | g2: Some(pin_group2.pin_group), | ||
| 118 | ..Default::default() | ||
| 119 | }; | ||
| 120 | |||
| 121 | let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, pin_groups, config).unwrap(); | ||
| 122 | |||
| 123 | // ---------- setting up acquisition banks ---------- | ||
| 124 | // sensor0 and sensor1 in this example belong to different TSC-groups, | ||
| 125 | // therefore we can acquire and read them both in one go. | ||
| 126 | let bank1 = touch_controller.create_acquisition_bank(AcquisitionBankPins { | ||
| 127 | g4_pin: Some(tsc_sensor0), | ||
| 128 | g2_pin: Some(tsc_sensor1), | ||
| 129 | ..Default::default() | ||
| 130 | }); | ||
| 131 | // `sensor1` and `sensor2` belongs to the same TSC-group, therefore we must make sure to | ||
| 132 | // acquire them one at the time. Therefore, we organize them into different acquisition banks. | ||
| 133 | let bank2 = touch_controller.create_acquisition_bank(AcquisitionBankPins { | ||
| 134 | g2_pin: Some(tsc_sensor2), | ||
| 135 | ..Default::default() | ||
| 136 | }); | ||
| 137 | |||
| 138 | // Check if TSC is ready | ||
| 139 | if touch_controller.get_state() != State::Ready { | ||
| 140 | crate::panic!("TSC not ready!"); | ||
| 141 | } | ||
| 142 | |||
| 143 | info!("TSC initialized successfully"); | ||
| 144 | |||
| 145 | // LED2 on the STM32F303ZE nucleo-board | ||
| 146 | let mut led = Output::new(context.PB7, Level::High, Speed::Low); | ||
| 147 | |||
| 148 | let mut led_state = false; | ||
| 149 | |||
| 150 | loop { | ||
| 151 | acquire_sensors(&mut touch_controller, &bank1).await; | ||
| 152 | let readings1 = touch_controller.get_acquisition_bank_values(&bank1); | ||
| 153 | acquire_sensors(&mut touch_controller, &bank2).await; | ||
| 154 | let readings2 = touch_controller.get_acquisition_bank_values(&bank2); | ||
| 155 | |||
| 156 | let mut touched_sensors_count = 0; | ||
| 157 | for reading in readings1.iter() { | ||
| 158 | info!("{}", reading); | ||
| 159 | if reading.sensor_value < SENSOR_THRESHOLD { | ||
| 160 | touched_sensors_count += 1; | ||
| 161 | } | ||
| 162 | } | ||
| 163 | for reading in readings2.iter() { | ||
| 164 | info!("{}", reading); | ||
| 165 | if reading.sensor_value < SENSOR_THRESHOLD { | ||
| 166 | touched_sensors_count += 1; | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | match touched_sensors_count { | ||
| 171 | 0 => { | ||
| 172 | // No sensors touched, turn off the LED | ||
| 173 | led.set_low(); | ||
| 174 | led_state = false; | ||
| 175 | } | ||
| 176 | 1 => { | ||
| 177 | // One sensor touched, blink slowly | ||
| 178 | led_state = !led_state; | ||
| 179 | if led_state { | ||
| 180 | led.set_high(); | ||
| 181 | } else { | ||
| 182 | led.set_low(); | ||
| 183 | } | ||
| 184 | Timer::after_millis(200).await; | ||
| 185 | } | ||
| 186 | 2 => { | ||
| 187 | // Two sensors touched, blink faster | ||
| 188 | led_state = !led_state; | ||
| 189 | if led_state { | ||
| 190 | led.set_high(); | ||
| 191 | } else { | ||
| 192 | led.set_low(); | ||
| 193 | } | ||
| 194 | Timer::after_millis(50).await; | ||
| 195 | } | ||
| 196 | 3 => { | ||
| 197 | // All three sensors touched, LED constantly on | ||
| 198 | led.set_high(); | ||
| 199 | led_state = true; | ||
| 200 | } | ||
| 201 | _ => crate::unreachable!(), // This case should never occur with 3 sensors | ||
| 202 | } | ||
| 203 | } | ||
| 204 | } | ||
diff --git a/examples/stm32f334/Cargo.toml b/examples/stm32f334/Cargo.toml index 1cc0a97da..c6b311fa5 100644 --- a/examples/stm32f334/Cargo.toml +++ b/examples/stm32f334/Cargo.toml | |||
| @@ -5,8 +5,8 @@ version = "0.1.0" | |||
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 10 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f334r8", "unstable-pac", "memory-x", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f334r8", "unstable-pac", "memory-x", "time-driver-any", "exti"] } |
| 12 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 12 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } |
diff --git a/examples/stm32f334/src/bin/pwm.rs b/examples/stm32f334/src/bin/pwm.rs index e6d1a6c02..2b0686121 100644 --- a/examples/stm32f334/src/bin/pwm.rs +++ b/examples/stm32f334/src/bin/pwm.rs | |||
| @@ -57,14 +57,14 @@ async fn main(_spawner: Spawner) { | |||
| 57 | // embassy_stm32::pac::HRTIM1 | 57 | // embassy_stm32::pac::HRTIM1 |
| 58 | // .tim(0) | 58 | // .tim(0) |
| 59 | // .setr(0) | 59 | // .setr(0) |
| 60 | // .modify(|w| w.set_sst(Activeeffect::SETACTIVE)); | 60 | // .modify(|w| w.set_sst(true)); |
| 61 | // | 61 | // |
| 62 | // Timer::after_millis(500).await; | 62 | // Timer::after_millis(500).await; |
| 63 | // | 63 | // |
| 64 | // embassy_stm32::pac::HRTIM1 | 64 | // embassy_stm32::pac::HRTIM1 |
| 65 | // .tim(0) | 65 | // .tim(0) |
| 66 | // .rstr(0) | 66 | // .rstr(0) |
| 67 | // .modify(|w| w.set_srt(Inactiveeffect::SETINACTIVE)); | 67 | // .modify(|w| w.set_srt(true)); |
| 68 | 68 | ||
| 69 | let max_duty = buck_converter.get_max_compare_value(); | 69 | let max_duty = buck_converter.get_max_compare_value(); |
| 70 | 70 | ||
diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index b85361596..4f0629fc6 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml | |||
| @@ -6,12 +6,12 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32f429zi to your chip name, if necessary. | 8 | # Change stm32f429zi to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-tim4", "exti", "chrono"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.1", 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", "executor-interrupt", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "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.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt" ] } | 13 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt" ] } |
| 14 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } | 14 | embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } |
| 15 | embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } | 15 | embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } |
| 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 17 | 17 | ||
| @@ -27,6 +27,7 @@ embedded-io-async = { version = "0.6.1" } | |||
| 27 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 27 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 28 | futures-util = { version = "0.3.30", default-features = false } | 28 | futures-util = { version = "0.3.30", default-features = false } |
| 29 | heapless = { version = "0.8", default-features = false } | 29 | heapless = { version = "0.8", default-features = false } |
| 30 | critical-section = "1.1" | ||
| 30 | nb = "1.0.0" | 31 | nb = "1.0.0" |
| 31 | embedded-storage = "0.3.1" | 32 | embedded-storage = "0.3.1" |
| 32 | micromath = "2.0.0" | 33 | micromath = "2.0.0" |
diff --git a/examples/stm32f4/src/bin/i2s_dma.rs b/examples/stm32f4/src/bin/i2s_dma.rs index 27b165f1b..68392847b 100644 --- a/examples/stm32f4/src/bin/i2s_dma.rs +++ b/examples/stm32f4/src/bin/i2s_dma.rs | |||
| @@ -1,13 +1,10 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::fmt::Write; | ||
| 5 | |||
| 6 | use defmt::*; | 4 | use defmt::*; |
| 7 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 8 | use embassy_stm32::i2s::{Config, I2S}; | 6 | use embassy_stm32::i2s::{Config, I2S}; |
| 9 | use embassy_stm32::time::Hertz; | 7 | use embassy_stm32::time::Hertz; |
| 10 | use heapless::String; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 9 | ||
| 13 | #[embassy_executor::main] | 10 | #[embassy_executor::main] |
| @@ -15,6 +12,8 @@ async fn main(_spawner: Spawner) { | |||
| 15 | let p = embassy_stm32::init(Default::default()); | 12 | let p = embassy_stm32::init(Default::default()); |
| 16 | info!("Hello World!"); | 13 | info!("Hello World!"); |
| 17 | 14 | ||
| 15 | let mut dma_buffer = [0x00_u16; 128]; | ||
| 16 | |||
| 18 | let mut i2s = I2S::new_txonly( | 17 | let mut i2s = I2S::new_txonly( |
| 19 | p.SPI2, | 18 | p.SPI2, |
| 20 | p.PC3, // sd | 19 | p.PC3, // sd |
| @@ -22,13 +21,13 @@ async fn main(_spawner: Spawner) { | |||
| 22 | p.PB10, // ck | 21 | p.PB10, // ck |
| 23 | p.PC6, // mck | 22 | p.PC6, // mck |
| 24 | p.DMA1_CH4, | 23 | p.DMA1_CH4, |
| 24 | &mut dma_buffer, | ||
| 25 | Hertz(1_000_000), | 25 | Hertz(1_000_000), |
| 26 | Config::default(), | 26 | Config::default(), |
| 27 | ); | 27 | ); |
| 28 | 28 | ||
| 29 | for n in 0u32.. { | 29 | for i in 0_u16.. { |
| 30 | let mut write: String<128> = String::new(); | 30 | i2s.write(&mut [i * 2; 64]).await.ok(); |
| 31 | core::write!(&mut write, "Hello DMA World {}!\r\n", n).unwrap(); | 31 | i2s.write(&mut [i * 2 + 1; 64]).await.ok(); |
| 32 | i2s.write(&mut write.as_bytes()).await.ok(); | ||
| 33 | } | 32 | } |
| 34 | } | 33 | } |
diff --git a/examples/stm32f4/src/bin/pwm.rs b/examples/stm32f4/src/bin/pwm.rs index 8844a9f0e..04811162b 100644 --- a/examples/stm32f4/src/bin/pwm.rs +++ b/examples/stm32f4/src/bin/pwm.rs | |||
| @@ -6,7 +6,6 @@ use embassy_executor::Spawner; | |||
| 6 | use embassy_stm32::gpio::OutputType; | 6 | use embassy_stm32::gpio::OutputType; |
| 7 | use embassy_stm32::time::khz; | 7 | use embassy_stm32::time::khz; |
| 8 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | 8 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; |
| 9 | use embassy_stm32::timer::Channel; | ||
| 10 | use embassy_time::Timer; | 9 | use embassy_time::Timer; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 11 | ||
| @@ -15,22 +14,22 @@ async fn main(_spawner: Spawner) { | |||
| 15 | let p = embassy_stm32::init(Default::default()); | 14 | let p = embassy_stm32::init(Default::default()); |
| 16 | info!("Hello World!"); | 15 | info!("Hello World!"); |
| 17 | 16 | ||
| 18 | let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull); | 17 | let ch1_pin = PwmPin::new_ch1(p.PE9, OutputType::PushPull); |
| 19 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10), Default::default()); | 18 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(10), Default::default()); |
| 20 | let max = pwm.get_max_duty(); | 19 | let mut ch1 = pwm.ch1(); |
| 21 | pwm.enable(Channel::Ch1); | 20 | ch1.enable(); |
| 22 | 21 | ||
| 23 | info!("PWM initialized"); | 22 | info!("PWM initialized"); |
| 24 | info!("PWM max duty {}", max); | 23 | info!("PWM max duty {}", ch1.max_duty_cycle()); |
| 25 | 24 | ||
| 26 | loop { | 25 | loop { |
| 27 | pwm.set_duty(Channel::Ch1, 0); | 26 | ch1.set_duty_cycle_fully_off(); |
| 28 | Timer::after_millis(300).await; | 27 | Timer::after_millis(300).await; |
| 29 | pwm.set_duty(Channel::Ch1, max / 4); | 28 | ch1.set_duty_cycle_fraction(1, 4); |
| 30 | Timer::after_millis(300).await; | 29 | Timer::after_millis(300).await; |
| 31 | pwm.set_duty(Channel::Ch1, max / 2); | 30 | ch1.set_duty_cycle_fraction(1, 2); |
| 32 | Timer::after_millis(300).await; | 31 | Timer::after_millis(300).await; |
| 33 | pwm.set_duty(Channel::Ch1, max - 1); | 32 | ch1.set_duty_cycle(ch1.max_duty_cycle() - 1); |
| 34 | Timer::after_millis(300).await; | 33 | Timer::after_millis(300).await; |
| 35 | } | 34 | } |
| 36 | } | 35 | } |
diff --git a/examples/stm32f4/src/bin/usb_uac_speaker.rs b/examples/stm32f4/src/bin/usb_uac_speaker.rs new file mode 100644 index 000000000..e22e07e63 --- /dev/null +++ b/examples/stm32f4/src/bin/usb_uac_speaker.rs | |||
| @@ -0,0 +1,390 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use core::cell::{Cell, RefCell}; | ||
| 5 | |||
| 6 | use defmt::{panic, *}; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_stm32::time::Hertz; | ||
| 9 | use embassy_stm32::{bind_interrupts, interrupt, peripherals, timer, usb, Config}; | ||
| 10 | use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; | ||
| 11 | use embassy_sync::blocking_mutex::Mutex; | ||
| 12 | use embassy_sync::signal::Signal; | ||
| 13 | use embassy_sync::zerocopy_channel; | ||
| 14 | use embassy_usb::class::uac1; | ||
| 15 | use embassy_usb::class::uac1::speaker::{self, Speaker}; | ||
| 16 | use embassy_usb::driver::EndpointError; | ||
| 17 | use heapless::Vec; | ||
| 18 | use micromath::F32Ext; | ||
| 19 | use static_cell::StaticCell; | ||
| 20 | use {defmt_rtt as _, panic_probe as _}; | ||
| 21 | |||
| 22 | bind_interrupts!(struct Irqs { | ||
| 23 | OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>; | ||
| 24 | }); | ||
| 25 | |||
| 26 | static TIMER: Mutex<CriticalSectionRawMutex, RefCell<Option<timer::low_level::Timer<peripherals::TIM2>>>> = | ||
| 27 | Mutex::new(RefCell::new(None)); | ||
| 28 | |||
| 29 | // A counter signal that is written by the feedback timer, once every `FEEDBACK_REFRESH_PERIOD`. | ||
| 30 | // At that point, a feedback value is sent to the host. | ||
| 31 | pub static FEEDBACK_SIGNAL: Signal<CriticalSectionRawMutex, u32> = Signal::new(); | ||
| 32 | |||
| 33 | // Stereo input | ||
| 34 | pub const INPUT_CHANNEL_COUNT: usize = 2; | ||
| 35 | |||
| 36 | // This example uses a fixed sample rate of 48 kHz. | ||
| 37 | pub const SAMPLE_RATE_HZ: u32 = 48_000; | ||
| 38 | pub const FEEDBACK_COUNTER_TICK_RATE: u32 = 42_000_000; | ||
| 39 | |||
| 40 | // Use 32 bit samples, which allow for a lot of (software) volume adjustment without degradation of quality. | ||
| 41 | pub const SAMPLE_WIDTH: uac1::SampleWidth = uac1::SampleWidth::Width4Byte; | ||
| 42 | pub const SAMPLE_WIDTH_BIT: usize = SAMPLE_WIDTH.in_bit(); | ||
| 43 | pub const SAMPLE_SIZE: usize = SAMPLE_WIDTH as usize; | ||
| 44 | pub const SAMPLE_SIZE_PER_S: usize = (SAMPLE_RATE_HZ as usize) * INPUT_CHANNEL_COUNT * SAMPLE_SIZE; | ||
| 45 | |||
| 46 | // Size of audio samples per 1 ms - for the full-speed USB frame period of 1 ms. | ||
| 47 | pub const USB_FRAME_SIZE: usize = SAMPLE_SIZE_PER_S.div_ceil(1000); | ||
| 48 | |||
| 49 | // Select front left and right audio channels. | ||
| 50 | pub const AUDIO_CHANNELS: [uac1::Channel; INPUT_CHANNEL_COUNT] = [uac1::Channel::LeftFront, uac1::Channel::RightFront]; | ||
| 51 | |||
| 52 | // Factor of two as a margin for feedback (this is an excessive amount) | ||
| 53 | pub const USB_MAX_PACKET_SIZE: usize = 2 * USB_FRAME_SIZE; | ||
| 54 | pub const USB_MAX_SAMPLE_COUNT: usize = USB_MAX_PACKET_SIZE / SAMPLE_SIZE; | ||
| 55 | |||
| 56 | // The data type that is exchanged via the zero-copy channel (a sample vector). | ||
| 57 | pub type SampleBlock = Vec<u32, USB_MAX_SAMPLE_COUNT>; | ||
| 58 | |||
| 59 | // Feedback is provided in 10.14 format for full-speed endpoints. | ||
| 60 | pub const FEEDBACK_REFRESH_PERIOD: uac1::FeedbackRefresh = uac1::FeedbackRefresh::Period8Frames; | ||
| 61 | const FEEDBACK_SHIFT: usize = 14; | ||
| 62 | |||
| 63 | const TICKS_PER_SAMPLE: f32 = (FEEDBACK_COUNTER_TICK_RATE as f32) / (SAMPLE_RATE_HZ as f32); | ||
| 64 | |||
| 65 | struct Disconnected {} | ||
| 66 | |||
| 67 | impl From<EndpointError> for Disconnected { | ||
| 68 | fn from(val: EndpointError) -> Self { | ||
| 69 | match val { | ||
| 70 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 71 | EndpointError::Disabled => Disconnected {}, | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | /// Sends feedback messages to the host. | ||
| 77 | async fn feedback_handler<'d, T: usb::Instance + 'd>( | ||
| 78 | feedback: &mut speaker::Feedback<'d, usb::Driver<'d, T>>, | ||
| 79 | feedback_factor: f32, | ||
| 80 | ) -> Result<(), Disconnected> { | ||
| 81 | let mut packet: Vec<u8, 4> = Vec::new(); | ||
| 82 | |||
| 83 | // Collects the fractional component of the feedback value that is lost by rounding. | ||
| 84 | let mut rest = 0.0_f32; | ||
| 85 | |||
| 86 | loop { | ||
| 87 | let counter = FEEDBACK_SIGNAL.wait().await; | ||
| 88 | |||
| 89 | packet.clear(); | ||
| 90 | |||
| 91 | let raw_value = counter as f32 * feedback_factor + rest; | ||
| 92 | let value = raw_value.round(); | ||
| 93 | rest = raw_value - value; | ||
| 94 | |||
| 95 | let value = value as u32; | ||
| 96 | packet.push(value as u8).unwrap(); | ||
| 97 | packet.push((value >> 8) as u8).unwrap(); | ||
| 98 | packet.push((value >> 16) as u8).unwrap(); | ||
| 99 | |||
| 100 | feedback.write_packet(&packet).await?; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | /// Handles streaming of audio data from the host. | ||
| 105 | async fn stream_handler<'d, T: usb::Instance + 'd>( | ||
| 106 | stream: &mut speaker::Stream<'d, usb::Driver<'d, T>>, | ||
| 107 | sender: &mut zerocopy_channel::Sender<'static, NoopRawMutex, SampleBlock>, | ||
| 108 | ) -> Result<(), Disconnected> { | ||
| 109 | loop { | ||
| 110 | let mut usb_data = [0u8; USB_MAX_PACKET_SIZE]; | ||
| 111 | let data_size = stream.read_packet(&mut usb_data).await?; | ||
| 112 | |||
| 113 | let word_count = data_size / SAMPLE_SIZE; | ||
| 114 | |||
| 115 | if word_count * SAMPLE_SIZE == data_size { | ||
| 116 | // Obtain a buffer from the channel | ||
| 117 | let samples = sender.send().await; | ||
| 118 | samples.clear(); | ||
| 119 | |||
| 120 | for w in 0..word_count { | ||
| 121 | let byte_offset = w * SAMPLE_SIZE; | ||
| 122 | let sample = u32::from_le_bytes(usb_data[byte_offset..byte_offset + SAMPLE_SIZE].try_into().unwrap()); | ||
| 123 | |||
| 124 | // Fill the sample buffer with data. | ||
| 125 | samples.push(sample).unwrap(); | ||
| 126 | } | ||
| 127 | |||
| 128 | sender.send_done(); | ||
| 129 | } else { | ||
| 130 | debug!("Invalid USB buffer size of {}, skipped.", data_size); | ||
| 131 | } | ||
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 135 | /// Receives audio samples from the USB streaming task and can play them back. | ||
| 136 | #[embassy_executor::task] | ||
| 137 | async fn audio_receiver_task(mut usb_audio_receiver: zerocopy_channel::Receiver<'static, NoopRawMutex, SampleBlock>) { | ||
| 138 | loop { | ||
| 139 | let _samples = usb_audio_receiver.receive().await; | ||
| 140 | // Use the samples, for example play back via the SAI peripheral. | ||
| 141 | |||
| 142 | // Notify the channel that the buffer is now ready to be reused | ||
| 143 | usb_audio_receiver.receive_done(); | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | /// Receives audio samples from the host. | ||
| 148 | #[embassy_executor::task] | ||
| 149 | async fn usb_streaming_task( | ||
| 150 | mut stream: speaker::Stream<'static, usb::Driver<'static, peripherals::USB_OTG_FS>>, | ||
| 151 | mut sender: zerocopy_channel::Sender<'static, NoopRawMutex, SampleBlock>, | ||
| 152 | ) { | ||
| 153 | loop { | ||
| 154 | stream.wait_connection().await; | ||
| 155 | _ = stream_handler(&mut stream, &mut sender).await; | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | /// Sends sample rate feedback to the host. | ||
| 160 | /// | ||
| 161 | /// The `feedback_factor` scales the feedback timer's counter value so that the result is the number of samples that | ||
| 162 | /// this device played back or "consumed" during one SOF period (1 ms) - in 10.14 format. | ||
| 163 | /// | ||
| 164 | /// Ideally, the `feedback_factor` that is calculated below would be an integer for avoiding numerical errors. | ||
| 165 | /// This is achieved by having `TICKS_PER_SAMPLE` be a power of two. For audio applications at a sample rate of 48 kHz, | ||
| 166 | /// 24.576 MHz would be one such option. | ||
| 167 | /// | ||
| 168 | /// A good choice for the STM32F4, which also has to generate a 48 MHz clock from its HSE (e.g. running at 8 MHz) | ||
| 169 | /// for USB, is to clock the feedback timer from the MCLK output of the SAI peripheral. The SAI peripheral then uses an | ||
| 170 | /// external clock. In that case, wiring the MCLK output to the timer clock input is required. | ||
| 171 | /// | ||
| 172 | /// This simple example just uses the internal clocks for supplying the feedback timer, | ||
| 173 | /// and does not even set up a SAI peripheral. | ||
| 174 | #[embassy_executor::task] | ||
| 175 | async fn usb_feedback_task(mut feedback: speaker::Feedback<'static, usb::Driver<'static, peripherals::USB_OTG_FS>>) { | ||
| 176 | let feedback_factor = | ||
| 177 | ((1 << FEEDBACK_SHIFT) as f32 / TICKS_PER_SAMPLE) / FEEDBACK_REFRESH_PERIOD.frame_count() as f32; | ||
| 178 | |||
| 179 | // Should be 2.3405714285714287... | ||
| 180 | info!("Using a feedback factor of {}.", feedback_factor); | ||
| 181 | |||
| 182 | loop { | ||
| 183 | feedback.wait_connection().await; | ||
| 184 | _ = feedback_handler(&mut feedback, feedback_factor).await; | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | #[embassy_executor::task] | ||
| 189 | async fn usb_task(mut usb_device: embassy_usb::UsbDevice<'static, usb::Driver<'static, peripherals::USB_OTG_FS>>) { | ||
| 190 | usb_device.run().await; | ||
| 191 | } | ||
| 192 | |||
| 193 | /// Checks for changes on the control monitor of the class. | ||
| 194 | /// | ||
| 195 | /// In this case, monitor changes of volume or mute state. | ||
| 196 | #[embassy_executor::task] | ||
| 197 | async fn usb_control_task(control_monitor: speaker::ControlMonitor<'static>) { | ||
| 198 | loop { | ||
| 199 | control_monitor.changed().await; | ||
| 200 | |||
| 201 | for channel in AUDIO_CHANNELS { | ||
| 202 | let volume = control_monitor.volume(channel).unwrap(); | ||
| 203 | info!("Volume changed to {} on channel {}.", volume, channel); | ||
| 204 | } | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | /// Feedback value measurement and calculation | ||
| 209 | /// | ||
| 210 | /// Used for measuring/calculating the number of samples that were received from the host during the | ||
| 211 | /// `FEEDBACK_REFRESH_PERIOD`. | ||
| 212 | /// | ||
| 213 | /// Configured in this example with | ||
| 214 | /// - a refresh period of 8 ms, and | ||
| 215 | /// - a tick rate of 42 MHz. | ||
| 216 | /// | ||
| 217 | /// This gives an (ideal) counter value of 336.000 for every update of the `FEEDBACK_SIGNAL`. | ||
| 218 | #[interrupt] | ||
| 219 | fn TIM2() { | ||
| 220 | static LAST_TICKS: Mutex<CriticalSectionRawMutex, Cell<u32>> = Mutex::new(Cell::new(0)); | ||
| 221 | static FRAME_COUNT: Mutex<CriticalSectionRawMutex, Cell<usize>> = Mutex::new(Cell::new(0)); | ||
| 222 | |||
| 223 | critical_section::with(|cs| { | ||
| 224 | // Read timer counter. | ||
| 225 | let timer = TIMER.borrow(cs).borrow().as_ref().unwrap().regs_gp32(); | ||
| 226 | |||
| 227 | let status = timer.sr().read(); | ||
| 228 | |||
| 229 | const CHANNEL_INDEX: usize = 0; | ||
| 230 | if status.ccif(CHANNEL_INDEX) { | ||
| 231 | let ticks = timer.ccr(CHANNEL_INDEX).read(); | ||
| 232 | |||
| 233 | let frame_count = FRAME_COUNT.borrow(cs); | ||
| 234 | let last_ticks = LAST_TICKS.borrow(cs); | ||
| 235 | |||
| 236 | frame_count.set(frame_count.get() + 1); | ||
| 237 | if frame_count.get() >= FEEDBACK_REFRESH_PERIOD.frame_count() { | ||
| 238 | frame_count.set(0); | ||
| 239 | FEEDBACK_SIGNAL.signal(ticks.wrapping_sub(last_ticks.get())); | ||
| 240 | last_ticks.set(ticks); | ||
| 241 | } | ||
| 242 | }; | ||
| 243 | |||
| 244 | // Clear trigger interrupt flag. | ||
| 245 | timer.sr().modify(|r| r.set_tif(false)); | ||
| 246 | }); | ||
| 247 | } | ||
| 248 | |||
| 249 | // If you are trying this and your USB device doesn't connect, the most | ||
| 250 | // common issues are the RCC config and vbus_detection | ||
| 251 | // | ||
| 252 | // See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure | ||
| 253 | // for more information. | ||
| 254 | #[embassy_executor::main] | ||
| 255 | async fn main(spawner: Spawner) { | ||
| 256 | info!("Hello World!"); | ||
| 257 | |||
| 258 | let mut config = Config::default(); | ||
| 259 | { | ||
| 260 | use embassy_stm32::rcc::*; | ||
| 261 | config.rcc.hse = Some(Hse { | ||
| 262 | freq: Hertz(8_000_000), | ||
| 263 | mode: HseMode::Bypass, | ||
| 264 | }); | ||
| 265 | config.rcc.pll_src = PllSource::HSE; | ||
| 266 | config.rcc.pll = Some(Pll { | ||
| 267 | prediv: PllPreDiv::DIV4, | ||
| 268 | mul: PllMul::MUL168, | ||
| 269 | divp: Some(PllPDiv::DIV2), // ((8 MHz / 4) * 168) / 2 = 168 Mhz. | ||
| 270 | divq: Some(PllQDiv::DIV7), // ((8 MHz / 4) * 168) / 7 = 48 Mhz. | ||
| 271 | divr: None, | ||
| 272 | }); | ||
| 273 | config.rcc.ahb_pre = AHBPrescaler::DIV1; | ||
| 274 | config.rcc.apb1_pre = APBPrescaler::DIV4; | ||
| 275 | config.rcc.apb2_pre = APBPrescaler::DIV2; | ||
| 276 | config.rcc.sys = Sysclk::PLL1_P; | ||
| 277 | config.rcc.mux.clk48sel = mux::Clk48sel::PLL1_Q; | ||
| 278 | } | ||
| 279 | let p = embassy_stm32::init(config); | ||
| 280 | |||
| 281 | // Configure all required buffers in a static way. | ||
| 282 | debug!("USB packet size is {} byte", USB_MAX_PACKET_SIZE); | ||
| 283 | static CONFIG_DESCRIPTOR: StaticCell<[u8; 256]> = StaticCell::new(); | ||
| 284 | let config_descriptor = CONFIG_DESCRIPTOR.init([0; 256]); | ||
| 285 | |||
| 286 | static BOS_DESCRIPTOR: StaticCell<[u8; 32]> = StaticCell::new(); | ||
| 287 | let bos_descriptor = BOS_DESCRIPTOR.init([0; 32]); | ||
| 288 | |||
| 289 | const CONTROL_BUF_SIZE: usize = 64; | ||
| 290 | static CONTROL_BUF: StaticCell<[u8; CONTROL_BUF_SIZE]> = StaticCell::new(); | ||
| 291 | let control_buf = CONTROL_BUF.init([0; CONTROL_BUF_SIZE]); | ||
| 292 | |||
| 293 | const FEEDBACK_BUF_SIZE: usize = 4; | ||
| 294 | static EP_OUT_BUFFER: StaticCell<[u8; FEEDBACK_BUF_SIZE + CONTROL_BUF_SIZE + USB_MAX_PACKET_SIZE]> = | ||
| 295 | StaticCell::new(); | ||
| 296 | let ep_out_buffer = EP_OUT_BUFFER.init([0u8; FEEDBACK_BUF_SIZE + CONTROL_BUF_SIZE + USB_MAX_PACKET_SIZE]); | ||
| 297 | |||
| 298 | static STATE: StaticCell<speaker::State> = StaticCell::new(); | ||
| 299 | let state = STATE.init(speaker::State::new()); | ||
| 300 | |||
| 301 | // Create the driver, from the HAL. | ||
| 302 | let mut usb_config = usb::Config::default(); | ||
| 303 | |||
| 304 | // Do not enable vbus_detection. This is a safe default that works in all boards. | ||
| 305 | // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need | ||
| 306 | // to enable vbus_detection to comply with the USB spec. If you enable it, the board | ||
| 307 | // has to support it or USB won't work at all. See docs on `vbus_detection` for details. | ||
| 308 | usb_config.vbus_detection = false; | ||
| 309 | |||
| 310 | let usb_driver = usb::Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, ep_out_buffer, usb_config); | ||
| 311 | |||
| 312 | // Basic USB device configuration | ||
| 313 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 314 | config.manufacturer = Some("Embassy"); | ||
| 315 | config.product = Some("USB-audio-speaker example"); | ||
| 316 | config.serial_number = Some("12345678"); | ||
| 317 | |||
| 318 | // Required for windows compatibility. | ||
| 319 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 320 | config.device_class = 0xEF; | ||
| 321 | config.device_sub_class = 0x02; | ||
| 322 | config.device_protocol = 0x01; | ||
| 323 | config.composite_with_iads = true; | ||
| 324 | |||
| 325 | let mut builder = embassy_usb::Builder::new( | ||
| 326 | usb_driver, | ||
| 327 | config, | ||
| 328 | config_descriptor, | ||
| 329 | bos_descriptor, | ||
| 330 | &mut [], // no msos descriptors | ||
| 331 | control_buf, | ||
| 332 | ); | ||
| 333 | |||
| 334 | // Create the UAC1 Speaker class components | ||
| 335 | let (stream, feedback, control_monitor) = Speaker::new( | ||
| 336 | &mut builder, | ||
| 337 | state, | ||
| 338 | USB_MAX_PACKET_SIZE as u16, | ||
| 339 | uac1::SampleWidth::Width4Byte, | ||
| 340 | &[SAMPLE_RATE_HZ], | ||
| 341 | &AUDIO_CHANNELS, | ||
| 342 | FEEDBACK_REFRESH_PERIOD, | ||
| 343 | ); | ||
| 344 | |||
| 345 | // Create the USB device | ||
| 346 | let usb_device = builder.build(); | ||
| 347 | |||
| 348 | // Establish a zero-copy channel for transferring received audio samples between tasks | ||
| 349 | static SAMPLE_BLOCKS: StaticCell<[SampleBlock; 2]> = StaticCell::new(); | ||
| 350 | let sample_blocks = SAMPLE_BLOCKS.init([Vec::new(), Vec::new()]); | ||
| 351 | |||
| 352 | static CHANNEL: StaticCell<zerocopy_channel::Channel<'_, NoopRawMutex, SampleBlock>> = StaticCell::new(); | ||
| 353 | let channel = CHANNEL.init(zerocopy_channel::Channel::new(sample_blocks)); | ||
| 354 | let (sender, receiver) = channel.split(); | ||
| 355 | |||
| 356 | // Run a timer for counting between SOF interrupts. | ||
| 357 | let mut tim2 = timer::low_level::Timer::new(p.TIM2); | ||
| 358 | tim2.set_tick_freq(Hertz(FEEDBACK_COUNTER_TICK_RATE)); | ||
| 359 | tim2.set_trigger_source(timer::low_level::TriggerSource::ITR1); // The USB SOF signal. | ||
| 360 | |||
| 361 | const TIMER_CHANNEL: timer::Channel = timer::Channel::Ch1; | ||
| 362 | tim2.set_input_ti_selection(TIMER_CHANNEL, timer::low_level::InputTISelection::TRC); | ||
| 363 | tim2.set_input_capture_prescaler(TIMER_CHANNEL, 0); | ||
| 364 | tim2.set_input_capture_filter(TIMER_CHANNEL, timer::low_level::FilterValue::FCK_INT_N2); | ||
| 365 | |||
| 366 | // Reset all interrupt flags. | ||
| 367 | tim2.regs_gp32().sr().write(|r| r.0 = 0); | ||
| 368 | |||
| 369 | // Enable routing of SOF to the timer. | ||
| 370 | tim2.regs_gp32().or().write(|r| *r = 0b10 << 10); | ||
| 371 | |||
| 372 | tim2.enable_channel(TIMER_CHANNEL, true); | ||
| 373 | tim2.enable_input_interrupt(TIMER_CHANNEL, true); | ||
| 374 | |||
| 375 | tim2.start(); | ||
| 376 | |||
| 377 | TIMER.lock(|p| p.borrow_mut().replace(tim2)); | ||
| 378 | |||
| 379 | // Unmask the TIM2 interrupt. | ||
| 380 | unsafe { | ||
| 381 | cortex_m::peripheral::NVIC::unmask(interrupt::TIM2); | ||
| 382 | } | ||
| 383 | |||
| 384 | // Launch USB audio tasks. | ||
| 385 | unwrap!(spawner.spawn(usb_control_task(control_monitor))); | ||
| 386 | unwrap!(spawner.spawn(usb_streaming_task(stream, sender))); | ||
| 387 | unwrap!(spawner.spawn(usb_feedback_task(feedback))); | ||
| 388 | unwrap!(spawner.spawn(usb_task(usb_device))); | ||
| 389 | unwrap!(spawner.spawn(audio_receiver_task(receiver))); | ||
| 390 | } | ||
diff --git a/examples/stm32f4/src/bin/ws2812_pwm.rs b/examples/stm32f4/src/bin/ws2812_pwm.rs index cbaff75fc..3ab93d6e0 100644 --- a/examples/stm32f4/src/bin/ws2812_pwm.rs +++ b/examples/stm32f4/src/bin/ws2812_pwm.rs | |||
| @@ -61,7 +61,7 @@ async fn main(_spawner: Spawner) { | |||
| 61 | // construct ws2812 non-return-to-zero (NRZ) code bit by bit | 61 | // construct ws2812 non-return-to-zero (NRZ) code bit by bit |
| 62 | // ws2812 only need 24 bits for each LED, but we add one bit more to keep PWM output low | 62 | // ws2812 only need 24 bits for each LED, but we add one bit more to keep PWM output low |
| 63 | 63 | ||
| 64 | let max_duty = ws2812_pwm.get_max_duty() as u16; | 64 | let max_duty = ws2812_pwm.max_duty_cycle(); |
| 65 | let n0 = 8 * max_duty / 25; // ws2812 Bit 0 high level timing | 65 | let n0 = 8 * max_duty / 25; // ws2812 Bit 0 high level timing |
| 66 | let n1 = 2 * n0; // ws2812 Bit 1 high level timing | 66 | let n1 = 2 * n0; // ws2812 Bit 1 high level timing |
| 67 | 67 | ||
| @@ -84,7 +84,7 @@ async fn main(_spawner: Spawner) { | |||
| 84 | let pwm_channel = Channel::Ch1; | 84 | let pwm_channel = Channel::Ch1; |
| 85 | 85 | ||
| 86 | // make sure PWM output keep low on first start | 86 | // make sure PWM output keep low on first start |
| 87 | ws2812_pwm.set_duty(pwm_channel, 0); | 87 | ws2812_pwm.channel(pwm_channel).set_duty_cycle(0); |
| 88 | 88 | ||
| 89 | // flip color at 2 Hz | 89 | // flip color at 2 Hz |
| 90 | let mut ticker = Ticker::every(Duration::from_millis(500)); | 90 | let mut ticker = Ticker::every(Duration::from_millis(500)); |
diff --git a/examples/stm32f469/Cargo.toml b/examples/stm32f469/Cargo.toml index 6a5bd0b29..a80409801 100644 --- a/examples/stm32f469/Cargo.toml +++ b/examples/stm32f469/Cargo.toml | |||
| @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" | |||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Specific examples only for stm32f469 | 8 | # Specific examples only for stm32f469 |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f469ni", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f469ni", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] } |
| 10 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | 10 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 11 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 11 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 12 | 12 | ||
| 13 | defmt = "0.3" | 13 | defmt = "0.3" |
diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 8c591ebd2..520b8bc42 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml | |||
| @@ -7,10 +7,10 @@ license = "MIT OR Apache-2.0" | |||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32f777zi to your chip name, if necessary. | 8 | # Change stm32f777zi to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f777zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f777zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.1", 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.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "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.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } | 13 | embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } |
| 14 | embedded-io-async = { version = "0.6.1" } | 14 | embedded-io-async = { version = "0.6.1" } |
| 15 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 15 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } |
| 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
diff --git a/examples/stm32f7/src/bin/qspi.rs b/examples/stm32f7/src/bin/qspi.rs index 90d319b7a..bd3287964 100644 --- a/examples/stm32f7/src/bin/qspi.rs +++ b/examples/stm32f7/src/bin/qspi.rs | |||
| @@ -72,7 +72,7 @@ impl<I: Instance> FlashMemory<I> { | |||
| 72 | address: None, | 72 | address: None, |
| 73 | dummy: DummyCycles::_0, | 73 | dummy: DummyCycles::_0, |
| 74 | }; | 74 | }; |
| 75 | self.qspi.command(transaction); | 75 | self.qspi.blocking_command(transaction); |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | pub fn reset_memory(&mut self) { | 78 | pub fn reset_memory(&mut self) { |
| @@ -143,7 +143,7 @@ impl<I: Instance> FlashMemory<I> { | |||
| 143 | dummy: DummyCycles::_0, | 143 | dummy: DummyCycles::_0, |
| 144 | }; | 144 | }; |
| 145 | self.enable_write(); | 145 | self.enable_write(); |
| 146 | self.qspi.command(transaction); | 146 | self.qspi.blocking_command(transaction); |
| 147 | self.wait_write_finish(); | 147 | self.wait_write_finish(); |
| 148 | } | 148 | } |
| 149 | 149 | ||
diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index a50074ce0..3d11610ce 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml | |||
| @@ -7,8 +7,8 @@ license = "MIT OR Apache-2.0" | |||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32g0b1re to your chip name, if necessary. | 8 | # Change stm32g0b1re to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g0b1re", "memory-x", "unstable-pac", "exti"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g0b1re", "memory-x", "unstable-pac", "exti"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } | 13 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } |
| 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
diff --git a/examples/stm32g0/src/bin/input_capture.rs b/examples/stm32g0/src/bin/input_capture.rs index 69fdae96d..bc814cb13 100644 --- a/examples/stm32g0/src/bin/input_capture.rs +++ b/examples/stm32g0/src/bin/input_capture.rs | |||
| @@ -47,10 +47,10 @@ async fn main(spawner: Spawner) { | |||
| 47 | unwrap!(spawner.spawn(blinky(p.PB1))); | 47 | unwrap!(spawner.spawn(blinky(p.PB1))); |
| 48 | 48 | ||
| 49 | // Connect PB1 and PA8 with a 1k Ohm resistor | 49 | // Connect PB1 and PA8 with a 1k Ohm resistor |
| 50 | let ch1 = PwmPin::new_ch1(p.PA8, OutputType::PushPull); | 50 | let ch1_pin = PwmPin::new_ch1(p.PA8, OutputType::PushPull); |
| 51 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(1), Default::default()); | 51 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(1), Default::default()); |
| 52 | pwm.enable(Channel::Ch1); | 52 | pwm.ch1().enable(); |
| 53 | pwm.set_duty(Channel::Ch1, 50); | 53 | pwm.ch1().set_duty_cycle(50); |
| 54 | 54 | ||
| 55 | let ch1 = CapturePin::new_ch1(p.PA0, Pull::None); | 55 | let ch1 = CapturePin::new_ch1(p.PA0, Pull::None); |
| 56 | let mut ic = InputCapture::new(p.TIM2, Some(ch1), None, None, None, Irqs, khz(1000), Default::default()); | 56 | let mut ic = InputCapture::new(p.TIM2, Some(ch1), None, None, None, Irqs, khz(1000), Default::default()); |
diff --git a/examples/stm32g0/src/bin/pwm_input.rs b/examples/stm32g0/src/bin/pwm_input.rs index 152ecda86..db9cf4f8a 100644 --- a/examples/stm32g0/src/bin/pwm_input.rs +++ b/examples/stm32g0/src/bin/pwm_input.rs | |||
| @@ -14,7 +14,6 @@ use embassy_stm32::gpio::{Level, Output, OutputType, Pull, Speed}; | |||
| 14 | use embassy_stm32::time::khz; | 14 | use embassy_stm32::time::khz; |
| 15 | use embassy_stm32::timer::pwm_input::PwmInput; | 15 | use embassy_stm32::timer::pwm_input::PwmInput; |
| 16 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | 16 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; |
| 17 | use embassy_stm32::timer::Channel; | ||
| 18 | use embassy_stm32::{bind_interrupts, peripherals, timer}; | 17 | use embassy_stm32::{bind_interrupts, peripherals, timer}; |
| 19 | use embassy_time::Timer; | 18 | use embassy_time::Timer; |
| 20 | use {defmt_rtt as _, panic_probe as _}; | 19 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -43,11 +42,10 @@ async fn main(spawner: Spawner) { | |||
| 43 | 42 | ||
| 44 | unwrap!(spawner.spawn(blinky(p.PB1))); | 43 | unwrap!(spawner.spawn(blinky(p.PB1))); |
| 45 | // Connect PA8 and PA6 with a 1k Ohm resistor | 44 | // Connect PA8 and PA6 with a 1k Ohm resistor |
| 46 | let ch1 = PwmPin::new_ch1(p.PA8, OutputType::PushPull); | 45 | let ch1_pin = PwmPin::new_ch1(p.PA8, OutputType::PushPull); |
| 47 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(1), Default::default()); | 46 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(1), Default::default()); |
| 48 | let max = pwm.get_max_duty(); | 47 | pwm.ch1().set_duty_cycle_fraction(1, 4); |
| 49 | pwm.set_duty(Channel::Ch1, max / 4); | 48 | pwm.ch1().enable(); |
| 50 | pwm.enable(Channel::Ch1); | ||
| 51 | 49 | ||
| 52 | let mut pwm_input = PwmInput::new(p.TIM2, p.PA0, Pull::None, khz(1000)); | 50 | let mut pwm_input = PwmInput::new(p.TIM2, p.PA0, Pull::None, khz(1000)); |
| 53 | pwm_input.enable(); | 51 | pwm_input.enable(); |
diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 2768147a1..87fa2c53a 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml | |||
| @@ -7,8 +7,8 @@ license = "MIT OR Apache-2.0" | |||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32g491re to your chip name, if necessary. | 8 | # Change stm32g491re to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 13 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } |
| 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
diff --git a/examples/stm32g4/src/bin/adc_dma.rs b/examples/stm32g4/src/bin/adc_dma.rs new file mode 100644 index 000000000..970623b32 --- /dev/null +++ b/examples/stm32g4/src/bin/adc_dma.rs | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::adc::{Adc, AdcChannel as _, SampleTime}; | ||
| 7 | use embassy_stm32::Config; | ||
| 8 | use embassy_time::Timer; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | static mut DMA_BUF: [u16; 2] = [0; 2]; | ||
| 12 | |||
| 13 | #[embassy_executor::main] | ||
| 14 | async fn main(_spawner: Spawner) { | ||
| 15 | let mut read_buffer = unsafe { &mut DMA_BUF[..] }; | ||
| 16 | |||
| 17 | let mut config = Config::default(); | ||
| 18 | { | ||
| 19 | use embassy_stm32::rcc::*; | ||
| 20 | config.rcc.pll = Some(Pll { | ||
| 21 | source: PllSource::HSI, | ||
| 22 | prediv: PllPreDiv::DIV4, | ||
| 23 | mul: PllMul::MUL85, | ||
| 24 | divp: None, | ||
| 25 | divq: None, | ||
| 26 | // Main system clock at 170 MHz | ||
| 27 | divr: Some(PllRDiv::DIV2), | ||
| 28 | }); | ||
| 29 | config.rcc.mux.adc12sel = mux::Adcsel::SYS; | ||
| 30 | config.rcc.sys = Sysclk::PLL1_R; | ||
| 31 | } | ||
| 32 | let p = embassy_stm32::init(config); | ||
| 33 | |||
| 34 | info!("Hello World!"); | ||
| 35 | |||
| 36 | let mut adc = Adc::new(p.ADC1); | ||
| 37 | |||
| 38 | let mut dma = p.DMA1_CH1; | ||
| 39 | let mut vrefint_channel = adc.enable_vrefint().degrade_adc(); | ||
| 40 | let mut pa0 = p.PA0.degrade_adc(); | ||
| 41 | |||
| 42 | loop { | ||
| 43 | adc.read( | ||
| 44 | &mut dma, | ||
| 45 | [ | ||
| 46 | (&mut vrefint_channel, SampleTime::CYCLES247_5), | ||
| 47 | (&mut pa0, SampleTime::CYCLES247_5), | ||
| 48 | ] | ||
| 49 | .into_iter(), | ||
| 50 | &mut read_buffer, | ||
| 51 | ) | ||
| 52 | .await; | ||
| 53 | |||
| 54 | let vrefint = read_buffer[0]; | ||
| 55 | let measured = read_buffer[1]; | ||
| 56 | info!("vrefint: {}", vrefint); | ||
| 57 | info!("measured: {}", measured); | ||
| 58 | Timer::after_millis(500).await; | ||
| 59 | } | ||
| 60 | } | ||
diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs index d4809a481..6c965012c 100644 --- a/examples/stm32g4/src/bin/pwm.rs +++ b/examples/stm32g4/src/bin/pwm.rs | |||
| @@ -6,7 +6,6 @@ use embassy_executor::Spawner; | |||
| 6 | use embassy_stm32::gpio::OutputType; | 6 | use embassy_stm32::gpio::OutputType; |
| 7 | use embassy_stm32::time::khz; | 7 | use embassy_stm32::time::khz; |
| 8 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | 8 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; |
| 9 | use embassy_stm32::timer::Channel; | ||
| 10 | use embassy_time::Timer; | 9 | use embassy_time::Timer; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 11 | ||
| @@ -15,22 +14,22 @@ async fn main(_spawner: Spawner) { | |||
| 15 | let p = embassy_stm32::init(Default::default()); | 14 | let p = embassy_stm32::init(Default::default()); |
| 16 | info!("Hello World!"); | 15 | info!("Hello World!"); |
| 17 | 16 | ||
| 18 | let ch1 = PwmPin::new_ch1(p.PC0, OutputType::PushPull); | 17 | let ch1_pin = PwmPin::new_ch1(p.PC0, OutputType::PushPull); |
| 19 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10), Default::default()); | 18 | let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(10), Default::default()); |
| 20 | let max = pwm.get_max_duty(); | 19 | let mut ch1 = pwm.ch1(); |
| 21 | pwm.enable(Channel::Ch1); | 20 | ch1.enable(); |
| 22 | 21 | ||
| 23 | info!("PWM initialized"); | 22 | info!("PWM initialized"); |
| 24 | info!("PWM max duty {}", max); | 23 | info!("PWM max duty {}", ch1.max_duty_cycle()); |
| 25 | 24 | ||
| 26 | loop { | 25 | loop { |
| 27 | pwm.set_duty(Channel::Ch1, 0); | 26 | ch1.set_duty_cycle_fully_off(); |
| 28 | Timer::after_millis(300).await; | 27 | Timer::after_millis(300).await; |
| 29 | pwm.set_duty(Channel::Ch1, max / 4); | 28 | ch1.set_duty_cycle_fraction(1, 4); |
| 30 | Timer::after_millis(300).await; | 29 | Timer::after_millis(300).await; |
| 31 | pwm.set_duty(Channel::Ch1, max / 2); | 30 | ch1.set_duty_cycle_fraction(1, 2); |
| 32 | Timer::after_millis(300).await; | 31 | Timer::after_millis(300).await; |
| 33 | pwm.set_duty(Channel::Ch1, max - 1); | 32 | ch1.set_duty_cycle(ch1.max_duty_cycle() - 1); |
| 34 | Timer::after_millis(300).await; | 33 | Timer::after_millis(300).await; |
| 35 | } | 34 | } |
| 36 | } | 35 | } |
diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index 30b1d2be9..516d491e5 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml | |||
| @@ -7,10 +7,10 @@ license = "MIT OR Apache-2.0" | |||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32h563zi to your chip name, if necessary. | 8 | # Change stm32h563zi to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h563zi", "memory-x", "time-driver-any", "exti", "unstable-pac", "low-power"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h563zi", "memory-x", "time-driver-any", "exti", "unstable-pac", "low-power"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.1", 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.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "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.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] } | 13 | embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] } |
| 14 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } |
| 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 16 | 16 | ||
| @@ -23,7 +23,7 @@ embedded-hal = "0.2.6" | |||
| 23 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | 23 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } |
| 24 | embedded-hal-async = { version = "1.0" } | 24 | embedded-hal-async = { version = "1.0" } |
| 25 | embedded-io-async = { version = "0.6.1" } | 25 | embedded-io-async = { version = "0.6.1" } |
| 26 | embedded-nal-async = { version = "0.7.1" } | 26 | embedded-nal-async = "0.8.0" |
| 27 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 27 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 28 | heapless = { version = "0.8", default-features = false } | 28 | heapless = { version = "0.8", default-features = false } |
| 29 | rand_core = "0.6.3" | 29 | rand_core = "0.6.3" |
diff --git a/examples/stm32h5/src/bin/adc.rs b/examples/stm32h5/src/bin/adc.rs new file mode 100644 index 000000000..c5d508ece --- /dev/null +++ b/examples/stm32h5/src/bin/adc.rs | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::adc::{Adc, SampleTime}; | ||
| 7 | use embassy_stm32::Config; | ||
| 8 | use embassy_time::Timer; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let mut config = Config::default(); | ||
| 14 | { | ||
| 15 | use embassy_stm32::rcc::*; | ||
| 16 | config.rcc.hsi = Some(HSIPrescaler::DIV1); | ||
| 17 | config.rcc.csi = true; | ||
| 18 | config.rcc.pll1 = Some(Pll { | ||
| 19 | source: PllSource::HSI, | ||
| 20 | prediv: PllPreDiv::DIV4, | ||
| 21 | mul: PllMul::MUL25, | ||
| 22 | divp: Some(PllDiv::DIV2), | ||
| 23 | divq: Some(PllDiv::DIV4), // SPI1 cksel defaults to pll1_q | ||
| 24 | divr: None, | ||
| 25 | }); | ||
| 26 | config.rcc.pll2 = Some(Pll { | ||
| 27 | source: PllSource::HSI, | ||
| 28 | prediv: PllPreDiv::DIV4, | ||
| 29 | mul: PllMul::MUL25, | ||
| 30 | divp: None, | ||
| 31 | divq: None, | ||
| 32 | divr: Some(PllDiv::DIV4), // 100mhz | ||
| 33 | }); | ||
| 34 | config.rcc.sys = Sysclk::PLL1_P; // 200 Mhz | ||
| 35 | config.rcc.ahb_pre = AHBPrescaler::DIV1; // 200 Mhz | ||
| 36 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 37 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 38 | config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 39 | config.rcc.voltage_scale = VoltageScale::Scale1; | ||
| 40 | config.rcc.mux.adcdacsel = mux::Adcdacsel::PLL2_R; | ||
| 41 | } | ||
| 42 | let mut p = embassy_stm32::init(config); | ||
| 43 | |||
| 44 | info!("Hello World!"); | ||
| 45 | |||
| 46 | let mut adc = Adc::new(p.ADC1); | ||
| 47 | |||
| 48 | adc.set_sample_time(SampleTime::CYCLES24_5); | ||
| 49 | |||
| 50 | let mut vrefint_channel = adc.enable_vrefint(); | ||
| 51 | |||
| 52 | loop { | ||
| 53 | let vrefint = adc.blocking_read(&mut vrefint_channel); | ||
| 54 | info!("vrefint: {}", vrefint); | ||
| 55 | let measured = adc.blocking_read(&mut p.PA0); | ||
| 56 | info!("measured: {}", measured); | ||
| 57 | Timer::after_millis(500).await; | ||
| 58 | } | ||
| 59 | } | ||
diff --git a/examples/stm32h5/src/bin/usb_uac_speaker.rs b/examples/stm32h5/src/bin/usb_uac_speaker.rs new file mode 100644 index 000000000..8c24fa916 --- /dev/null +++ b/examples/stm32h5/src/bin/usb_uac_speaker.rs | |||
| @@ -0,0 +1,381 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use core::cell::{Cell, RefCell}; | ||
| 5 | |||
| 6 | use defmt::{panic, *}; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_stm32::time::Hertz; | ||
| 9 | use embassy_stm32::{bind_interrupts, interrupt, peripherals, timer, usb, Config}; | ||
| 10 | use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; | ||
| 11 | use embassy_sync::blocking_mutex::Mutex; | ||
| 12 | use embassy_sync::signal::Signal; | ||
| 13 | use embassy_sync::zerocopy_channel; | ||
| 14 | use embassy_usb::class::uac1; | ||
| 15 | use embassy_usb::class::uac1::speaker::{self, Speaker}; | ||
| 16 | use embassy_usb::driver::EndpointError; | ||
| 17 | use heapless::Vec; | ||
| 18 | use micromath::F32Ext; | ||
| 19 | use static_cell::StaticCell; | ||
| 20 | use {defmt_rtt as _, panic_probe as _}; | ||
| 21 | |||
| 22 | bind_interrupts!(struct Irqs { | ||
| 23 | USB_DRD_FS => usb::InterruptHandler<peripherals::USB>; | ||
| 24 | }); | ||
| 25 | |||
| 26 | static TIMER: Mutex<CriticalSectionRawMutex, RefCell<Option<timer::low_level::Timer<peripherals::TIM5>>>> = | ||
| 27 | Mutex::new(RefCell::new(None)); | ||
| 28 | |||
| 29 | // A counter signal that is written by the feedback timer, once every `FEEDBACK_REFRESH_PERIOD`. | ||
| 30 | // At that point, a feedback value is sent to the host. | ||
| 31 | pub static FEEDBACK_SIGNAL: Signal<CriticalSectionRawMutex, u32> = Signal::new(); | ||
| 32 | |||
| 33 | // Stereo input | ||
| 34 | pub const INPUT_CHANNEL_COUNT: usize = 2; | ||
| 35 | |||
| 36 | // This example uses a fixed sample rate of 48 kHz. | ||
| 37 | pub const SAMPLE_RATE_HZ: u32 = 48_000; | ||
| 38 | pub const FEEDBACK_COUNTER_TICK_RATE: u32 = 31_250_000; | ||
| 39 | |||
| 40 | // Use 32 bit samples, which allow for a lot of (software) volume adjustment without degradation of quality. | ||
| 41 | pub const SAMPLE_WIDTH: uac1::SampleWidth = uac1::SampleWidth::Width4Byte; | ||
| 42 | pub const SAMPLE_WIDTH_BIT: usize = SAMPLE_WIDTH.in_bit(); | ||
| 43 | pub const SAMPLE_SIZE: usize = SAMPLE_WIDTH as usize; | ||
| 44 | pub const SAMPLE_SIZE_PER_S: usize = (SAMPLE_RATE_HZ as usize) * INPUT_CHANNEL_COUNT * SAMPLE_SIZE; | ||
| 45 | |||
| 46 | // Size of audio samples per 1 ms - for the full-speed USB frame period of 1 ms. | ||
| 47 | pub const USB_FRAME_SIZE: usize = SAMPLE_SIZE_PER_S.div_ceil(1000); | ||
| 48 | |||
| 49 | // Select front left and right audio channels. | ||
| 50 | pub const AUDIO_CHANNELS: [uac1::Channel; INPUT_CHANNEL_COUNT] = [uac1::Channel::LeftFront, uac1::Channel::RightFront]; | ||
| 51 | |||
| 52 | // Factor of two as a margin for feedback (this is an excessive amount) | ||
| 53 | pub const USB_MAX_PACKET_SIZE: usize = 2 * USB_FRAME_SIZE; | ||
| 54 | pub const USB_MAX_SAMPLE_COUNT: usize = USB_MAX_PACKET_SIZE / SAMPLE_SIZE; | ||
| 55 | |||
| 56 | // The data type that is exchanged via the zero-copy channel (a sample vector). | ||
| 57 | pub type SampleBlock = Vec<u32, USB_MAX_SAMPLE_COUNT>; | ||
| 58 | |||
| 59 | // Feedback is provided in 10.14 format for full-speed endpoints. | ||
| 60 | pub const FEEDBACK_REFRESH_PERIOD: uac1::FeedbackRefresh = uac1::FeedbackRefresh::Period8Frames; | ||
| 61 | const FEEDBACK_SHIFT: usize = 14; | ||
| 62 | |||
| 63 | const TICKS_PER_SAMPLE: f32 = (FEEDBACK_COUNTER_TICK_RATE as f32) / (SAMPLE_RATE_HZ as f32); | ||
| 64 | |||
| 65 | struct Disconnected {} | ||
| 66 | |||
| 67 | impl From<EndpointError> for Disconnected { | ||
| 68 | fn from(val: EndpointError) -> Self { | ||
| 69 | match val { | ||
| 70 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 71 | EndpointError::Disabled => Disconnected {}, | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | /// Sends feedback messages to the host. | ||
| 77 | async fn feedback_handler<'d, T: usb::Instance + 'd>( | ||
| 78 | feedback: &mut speaker::Feedback<'d, usb::Driver<'d, T>>, | ||
| 79 | feedback_factor: f32, | ||
| 80 | ) -> Result<(), Disconnected> { | ||
| 81 | let mut packet: Vec<u8, 4> = Vec::new(); | ||
| 82 | |||
| 83 | // Collects the fractional component of the feedback value that is lost by rounding. | ||
| 84 | let mut rest = 0.0_f32; | ||
| 85 | |||
| 86 | loop { | ||
| 87 | let counter = FEEDBACK_SIGNAL.wait().await; | ||
| 88 | |||
| 89 | packet.clear(); | ||
| 90 | |||
| 91 | let raw_value = counter as f32 * feedback_factor + rest; | ||
| 92 | let value = raw_value.round(); | ||
| 93 | rest = raw_value - value; | ||
| 94 | |||
| 95 | let value = value as u32; | ||
| 96 | |||
| 97 | debug!("Feedback value: {}", value); | ||
| 98 | |||
| 99 | packet.push(value as u8).unwrap(); | ||
| 100 | packet.push((value >> 8) as u8).unwrap(); | ||
| 101 | packet.push((value >> 16) as u8).unwrap(); | ||
| 102 | |||
| 103 | feedback.write_packet(&packet).await?; | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | /// Handles streaming of audio data from the host. | ||
| 108 | async fn stream_handler<'d, T: usb::Instance + 'd>( | ||
| 109 | stream: &mut speaker::Stream<'d, usb::Driver<'d, T>>, | ||
| 110 | sender: &mut zerocopy_channel::Sender<'static, NoopRawMutex, SampleBlock>, | ||
| 111 | ) -> Result<(), Disconnected> { | ||
| 112 | loop { | ||
| 113 | let mut usb_data = [0u8; USB_MAX_PACKET_SIZE]; | ||
| 114 | let data_size = stream.read_packet(&mut usb_data).await?; | ||
| 115 | |||
| 116 | let word_count = data_size / SAMPLE_SIZE; | ||
| 117 | |||
| 118 | if word_count * SAMPLE_SIZE == data_size { | ||
| 119 | // Obtain a buffer from the channel | ||
| 120 | let samples = sender.send().await; | ||
| 121 | samples.clear(); | ||
| 122 | |||
| 123 | for w in 0..word_count { | ||
| 124 | let byte_offset = w * SAMPLE_SIZE; | ||
| 125 | let sample = u32::from_le_bytes(usb_data[byte_offset..byte_offset + SAMPLE_SIZE].try_into().unwrap()); | ||
| 126 | |||
| 127 | // Fill the sample buffer with data. | ||
| 128 | samples.push(sample).unwrap(); | ||
| 129 | } | ||
| 130 | |||
| 131 | sender.send_done(); | ||
| 132 | } else { | ||
| 133 | debug!("Invalid USB buffer size of {}, skipped.", data_size); | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | /// Receives audio samples from the USB streaming task and can play them back. | ||
| 139 | #[embassy_executor::task] | ||
| 140 | async fn audio_receiver_task(mut usb_audio_receiver: zerocopy_channel::Receiver<'static, NoopRawMutex, SampleBlock>) { | ||
| 141 | loop { | ||
| 142 | let _samples = usb_audio_receiver.receive().await; | ||
| 143 | // Use the samples, for example play back via the SAI peripheral. | ||
| 144 | |||
| 145 | // Notify the channel that the buffer is now ready to be reused | ||
| 146 | usb_audio_receiver.receive_done(); | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | /// Receives audio samples from the host. | ||
| 151 | #[embassy_executor::task] | ||
| 152 | async fn usb_streaming_task( | ||
| 153 | mut stream: speaker::Stream<'static, usb::Driver<'static, peripherals::USB>>, | ||
| 154 | mut sender: zerocopy_channel::Sender<'static, NoopRawMutex, SampleBlock>, | ||
| 155 | ) { | ||
| 156 | loop { | ||
| 157 | stream.wait_connection().await; | ||
| 158 | info!("USB connected."); | ||
| 159 | _ = stream_handler(&mut stream, &mut sender).await; | ||
| 160 | info!("USB disconnected."); | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | /// Sends sample rate feedback to the host. | ||
| 165 | /// | ||
| 166 | /// The `feedback_factor` scales the feedback timer's counter value so that the result is the number of samples that | ||
| 167 | /// this device played back or "consumed" during one SOF period (1 ms) - in 10.14 format. | ||
| 168 | /// | ||
| 169 | /// Ideally, the `feedback_factor` that is calculated below would be an integer for avoiding numerical errors. | ||
| 170 | /// This is achieved by having `TICKS_PER_SAMPLE` be a power of two. For audio applications at a sample rate of 48 kHz, | ||
| 171 | /// 24.576 MHz would be one such option. | ||
| 172 | #[embassy_executor::task] | ||
| 173 | async fn usb_feedback_task(mut feedback: speaker::Feedback<'static, usb::Driver<'static, peripherals::USB>>) { | ||
| 174 | let feedback_factor = | ||
| 175 | ((1 << FEEDBACK_SHIFT) as f32 / TICKS_PER_SAMPLE) / FEEDBACK_REFRESH_PERIOD.frame_count() as f32; | ||
| 176 | |||
| 177 | loop { | ||
| 178 | feedback.wait_connection().await; | ||
| 179 | _ = feedback_handler(&mut feedback, feedback_factor).await; | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | #[embassy_executor::task] | ||
| 184 | async fn usb_task(mut usb_device: embassy_usb::UsbDevice<'static, usb::Driver<'static, peripherals::USB>>) { | ||
| 185 | usb_device.run().await; | ||
| 186 | } | ||
| 187 | |||
| 188 | /// Checks for changes on the control monitor of the class. | ||
| 189 | /// | ||
| 190 | /// In this case, monitor changes of volume or mute state. | ||
| 191 | #[embassy_executor::task] | ||
| 192 | async fn usb_control_task(control_monitor: speaker::ControlMonitor<'static>) { | ||
| 193 | loop { | ||
| 194 | control_monitor.changed().await; | ||
| 195 | |||
| 196 | for channel in AUDIO_CHANNELS { | ||
| 197 | let volume = control_monitor.volume(channel).unwrap(); | ||
| 198 | info!("Volume changed to {} on channel {}.", volume, channel); | ||
| 199 | } | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | /// Feedback value measurement and calculation | ||
| 204 | /// | ||
| 205 | /// Used for measuring/calculating the number of samples that were received from the host during the | ||
| 206 | /// `FEEDBACK_REFRESH_PERIOD`. | ||
| 207 | /// | ||
| 208 | /// Configured in this example with | ||
| 209 | /// - a refresh period of 8 ms, and | ||
| 210 | /// - a tick rate of 42 MHz. | ||
| 211 | /// | ||
| 212 | /// This gives an (ideal) counter value of 336.000 for every update of the `FEEDBACK_SIGNAL`. | ||
| 213 | #[interrupt] | ||
| 214 | fn TIM5() { | ||
| 215 | static LAST_TICKS: Mutex<CriticalSectionRawMutex, Cell<u32>> = Mutex::new(Cell::new(0)); | ||
| 216 | static FRAME_COUNT: Mutex<CriticalSectionRawMutex, Cell<usize>> = Mutex::new(Cell::new(0)); | ||
| 217 | |||
| 218 | critical_section::with(|cs| { | ||
| 219 | // Read timer counter. | ||
| 220 | let timer = TIMER.borrow(cs).borrow().as_ref().unwrap().regs_gp32(); | ||
| 221 | |||
| 222 | let status = timer.sr().read(); | ||
| 223 | |||
| 224 | const CHANNEL_INDEX: usize = 0; | ||
| 225 | if status.ccif(CHANNEL_INDEX) { | ||
| 226 | let ticks = timer.ccr(CHANNEL_INDEX).read(); | ||
| 227 | |||
| 228 | let frame_count = FRAME_COUNT.borrow(cs); | ||
| 229 | let last_ticks = LAST_TICKS.borrow(cs); | ||
| 230 | |||
| 231 | frame_count.set(frame_count.get() + 1); | ||
| 232 | if frame_count.get() >= FEEDBACK_REFRESH_PERIOD.frame_count() { | ||
| 233 | frame_count.set(0); | ||
| 234 | FEEDBACK_SIGNAL.signal(ticks.wrapping_sub(last_ticks.get())); | ||
| 235 | last_ticks.set(ticks); | ||
| 236 | } | ||
| 237 | }; | ||
| 238 | |||
| 239 | // Clear trigger interrupt flag. | ||
| 240 | timer.sr().modify(|r| r.set_tif(false)); | ||
| 241 | }); | ||
| 242 | } | ||
| 243 | |||
| 244 | // If you are trying this and your USB device doesn't connect, the most | ||
| 245 | // common issues are the RCC config and vbus_detection | ||
| 246 | // | ||
| 247 | // See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure | ||
| 248 | // for more information. | ||
| 249 | #[embassy_executor::main] | ||
| 250 | async fn main(spawner: Spawner) { | ||
| 251 | let mut config = Config::default(); | ||
| 252 | { | ||
| 253 | use embassy_stm32::rcc::*; | ||
| 254 | config.rcc.hsi = None; | ||
| 255 | config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB | ||
| 256 | config.rcc.hse = Some(Hse { | ||
| 257 | freq: Hertz(8_000_000), | ||
| 258 | mode: HseMode::BypassDigital, | ||
| 259 | }); | ||
| 260 | config.rcc.pll1 = Some(Pll { | ||
| 261 | source: PllSource::HSE, | ||
| 262 | prediv: PllPreDiv::DIV2, | ||
| 263 | mul: PllMul::MUL125, | ||
| 264 | divp: Some(PllDiv::DIV2), // 250 Mhz | ||
| 265 | divq: None, | ||
| 266 | divr: None, | ||
| 267 | }); | ||
| 268 | config.rcc.pll2 = Some(Pll { | ||
| 269 | source: PllSource::HSE, | ||
| 270 | prediv: PllPreDiv::DIV4, | ||
| 271 | mul: PllMul::MUL123, | ||
| 272 | divp: Some(PllDiv::DIV20), // 12.3 Mhz, close to 12.288 MHz for 48 kHz audio | ||
| 273 | divq: None, | ||
| 274 | divr: None, | ||
| 275 | }); | ||
| 276 | config.rcc.ahb_pre = AHBPrescaler::DIV2; | ||
| 277 | config.rcc.apb1_pre = APBPrescaler::DIV4; | ||
| 278 | config.rcc.apb2_pre = APBPrescaler::DIV2; | ||
| 279 | config.rcc.apb3_pre = APBPrescaler::DIV4; | ||
| 280 | config.rcc.sys = Sysclk::PLL1_P; | ||
| 281 | config.rcc.voltage_scale = VoltageScale::Scale0; | ||
| 282 | config.rcc.mux.usbsel = mux::Usbsel::HSI48; | ||
| 283 | config.rcc.mux.sai2sel = mux::Saisel::PLL2_P; | ||
| 284 | } | ||
| 285 | let p = embassy_stm32::init(config); | ||
| 286 | |||
| 287 | info!("Hello World!"); | ||
| 288 | |||
| 289 | // Configure all required buffers in a static way. | ||
| 290 | debug!("USB packet size is {} byte", USB_MAX_PACKET_SIZE); | ||
| 291 | static CONFIG_DESCRIPTOR: StaticCell<[u8; 256]> = StaticCell::new(); | ||
| 292 | let config_descriptor = CONFIG_DESCRIPTOR.init([0; 256]); | ||
| 293 | |||
| 294 | static BOS_DESCRIPTOR: StaticCell<[u8; 32]> = StaticCell::new(); | ||
| 295 | let bos_descriptor = BOS_DESCRIPTOR.init([0; 32]); | ||
| 296 | |||
| 297 | const CONTROL_BUF_SIZE: usize = 64; | ||
| 298 | static CONTROL_BUF: StaticCell<[u8; CONTROL_BUF_SIZE]> = StaticCell::new(); | ||
| 299 | let control_buf = CONTROL_BUF.init([0; CONTROL_BUF_SIZE]); | ||
| 300 | |||
| 301 | static STATE: StaticCell<speaker::State> = StaticCell::new(); | ||
| 302 | let state = STATE.init(speaker::State::new()); | ||
| 303 | |||
| 304 | let usb_driver = usb::Driver::new(p.USB, Irqs, p.PA12, p.PA11); | ||
| 305 | |||
| 306 | // Basic USB device configuration | ||
| 307 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 308 | config.manufacturer = Some("Embassy"); | ||
| 309 | config.product = Some("USB-audio-speaker example"); | ||
| 310 | config.serial_number = Some("12345678"); | ||
| 311 | |||
| 312 | // Required for windows compatibility. | ||
| 313 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 314 | config.device_class = 0xEF; | ||
| 315 | config.device_sub_class = 0x02; | ||
| 316 | config.device_protocol = 0x01; | ||
| 317 | config.composite_with_iads = true; | ||
| 318 | |||
| 319 | let mut builder = embassy_usb::Builder::new( | ||
| 320 | usb_driver, | ||
| 321 | config, | ||
| 322 | config_descriptor, | ||
| 323 | bos_descriptor, | ||
| 324 | &mut [], // no msos descriptors | ||
| 325 | control_buf, | ||
| 326 | ); | ||
| 327 | |||
| 328 | // Create the UAC1 Speaker class components | ||
| 329 | let (stream, feedback, control_monitor) = Speaker::new( | ||
| 330 | &mut builder, | ||
| 331 | state, | ||
| 332 | USB_MAX_PACKET_SIZE as u16, | ||
| 333 | uac1::SampleWidth::Width4Byte, | ||
| 334 | &[SAMPLE_RATE_HZ], | ||
| 335 | &AUDIO_CHANNELS, | ||
| 336 | FEEDBACK_REFRESH_PERIOD, | ||
| 337 | ); | ||
| 338 | |||
| 339 | // Create the USB device | ||
| 340 | let usb_device = builder.build(); | ||
| 341 | |||
| 342 | // Establish a zero-copy channel for transferring received audio samples between tasks | ||
| 343 | static SAMPLE_BLOCKS: StaticCell<[SampleBlock; 2]> = StaticCell::new(); | ||
| 344 | let sample_blocks = SAMPLE_BLOCKS.init([Vec::new(), Vec::new()]); | ||
| 345 | |||
| 346 | static CHANNEL: StaticCell<zerocopy_channel::Channel<'_, NoopRawMutex, SampleBlock>> = StaticCell::new(); | ||
| 347 | let channel = CHANNEL.init(zerocopy_channel::Channel::new(sample_blocks)); | ||
| 348 | let (sender, receiver) = channel.split(); | ||
| 349 | |||
| 350 | // Run a timer for counting between SOF interrupts. | ||
| 351 | let mut tim5 = timer::low_level::Timer::new(p.TIM5); | ||
| 352 | tim5.set_tick_freq(Hertz(FEEDBACK_COUNTER_TICK_RATE)); | ||
| 353 | tim5.set_trigger_source(timer::low_level::TriggerSource::ITR12); // The USB SOF signal. | ||
| 354 | |||
| 355 | const TIMER_CHANNEL: timer::Channel = timer::Channel::Ch1; | ||
| 356 | tim5.set_input_ti_selection(TIMER_CHANNEL, timer::low_level::InputTISelection::TRC); | ||
| 357 | tim5.set_input_capture_prescaler(TIMER_CHANNEL, 0); | ||
| 358 | tim5.set_input_capture_filter(TIMER_CHANNEL, timer::low_level::FilterValue::FCK_INT_N2); | ||
| 359 | |||
| 360 | // Reset all interrupt flags. | ||
| 361 | tim5.regs_gp32().sr().write(|r| r.0 = 0); | ||
| 362 | |||
| 363 | tim5.enable_channel(TIMER_CHANNEL, true); | ||
| 364 | tim5.enable_input_interrupt(TIMER_CHANNEL, true); | ||
| 365 | |||
| 366 | tim5.start(); | ||
| 367 | |||
| 368 | TIMER.lock(|p| p.borrow_mut().replace(tim5)); | ||
| 369 | |||
| 370 | // Unmask the TIM5 interrupt. | ||
| 371 | unsafe { | ||
| 372 | cortex_m::peripheral::NVIC::unmask(interrupt::TIM5); | ||
| 373 | } | ||
| 374 | |||
| 375 | // Launch USB audio tasks. | ||
| 376 | unwrap!(spawner.spawn(usb_control_task(control_monitor))); | ||
| 377 | unwrap!(spawner.spawn(usb_streaming_task(stream, sender))); | ||
| 378 | unwrap!(spawner.spawn(usb_feedback_task(feedback))); | ||
| 379 | unwrap!(spawner.spawn(usb_task(usb_device))); | ||
| 380 | unwrap!(spawner.spawn(audio_receiver_task(receiver))); | ||
| 381 | } | ||
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 13fce7dc7..68a0c3d88 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml | |||
| @@ -7,11 +7,11 @@ license = "MIT OR Apache-2.0" | |||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32h743bi to your chip name, if necessary. | 8 | # Change stm32h743bi to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h743bi", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h743bi", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } | 11 | embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } |
| 12 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | 12 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 13 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 13 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 14 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } | 14 | embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } |
| 15 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 15 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } |
| 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 17 | 17 | ||
| @@ -23,7 +23,7 @@ cortex-m-rt = "0.7.0" | |||
| 23 | embedded-hal = "0.2.6" | 23 | embedded-hal = "0.2.6" |
| 24 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | 24 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } |
| 25 | embedded-hal-async = { version = "1.0" } | 25 | embedded-hal-async = { version = "1.0" } |
| 26 | embedded-nal-async = { version = "0.7.1" } | 26 | embedded-nal-async = "0.8.0" |
| 27 | embedded-io-async = { version = "0.6.1" } | 27 | embedded-io-async = { version = "0.6.1" } |
| 28 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 28 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 29 | heapless = { version = "0.8", default-features = false } | 29 | heapless = { version = "0.8", default-features = false } |
diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 24983ca85..a1558b079 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; | ||
| 5 | |||
| 4 | use defmt::*; | 6 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 6 | use embassy_net::tcp::client::{TcpClient, TcpClientState}; | 8 | use embassy_net::tcp::client::{TcpClient, TcpClientState}; |
| @@ -12,7 +14,7 @@ use embassy_stm32::rng::Rng; | |||
| 12 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; | 14 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; |
| 13 | use embassy_time::Timer; | 15 | use embassy_time::Timer; |
| 14 | use embedded_io_async::Write; | 16 | use embedded_io_async::Write; |
| 15 | use embedded_nal_async::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpConnect}; | 17 | use embedded_nal_async::TcpConnect; |
| 16 | use rand_core::RngCore; | 18 | use rand_core::RngCore; |
| 17 | use static_cell::StaticCell; | 19 | use static_cell::StaticCell; |
| 18 | use {defmt_rtt as _, panic_probe as _}; | 20 | use {defmt_rtt as _, panic_probe as _}; |
diff --git a/examples/stm32h7/src/bin/eth_client_mii.rs b/examples/stm32h7/src/bin/eth_client_mii.rs index 768d85993..a352ef444 100644 --- a/examples/stm32h7/src/bin/eth_client_mii.rs +++ b/examples/stm32h7/src/bin/eth_client_mii.rs | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use core::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; | ||
| 5 | |||
| 4 | use defmt::*; | 6 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 6 | use embassy_net::tcp::client::{TcpClient, TcpClientState}; | 8 | use embassy_net::tcp::client::{TcpClient, TcpClientState}; |
| @@ -12,7 +14,7 @@ use embassy_stm32::rng::Rng; | |||
| 12 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; | 14 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; |
| 13 | use embassy_time::Timer; | 15 | use embassy_time::Timer; |
| 14 | use embedded_io_async::Write; | 16 | use embedded_io_async::Write; |
| 15 | use embedded_nal_async::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpConnect}; | 17 | use embedded_nal_async::TcpConnect; |
| 16 | use rand_core::RngCore; | 18 | use rand_core::RngCore; |
| 17 | use static_cell::StaticCell; | 19 | use static_cell::StaticCell; |
| 18 | use {defmt_rtt as _, panic_probe as _}; | 20 | use {defmt_rtt as _, panic_probe as _}; |
diff --git a/examples/stm32h7/src/bin/i2c_shared.rs b/examples/stm32h7/src/bin/i2c_shared.rs index 6f4815582..136b91eeb 100644 --- a/examples/stm32h7/src/bin/i2c_shared.rs +++ b/examples/stm32h7/src/bin/i2c_shared.rs | |||
| @@ -10,8 +10,10 @@ use embassy_stm32::i2c::{self, I2c}; | |||
| 10 | use embassy_stm32::mode::Async; | 10 | use embassy_stm32::mode::Async; |
| 11 | use embassy_stm32::time::Hertz; | 11 | use embassy_stm32::time::Hertz; |
| 12 | use embassy_stm32::{bind_interrupts, peripherals}; | 12 | use embassy_stm32::{bind_interrupts, peripherals}; |
| 13 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
| 13 | use embassy_sync::blocking_mutex::NoopMutex; | 14 | use embassy_sync::blocking_mutex::NoopMutex; |
| 14 | use embassy_time::{Duration, Timer}; | 15 | use embassy_time::{Duration, Timer}; |
| 16 | use embedded_hal_1::i2c::I2c as _; | ||
| 15 | use static_cell::StaticCell; | 17 | use static_cell::StaticCell; |
| 16 | use {defmt_rtt as _, panic_probe as _}; | 18 | use {defmt_rtt as _, panic_probe as _}; |
| 17 | 19 | ||
| @@ -31,7 +33,7 @@ bind_interrupts!(struct Irqs { | |||
| 31 | }); | 33 | }); |
| 32 | 34 | ||
| 33 | #[embassy_executor::task] | 35 | #[embassy_executor::task] |
| 34 | async fn temperature(mut i2c: impl embedded_hal_1::i2c::I2c + 'static) { | 36 | async fn temperature(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async>>) { |
| 35 | let mut data = [0u8; 2]; | 37 | let mut data = [0u8; 2]; |
| 36 | 38 | ||
| 37 | loop { | 39 | loop { |
| @@ -48,7 +50,7 @@ async fn temperature(mut i2c: impl embedded_hal_1::i2c::I2c + 'static) { | |||
| 48 | } | 50 | } |
| 49 | 51 | ||
| 50 | #[embassy_executor::task] | 52 | #[embassy_executor::task] |
| 51 | async fn humidity(mut i2c: impl embedded_hal_1::i2c::I2c + 'static) { | 53 | async fn humidity(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async>>) { |
| 52 | let mut data = [0u8; 6]; | 54 | let mut data = [0u8; 6]; |
| 53 | 55 | ||
| 54 | loop { | 56 | loop { |
diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs index 1e48ba67b..a1c53fc3f 100644 --- a/examples/stm32h7/src/bin/pwm.rs +++ b/examples/stm32h7/src/bin/pwm.rs | |||
| @@ -6,7 +6,6 @@ use embassy_executor::Spawner; | |||
| 6 | use embassy_stm32::gpio::OutputType; | 6 | use embassy_stm32::gpio::OutputType; |
| 7 | use embassy_stm32::time::khz; | 7 | use embassy_stm32::time::khz; |
| 8 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | 8 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; |
| 9 | use embassy_stm32::timer::Channel; | ||
| 10 | use embassy_stm32::Config; | 9 | use embassy_stm32::Config; |
| 11 | use embassy_time::Timer; | 10 | use embassy_time::Timer; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -37,22 +36,22 @@ async fn main(_spawner: Spawner) { | |||
| 37 | let p = embassy_stm32::init(config); | 36 | let p = embassy_stm32::init(config); |
| 38 | info!("Hello World!"); | 37 | info!("Hello World!"); |
| 39 | 38 | ||
| 40 | let ch1 = PwmPin::new_ch1(p.PA6, OutputType::PushPull); | 39 | let ch1_pin = PwmPin::new_ch1(p.PA6, OutputType::PushPull); |
| 41 | let mut pwm = SimplePwm::new(p.TIM3, Some(ch1), None, None, None, khz(10), Default::default()); | 40 | let mut pwm = SimplePwm::new(p.TIM3, Some(ch1_pin), None, None, None, khz(10), Default::default()); |
| 42 | let max = pwm.get_max_duty(); | 41 | let mut ch1 = pwm.ch1(); |
| 43 | pwm.enable(Channel::Ch1); | 42 | ch1.enable(); |
| 44 | 43 | ||
| 45 | info!("PWM initialized"); | 44 | info!("PWM initialized"); |
| 46 | info!("PWM max duty {}", max); | 45 | info!("PWM max duty {}", ch1.max_duty_cycle()); |
| 47 | 46 | ||
| 48 | loop { | 47 | loop { |
| 49 | pwm.set_duty(Channel::Ch1, 0); | 48 | ch1.set_duty_cycle_fully_off(); |
| 50 | Timer::after_millis(300).await; | 49 | Timer::after_millis(300).await; |
| 51 | pwm.set_duty(Channel::Ch1, max / 4); | 50 | ch1.set_duty_cycle_fraction(1, 4); |
| 52 | Timer::after_millis(300).await; | 51 | Timer::after_millis(300).await; |
| 53 | pwm.set_duty(Channel::Ch1, max / 2); | 52 | ch1.set_duty_cycle_fraction(1, 2); |
| 54 | Timer::after_millis(300).await; | 53 | Timer::after_millis(300).await; |
| 55 | pwm.set_duty(Channel::Ch1, max - 1); | 54 | ch1.set_duty_cycle(ch1.max_duty_cycle() - 1); |
| 56 | Timer::after_millis(300).await; | 55 | Timer::after_millis(300).await; |
| 57 | } | 56 | } |
| 58 | } | 57 | } |
diff --git a/examples/stm32h7/src/bin/sai.rs b/examples/stm32h7/src/bin/sai.rs index f6735e235..95ffe257a 100644 --- a/examples/stm32h7/src/bin/sai.rs +++ b/examples/stm32h7/src/bin/sai.rs | |||
| @@ -81,8 +81,9 @@ async fn main(_spawner: Spawner) { | |||
| 81 | rx_config.sync_output = false; | 81 | rx_config.sync_output = false; |
| 82 | 82 | ||
| 83 | let tx_buffer: &mut [u32] = unsafe { | 83 | let tx_buffer: &mut [u32] = unsafe { |
| 84 | TX_BUFFER.initialize_all_copied(0); | 84 | let buf = &mut *core::ptr::addr_of_mut!(TX_BUFFER); |
| 85 | let (ptr, len) = TX_BUFFER.get_ptr_len(); | 85 | buf.initialize_all_copied(0); |
| 86 | let (ptr, len) = buf.get_ptr_len(); | ||
| 86 | core::slice::from_raw_parts_mut(ptr, len) | 87 | core::slice::from_raw_parts_mut(ptr, len) |
| 87 | }; | 88 | }; |
| 88 | 89 | ||
| @@ -98,15 +99,15 @@ async fn main(_spawner: Spawner) { | |||
| 98 | ); | 99 | ); |
| 99 | 100 | ||
| 100 | let rx_buffer: &mut [u32] = unsafe { | 101 | let rx_buffer: &mut [u32] = unsafe { |
| 101 | RX_BUFFER.initialize_all_copied(0); | 102 | let buf = &mut *core::ptr::addr_of_mut!(RX_BUFFER); |
| 102 | let (ptr, len) = RX_BUFFER.get_ptr_len(); | 103 | buf.initialize_all_copied(0); |
| 104 | let (ptr, len) = buf.get_ptr_len(); | ||
| 103 | core::slice::from_raw_parts_mut(ptr, len) | 105 | core::slice::from_raw_parts_mut(ptr, len) |
| 104 | }; | 106 | }; |
| 105 | 107 | ||
| 106 | let mut sai_receiver = Sai::new_synchronous(sub_block_rx, p.PE3, p.DMA1_CH1, rx_buffer, rx_config); | 108 | let mut sai_receiver = Sai::new_synchronous(sub_block_rx, p.PE3, p.DMA1_CH1, rx_buffer, rx_config); |
| 107 | 109 | ||
| 108 | sai_receiver.start(); | 110 | sai_receiver.start().unwrap(); |
| 109 | sai_transmitter.start(); | ||
| 110 | 111 | ||
| 111 | let mut buf = [0u32; HALF_DMA_BUFFER_LENGTH]; | 112 | let mut buf = [0u32; HALF_DMA_BUFFER_LENGTH]; |
| 112 | 113 | ||
diff --git a/examples/stm32h7/src/bin/spi_bdma.rs b/examples/stm32h7/src/bin/spi_bdma.rs index 43fb6b41c..9166fe9b6 100644 --- a/examples/stm32h7/src/bin/spi_bdma.rs +++ b/examples/stm32h7/src/bin/spi_bdma.rs | |||
| @@ -22,10 +22,11 @@ static mut RAM_D3: GroundedArrayCell<u8, 256> = GroundedArrayCell::uninit(); | |||
| 22 | #[embassy_executor::task] | 22 | #[embassy_executor::task] |
| 23 | async fn main_task(mut spi: spi::Spi<'static, Async>) { | 23 | async fn main_task(mut spi: spi::Spi<'static, Async>) { |
| 24 | let (read_buffer, write_buffer) = unsafe { | 24 | let (read_buffer, write_buffer) = unsafe { |
| 25 | RAM_D3.initialize_all_copied(0); | 25 | let ram = &mut *core::ptr::addr_of_mut!(RAM_D3); |
| 26 | ram.initialize_all_copied(0); | ||
| 26 | ( | 27 | ( |
| 27 | RAM_D3.get_subslice_mut_unchecked(0, 128), | 28 | ram.get_subslice_mut_unchecked(0, 128), |
| 28 | RAM_D3.get_subslice_mut_unchecked(128, 128), | 29 | ram.get_subslice_mut_unchecked(128, 128), |
| 29 | ) | 30 | ) |
| 30 | }; | 31 | }; |
| 31 | 32 | ||
diff --git a/examples/stm32h723/.cargo/config.toml b/examples/stm32h723/.cargo/config.toml new file mode 100644 index 000000000..2e53663c5 --- /dev/null +++ b/examples/stm32h723/.cargo/config.toml | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | [target.thumbv7em-none-eabihf] | ||
| 2 | runner = 'probe-rs run --chip STM32H723ZGTx' | ||
| 3 | |||
| 4 | [build] | ||
| 5 | target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) | ||
| 6 | |||
| 7 | [env] | ||
| 8 | DEFMT_LOG = "trace" | ||
diff --git a/examples/stm32h723/Cargo.toml b/examples/stm32h723/Cargo.toml new file mode 100644 index 000000000..82f3cb9c2 --- /dev/null +++ b/examples/stm32h723/Cargo.toml | |||
| @@ -0,0 +1,69 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-stm32h723-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | # Change stm32h723zg to your chip name, if necessary. | ||
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h723zg", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } | ||
| 10 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } | ||
| 11 | embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | ||
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | ||
| 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | ||
| 14 | |||
| 15 | defmt = "0.3" | ||
| 16 | defmt-rtt = "0.4" | ||
| 17 | |||
| 18 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||
| 19 | cortex-m-rt = "0.7.0" | ||
| 20 | embedded-hal = "0.2.6" | ||
| 21 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | ||
| 22 | embedded-hal-async = { version = "1.0" } | ||
| 23 | embedded-nal-async = "0.8.0" | ||
| 24 | embedded-io-async = { version = "0.6.1" } | ||
| 25 | panic-probe = { version = "0.3", features = ["print-defmt"] } | ||
| 26 | heapless = { version = "0.8", default-features = false } | ||
| 27 | rand_core = "0.6.3" | ||
| 28 | critical-section = "1.1" | ||
| 29 | static_cell = "2" | ||
| 30 | chrono = { version = "^0.4", default-features = false } | ||
| 31 | grounded = "0.2.0" | ||
| 32 | |||
| 33 | # cargo build/run | ||
| 34 | [profile.dev] | ||
| 35 | codegen-units = 1 | ||
| 36 | debug = 2 | ||
| 37 | debug-assertions = true # <- | ||
| 38 | incremental = false | ||
| 39 | opt-level = 3 # <- | ||
| 40 | overflow-checks = true # <- | ||
| 41 | |||
| 42 | # cargo test | ||
| 43 | [profile.test] | ||
| 44 | codegen-units = 1 | ||
| 45 | debug = 2 | ||
| 46 | debug-assertions = true # <- | ||
| 47 | incremental = false | ||
| 48 | opt-level = 3 # <- | ||
| 49 | overflow-checks = true # <- | ||
| 50 | |||
| 51 | # cargo build/run --release | ||
| 52 | [profile.release] | ||
| 53 | codegen-units = 1 | ||
| 54 | debug = 2 | ||
| 55 | debug-assertions = false # <- | ||
| 56 | incremental = false | ||
| 57 | lto = 'fat' | ||
| 58 | opt-level = 3 # <- | ||
| 59 | overflow-checks = false # <- | ||
| 60 | |||
| 61 | # cargo test --release | ||
| 62 | [profile.bench] | ||
| 63 | codegen-units = 1 | ||
| 64 | debug = 2 | ||
| 65 | debug-assertions = false # <- | ||
| 66 | incremental = false | ||
| 67 | lto = 'fat' | ||
| 68 | opt-level = 3 # <- | ||
| 69 | overflow-checks = false # <- | ||
diff --git a/examples/stm32h723/build.rs b/examples/stm32h723/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/examples/stm32h723/build.rs | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | //! This build script copies the `memory.x` file from the crate root into | ||
| 2 | //! a directory where the linker can always find it at build time. | ||
| 3 | //! For many projects this is optional, as the linker always searches the | ||
| 4 | //! project root directory -- wherever `Cargo.toml` is. However, if you | ||
| 5 | //! are using a workspace or have a more complicated build setup, this | ||
| 6 | //! build script becomes required. Additionally, by requesting that | ||
| 7 | //! Cargo re-run the build script whenever `memory.x` is changed, | ||
| 8 | //! updating `memory.x` ensures a rebuild of the application with the | ||
| 9 | //! new memory settings. | ||
| 10 | |||
| 11 | use std::env; | ||
| 12 | use std::fs::File; | ||
| 13 | use std::io::Write; | ||
| 14 | use std::path::PathBuf; | ||
| 15 | |||
| 16 | fn main() { | ||
| 17 | // Put `memory.x` in our output directory and ensure it's | ||
| 18 | // on the linker search path. | ||
| 19 | let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); | ||
| 20 | File::create(out.join("memory.x")) | ||
| 21 | .unwrap() | ||
| 22 | .write_all(include_bytes!("memory.x")) | ||
| 23 | .unwrap(); | ||
| 24 | println!("cargo:rustc-link-search={}", out.display()); | ||
| 25 | |||
| 26 | // By default, Cargo will re-run a build script whenever | ||
| 27 | // any file in the project changes. By specifying `memory.x` | ||
| 28 | // here, we ensure the build script is only re-run when | ||
| 29 | // `memory.x` is changed. | ||
| 30 | println!("cargo:rerun-if-changed=memory.x"); | ||
| 31 | |||
| 32 | println!("cargo:rustc-link-arg-bins=--nmagic"); | ||
| 33 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); | ||
| 34 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | ||
| 35 | } | ||
diff --git a/examples/stm32h723/memory.x b/examples/stm32h723/memory.x new file mode 100644 index 000000000..aa4c00505 --- /dev/null +++ b/examples/stm32h723/memory.x | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | MEMORY | ||
| 2 | { | ||
| 3 | /* This file is intended for parts in the STM32H723 family. (RM0468) */ | ||
| 4 | /* - FLASH and RAM are mandatory memory sections. */ | ||
| 5 | /* - The sum of all non-FLASH sections must add to 564k total device RAM. */ | ||
| 6 | /* - The FLASH section size must match your device, see table below. */ | ||
| 7 | |||
| 8 | /* FLASH */ | ||
| 9 | /* Select the appropriate FLASH size for your device. */ | ||
| 10 | /* - STM32H730xB 128K */ | ||
| 11 | /* - STM32H723xE/725xE 512K */ | ||
| 12 | /* - STM32H723xG/725xG/733xG/735xG 1M */ | ||
| 13 | FLASH1 : ORIGIN = 0x08000000, LENGTH = 1M | ||
| 14 | |||
| 15 | /* Data TCM */ | ||
| 16 | /* - Two contiguous 64KB RAMs. */ | ||
| 17 | /* - Used for interrupt handlers, stacks and general RAM. */ | ||
| 18 | /* - Zero wait-states. */ | ||
| 19 | /* - The DTCM is taken as the origin of the base ram. (See below.) */ | ||
| 20 | /* This is also where the interrupt table and such will live, */ | ||
| 21 | /* which is required for deterministic performance. */ | ||
| 22 | DTCM : ORIGIN = 0x20000000, LENGTH = 128K | ||
| 23 | |||
| 24 | /* Instruction TCM */ | ||
| 25 | /* - More memory can be assigned to ITCM. See AXI SRAM notes, below. */ | ||
| 26 | /* - Used for latency-critical interrupt handlers etc. */ | ||
| 27 | /* - Zero wait-states. */ | ||
| 28 | ITCM : ORIGIN = 0x00000000, LENGTH = 64K + 0K | ||
| 29 | |||
| 30 | /* AXI SRAM */ | ||
| 31 | /* - AXISRAM is in D1 and accessible by all system masters except BDMA. */ | ||
| 32 | /* - Suitable for application data not stored in DTCM. */ | ||
| 33 | /* - Zero wait-states. */ | ||
| 34 | /* - The 192k of extra shared RAM is fully allotted to the AXI SRAM by default. */ | ||
| 35 | /* As a result: 64k (64k + 0k) for ITCM and 320k (128k + 192k) for AXI SRAM. */ | ||
| 36 | /* This can be re-configured via the TCM_AXI_SHARED[1,0] register when more */ | ||
| 37 | /* ITCM is required. */ | ||
| 38 | AXISRAM : ORIGIN = 0x24000000, LENGTH = 128K + 192K | ||
| 39 | |||
| 40 | /* AHB SRAM */ | ||
| 41 | /* - SRAM1-2 are in D2 and accessible by all system masters except BDMA, LTDC */ | ||
| 42 | /* and SDMMC1. Suitable for use as DMA buffers. */ | ||
| 43 | /* - SRAM4 is in D3 and additionally accessible by the BDMA. Used for BDMA */ | ||
| 44 | /* buffers, for storing application data in lower-power modes. */ | ||
| 45 | /* - Zero wait-states. */ | ||
| 46 | SRAM1 : ORIGIN = 0x30000000, LENGTH = 16K | ||
| 47 | SRAM2 : ORIGIN = 0x30040000, LENGTH = 16K | ||
| 48 | SRAM4 : ORIGIN = 0x38000000, LENGTH = 16K | ||
| 49 | |||
| 50 | /* Backup SRAM */ | ||
| 51 | /* Used to store data during low-power sleeps. */ | ||
| 52 | BSRAM : ORIGIN = 0x38800000, LENGTH = 4K | ||
| 53 | } | ||
| 54 | |||
| 55 | /* | ||
| 56 | /* Assign the memory regions defined above for use. */ | ||
| 57 | /* | ||
| 58 | |||
| 59 | /* Provide the mandatory FLASH and RAM definitions for cortex-m-rt's linker script. */ | ||
| 60 | REGION_ALIAS(FLASH, FLASH1); | ||
| 61 | REGION_ALIAS(RAM, DTCM); | ||
| 62 | |||
| 63 | /* The location of the stack can be overridden using the `_stack_start` symbol. */ | ||
| 64 | /* - Set the stack location at the end of RAM, using all remaining space. */ | ||
| 65 | _stack_start = ORIGIN(RAM) + LENGTH(RAM); | ||
| 66 | |||
| 67 | /* The location of the .text section can be overridden using the */ | ||
| 68 | /* `_stext` symbol. By default it will place after .vector_table. */ | ||
| 69 | /* _stext = ORIGIN(FLASH) + 0x40c; */ | ||
| 70 | |||
| 71 | /* Define sections for placing symbols into the extra memory regions above. */ | ||
| 72 | /* This makes them accessible from code. */ | ||
| 73 | /* - ITCM, DTCM and AXISRAM connect to a 64-bit wide bus -> align to 8 bytes. */ | ||
| 74 | /* - All other memories connect to a 32-bit wide bus -> align to 4 bytes. */ | ||
| 75 | SECTIONS { | ||
| 76 | .itcm (NOLOAD) : ALIGN(8) { | ||
| 77 | *(.itcm .itcm.*); | ||
| 78 | . = ALIGN(8); | ||
| 79 | } > ITCM | ||
| 80 | |||
| 81 | .axisram (NOLOAD) : ALIGN(8) { | ||
| 82 | *(.axisram .axisram.*); | ||
| 83 | . = ALIGN(8); | ||
| 84 | } > AXISRAM | ||
| 85 | |||
| 86 | .sram1 (NOLOAD) : ALIGN(4) { | ||
| 87 | *(.sram1 .sram1.*); | ||
| 88 | . = ALIGN(4); | ||
| 89 | } > SRAM1 | ||
| 90 | |||
| 91 | .sram2 (NOLOAD) : ALIGN(4) { | ||
| 92 | *(.sram2 .sram2.*); | ||
| 93 | . = ALIGN(4); | ||
| 94 | } > SRAM2 | ||
| 95 | |||
| 96 | .sram4 (NOLOAD) : ALIGN(4) { | ||
| 97 | *(.sram4 .sram4.*); | ||
| 98 | . = ALIGN(4); | ||
| 99 | } > SRAM4 | ||
| 100 | |||
| 101 | .bsram (NOLOAD) : ALIGN(4) { | ||
| 102 | *(.bsram .bsram.*); | ||
| 103 | . = ALIGN(4); | ||
| 104 | } > BSRAM | ||
| 105 | |||
| 106 | }; | ||
diff --git a/examples/stm32h723/src/bin/spdifrx.rs b/examples/stm32h723/src/bin/spdifrx.rs new file mode 100644 index 000000000..69ef5cd07 --- /dev/null +++ b/examples/stm32h723/src/bin/spdifrx.rs | |||
| @@ -0,0 +1,165 @@ | |||
| 1 | //! This example receives inputs on SPDIFRX and outputs on SAI4. | ||
| 2 | //! | ||
| 3 | //! Only very few controllers connect the SPDIFRX symbol clock to a SAI peripheral's clock input. | ||
| 4 | //! However, this is necessary for synchronizing the symbol rates and avoiding glitches. | ||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | |||
| 8 | use defmt::{info, trace}; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_futures::select::{self, select, Either}; | ||
| 11 | use embassy_stm32::spdifrx::{self, Spdifrx}; | ||
| 12 | use embassy_stm32::{bind_interrupts, peripherals, sai}; | ||
| 13 | use grounded::uninit::GroundedArrayCell; | ||
| 14 | use hal::sai::*; | ||
| 15 | use {defmt_rtt as _, embassy_stm32 as hal, panic_probe as _}; | ||
| 16 | |||
| 17 | bind_interrupts!(struct Irqs { | ||
| 18 | SPDIF_RX => spdifrx::GlobalInterruptHandler<peripherals::SPDIFRX1>; | ||
| 19 | }); | ||
| 20 | |||
| 21 | const CHANNEL_COUNT: usize = 2; | ||
| 22 | const BLOCK_LENGTH: usize = 64; | ||
| 23 | const HALF_DMA_BUFFER_LENGTH: usize = BLOCK_LENGTH * CHANNEL_COUNT; | ||
| 24 | const DMA_BUFFER_LENGTH: usize = HALF_DMA_BUFFER_LENGTH * 2; // 2 half-blocks | ||
| 25 | |||
| 26 | // DMA buffers must be in special regions. Refer https://embassy.dev/book/#_stm32_bdma_only_working_out_of_some_ram_regions | ||
| 27 | #[link_section = ".sram1"] | ||
| 28 | static mut SPDIFRX_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit(); | ||
| 29 | |||
| 30 | #[link_section = ".sram4"] | ||
| 31 | static mut SAI_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit(); | ||
| 32 | |||
| 33 | #[embassy_executor::main] | ||
| 34 | async fn main(_spawner: Spawner) { | ||
| 35 | let mut peripheral_config = embassy_stm32::Config::default(); | ||
| 36 | { | ||
| 37 | use embassy_stm32::rcc::*; | ||
| 38 | peripheral_config.rcc.hsi = Some(HSIPrescaler::DIV1); | ||
| 39 | peripheral_config.rcc.pll1 = Some(Pll { | ||
| 40 | source: PllSource::HSI, | ||
| 41 | prediv: PllPreDiv::DIV16, | ||
| 42 | mul: PllMul::MUL200, | ||
| 43 | divp: Some(PllDiv::DIV2), // 400 MHz | ||
| 44 | divq: Some(PllDiv::DIV2), | ||
| 45 | divr: Some(PllDiv::DIV2), | ||
| 46 | }); | ||
| 47 | peripheral_config.rcc.sys = Sysclk::PLL1_P; | ||
| 48 | peripheral_config.rcc.ahb_pre = AHBPrescaler::DIV2; | ||
| 49 | peripheral_config.rcc.apb1_pre = APBPrescaler::DIV2; | ||
| 50 | peripheral_config.rcc.apb2_pre = APBPrescaler::DIV2; | ||
| 51 | peripheral_config.rcc.apb3_pre = APBPrescaler::DIV2; | ||
| 52 | peripheral_config.rcc.apb4_pre = APBPrescaler::DIV2; | ||
| 53 | |||
| 54 | peripheral_config.rcc.mux.spdifrxsel = mux::Spdifrxsel::PLL1_Q; | ||
| 55 | } | ||
| 56 | let mut p = embassy_stm32::init(peripheral_config); | ||
| 57 | |||
| 58 | info!("SPDIFRX to SAI4 bridge"); | ||
| 59 | |||
| 60 | // Use SPDIFRX clock for SAI. | ||
| 61 | // This ensures equal rates of sample production and consumption. | ||
| 62 | let clk_source = embassy_stm32::pac::rcc::vals::Saiasel::_RESERVED_5; | ||
| 63 | embassy_stm32::pac::RCC.d3ccipr().modify(|w| { | ||
| 64 | w.set_sai4asel(clk_source); | ||
| 65 | }); | ||
| 66 | |||
| 67 | let sai_buffer: &mut [u32] = unsafe { | ||
| 68 | SAI_BUFFER.initialize_all_copied(0); | ||
| 69 | let (ptr, len) = SAI_BUFFER.get_ptr_len(); | ||
| 70 | core::slice::from_raw_parts_mut(ptr, len) | ||
| 71 | }; | ||
| 72 | |||
| 73 | let spdifrx_buffer: &mut [u32] = unsafe { | ||
| 74 | SPDIFRX_BUFFER.initialize_all_copied(0); | ||
| 75 | let (ptr, len) = SPDIFRX_BUFFER.get_ptr_len(); | ||
| 76 | core::slice::from_raw_parts_mut(ptr, len) | ||
| 77 | }; | ||
| 78 | |||
| 79 | let mut sai_transmitter = new_sai_transmitter( | ||
| 80 | &mut p.SAI4, | ||
| 81 | &mut p.PD13, | ||
| 82 | &mut p.PC1, | ||
| 83 | &mut p.PD12, | ||
| 84 | &mut p.BDMA_CH0, | ||
| 85 | sai_buffer, | ||
| 86 | ); | ||
| 87 | let mut spdif_receiver = new_spdif_receiver(&mut p.SPDIFRX1, &mut p.PD7, &mut p.DMA2_CH7, spdifrx_buffer); | ||
| 88 | spdif_receiver.start(); | ||
| 89 | |||
| 90 | let mut renew_sai = false; | ||
| 91 | loop { | ||
| 92 | let mut buf = [0u32; HALF_DMA_BUFFER_LENGTH]; | ||
| 93 | |||
| 94 | if renew_sai { | ||
| 95 | renew_sai = false; | ||
| 96 | trace!("Renew SAI."); | ||
| 97 | drop(sai_transmitter); | ||
| 98 | sai_transmitter = new_sai_transmitter( | ||
| 99 | &mut p.SAI4, | ||
| 100 | &mut p.PD13, | ||
| 101 | &mut p.PC1, | ||
| 102 | &mut p.PD12, | ||
| 103 | &mut p.BDMA_CH0, | ||
| 104 | sai_buffer, | ||
| 105 | ); | ||
| 106 | } | ||
| 107 | |||
| 108 | match select(spdif_receiver.read(&mut buf), sai_transmitter.wait_write_error()).await { | ||
| 109 | Either::First(spdif_read_result) => match spdif_read_result { | ||
| 110 | Ok(_) => (), | ||
| 111 | Err(spdifrx::Error::RingbufferError(_)) => { | ||
| 112 | trace!("SPDIFRX ringbuffer error. Renew."); | ||
| 113 | drop(spdif_receiver); | ||
| 114 | spdif_receiver = new_spdif_receiver(&mut p.SPDIFRX1, &mut p.PD7, &mut p.DMA2_CH7, spdifrx_buffer); | ||
| 115 | spdif_receiver.start(); | ||
| 116 | continue; | ||
| 117 | } | ||
| 118 | Err(spdifrx::Error::ChannelSyncError) => { | ||
| 119 | trace!("SPDIFRX channel sync (left/right assignment) error."); | ||
| 120 | continue; | ||
| 121 | } | ||
| 122 | }, | ||
| 123 | Either::Second(_) => { | ||
| 124 | renew_sai = true; | ||
| 125 | continue; | ||
| 126 | } | ||
| 127 | }; | ||
| 128 | |||
| 129 | renew_sai = sai_transmitter.write(&buf).await.is_err(); | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | /// Creates a new SPDIFRX instance for receiving sample data. | ||
| 134 | /// | ||
| 135 | /// Used (again) after dropping the SPDIFRX instance, in case of errors (e.g. source disconnect). | ||
| 136 | fn new_spdif_receiver<'d>( | ||
| 137 | spdifrx: &'d mut peripherals::SPDIFRX1, | ||
| 138 | input_pin: &'d mut peripherals::PD7, | ||
| 139 | dma: &'d mut peripherals::DMA2_CH7, | ||
| 140 | buf: &'d mut [u32], | ||
| 141 | ) -> Spdifrx<'d, peripherals::SPDIFRX1> { | ||
| 142 | Spdifrx::new(spdifrx, Irqs, spdifrx::Config::default(), input_pin, dma, buf) | ||
| 143 | } | ||
| 144 | |||
| 145 | /// Creates a new SAI4 instance for transmitting sample data. | ||
| 146 | /// | ||
| 147 | /// Used (again) after dropping the SAI4 instance, in case of errors (e.g. buffer overrun). | ||
| 148 | fn new_sai_transmitter<'d>( | ||
| 149 | sai: &'d mut peripherals::SAI4, | ||
| 150 | sck: &'d mut peripherals::PD13, | ||
| 151 | sd: &'d mut peripherals::PC1, | ||
| 152 | fs: &'d mut peripherals::PD12, | ||
| 153 | dma: &'d mut peripherals::BDMA_CH0, | ||
| 154 | buf: &'d mut [u32], | ||
| 155 | ) -> Sai<'d, peripherals::SAI4, u32> { | ||
| 156 | let mut sai_config = hal::sai::Config::default(); | ||
| 157 | sai_config.slot_count = hal::sai::word::U4(CHANNEL_COUNT as u8); | ||
| 158 | sai_config.slot_enable = 0xFFFF; // All slots | ||
| 159 | sai_config.data_size = sai::DataSize::Data32; | ||
| 160 | sai_config.frame_length = (CHANNEL_COUNT * 32) as u8; | ||
| 161 | sai_config.master_clock_divider = hal::sai::MasterClockDivider::MasterClockDisabled; | ||
| 162 | |||
| 163 | let (sub_block_tx, _) = hal::sai::split_subblocks(sai); | ||
| 164 | Sai::new_asynchronous(sub_block_tx, sck, sd, fs, dma, buf, sai_config) | ||
| 165 | } | ||
diff --git a/examples/stm32h735/Cargo.toml b/examples/stm32h735/Cargo.toml index 93e9575b6..a517b9727 100644 --- a/examples/stm32h735/Cargo.toml +++ b/examples/stm32h735/Cargo.toml | |||
| @@ -6,9 +6,9 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h735ig", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } | 8 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h735ig", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } |
| 9 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 9 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 10 | embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } | 10 | embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } |
| 11 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "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.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 14 | 14 | ||
diff --git a/examples/stm32h755cm4/Cargo.toml b/examples/stm32h755cm4/Cargo.toml index 7a42fbdaa..1d4d3eb85 100644 --- a/examples/stm32h755cm4/Cargo.toml +++ b/examples/stm32h755cm4/Cargo.toml | |||
| @@ -7,11 +7,11 @@ license = "MIT OR Apache-2.0" | |||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32h755zi-cm4 to your chip name, if necessary. | 8 | # Change stm32h755zi-cm4 to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm4", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm4", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } | 11 | embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } |
| 12 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | 12 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 13 | embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 13 | embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 14 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } | 14 | embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } |
| 15 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 15 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } |
| 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 17 | 17 | ||
| @@ -23,7 +23,7 @@ cortex-m-rt = "0.7.0" | |||
| 23 | embedded-hal = "0.2.6" | 23 | embedded-hal = "0.2.6" |
| 24 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | 24 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } |
| 25 | embedded-hal-async = { version = "1.0" } | 25 | embedded-hal-async = { version = "1.0" } |
| 26 | embedded-nal-async = { version = "0.7.1" } | 26 | embedded-nal-async = "0.8.0" |
| 27 | embedded-io-async = { version = "0.6.1" } | 27 | embedded-io-async = { version = "0.6.1" } |
| 28 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 28 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 29 | heapless = { version = "0.8", default-features = false } | 29 | heapless = { version = "0.8", default-features = false } |
diff --git a/examples/stm32h755cm7/Cargo.toml b/examples/stm32h755cm7/Cargo.toml index 4f0f69c3f..76c88c806 100644 --- a/examples/stm32h755cm7/Cargo.toml +++ b/examples/stm32h755cm7/Cargo.toml | |||
| @@ -7,11 +7,11 @@ license = "MIT OR Apache-2.0" | |||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32h743bi to your chip name, if necessary. | 8 | # Change stm32h743bi to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm7", "time-driver-tim3", "exti", "memory-x", "unstable-pac", "chrono"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm7", "time-driver-tim3", "exti", "memory-x", "unstable-pac", "chrono"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } | 11 | embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } |
| 12 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | 12 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } |
| 13 | embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 13 | embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 14 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } | 14 | embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } |
| 15 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 15 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } |
| 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 16 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 17 | 17 | ||
| @@ -23,7 +23,7 @@ cortex-m-rt = "0.7.0" | |||
| 23 | embedded-hal = "0.2.6" | 23 | embedded-hal = "0.2.6" |
| 24 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | 24 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } |
| 25 | embedded-hal-async = { version = "1.0" } | 25 | embedded-hal-async = { version = "1.0" } |
| 26 | embedded-nal-async = { version = "0.7.1" } | 26 | embedded-nal-async = "0.8.0" |
| 27 | embedded-io-async = { version = "0.6.1" } | 27 | embedded-io-async = { version = "0.6.1" } |
| 28 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 28 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 29 | heapless = { version = "0.8", default-features = false } | 29 | heapless = { version = "0.8", default-features = false } |
diff --git a/examples/stm32h7b0/.cargo/config.toml b/examples/stm32h7b0/.cargo/config.toml new file mode 100644 index 000000000..870849a27 --- /dev/null +++ b/examples/stm32h7b0/.cargo/config.toml | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | [target.thumbv7em-none-eabihf] | ||
| 2 | runner = 'probe-rs run --chip STM32H7B0VBTx' | ||
| 3 | |||
| 4 | [build] | ||
| 5 | target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) | ||
| 6 | |||
| 7 | [env] | ||
| 8 | DEFMT_LOG = "trace" | ||
diff --git a/examples/stm32h7b0/Cargo.toml b/examples/stm32h7b0/Cargo.toml new file mode 100644 index 000000000..aba398fa5 --- /dev/null +++ b/examples/stm32h7b0/Cargo.toml | |||
| @@ -0,0 +1,74 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-stm32h7b0-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7b0vb", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } | ||
| 9 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } | ||
| 10 | embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } | ||
| 11 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | ||
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | ||
| 13 | embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } | ||
| 14 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | ||
| 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | ||
| 16 | |||
| 17 | defmt = "0.3" | ||
| 18 | defmt-rtt = "0.4" | ||
| 19 | |||
| 20 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||
| 21 | cortex-m-rt = "0.7.0" | ||
| 22 | embedded-hal = "0.2.6" | ||
| 23 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | ||
| 24 | embedded-hal-async = { version = "1.0" } | ||
| 25 | embedded-nal-async = "0.8.0" | ||
| 26 | embedded-io-async = { version = "0.6.1" } | ||
| 27 | panic-probe = { version = "0.3", features = ["print-defmt"] } | ||
| 28 | heapless = { version = "0.8", default-features = false } | ||
| 29 | rand_core = "0.6.3" | ||
| 30 | critical-section = "1.1" | ||
| 31 | micromath = "2.0.0" | ||
| 32 | stm32-fmc = "0.3.0" | ||
| 33 | embedded-storage = "0.3.1" | ||
| 34 | static_cell = "2" | ||
| 35 | chrono = { version = "^0.4", default-features = false } | ||
| 36 | grounded = "0.2.0" | ||
| 37 | |||
| 38 | # cargo build/run | ||
| 39 | [profile.dev] | ||
| 40 | codegen-units = 1 | ||
| 41 | debug = 2 | ||
| 42 | debug-assertions = true # <- | ||
| 43 | incremental = false | ||
| 44 | opt-level = 3 # <- | ||
| 45 | overflow-checks = true # <- | ||
| 46 | |||
| 47 | # cargo test | ||
| 48 | [profile.test] | ||
| 49 | codegen-units = 1 | ||
| 50 | debug = 2 | ||
| 51 | debug-assertions = true # <- | ||
| 52 | incremental = false | ||
| 53 | opt-level = 3 # <- | ||
| 54 | overflow-checks = true # <- | ||
| 55 | |||
| 56 | # cargo build/run --release | ||
| 57 | [profile.release] | ||
| 58 | codegen-units = 1 | ||
| 59 | debug = 2 | ||
| 60 | debug-assertions = false # <- | ||
| 61 | incremental = false | ||
| 62 | lto = 'fat' | ||
| 63 | opt-level = 3 # <- | ||
| 64 | overflow-checks = false # <- | ||
| 65 | |||
| 66 | # cargo test --release | ||
| 67 | [profile.bench] | ||
| 68 | codegen-units = 1 | ||
| 69 | debug = 2 | ||
| 70 | debug-assertions = false # <- | ||
| 71 | incremental = false | ||
| 72 | lto = 'fat' | ||
| 73 | opt-level = 3 # <- | ||
| 74 | overflow-checks = false # <- | ||
diff --git a/examples/stm32h7b0/build.rs b/examples/stm32h7b0/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/examples/stm32h7b0/build.rs | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | //! This build script copies the `memory.x` file from the crate root into | ||
| 2 | //! a directory where the linker can always find it at build time. | ||
| 3 | //! For many projects this is optional, as the linker always searches the | ||
| 4 | //! project root directory -- wherever `Cargo.toml` is. However, if you | ||
| 5 | //! are using a workspace or have a more complicated build setup, this | ||
| 6 | //! build script becomes required. Additionally, by requesting that | ||
| 7 | //! Cargo re-run the build script whenever `memory.x` is changed, | ||
| 8 | //! updating `memory.x` ensures a rebuild of the application with the | ||
| 9 | //! new memory settings. | ||
| 10 | |||
| 11 | use std::env; | ||
| 12 | use std::fs::File; | ||
| 13 | use std::io::Write; | ||
| 14 | use std::path::PathBuf; | ||
| 15 | |||
| 16 | fn main() { | ||
| 17 | // Put `memory.x` in our output directory and ensure it's | ||
| 18 | // on the linker search path. | ||
| 19 | let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); | ||
| 20 | File::create(out.join("memory.x")) | ||
| 21 | .unwrap() | ||
| 22 | .write_all(include_bytes!("memory.x")) | ||
| 23 | .unwrap(); | ||
| 24 | println!("cargo:rustc-link-search={}", out.display()); | ||
| 25 | |||
| 26 | // By default, Cargo will re-run a build script whenever | ||
| 27 | // any file in the project changes. By specifying `memory.x` | ||
| 28 | // here, we ensure the build script is only re-run when | ||
| 29 | // `memory.x` is changed. | ||
| 30 | println!("cargo:rerun-if-changed=memory.x"); | ||
| 31 | |||
| 32 | println!("cargo:rustc-link-arg-bins=--nmagic"); | ||
| 33 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); | ||
| 34 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | ||
| 35 | } | ||
diff --git a/examples/stm32h7b0/memory.x b/examples/stm32h7b0/memory.x new file mode 100644 index 000000000..6eb1bb7c1 --- /dev/null +++ b/examples/stm32h7b0/memory.x | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | MEMORY | ||
| 2 | { | ||
| 3 | FLASH : ORIGIN = 0x08000000, LENGTH = 128K /* BANK_1 */ | ||
| 4 | RAM : ORIGIN = 0x24000000, LENGTH = 512K /* SRAM */ | ||
| 5 | } \ No newline at end of file | ||
diff --git a/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs b/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs new file mode 100644 index 000000000..dffb740a9 --- /dev/null +++ b/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs | |||
| @@ -0,0 +1,433 @@ | |||
| 1 | #![no_main] | ||
| 2 | #![no_std] | ||
| 3 | |||
| 4 | // Tested on weact stm32h7b0 board + w25q64 spi flash | ||
| 5 | |||
| 6 | use defmt::info; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 9 | use embassy_stm32::mode::Blocking; | ||
| 10 | use embassy_stm32::ospi::{ | ||
| 11 | AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, Instance, MemorySize, MemoryType, Ospi, | ||
| 12 | OspiWidth, TransferConfig, WrapSize, | ||
| 13 | }; | ||
| 14 | use embassy_stm32::time::Hertz; | ||
| 15 | use embassy_stm32::Config; | ||
| 16 | use embassy_time::Timer; | ||
| 17 | use {defmt_rtt as _, panic_probe as _}; | ||
| 18 | |||
| 19 | #[embassy_executor::main] | ||
| 20 | async fn main(_spawner: Spawner) { | ||
| 21 | // RCC config | ||
| 22 | let mut config = Config::default(); | ||
| 23 | info!("START"); | ||
| 24 | { | ||
| 25 | use embassy_stm32::rcc::*; | ||
| 26 | config.rcc.hsi = Some(HSIPrescaler::DIV1); | ||
| 27 | config.rcc.csi = true; | ||
| 28 | // Needed for USB | ||
| 29 | config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); | ||
| 30 | // External oscillator 25MHZ | ||
| 31 | config.rcc.hse = Some(Hse { | ||
| 32 | freq: Hertz(25_000_000), | ||
| 33 | mode: HseMode::Oscillator, | ||
| 34 | }); | ||
| 35 | config.rcc.pll1 = Some(Pll { | ||
| 36 | source: PllSource::HSE, | ||
| 37 | prediv: PllPreDiv::DIV5, | ||
| 38 | mul: PllMul::MUL112, | ||
| 39 | divp: Some(PllDiv::DIV2), | ||
| 40 | divq: Some(PllDiv::DIV2), | ||
| 41 | divr: Some(PllDiv::DIV2), | ||
| 42 | }); | ||
| 43 | config.rcc.sys = Sysclk::PLL1_P; | ||
| 44 | config.rcc.ahb_pre = AHBPrescaler::DIV2; | ||
| 45 | config.rcc.apb1_pre = APBPrescaler::DIV2; | ||
| 46 | config.rcc.apb2_pre = APBPrescaler::DIV2; | ||
| 47 | config.rcc.apb3_pre = APBPrescaler::DIV2; | ||
| 48 | config.rcc.apb4_pre = APBPrescaler::DIV2; | ||
| 49 | config.rcc.voltage_scale = VoltageScale::Scale0; | ||
| 50 | } | ||
| 51 | |||
| 52 | // Initialize peripherals | ||
| 53 | let p = embassy_stm32::init(config); | ||
| 54 | |||
| 55 | let qspi_config = embassy_stm32::ospi::Config { | ||
| 56 | fifo_threshold: FIFOThresholdLevel::_16Bytes, | ||
| 57 | memory_type: MemoryType::Micron, | ||
| 58 | device_size: MemorySize::_8MiB, | ||
| 59 | chip_select_high_time: ChipSelectHighTime::_1Cycle, | ||
| 60 | free_running_clock: false, | ||
| 61 | clock_mode: false, | ||
| 62 | wrap_size: WrapSize::None, | ||
| 63 | clock_prescaler: 4, | ||
| 64 | sample_shifting: true, | ||
| 65 | delay_hold_quarter_cycle: false, | ||
| 66 | chip_select_boundary: 0, | ||
| 67 | delay_block_bypass: true, | ||
| 68 | max_transfer: 0, | ||
| 69 | refresh: 0, | ||
| 70 | }; | ||
| 71 | let ospi = embassy_stm32::ospi::Ospi::new_blocking_quadspi( | ||
| 72 | p.OCTOSPI1, | ||
| 73 | p.PB2, | ||
| 74 | p.PD11, | ||
| 75 | p.PD12, | ||
| 76 | p.PE2, | ||
| 77 | p.PD13, | ||
| 78 | p.PB6, | ||
| 79 | qspi_config, | ||
| 80 | ); | ||
| 81 | |||
| 82 | let mut flash = FlashMemory::new(ospi).await; | ||
| 83 | |||
| 84 | let flash_id = flash.read_id(); | ||
| 85 | info!("FLASH ID: {=[u8]:x}", flash_id); | ||
| 86 | let mut wr_buf = [0u8; 8]; | ||
| 87 | for i in 0..8 { | ||
| 88 | wr_buf[i] = i as u8; | ||
| 89 | } | ||
| 90 | let mut rd_buf = [0u8; 8]; | ||
| 91 | flash.erase_sector(0).await; | ||
| 92 | flash.write_memory(0, &wr_buf, true).await; | ||
| 93 | flash.read_memory(0, &mut rd_buf, true); | ||
| 94 | info!("WRITE BUF: {=[u8]:#X}", wr_buf); | ||
| 95 | info!("READ BUF: {=[u8]:#X}", rd_buf); | ||
| 96 | flash.enable_mm().await; | ||
| 97 | info!("Enabled memory mapped mode"); | ||
| 98 | |||
| 99 | let first_u32 = unsafe { *(0x90000000 as *const u32) }; | ||
| 100 | assert_eq!(first_u32, 0x03020100); | ||
| 101 | |||
| 102 | let second_u32 = unsafe { *(0x90000004 as *const u32) }; | ||
| 103 | assert_eq!(second_u32, 0x07060504); | ||
| 104 | flash.disable_mm().await; | ||
| 105 | |||
| 106 | info!("DONE"); | ||
| 107 | // Output pin PE3 | ||
| 108 | let mut led = Output::new(p.PE3, Level::Low, Speed::Low); | ||
| 109 | |||
| 110 | loop { | ||
| 111 | led.toggle(); | ||
| 112 | Timer::after_millis(1000).await; | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | const MEMORY_PAGE_SIZE: usize = 8; | ||
| 117 | |||
| 118 | const CMD_QUAD_READ: u8 = 0x6B; | ||
| 119 | |||
| 120 | const CMD_QUAD_WRITE_PG: u8 = 0x32; | ||
| 121 | |||
| 122 | const CMD_READ_ID: u8 = 0x9F; | ||
| 123 | |||
| 124 | const CMD_ENABLE_RESET: u8 = 0x66; | ||
| 125 | const CMD_RESET: u8 = 0x99; | ||
| 126 | |||
| 127 | const CMD_WRITE_ENABLE: u8 = 0x06; | ||
| 128 | |||
| 129 | const CMD_CHIP_ERASE: u8 = 0xC7; | ||
| 130 | const CMD_SECTOR_ERASE: u8 = 0x20; | ||
| 131 | const CMD_BLOCK_ERASE_32K: u8 = 0x52; | ||
| 132 | const CMD_BLOCK_ERASE_64K: u8 = 0xD8; | ||
| 133 | |||
| 134 | const CMD_READ_SR: u8 = 0x05; | ||
| 135 | const CMD_READ_CR: u8 = 0x35; | ||
| 136 | |||
| 137 | const CMD_WRITE_SR: u8 = 0x01; | ||
| 138 | const CMD_WRITE_CR: u8 = 0x31; | ||
| 139 | |||
| 140 | /// Implementation of access to flash chip. | ||
| 141 | /// Chip commands are hardcoded as it depends on used chip. | ||
| 142 | /// This implementation is using chip GD25Q64C from Giga Device | ||
| 143 | pub struct FlashMemory<I: Instance> { | ||
| 144 | ospi: Ospi<'static, I, Blocking>, | ||
| 145 | } | ||
| 146 | |||
| 147 | impl<I: Instance> FlashMemory<I> { | ||
| 148 | pub async fn new(ospi: Ospi<'static, I, Blocking>) -> Self { | ||
| 149 | let mut memory = Self { ospi }; | ||
| 150 | |||
| 151 | memory.reset_memory().await; | ||
| 152 | memory.enable_quad(); | ||
| 153 | memory | ||
| 154 | } | ||
| 155 | |||
| 156 | async fn qpi_mode(&mut self) { | ||
| 157 | // Enter qpi mode | ||
| 158 | self.exec_command(0x38).await; | ||
| 159 | |||
| 160 | // Set read param | ||
| 161 | let transaction = TransferConfig { | ||
| 162 | iwidth: OspiWidth::QUAD, | ||
| 163 | dwidth: OspiWidth::QUAD, | ||
| 164 | instruction: Some(0xC0), | ||
| 165 | ..Default::default() | ||
| 166 | }; | ||
| 167 | self.enable_write().await; | ||
| 168 | self.ospi.blocking_write(&[0x30_u8], transaction).unwrap(); | ||
| 169 | self.wait_write_finish(); | ||
| 170 | } | ||
| 171 | |||
| 172 | pub async fn disable_mm(&mut self) { | ||
| 173 | self.ospi.disable_memory_mapped_mode(); | ||
| 174 | } | ||
| 175 | |||
| 176 | pub async fn enable_mm(&mut self) { | ||
| 177 | self.qpi_mode().await; | ||
| 178 | |||
| 179 | let read_config = TransferConfig { | ||
| 180 | iwidth: OspiWidth::QUAD, | ||
| 181 | isize: AddressSize::_8Bit, | ||
| 182 | adwidth: OspiWidth::QUAD, | ||
| 183 | adsize: AddressSize::_24bit, | ||
| 184 | dwidth: OspiWidth::QUAD, | ||
| 185 | instruction: Some(0x0B), // Fast read in QPI mode | ||
| 186 | dummy: DummyCycles::_8, | ||
| 187 | ..Default::default() | ||
| 188 | }; | ||
| 189 | |||
| 190 | let write_config = TransferConfig { | ||
| 191 | iwidth: OspiWidth::SING, | ||
| 192 | isize: AddressSize::_8Bit, | ||
| 193 | adwidth: OspiWidth::SING, | ||
| 194 | adsize: AddressSize::_24bit, | ||
| 195 | dwidth: OspiWidth::QUAD, | ||
| 196 | instruction: Some(0x32), // Write config | ||
| 197 | dummy: DummyCycles::_0, | ||
| 198 | ..Default::default() | ||
| 199 | }; | ||
| 200 | self.ospi.enable_memory_mapped_mode(read_config, write_config).unwrap(); | ||
| 201 | } | ||
| 202 | |||
| 203 | fn enable_quad(&mut self) { | ||
| 204 | let cr = self.read_cr(); | ||
| 205 | // info!("Read cr: {:x}", cr); | ||
| 206 | self.write_cr(cr | 0x02); | ||
| 207 | // info!("Read cr after writing: {:x}", cr); | ||
| 208 | } | ||
| 209 | |||
| 210 | pub fn disable_quad(&mut self) { | ||
| 211 | let cr = self.read_cr(); | ||
| 212 | self.write_cr(cr & (!(0x02))); | ||
| 213 | } | ||
| 214 | |||
| 215 | async fn exec_command_4(&mut self, cmd: u8) { | ||
| 216 | let transaction = TransferConfig { | ||
| 217 | iwidth: OspiWidth::QUAD, | ||
| 218 | adwidth: OspiWidth::NONE, | ||
| 219 | // adsize: AddressSize::_24bit, | ||
| 220 | dwidth: OspiWidth::NONE, | ||
| 221 | instruction: Some(cmd as u32), | ||
| 222 | address: None, | ||
| 223 | dummy: DummyCycles::_0, | ||
| 224 | ..Default::default() | ||
| 225 | }; | ||
| 226 | self.ospi.blocking_command(&transaction).unwrap(); | ||
| 227 | } | ||
| 228 | |||
| 229 | async fn exec_command(&mut self, cmd: u8) { | ||
| 230 | let transaction = TransferConfig { | ||
| 231 | iwidth: OspiWidth::SING, | ||
| 232 | adwidth: OspiWidth::NONE, | ||
| 233 | // adsize: AddressSize::_24bit, | ||
| 234 | dwidth: OspiWidth::NONE, | ||
| 235 | instruction: Some(cmd as u32), | ||
| 236 | address: None, | ||
| 237 | dummy: DummyCycles::_0, | ||
| 238 | ..Default::default() | ||
| 239 | }; | ||
| 240 | // info!("Excuting command: {:x}", transaction.instruction); | ||
| 241 | self.ospi.blocking_command(&transaction).unwrap(); | ||
| 242 | } | ||
| 243 | |||
| 244 | pub async fn reset_memory(&mut self) { | ||
| 245 | self.exec_command_4(CMD_ENABLE_RESET).await; | ||
| 246 | self.exec_command_4(CMD_RESET).await; | ||
| 247 | self.exec_command(CMD_ENABLE_RESET).await; | ||
| 248 | self.exec_command(CMD_RESET).await; | ||
| 249 | self.wait_write_finish(); | ||
| 250 | } | ||
| 251 | |||
| 252 | pub async fn enable_write(&mut self) { | ||
| 253 | self.exec_command(CMD_WRITE_ENABLE).await; | ||
| 254 | } | ||
| 255 | |||
| 256 | pub fn read_id(&mut self) -> [u8; 3] { | ||
| 257 | let mut buffer = [0; 3]; | ||
| 258 | let transaction: TransferConfig = TransferConfig { | ||
| 259 | iwidth: OspiWidth::SING, | ||
| 260 | isize: AddressSize::_8Bit, | ||
| 261 | adwidth: OspiWidth::NONE, | ||
| 262 | // adsize: AddressSize::_24bit, | ||
| 263 | dwidth: OspiWidth::SING, | ||
| 264 | instruction: Some(CMD_READ_ID as u32), | ||
| 265 | ..Default::default() | ||
| 266 | }; | ||
| 267 | // info!("Reading id: 0x{:X}", transaction.instruction); | ||
| 268 | self.ospi.blocking_read(&mut buffer, transaction).unwrap(); | ||
| 269 | buffer | ||
| 270 | } | ||
| 271 | |||
| 272 | pub fn read_id_4(&mut self) -> [u8; 3] { | ||
| 273 | let mut buffer = [0; 3]; | ||
| 274 | let transaction: TransferConfig = TransferConfig { | ||
| 275 | iwidth: OspiWidth::SING, | ||
| 276 | isize: AddressSize::_8Bit, | ||
| 277 | adwidth: OspiWidth::NONE, | ||
| 278 | dwidth: OspiWidth::QUAD, | ||
| 279 | instruction: Some(CMD_READ_ID as u32), | ||
| 280 | ..Default::default() | ||
| 281 | }; | ||
| 282 | info!("Reading id: 0x{:X}", transaction.instruction); | ||
| 283 | self.ospi.blocking_read(&mut buffer, transaction).unwrap(); | ||
| 284 | buffer | ||
| 285 | } | ||
| 286 | |||
| 287 | pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8], use_dma: bool) { | ||
| 288 | let transaction = TransferConfig { | ||
| 289 | iwidth: OspiWidth::SING, | ||
| 290 | adwidth: OspiWidth::SING, | ||
| 291 | adsize: AddressSize::_24bit, | ||
| 292 | dwidth: OspiWidth::QUAD, | ||
| 293 | instruction: Some(CMD_QUAD_READ as u32), | ||
| 294 | address: Some(addr), | ||
| 295 | dummy: DummyCycles::_8, | ||
| 296 | ..Default::default() | ||
| 297 | }; | ||
| 298 | if use_dma { | ||
| 299 | self.ospi.blocking_read(buffer, transaction).unwrap(); | ||
| 300 | } else { | ||
| 301 | self.ospi.blocking_read(buffer, transaction).unwrap(); | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | fn wait_write_finish(&mut self) { | ||
| 306 | while (self.read_sr() & 0x01) != 0 {} | ||
| 307 | } | ||
| 308 | |||
| 309 | async fn perform_erase(&mut self, addr: u32, cmd: u8) { | ||
| 310 | let transaction = TransferConfig { | ||
| 311 | iwidth: OspiWidth::SING, | ||
| 312 | adwidth: OspiWidth::SING, | ||
| 313 | adsize: AddressSize::_24bit, | ||
| 314 | dwidth: OspiWidth::NONE, | ||
| 315 | instruction: Some(cmd as u32), | ||
| 316 | address: Some(addr), | ||
| 317 | dummy: DummyCycles::_0, | ||
| 318 | ..Default::default() | ||
| 319 | }; | ||
| 320 | self.enable_write().await; | ||
| 321 | self.ospi.blocking_command(&transaction).unwrap(); | ||
| 322 | self.wait_write_finish(); | ||
| 323 | } | ||
| 324 | |||
| 325 | pub async fn erase_sector(&mut self, addr: u32) { | ||
| 326 | self.perform_erase(addr, CMD_SECTOR_ERASE).await; | ||
| 327 | } | ||
| 328 | |||
| 329 | pub async fn erase_block_32k(&mut self, addr: u32) { | ||
| 330 | self.perform_erase(addr, CMD_BLOCK_ERASE_32K).await; | ||
| 331 | } | ||
| 332 | |||
| 333 | pub async fn erase_block_64k(&mut self, addr: u32) { | ||
| 334 | self.perform_erase(addr, CMD_BLOCK_ERASE_64K).await; | ||
| 335 | } | ||
| 336 | |||
| 337 | pub async fn erase_chip(&mut self) { | ||
| 338 | self.exec_command(CMD_CHIP_ERASE).await; | ||
| 339 | } | ||
| 340 | |||
| 341 | async fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize, use_dma: bool) { | ||
| 342 | assert!( | ||
| 343 | (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32, | ||
| 344 | "write_page(): page write length exceeds page boundary (len = {}, addr = {:X}", | ||
| 345 | len, | ||
| 346 | addr | ||
| 347 | ); | ||
| 348 | |||
| 349 | let transaction = TransferConfig { | ||
| 350 | iwidth: OspiWidth::SING, | ||
| 351 | adsize: AddressSize::_24bit, | ||
| 352 | adwidth: OspiWidth::SING, | ||
| 353 | dwidth: OspiWidth::QUAD, | ||
| 354 | instruction: Some(CMD_QUAD_WRITE_PG as u32), | ||
| 355 | address: Some(addr), | ||
| 356 | dummy: DummyCycles::_0, | ||
| 357 | ..Default::default() | ||
| 358 | }; | ||
| 359 | self.enable_write().await; | ||
| 360 | if use_dma { | ||
| 361 | self.ospi.blocking_write(buffer, transaction).unwrap(); | ||
| 362 | } else { | ||
| 363 | self.ospi.blocking_write(buffer, transaction).unwrap(); | ||
| 364 | } | ||
| 365 | self.wait_write_finish(); | ||
| 366 | } | ||
| 367 | |||
| 368 | pub async fn write_memory(&mut self, addr: u32, buffer: &[u8], use_dma: bool) { | ||
| 369 | let mut left = buffer.len(); | ||
| 370 | let mut place = addr; | ||
| 371 | let mut chunk_start = 0; | ||
| 372 | |||
| 373 | while left > 0 { | ||
| 374 | let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize; | ||
| 375 | let chunk_size = if left >= max_chunk_size { max_chunk_size } else { left }; | ||
| 376 | let chunk = &buffer[chunk_start..(chunk_start + chunk_size)]; | ||
| 377 | self.write_page(place, chunk, chunk_size, use_dma).await; | ||
| 378 | place += chunk_size as u32; | ||
| 379 | left -= chunk_size; | ||
| 380 | chunk_start += chunk_size; | ||
| 381 | } | ||
| 382 | } | ||
| 383 | |||
| 384 | fn read_register(&mut self, cmd: u8) -> u8 { | ||
| 385 | let mut buffer = [0; 1]; | ||
| 386 | let transaction: TransferConfig = TransferConfig { | ||
| 387 | iwidth: OspiWidth::SING, | ||
| 388 | isize: AddressSize::_8Bit, | ||
| 389 | adwidth: OspiWidth::NONE, | ||
| 390 | adsize: AddressSize::_24bit, | ||
| 391 | dwidth: OspiWidth::SING, | ||
| 392 | instruction: Some(cmd as u32), | ||
| 393 | address: None, | ||
| 394 | dummy: DummyCycles::_0, | ||
| 395 | ..Default::default() | ||
| 396 | }; | ||
| 397 | self.ospi.blocking_read(&mut buffer, transaction).unwrap(); | ||
| 398 | // info!("Read w25q64 register: 0x{:x}", buffer[0]); | ||
| 399 | buffer[0] | ||
| 400 | } | ||
| 401 | |||
| 402 | fn write_register(&mut self, cmd: u8, value: u8) { | ||
| 403 | let buffer = [value; 1]; | ||
| 404 | let transaction: TransferConfig = TransferConfig { | ||
| 405 | iwidth: OspiWidth::SING, | ||
| 406 | isize: AddressSize::_8Bit, | ||
| 407 | instruction: Some(cmd as u32), | ||
| 408 | adsize: AddressSize::_24bit, | ||
| 409 | adwidth: OspiWidth::NONE, | ||
| 410 | dwidth: OspiWidth::SING, | ||
| 411 | address: None, | ||
| 412 | dummy: DummyCycles::_0, | ||
| 413 | ..Default::default() | ||
| 414 | }; | ||
| 415 | self.ospi.blocking_write(&buffer, transaction).unwrap(); | ||
| 416 | } | ||
| 417 | |||
| 418 | pub fn read_sr(&mut self) -> u8 { | ||
| 419 | self.read_register(CMD_READ_SR) | ||
| 420 | } | ||
| 421 | |||
| 422 | pub fn read_cr(&mut self) -> u8 { | ||
| 423 | self.read_register(CMD_READ_CR) | ||
| 424 | } | ||
| 425 | |||
| 426 | pub fn write_sr(&mut self, value: u8) { | ||
| 427 | self.write_register(CMD_WRITE_SR, value); | ||
| 428 | } | ||
| 429 | |||
| 430 | pub fn write_cr(&mut self, value: u8) { | ||
| 431 | self.write_register(CMD_WRITE_CR, value); | ||
| 432 | } | ||
| 433 | } | ||
diff --git a/examples/stm32h7rs/Cargo.toml b/examples/stm32h7rs/Cargo.toml index f97dfd722..1d957e2cc 100644 --- a/examples/stm32h7rs/Cargo.toml +++ b/examples/stm32h7rs/Cargo.toml | |||
| @@ -7,10 +7,10 @@ license = "MIT OR Apache-2.0" | |||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32h743bi to your chip name, if necessary. | 8 | # Change stm32h743bi to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7s3l8", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7s3l8", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.1", 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", "executor-interrupt", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "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.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } | 13 | embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } |
| 14 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } |
| 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 16 | 16 | ||
| @@ -22,7 +22,7 @@ cortex-m-rt = "0.7.0" | |||
| 22 | embedded-hal = "0.2.6" | 22 | embedded-hal = "0.2.6" |
| 23 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | 23 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } |
| 24 | embedded-hal-async = { version = "1.0" } | 24 | embedded-hal-async = { version = "1.0" } |
| 25 | embedded-nal-async = { version = "0.7.1" } | 25 | embedded-nal-async = "0.8.0" |
| 26 | embedded-io-async = { version = "0.6.1" } | 26 | embedded-io-async = { version = "0.6.1" } |
| 27 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 27 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 28 | heapless = { version = "0.8", default-features = false } | 28 | heapless = { version = "0.8", default-features = false } |
diff --git a/examples/stm32l0/.cargo/config.toml b/examples/stm32l0/.cargo/config.toml index b050334b2..fed9cf9ce 100644 --- a/examples/stm32l0/.cargo/config.toml +++ b/examples/stm32l0/.cargo/config.toml | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] |
| 2 | # replace your chip as listed in `probe-rs chip list` | 2 | # replace your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-rs run --chip STM32L053R8Tx" | 3 | runner = "probe-rs run --chip STM32L073RZTx" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv6m-none-eabi" | 6 | target = "thumbv6m-none-eabi" |
diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 2577f19e0..5cc312a50 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml | |||
| @@ -6,9 +6,9 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32l072cz to your chip name, if necessary. | 8 | # Change stm32l072cz to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "unstable-pac", "time-driver-any", "exti", "memory-x"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l073rz", "unstable-pac", "time-driver-any", "exti", "memory-x"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | 13 | ||
| 14 | defmt = "0.3" | 14 | defmt = "0.3" |
diff --git a/examples/stm32l0/README.md b/examples/stm32l0/README.md new file mode 100644 index 000000000..82d222027 --- /dev/null +++ b/examples/stm32l0/README.md | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | # Examples for STM32L0 family | ||
| 2 | Run individual examples with | ||
| 3 | ``` | ||
| 4 | cargo run --bin <module-name> | ||
| 5 | ``` | ||
| 6 | for example | ||
| 7 | ``` | ||
| 8 | cargo run --bin blinky | ||
| 9 | ``` | ||
| 10 | |||
| 11 | ## Checklist before running examples | ||
| 12 | You might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using. | ||
| 13 | |||
| 14 | * [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L073RZ it should be `probe-rs run --chip STM32L073RZTx`. (use `probe-rs chip list` to find your chip) | ||
| 15 | * [ ] Update Cargo.toml to have the correct `embassy-stm32` feature. For example for L073RZ it should be `stm32l073rz`. Look in the `Cargo.toml` file of the `embassy-stm32` project to find the correct feature flag for your chip. | ||
| 16 | * [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately. | ||
| 17 | * [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic | ||
| 18 | |||
| 19 | If you are unsure, please drop by the Embassy Matrix chat for support, and let us know: | ||
| 20 | |||
| 21 | * Which example you are trying to run | ||
| 22 | * Which chip and board you are using | ||
| 23 | |||
| 24 | Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org | ||
diff --git a/examples/stm32l0/src/bin/async-tsc.rs b/examples/stm32l0/src/bin/async-tsc.rs deleted file mode 100644 index c40b86af9..000000000 --- a/examples/stm32l0/src/bin/async-tsc.rs +++ /dev/null | |||
| @@ -1,122 +0,0 @@ | |||
| 1 | // Example of async TSC (Touch Sensing Controller) that lights an LED when touch is detected. | ||
| 2 | // | ||
| 3 | // Suggested physical setup on STM32L073RZ Nucleo board: | ||
| 4 | // - Connect a 1000pF capacitor between pin A0 and GND. This is your sampling capacitor. | ||
| 5 | // - Connect one end of a 1K resistor to pin A1 and leave the other end loose. | ||
| 6 | // The loose end will act as touch sensor which will register your touch. | ||
| 7 | // | ||
| 8 | // Troubleshooting the setup: | ||
| 9 | // - If no touch seems to be registered, then try to disconnect the sampling capacitor from GND momentarily, | ||
| 10 | // now the led should light up. Next try using a different value for the sampling capacitor. | ||
| 11 | // Also experiment with increasing the values for `ct_pulse_high_length`, `ct_pulse_low_length`, `pulse_generator_prescaler`, `max_count_value` and `discharge_delay`. | ||
| 12 | // | ||
| 13 | // All configuration values and sampling capacitor value have been determined experimentally. | ||
| 14 | // Suitable configuration and discharge delay values are highly dependent on the value of the sample capacitor. For example, a shorter discharge delay can be used with smaller capacitor values. | ||
| 15 | // | ||
| 16 | #![no_std] | ||
| 17 | #![no_main] | ||
| 18 | |||
| 19 | use defmt::*; | ||
| 20 | use embassy_stm32::bind_interrupts; | ||
| 21 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 22 | use embassy_stm32::tsc::{self, *}; | ||
| 23 | use embassy_time::Timer; | ||
| 24 | use {defmt_rtt as _, panic_probe as _}; | ||
| 25 | |||
| 26 | bind_interrupts!(struct Irqs { | ||
| 27 | TSC => InterruptHandler<embassy_stm32::peripherals::TSC>; | ||
| 28 | }); | ||
| 29 | |||
| 30 | #[cortex_m_rt::exception] | ||
| 31 | unsafe fn HardFault(_: &cortex_m_rt::ExceptionFrame) -> ! { | ||
| 32 | cortex_m::peripheral::SCB::sys_reset(); | ||
| 33 | } | ||
| 34 | |||
| 35 | /// This example is written for the nucleo-stm32l073rz, with a stm32l073rz chip. | ||
| 36 | /// | ||
| 37 | /// Make sure you check/update the following (whether you use the L073RZ or another board): | ||
| 38 | /// | ||
| 39 | /// * [ ] Update .cargo/config.toml with the correct `probe-rs run --chip STM32L073RZTx`chip name. | ||
| 40 | /// * [ ] Update Cargo.toml to have the correct `embassy-stm32` feature, for L073RZ it should be `stm32l073rz`. | ||
| 41 | /// * [ ] If your board has a special clock or power configuration, make sure that it is | ||
| 42 | /// set up appropriately. | ||
| 43 | /// * [ ] If your board has different pin mapping, update any pin numbers or peripherals | ||
| 44 | /// to match your schematic | ||
| 45 | /// | ||
| 46 | /// If you are unsure, please drop by the Embassy Matrix chat for support, and let us know: | ||
| 47 | /// | ||
| 48 | /// * Which example you are trying to run | ||
| 49 | /// * Which chip and board you are using | ||
| 50 | /// | ||
| 51 | /// Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org | ||
| 52 | #[embassy_executor::main] | ||
| 53 | async fn main(_spawner: embassy_executor::Spawner) { | ||
| 54 | let device_config = embassy_stm32::Config::default(); | ||
| 55 | let context = embassy_stm32::init(device_config); | ||
| 56 | |||
| 57 | let config = tsc::Config { | ||
| 58 | ct_pulse_high_length: ChargeTransferPulseCycle::_4, | ||
| 59 | ct_pulse_low_length: ChargeTransferPulseCycle::_4, | ||
| 60 | spread_spectrum: false, | ||
| 61 | spread_spectrum_deviation: SSDeviation::new(2).unwrap(), | ||
| 62 | spread_spectrum_prescaler: false, | ||
| 63 | pulse_generator_prescaler: PGPrescalerDivider::_16, | ||
| 64 | max_count_value: MaxCount::_255, | ||
| 65 | io_default_mode: false, | ||
| 66 | synchro_pin_polarity: false, | ||
| 67 | acquisition_mode: false, | ||
| 68 | max_count_interrupt: false, | ||
| 69 | channel_ios: TscIOPin::Group1Io1.into(), | ||
| 70 | shield_ios: 0, // no shield | ||
| 71 | sampling_ios: TscIOPin::Group1Io2.into(), | ||
| 72 | }; | ||
| 73 | |||
| 74 | let mut g1: PinGroup<embassy_stm32::peripherals::TSC, G1> = PinGroup::new(); | ||
| 75 | g1.set_io1(context.PA0, PinType::Sample); | ||
| 76 | g1.set_io2(context.PA1, PinType::Channel); | ||
| 77 | |||
| 78 | let mut touch_controller = tsc::Tsc::new_async( | ||
| 79 | context.TSC, | ||
| 80 | Some(g1), | ||
| 81 | None, | ||
| 82 | None, | ||
| 83 | None, | ||
| 84 | None, | ||
| 85 | None, | ||
| 86 | None, | ||
| 87 | None, | ||
| 88 | config, | ||
| 89 | Irqs, | ||
| 90 | ); | ||
| 91 | |||
| 92 | // Check if TSC is ready | ||
| 93 | if touch_controller.get_state() != State::Ready { | ||
| 94 | info!("TSC not ready!"); | ||
| 95 | loop {} // Halt execution | ||
| 96 | } | ||
| 97 | info!("TSC initialized successfully"); | ||
| 98 | |||
| 99 | // LED2 on the STM32L073RZ nucleo-board (PA5) | ||
| 100 | let mut led = Output::new(context.PA5, Level::High, Speed::Low); | ||
| 101 | |||
| 102 | // smaller sample capacitor discharge faster and can be used with shorter delay. | ||
| 103 | let discharge_delay = 5; // ms | ||
| 104 | |||
| 105 | info!("Starting touch_controller interface"); | ||
| 106 | loop { | ||
| 107 | touch_controller.start(); | ||
| 108 | touch_controller.pend_for_acquisition().await; | ||
| 109 | touch_controller.discharge_io(true); | ||
| 110 | Timer::after_millis(discharge_delay).await; | ||
| 111 | |||
| 112 | let grp1_status = touch_controller.group_get_status(Group::One); | ||
| 113 | match grp1_status { | ||
| 114 | GroupStatus::Complete => { | ||
| 115 | let group_one_val = touch_controller.group_get_value(Group::One); | ||
| 116 | info!("{}", group_one_val); | ||
| 117 | led.set_high(); | ||
| 118 | } | ||
| 119 | GroupStatus::Ongoing => led.set_low(), | ||
| 120 | } | ||
| 121 | } | ||
| 122 | } | ||
diff --git a/examples/stm32l0/src/bin/blocking-tsc.rs b/examples/stm32l0/src/bin/blocking-tsc.rs deleted file mode 100644 index 7e4f40946..000000000 --- a/examples/stm32l0/src/bin/blocking-tsc.rs +++ /dev/null | |||
| @@ -1,116 +0,0 @@ | |||
| 1 | // Example of polling TSC (Touch Sensing Controller) that lights an LED when touch is detected. | ||
| 2 | // | ||
| 3 | // Suggested physical setup on STM32L073RZ Nucleo board: | ||
| 4 | // - Connect a 1000pF capacitor between pin A0 and GND. This is your sampling capacitor. | ||
| 5 | // - Connect one end of a 1K resistor to pin A1 and leave the other end loose. | ||
| 6 | // The loose end will act as touch sensor which will register your touch. | ||
| 7 | // | ||
| 8 | // Troubleshooting the setup: | ||
| 9 | // - If no touch seems to be registered, then try to disconnect the sampling capacitor from GND momentarily, | ||
| 10 | // now the led should light up. Next try using a different value for the sampling capacitor. | ||
| 11 | // Also experiment with increasing the values for `ct_pulse_high_length`, `ct_pulse_low_length`, `pulse_generator_prescaler`, `max_count_value` and `discharge_delay`. | ||
| 12 | // | ||
| 13 | // All configuration values and sampling capacitor value have been determined experimentally. | ||
| 14 | // Suitable configuration and discharge delay values are highly dependent on the value of the sample capacitor. For example, a shorter discharge delay can be used with smaller capacitor values. | ||
| 15 | // | ||
| 16 | #![no_std] | ||
| 17 | #![no_main] | ||
| 18 | |||
| 19 | use defmt::*; | ||
| 20 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 21 | use embassy_stm32::tsc::{self, *}; | ||
| 22 | use embassy_time::Timer; | ||
| 23 | use {defmt_rtt as _, panic_probe as _}; | ||
| 24 | |||
| 25 | /// This example is written for the nucleo-stm32l073rz, with a stm32l073rz chip. | ||
| 26 | /// | ||
| 27 | /// Make sure you check/update the following (whether you use the L073RZ or another board): | ||
| 28 | /// | ||
| 29 | /// * [ ] Update .cargo/config.toml with the correct `probe-rs run --chip STM32L073RZTx`chip name. | ||
| 30 | /// * [ ] Update Cargo.toml to have the correct `embassy-stm32` feature, for L073RZ it should be `stm32l073rz`. | ||
| 31 | /// * [ ] If your board has a special clock or power configuration, make sure that it is | ||
| 32 | /// set up appropriately. | ||
| 33 | /// * [ ] If your board has different pin mapping, update any pin numbers or peripherals | ||
| 34 | /// to match your schematic | ||
| 35 | /// | ||
| 36 | /// If you are unsure, please drop by the Embassy Matrix chat for support, and let us know: | ||
| 37 | /// | ||
| 38 | /// * Which example you are trying to run | ||
| 39 | /// * Which chip and board you are using | ||
| 40 | /// | ||
| 41 | /// Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org | ||
| 42 | #[embassy_executor::main] | ||
| 43 | async fn main(_spawner: embassy_executor::Spawner) { | ||
| 44 | let device_config = embassy_stm32::Config::default(); | ||
| 45 | let context = embassy_stm32::init(device_config); | ||
| 46 | |||
| 47 | let tsc_conf = Config { | ||
| 48 | ct_pulse_high_length: ChargeTransferPulseCycle::_4, | ||
| 49 | ct_pulse_low_length: ChargeTransferPulseCycle::_4, | ||
| 50 | spread_spectrum: false, | ||
| 51 | spread_spectrum_deviation: SSDeviation::new(2).unwrap(), | ||
| 52 | spread_spectrum_prescaler: false, | ||
| 53 | pulse_generator_prescaler: PGPrescalerDivider::_16, | ||
| 54 | max_count_value: MaxCount::_255, | ||
| 55 | io_default_mode: false, | ||
| 56 | synchro_pin_polarity: false, | ||
| 57 | acquisition_mode: false, | ||
| 58 | max_count_interrupt: false, | ||
| 59 | channel_ios: TscIOPin::Group1Io1.into(), | ||
| 60 | shield_ios: 0, // no shield | ||
| 61 | sampling_ios: TscIOPin::Group1Io2.into(), | ||
| 62 | }; | ||
| 63 | |||
| 64 | let mut g1: PinGroup<embassy_stm32::peripherals::TSC, G1> = PinGroup::new(); | ||
| 65 | g1.set_io1(context.PA0, PinType::Sample); | ||
| 66 | g1.set_io2(context.PA1, PinType::Channel); | ||
| 67 | |||
| 68 | let mut touch_controller = tsc::Tsc::new_blocking( | ||
| 69 | context.TSC, | ||
| 70 | Some(g1), | ||
| 71 | None, | ||
| 72 | None, | ||
| 73 | None, | ||
| 74 | None, | ||
| 75 | None, | ||
| 76 | None, | ||
| 77 | None, | ||
| 78 | tsc_conf, | ||
| 79 | ); | ||
| 80 | |||
| 81 | // Check if TSC is ready | ||
| 82 | if touch_controller.get_state() != State::Ready { | ||
| 83 | info!("TSC not ready!"); | ||
| 84 | loop {} // Halt execution | ||
| 85 | } | ||
| 86 | info!("TSC initialized successfully"); | ||
| 87 | |||
| 88 | // LED2 on the STM32L073RZ nucleo-board (PA5) | ||
| 89 | let mut led = Output::new(context.PA5, Level::High, Speed::Low); | ||
| 90 | |||
| 91 | // smaller sample capacitor discharge faster and can be used with shorter delay. | ||
| 92 | let discharge_delay = 5; // ms | ||
| 93 | |||
| 94 | // the interval at which the loop polls for new touch sensor values | ||
| 95 | let polling_interval = 100; // ms | ||
| 96 | |||
| 97 | info!("polling for touch"); | ||
| 98 | loop { | ||
| 99 | touch_controller.start(); | ||
| 100 | touch_controller.poll_for_acquisition(); | ||
| 101 | touch_controller.discharge_io(true); | ||
| 102 | Timer::after_millis(discharge_delay).await; | ||
| 103 | |||
| 104 | let grp1_status = touch_controller.group_get_status(Group::One); | ||
| 105 | match grp1_status { | ||
| 106 | GroupStatus::Complete => { | ||
| 107 | let group_one_val = touch_controller.group_get_value(Group::One); | ||
| 108 | info!("{}", group_one_val); | ||
| 109 | led.set_high(); | ||
| 110 | } | ||
| 111 | GroupStatus::Ongoing => led.set_low(), | ||
| 112 | } | ||
| 113 | |||
| 114 | Timer::after_millis(polling_interval).await; | ||
| 115 | } | ||
| 116 | } | ||
diff --git a/examples/stm32l0/src/bin/tsc_async.rs b/examples/stm32l0/src/bin/tsc_async.rs new file mode 100644 index 000000000..dae351c2e --- /dev/null +++ b/examples/stm32l0/src/bin/tsc_async.rs | |||
| @@ -0,0 +1,116 @@ | |||
| 1 | // Example of async TSC (Touch Sensing Controller) that lights an LED when touch is detected. | ||
| 2 | // | ||
| 3 | // This example demonstrates: | ||
| 4 | // 1. Configuring a single TSC channel pin | ||
| 5 | // 2. Using the blocking TSC interface with polling | ||
| 6 | // 3. Waiting for acquisition completion using `poll_for_acquisition` | ||
| 7 | // 4. Reading touch values and controlling an LED based on the results | ||
| 8 | // | ||
| 9 | // Suggested physical setup on STM32L073RZ Nucleo board: | ||
| 10 | // - Connect a 1000pF capacitor between pin PA0 and GND. This is your sampling capacitor. | ||
| 11 | // - Connect one end of a 1K resistor to pin PA1 and leave the other end loose. | ||
| 12 | // The loose end will act as the touch sensor which will register your touch. | ||
| 13 | // | ||
| 14 | // The example uses two pins from Group 1 of the TSC on the STM32L073RZ Nucleo board: | ||
| 15 | // - PA0 as the sampling capacitor, TSC group 1 IO1 (label A0) | ||
| 16 | // - PA1 as the channel pin, TSC group 1 IO2 (label A1) | ||
| 17 | // | ||
| 18 | // The program continuously reads the touch sensor value: | ||
| 19 | // - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value. | ||
| 20 | // - The LED is turned on when touch is detected (sensor value < 25). | ||
| 21 | // - Touch values are logged to the console. | ||
| 22 | // | ||
| 23 | // Troubleshooting: | ||
| 24 | // - If touch is not detected, try adjusting the SENSOR_THRESHOLD value. | ||
| 25 | // - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, | ||
| 26 | // pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity. | ||
| 27 | // | ||
| 28 | // Note: Configuration values and sampling capacitor value have been determined experimentally. | ||
| 29 | // Optimal values may vary based on your specific hardware setup. | ||
| 30 | // Pins have been chosen for their convenient locations on the STM32L073RZ board. Refer to the | ||
| 31 | // official relevant STM32 datasheets and nucleo-board user manuals to find suitable | ||
| 32 | // alternative pins. | ||
| 33 | // | ||
| 34 | // Beware for STM32L073RZ nucleo-board, that PA2 and PA3 is used for the uart connection to | ||
| 35 | // the programmer chip. If you try to use these two pins for TSC, you will get strange | ||
| 36 | // readings, unless you somehow reconfigure/re-wire your nucleo-board. | ||
| 37 | // No errors or warnings will be emitted, they will just silently not work as expected. | ||
| 38 | // (see nucleo user manual UM1724, Rev 14, page 25) | ||
| 39 | |||
| 40 | #![no_std] | ||
| 41 | #![no_main] | ||
| 42 | |||
| 43 | use defmt::*; | ||
| 44 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 45 | use embassy_stm32::tsc::{self, *}; | ||
| 46 | use embassy_stm32::{bind_interrupts, peripherals}; | ||
| 47 | use embassy_time::Timer; | ||
| 48 | use {defmt_rtt as _, panic_probe as _}; | ||
| 49 | |||
| 50 | bind_interrupts!(struct Irqs { | ||
| 51 | TSC => InterruptHandler<embassy_stm32::peripherals::TSC>; | ||
| 52 | }); | ||
| 53 | const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup | ||
| 54 | |||
| 55 | #[embassy_executor::main] | ||
| 56 | async fn main(_spawner: embassy_executor::Spawner) { | ||
| 57 | let device_config = embassy_stm32::Config::default(); | ||
| 58 | let context = embassy_stm32::init(device_config); | ||
| 59 | |||
| 60 | let mut pin_group: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default(); | ||
| 61 | pin_group.set_io1::<tsc::pin_roles::Sample>(context.PA0); | ||
| 62 | let sensor = pin_group.set_io2::<tsc::pin_roles::Channel>(context.PA1); | ||
| 63 | |||
| 64 | let tsc_conf = Config { | ||
| 65 | ct_pulse_high_length: ChargeTransferPulseCycle::_4, | ||
| 66 | ct_pulse_low_length: ChargeTransferPulseCycle::_4, | ||
| 67 | spread_spectrum: false, | ||
| 68 | spread_spectrum_deviation: SSDeviation::new(2).unwrap(), | ||
| 69 | spread_spectrum_prescaler: false, | ||
| 70 | pulse_generator_prescaler: PGPrescalerDivider::_16, | ||
| 71 | max_count_value: MaxCount::_255, | ||
| 72 | io_default_mode: false, | ||
| 73 | synchro_pin_polarity: false, | ||
| 74 | acquisition_mode: false, | ||
| 75 | max_count_interrupt: false, | ||
| 76 | }; | ||
| 77 | |||
| 78 | let pin_groups: PinGroups<peripherals::TSC> = PinGroups { | ||
| 79 | g1: Some(pin_group.pin_group), | ||
| 80 | ..Default::default() | ||
| 81 | }; | ||
| 82 | |||
| 83 | let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, tsc_conf, Irqs).unwrap(); | ||
| 84 | |||
| 85 | // Check if TSC is ready | ||
| 86 | if touch_controller.get_state() != State::Ready { | ||
| 87 | info!("TSC not ready!"); | ||
| 88 | return; | ||
| 89 | } | ||
| 90 | info!("TSC initialized successfully"); | ||
| 91 | |||
| 92 | // LED2 on the STM32L073RZ nucleo-board (PA5) | ||
| 93 | let mut led = Output::new(context.PA5, Level::Low, Speed::Low); | ||
| 94 | |||
| 95 | let discharge_delay = 5; // ms | ||
| 96 | |||
| 97 | info!("Starting touch_controller interface"); | ||
| 98 | loop { | ||
| 99 | touch_controller.set_active_channels_mask(sensor.pin.into()); | ||
| 100 | touch_controller.start(); | ||
| 101 | touch_controller.pend_for_acquisition().await; | ||
| 102 | touch_controller.discharge_io(true); | ||
| 103 | Timer::after_millis(discharge_delay).await; | ||
| 104 | |||
| 105 | let group_val = touch_controller.group_get_value(sensor.pin.group()); | ||
| 106 | info!("Touch value: {}", group_val); | ||
| 107 | |||
| 108 | if group_val < SENSOR_THRESHOLD { | ||
| 109 | led.set_high(); | ||
| 110 | } else { | ||
| 111 | led.set_low(); | ||
| 112 | } | ||
| 113 | |||
| 114 | Timer::after_millis(100).await; | ||
| 115 | } | ||
| 116 | } | ||
diff --git a/examples/stm32l0/src/bin/tsc_blocking.rs b/examples/stm32l0/src/bin/tsc_blocking.rs new file mode 100644 index 000000000..e1f24639b --- /dev/null +++ b/examples/stm32l0/src/bin/tsc_blocking.rs | |||
| @@ -0,0 +1,142 @@ | |||
| 1 | // Example of blocking TSC (Touch Sensing Controller) that lights an LED when touch is detected. | ||
| 2 | // | ||
| 3 | // This example demonstrates: | ||
| 4 | // 1. Configuring a single TSC channel pin | ||
| 5 | // 2. Using the blocking TSC interface with polling | ||
| 6 | // 3. Waiting for acquisition completion using `poll_for_acquisition` | ||
| 7 | // 4. Reading touch values and controlling an LED based on the results | ||
| 8 | // | ||
| 9 | // Suggested physical setup on STM32L073RZ Nucleo board: | ||
| 10 | // - Connect a 1000pF capacitor between pin PA0 and GND. This is your sampling capacitor. | ||
| 11 | // - Connect one end of a 1K resistor to pin PA1 and leave the other end loose. | ||
| 12 | // The loose end will act as the touch sensor which will register your touch. | ||
| 13 | // | ||
| 14 | // The example uses two pins from Group 1 of the TSC on the STM32L073RZ Nucleo board: | ||
| 15 | // - PA0 as the sampling capacitor, TSC group 1 IO1 (label A0) | ||
| 16 | // - PA1 as the channel pin, TSC group 1 IO2 (label A1) | ||
| 17 | // | ||
| 18 | // The program continuously reads the touch sensor value: | ||
| 19 | // - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value. | ||
| 20 | // - The LED is turned on when touch is detected (sensor value < 25). | ||
| 21 | // - Touch values are logged to the console. | ||
| 22 | // | ||
| 23 | // Troubleshooting: | ||
| 24 | // - If touch is not detected, try adjusting the SENSOR_THRESHOLD value. | ||
| 25 | // - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, | ||
| 26 | // pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity. | ||
| 27 | // | ||
| 28 | // Note: Configuration values and sampling capacitor value have been determined experimentally. | ||
| 29 | // Optimal values may vary based on your specific hardware setup. | ||
| 30 | // Pins have been chosen for their convenient locations on the STM32L073RZ board. Refer to the | ||
| 31 | // official relevant STM32 datasheets and nucleo-board user manuals to find suitable | ||
| 32 | // alternative pins. | ||
| 33 | // | ||
| 34 | // Beware for STM32L073RZ nucleo-board, that PA2 and PA3 is used for the uart connection to | ||
| 35 | // the programmer chip. If you try to use these two pins for TSC, you will get strange | ||
| 36 | // readings, unless you somehow reconfigure/re-wire your nucleo-board. | ||
| 37 | // No errors or warnings will be emitted, they will just silently not work as expected. | ||
| 38 | // (see nucleo user manual UM1724, Rev 14, page 25) | ||
| 39 | |||
| 40 | #![no_std] | ||
| 41 | #![no_main] | ||
| 42 | |||
| 43 | use defmt::*; | ||
| 44 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 45 | use embassy_stm32::tsc::{self, *}; | ||
| 46 | use embassy_stm32::{mode, peripherals}; | ||
| 47 | use embassy_time::Timer; | ||
| 48 | use {defmt_rtt as _, panic_probe as _}; | ||
| 49 | |||
| 50 | const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup | ||
| 51 | |||
| 52 | #[embassy_executor::main] | ||
| 53 | async fn main(_spawner: embassy_executor::Spawner) { | ||
| 54 | let device_config = embassy_stm32::Config::default(); | ||
| 55 | let context = embassy_stm32::init(device_config); | ||
| 56 | |||
| 57 | let tsc_conf = Config { | ||
| 58 | ct_pulse_high_length: ChargeTransferPulseCycle::_4, | ||
| 59 | ct_pulse_low_length: ChargeTransferPulseCycle::_4, | ||
| 60 | spread_spectrum: false, | ||
| 61 | spread_spectrum_deviation: SSDeviation::new(2).unwrap(), | ||
| 62 | spread_spectrum_prescaler: false, | ||
| 63 | pulse_generator_prescaler: PGPrescalerDivider::_16, | ||
| 64 | max_count_value: MaxCount::_255, | ||
| 65 | io_default_mode: false, | ||
| 66 | synchro_pin_polarity: false, | ||
| 67 | acquisition_mode: false, | ||
| 68 | max_count_interrupt: false, | ||
| 69 | }; | ||
| 70 | |||
| 71 | let mut g1: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default(); | ||
| 72 | g1.set_io1::<tsc::pin_roles::Sample>(context.PA0); | ||
| 73 | let tsc_sensor = g1.set_io2::<tsc::pin_roles::Channel>(context.PA1); | ||
| 74 | |||
| 75 | let pin_groups: PinGroups<peripherals::TSC> = PinGroups { | ||
| 76 | g1: Some(g1.pin_group), | ||
| 77 | ..Default::default() | ||
| 78 | }; | ||
| 79 | |||
| 80 | let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, pin_groups, tsc_conf).unwrap(); | ||
| 81 | |||
| 82 | // Check if TSC is ready | ||
| 83 | if touch_controller.get_state() != State::Ready { | ||
| 84 | crate::panic!("TSC not ready!"); | ||
| 85 | } | ||
| 86 | info!("TSC initialized successfully"); | ||
| 87 | |||
| 88 | // LED2 on the STM32L073RZ nucleo-board (PA5) | ||
| 89 | let mut led = Output::new(context.PA5, Level::High, Speed::Low); | ||
| 90 | |||
| 91 | // smaller sample capacitor discharge faster and can be used with shorter delay. | ||
| 92 | let discharge_delay = 5; // ms | ||
| 93 | |||
| 94 | // the interval at which the loop polls for new touch sensor values | ||
| 95 | let polling_interval = 100; // ms | ||
| 96 | |||
| 97 | info!("polling for touch"); | ||
| 98 | loop { | ||
| 99 | touch_controller.set_active_channels_mask(tsc_sensor.pin.into()); | ||
| 100 | touch_controller.start(); | ||
| 101 | touch_controller.poll_for_acquisition(); | ||
| 102 | touch_controller.discharge_io(true); | ||
| 103 | Timer::after_millis(discharge_delay).await; | ||
| 104 | |||
| 105 | match read_touch_value(&mut touch_controller, tsc_sensor.pin).await { | ||
| 106 | Some(v) => { | ||
| 107 | info!("sensor value {}", v); | ||
| 108 | if v < SENSOR_THRESHOLD { | ||
| 109 | led.set_high(); | ||
| 110 | } else { | ||
| 111 | led.set_low(); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | None => led.set_low(), | ||
| 115 | } | ||
| 116 | |||
| 117 | Timer::after_millis(polling_interval).await; | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10; | ||
| 122 | |||
| 123 | // attempt to read group status and delay when still ongoing | ||
| 124 | async fn read_touch_value( | ||
| 125 | touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Blocking>, | ||
| 126 | sensor_pin: tsc::IOPin, | ||
| 127 | ) -> Option<u16> { | ||
| 128 | for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS { | ||
| 129 | match touch_controller.group_get_status(sensor_pin.group()) { | ||
| 130 | GroupStatus::Complete => { | ||
| 131 | return Some(touch_controller.group_get_value(sensor_pin.group())); | ||
| 132 | } | ||
| 133 | GroupStatus::Ongoing => { | ||
| 134 | // if you end up here a lot, then you prob need to increase discharge_delay | ||
| 135 | // or consider changing the code to adjust the discharge_delay dynamically | ||
| 136 | info!("Acquisition still ongoing"); | ||
| 137 | Timer::after_millis(1).await; | ||
| 138 | } | ||
| 139 | } | ||
| 140 | } | ||
| 141 | None | ||
| 142 | } | ||
diff --git a/examples/stm32l0/src/bin/tsc_multipin.rs b/examples/stm32l0/src/bin/tsc_multipin.rs new file mode 100644 index 000000000..6343de141 --- /dev/null +++ b/examples/stm32l0/src/bin/tsc_multipin.rs | |||
| @@ -0,0 +1,209 @@ | |||
| 1 | // Example of TSC (Touch Sensing Controller) using multiple pins from the same tsc-group. | ||
| 2 | // | ||
| 3 | // What is special about using multiple TSC pins as sensor channels from the same TSC group, | ||
| 4 | // is that only one TSC pin for each TSC group can be acquired and read at the time. | ||
| 5 | // To control which channel pins are acquired and read, we must write a mask before initiating an | ||
| 6 | // acquisition. To help manage and abstract all this business away, we can organize our channel | ||
| 7 | // pins into acquisition banks. Each acquisition bank can contain exactly one channel pin per TSC | ||
| 8 | // group and it will contain the relevant mask. | ||
| 9 | // | ||
| 10 | // This example demonstrates how to: | ||
| 11 | // 1. Configure multiple channel pins within a single TSC group | ||
| 12 | // 2. Use the set_active_channels_bank method to switch between sets of different channels (acquisition banks) | ||
| 13 | // 3. Read and interpret touch values from multiple channels in the same group | ||
| 14 | // | ||
| 15 | // Suggested physical setup on STM32L073RZ Nucleo board: | ||
| 16 | // - Connect a 1000pF capacitor between pin PA0 (label A0) and GND. This is the sampling capacitor for TSC | ||
| 17 | // group 1. | ||
| 18 | // - Connect one end of a 1K resistor to pin PA1 (label A1) and leave the other end loose. | ||
| 19 | // The loose end will act as a touch sensor. | ||
| 20 | // | ||
| 21 | // - Connect a 1000pF capacitor between pin PB3 (label D3) and GND. This is the sampling capacitor for TSC | ||
| 22 | // group 5. | ||
| 23 | // - Connect one end of another 1K resistor to pin PB4 and leave the other end loose. | ||
| 24 | // The loose end will act as a touch sensor. | ||
| 25 | // - Connect one end of another 1K resistor to pin PB6 and leave the other end loose. | ||
| 26 | // The loose end will act as a touch sensor. | ||
| 27 | // | ||
| 28 | // The example uses pins from two TSC groups. | ||
| 29 | // - PA0 as sampling capacitor, TSC group 1 IO1 (label A0) | ||
| 30 | // - PA1 as channel, TSC group 1 IO2 (label A1) | ||
| 31 | // - PB3 as sampling capacitor, TSC group 5 IO1 (label D3) | ||
| 32 | // - PB4 as channel, TSC group 5 IO2 (label D10) | ||
| 33 | // - PB6 as channel, TSC group 5 IO3 (label D5) | ||
| 34 | // | ||
| 35 | // The pins have been chosen to make it easy to simply add capacitors directly onto the board and | ||
| 36 | // connect one leg to GND, and to easily add resistors to the board with no special connectors, | ||
| 37 | // breadboards, special wires or soldering required. All you need is the capacitors and resistors. | ||
| 38 | // | ||
| 39 | // The program reads the designated channel pins and adjusts the LED blinking | ||
| 40 | // pattern based on which sensor(s) are touched: | ||
| 41 | // - No touch: LED off | ||
| 42 | // - one sensor touched: Slow blinking | ||
| 43 | // - two sensors touched: Fast blinking | ||
| 44 | // - three sensors touched: LED constantly on | ||
| 45 | // | ||
| 46 | // Troubleshooting: | ||
| 47 | // - If touch is not detected, try adjusting the SENSOR_THRESHOLD value. | ||
| 48 | // - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, | ||
| 49 | // pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity. | ||
| 50 | // | ||
| 51 | // Note: Configuration values and sampling capacitor value have been determined experimentally. | ||
| 52 | // Optimal values may vary based on your specific hardware setup. | ||
| 53 | // Pins have been chosen for their convenient locations on the STM32L073RZ board. Refer to the | ||
| 54 | // official relevant STM32 datasheets and nucleo-board user manuals to find suitable | ||
| 55 | // alternative pins. | ||
| 56 | // | ||
| 57 | // Beware for STM32L073RZ nucleo-board, that PA2 and PA3 is used for the uart connection to | ||
| 58 | // the programmer chip. If you try to use these two pins for TSC, you will get strange | ||
| 59 | // readings, unless you somehow reconfigure/re-wire your nucleo-board. | ||
| 60 | // No errors or warnings will be emitted, they will just silently not work as expected. | ||
| 61 | // (see nucleo user manual UM1724, Rev 14, page 25) | ||
| 62 | |||
| 63 | #![no_std] | ||
| 64 | #![no_main] | ||
| 65 | |||
| 66 | use defmt::*; | ||
| 67 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 68 | use embassy_stm32::tsc::{self, *}; | ||
| 69 | use embassy_stm32::{bind_interrupts, mode, peripherals}; | ||
| 70 | use embassy_time::Timer; | ||
| 71 | use {defmt_rtt as _, panic_probe as _}; | ||
| 72 | |||
| 73 | bind_interrupts!(struct Irqs { | ||
| 74 | TSC => InterruptHandler<embassy_stm32::peripherals::TSC>; | ||
| 75 | }); | ||
| 76 | |||
| 77 | const SENSOR_THRESHOLD: u16 = 35; | ||
| 78 | |||
| 79 | async fn acquire_sensors( | ||
| 80 | touch_controller: &mut Tsc<'static, peripherals::TSC, mode::Async>, | ||
| 81 | tsc_acquisition_bank: &AcquisitionBank, | ||
| 82 | ) { | ||
| 83 | touch_controller.set_active_channels_bank(tsc_acquisition_bank); | ||
| 84 | touch_controller.start(); | ||
| 85 | touch_controller.pend_for_acquisition().await; | ||
| 86 | touch_controller.discharge_io(true); | ||
| 87 | let discharge_delay = 5; // ms | ||
| 88 | Timer::after_millis(discharge_delay).await; | ||
| 89 | } | ||
| 90 | |||
| 91 | #[embassy_executor::main] | ||
| 92 | async fn main(_spawner: embassy_executor::Spawner) { | ||
| 93 | let device_config = embassy_stm32::Config::default(); | ||
| 94 | let context = embassy_stm32::init(device_config); | ||
| 95 | |||
| 96 | // ---------- initial configuration of TSC ---------- | ||
| 97 | let mut pin_group1: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default(); | ||
| 98 | pin_group1.set_io1::<tsc::pin_roles::Sample>(context.PA0); | ||
| 99 | let tsc_sensor0 = pin_group1.set_io2(context.PA1); | ||
| 100 | |||
| 101 | let mut pin_group5: PinGroupWithRoles<peripherals::TSC, G5> = PinGroupWithRoles::default(); | ||
| 102 | pin_group5.set_io1::<tsc::pin_roles::Sample>(context.PB3); | ||
| 103 | let tsc_sensor1 = pin_group5.set_io2(context.PB4); | ||
| 104 | let tsc_sensor2 = pin_group5.set_io3(context.PB6); | ||
| 105 | |||
| 106 | let config = tsc::Config { | ||
| 107 | ct_pulse_high_length: ChargeTransferPulseCycle::_16, | ||
| 108 | ct_pulse_low_length: ChargeTransferPulseCycle::_16, | ||
| 109 | spread_spectrum: false, | ||
| 110 | spread_spectrum_deviation: SSDeviation::new(2).unwrap(), | ||
| 111 | spread_spectrum_prescaler: false, | ||
| 112 | pulse_generator_prescaler: PGPrescalerDivider::_16, | ||
| 113 | max_count_value: MaxCount::_255, | ||
| 114 | io_default_mode: false, | ||
| 115 | synchro_pin_polarity: false, | ||
| 116 | acquisition_mode: false, | ||
| 117 | max_count_interrupt: false, | ||
| 118 | }; | ||
| 119 | |||
| 120 | let pin_groups: PinGroups<peripherals::TSC> = PinGroups { | ||
| 121 | g1: Some(pin_group1.pin_group), | ||
| 122 | g5: Some(pin_group5.pin_group), | ||
| 123 | ..Default::default() | ||
| 124 | }; | ||
| 125 | |||
| 126 | let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, config, Irqs).unwrap(); | ||
| 127 | |||
| 128 | // ---------- setting up acquisition banks ---------- | ||
| 129 | // sensor0 and sensor1 in this example belong to different TSC-groups, | ||
| 130 | // therefore we can acquire and read them both in one go. | ||
| 131 | let bank1 = touch_controller.create_acquisition_bank(AcquisitionBankPins { | ||
| 132 | g1_pin: Some(tsc_sensor0), | ||
| 133 | g5_pin: Some(tsc_sensor1), | ||
| 134 | ..Default::default() | ||
| 135 | }); | ||
| 136 | // `sensor1` and `sensor2` belongs to the same TSC-group, therefore we must make sure to | ||
| 137 | // acquire them one at the time. Therefore, we organize them into different acquisition banks. | ||
| 138 | let bank2 = touch_controller.create_acquisition_bank(AcquisitionBankPins { | ||
| 139 | g5_pin: Some(tsc_sensor2), | ||
| 140 | ..Default::default() | ||
| 141 | }); | ||
| 142 | |||
| 143 | // Check if TSC is ready | ||
| 144 | if touch_controller.get_state() != State::Ready { | ||
| 145 | crate::panic!("TSC not ready!"); | ||
| 146 | } | ||
| 147 | |||
| 148 | info!("TSC initialized successfully"); | ||
| 149 | |||
| 150 | // LED2 on the STM32L073RZ nucleo-board (PA5) | ||
| 151 | let mut led = Output::new(context.PA5, Level::High, Speed::Low); | ||
| 152 | |||
| 153 | let mut led_state = false; | ||
| 154 | |||
| 155 | loop { | ||
| 156 | acquire_sensors(&mut touch_controller, &bank1).await; | ||
| 157 | let readings1 = touch_controller.get_acquisition_bank_values(&bank1); | ||
| 158 | acquire_sensors(&mut touch_controller, &bank2).await; | ||
| 159 | let readings2 = touch_controller.get_acquisition_bank_values(&bank2); | ||
| 160 | |||
| 161 | let mut touched_sensors_count = 0; | ||
| 162 | for reading in readings1.iter() { | ||
| 163 | info!("{}", reading); | ||
| 164 | if reading.sensor_value < SENSOR_THRESHOLD { | ||
| 165 | touched_sensors_count += 1; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | for reading in readings2.iter() { | ||
| 169 | info!("{}", reading); | ||
| 170 | if reading.sensor_value < SENSOR_THRESHOLD { | ||
| 171 | touched_sensors_count += 1; | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | match touched_sensors_count { | ||
| 176 | 0 => { | ||
| 177 | // No sensors touched, turn off the LED | ||
| 178 | led.set_low(); | ||
| 179 | led_state = false; | ||
| 180 | } | ||
| 181 | 1 => { | ||
| 182 | // One sensor touched, blink slowly | ||
| 183 | led_state = !led_state; | ||
| 184 | if led_state { | ||
| 185 | led.set_high(); | ||
| 186 | } else { | ||
| 187 | led.set_low(); | ||
| 188 | } | ||
| 189 | Timer::after_millis(200).await; | ||
| 190 | } | ||
| 191 | 2 => { | ||
| 192 | // Two sensors touched, blink faster | ||
| 193 | led_state = !led_state; | ||
| 194 | if led_state { | ||
| 195 | led.set_high(); | ||
| 196 | } else { | ||
| 197 | led.set_low(); | ||
| 198 | } | ||
| 199 | Timer::after_millis(50).await; | ||
| 200 | } | ||
| 201 | 3 => { | ||
| 202 | // All three sensors touched, LED constantly on | ||
| 203 | led.set_high(); | ||
| 204 | led_state = true; | ||
| 205 | } | ||
| 206 | _ => crate::unreachable!(), // This case should never occur with 3 sensors | ||
| 207 | } | ||
| 208 | } | ||
| 209 | } | ||
diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index 062044f32..31b6785fa 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml | |||
| @@ -5,8 +5,8 @@ version = "0.1.0" | |||
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 10 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } |
| 12 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 12 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } |
diff --git a/examples/stm32l1/src/bin/usart.rs b/examples/stm32l1/src/bin/usart.rs new file mode 100644 index 000000000..dba79b8b4 --- /dev/null +++ b/examples/stm32l1/src/bin/usart.rs | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use cortex_m_rt::entry; | ||
| 5 | use defmt::*; | ||
| 6 | use embassy_stm32::usart::{Config, Uart}; | ||
| 7 | use embassy_stm32::{bind_interrupts, peripherals, usart}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | bind_interrupts!(struct Irqs { | ||
| 11 | USART2 => usart::InterruptHandler<peripherals::USART2>; | ||
| 12 | }); | ||
| 13 | |||
| 14 | #[entry] | ||
| 15 | fn main() -> ! { | ||
| 16 | info!("Hello World!"); | ||
| 17 | |||
| 18 | let p = embassy_stm32::init(Default::default()); | ||
| 19 | |||
| 20 | let config = Config::default(); | ||
| 21 | let mut usart = Uart::new_blocking(p.USART2, p.PA3, p.PA2, config).unwrap(); | ||
| 22 | let desired_baudrate = 9600; // Default is 115200 and 9600 is used as example | ||
| 23 | |||
| 24 | match usart.set_baudrate(desired_baudrate) { | ||
| 25 | Ok(_) => info!("Baud rate set to {}", desired_baudrate), | ||
| 26 | Err(err) => error!("Error setting baudrate to {}: {}", desired_baudrate, err), | ||
| 27 | } | ||
| 28 | |||
| 29 | unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); | ||
| 30 | info!("wrote Hello, starting echo"); | ||
| 31 | |||
| 32 | let mut buf = [0u8; 1]; | ||
| 33 | loop { | ||
| 34 | unwrap!(usart.blocking_read(&mut buf)); | ||
| 35 | unwrap!(usart.blocking_write(&buf)); | ||
| 36 | } | ||
| 37 | } | ||
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..3fde18ecd 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml | |||
| @@ -6,14 +6,14 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32l4s5vi to your chip name, if necessary. | 8 | # Change stm32l4s5vi to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4s5qi", "memory-x", "time-driver-any", "exti", "chrono"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4r5zi", "memory-x", "time-driver-any", "exti", "chrono"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.1", 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.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "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.3.2", 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.2.0", path = "../../embassy-embedded-hal" } |
| 14 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } |
| 15 | embassy-net-adin1110 = { version = "0.2.0", path = "../../embassy-net-adin1110" } | 15 | embassy-net-adin1110 = { version = "0.2.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.5.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"] } |
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/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs index be4270ada..4a7c01f9f 100644 --- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs | |||
| @@ -51,7 +51,7 @@ bind_interrupts!(struct Irqs { | |||
| 51 | // MAC-address used by the adin1110 | 51 | // MAC-address used by the adin1110 |
| 52 | const MAC: [u8; 6] = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]; | 52 | const MAC: [u8; 6] = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]; |
| 53 | // Static IP settings | 53 | // Static IP settings |
| 54 | const IP_ADDRESS: Ipv4Cidr = Ipv4Cidr::new(Ipv4Address([192, 168, 1, 5]), 24); | 54 | const IP_ADDRESS: Ipv4Cidr = Ipv4Cidr::new(Ipv4Address::new(192, 168, 1, 5), 24); |
| 55 | // Listen port for the webserver | 55 | // Listen port for the webserver |
| 56 | const HTTP_LISTEN_PORT: u16 = 80; | 56 | const HTTP_LISTEN_PORT: u16 = 80; |
| 57 | 57 | ||
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/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 16c184de2..2b8a2c064 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml | |||
| @@ -7,11 +7,11 @@ license = "MIT OR Apache-2.0" | |||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32l552ze to your chip name, if necessary. | 8 | # Change stm32l552ze to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "memory-x", "low-power"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "memory-x", "low-power"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.1", 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.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "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.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 13 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } |
| 14 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } | 14 | embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } |
| 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 16 | usbd-hid = "0.8.1" | 16 | usbd-hid = "0.8.1" |
| 17 | 17 | ||
diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml index 2e890cdb5..11953acfc 100644 --- a/examples/stm32u0/Cargo.toml +++ b/examples/stm32u0/Cargo.toml | |||
| @@ -7,8 +7,8 @@ license = "MIT OR Apache-2.0" | |||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32u083rc to your chip name, if necessary. | 8 | # Change stm32u083rc to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti", "chrono"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti", "chrono"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } | 13 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } |
| 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
diff --git a/examples/stm32u5/.cargo/config.toml b/examples/stm32u5/.cargo/config.toml index 36c5b63a6..bdbd86354 100644 --- a/examples/stm32u5/.cargo/config.toml +++ b/examples/stm32u5/.cargo/config.toml | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] |
| 2 | # replace STM32U585AIIx with your chip as listed in `probe-rs chip list` | 2 | # replace STM32U5G9ZJTxQ with your chip as listed in `probe-rs chip list` |
| 3 | runner = "probe-rs run --chip STM32U585AIIx" | 3 | runner = "probe-rs run --chip STM32U5G9ZJTxQ" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv8m.main-none-eabihf" | 6 | target = "thumbv8m.main-none-eabihf" |
diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 20d64c6f7..68a17ce43 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml | |||
| @@ -5,10 +5,10 @@ version = "0.1.0" | |||
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32u585ai to your chip name, if necessary. | 8 | # Change stm32u5g9zj to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "unstable-pac", "stm32u5g9zj", "time-driver-any", "memory-x" ] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.1", 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.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "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.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 13 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } |
| 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| @@ -21,6 +21,8 @@ cortex-m-rt = "0.7.0" | |||
| 21 | embedded-hal = "0.2.6" | 21 | embedded-hal = "0.2.6" |
| 22 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 22 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 23 | heapless = { version = "0.8", default-features = false } | 23 | heapless = { version = "0.8", default-features = false } |
| 24 | embedded-graphics = { version = "0.8.1" } | ||
| 25 | tinybmp = { version = "0.6.0" } | ||
| 24 | 26 | ||
| 25 | micromath = "2.0.0" | 27 | micromath = "2.0.0" |
| 26 | 28 | ||
diff --git a/examples/stm32u5/src/bin/ferris.bmp b/examples/stm32u5/src/bin/ferris.bmp new file mode 100644 index 000000000..7a222ab84 --- /dev/null +++ b/examples/stm32u5/src/bin/ferris.bmp | |||
| Binary files differ | |||
diff --git a/examples/stm32u5/src/bin/i2c.rs b/examples/stm32u5/src/bin/i2c.rs index 19a78eac9..d5f5d6f60 100644 --- a/examples/stm32u5/src/bin/i2c.rs +++ b/examples/stm32u5/src/bin/i2c.rs | |||
| @@ -13,7 +13,7 @@ const WHOAMI: u8 = 0x0F; | |||
| 13 | #[embassy_executor::main] | 13 | #[embassy_executor::main] |
| 14 | async fn main(_spawner: Spawner) { | 14 | async fn main(_spawner: Spawner) { |
| 15 | let p = embassy_stm32::init(Default::default()); | 15 | let p = embassy_stm32::init(Default::default()); |
| 16 | let mut i2c = I2c::new_blocking(p.I2C2, p.PH4, p.PH5, Hertz(100_000), Default::default()); | 16 | let mut i2c = I2c::new_blocking(p.I2C2, p.PF1, p.PF0, Hertz(100_000), Default::default()); |
| 17 | 17 | ||
| 18 | let mut data = [0u8; 1]; | 18 | let mut data = [0u8; 1]; |
| 19 | unwrap!(i2c.blocking_write_read(HTS221_ADDRESS, &[WHOAMI], &mut data)); | 19 | unwrap!(i2c.blocking_write_read(HTS221_ADDRESS, &[WHOAMI], &mut data)); |
diff --git a/examples/stm32u5/src/bin/ltdc.rs b/examples/stm32u5/src/bin/ltdc.rs new file mode 100644 index 000000000..bd59a9148 --- /dev/null +++ b/examples/stm32u5/src/bin/ltdc.rs | |||
| @@ -0,0 +1,461 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![macro_use] | ||
| 4 | #![allow(static_mut_refs)] | ||
| 5 | |||
| 6 | /// This example was derived from examples\stm32h735\src\bin\ltdc.rs | ||
| 7 | /// It demonstrates the LTDC lcd display peripheral and was tested on an STM32U5G9J-DK2 demo board (embassy-stm32 feature "stm32u5g9zj" and probe-rs chip "STM32U5G9ZJTxQ") | ||
| 8 | /// | ||
| 9 | use bouncy_box::BouncyBox; | ||
| 10 | use defmt::{info, unwrap}; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 13 | use embassy_stm32::ltdc::{self, Ltdc, LtdcConfiguration, LtdcLayer, LtdcLayerConfig, PolarityActive, PolarityEdge}; | ||
| 14 | use embassy_stm32::{bind_interrupts, peripherals}; | ||
| 15 | use embassy_time::{Duration, Timer}; | ||
| 16 | use embedded_graphics::draw_target::DrawTarget; | ||
| 17 | use embedded_graphics::geometry::{OriginDimensions, Point, Size}; | ||
| 18 | use embedded_graphics::image::Image; | ||
| 19 | use embedded_graphics::pixelcolor::raw::RawU24; | ||
| 20 | use embedded_graphics::pixelcolor::Rgb888; | ||
| 21 | use embedded_graphics::prelude::*; | ||
| 22 | use embedded_graphics::primitives::Rectangle; | ||
| 23 | use embedded_graphics::Pixel; | ||
| 24 | use heapless::{Entry, FnvIndexMap}; | ||
| 25 | use tinybmp::Bmp; | ||
| 26 | use {defmt_rtt as _, panic_probe as _}; | ||
| 27 | |||
| 28 | const DISPLAY_WIDTH: usize = 800; | ||
| 29 | const DISPLAY_HEIGHT: usize = 480; | ||
| 30 | const MY_TASK_POOL_SIZE: usize = 2; | ||
| 31 | |||
| 32 | // the following two display buffers consume 261120 bytes that just about fits into axis ram found on the mcu | ||
| 33 | pub static mut FB1: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT]; | ||
| 34 | pub static mut FB2: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT]; | ||
| 35 | |||
| 36 | bind_interrupts!(struct Irqs { | ||
| 37 | LTDC => ltdc::InterruptHandler<peripherals::LTDC>; | ||
| 38 | }); | ||
| 39 | |||
| 40 | const NUM_COLORS: usize = 256; | ||
| 41 | |||
| 42 | #[embassy_executor::main] | ||
| 43 | async fn main(spawner: Spawner) { | ||
| 44 | let p = rcc_setup::stm32u5g9zj_init(); | ||
| 45 | |||
| 46 | // enable ICACHE | ||
| 47 | embassy_stm32::pac::ICACHE.cr().write(|w| { | ||
| 48 | w.set_en(true); | ||
| 49 | }); | ||
| 50 | |||
| 51 | // blink the led on another task | ||
| 52 | let led = Output::new(p.PD2, Level::High, Speed::Low); | ||
| 53 | unwrap!(spawner.spawn(led_task(led))); | ||
| 54 | |||
| 55 | // numbers from STM32U5G9J-DK2.ioc | ||
| 56 | const RK050HR18H_HSYNC: u16 = 5; // Horizontal synchronization | ||
| 57 | const RK050HR18H_HBP: u16 = 8; // Horizontal back porch | ||
| 58 | const RK050HR18H_HFP: u16 = 8; // Horizontal front porch | ||
| 59 | const RK050HR18H_VSYNC: u16 = 5; // Vertical synchronization | ||
| 60 | const RK050HR18H_VBP: u16 = 8; // Vertical back porch | ||
| 61 | const RK050HR18H_VFP: u16 = 8; // Vertical front porch | ||
| 62 | |||
| 63 | // NOTE: all polarities have to be reversed with respect to the STM32U5G9J-DK2 CubeMX parametrization | ||
| 64 | let ltdc_config = LtdcConfiguration { | ||
| 65 | active_width: DISPLAY_WIDTH as _, | ||
| 66 | active_height: DISPLAY_HEIGHT as _, | ||
| 67 | h_back_porch: RK050HR18H_HBP, | ||
| 68 | h_front_porch: RK050HR18H_HFP, | ||
| 69 | v_back_porch: RK050HR18H_VBP, | ||
| 70 | v_front_porch: RK050HR18H_VFP, | ||
| 71 | h_sync: RK050HR18H_HSYNC, | ||
| 72 | v_sync: RK050HR18H_VSYNC, | ||
| 73 | h_sync_polarity: PolarityActive::ActiveHigh, | ||
| 74 | v_sync_polarity: PolarityActive::ActiveHigh, | ||
| 75 | data_enable_polarity: PolarityActive::ActiveHigh, | ||
| 76 | pixel_clock_polarity: PolarityEdge::RisingEdge, | ||
| 77 | }; | ||
| 78 | |||
| 79 | info!("init ltdc"); | ||
| 80 | let mut ltdc_de = Output::new(p.PD6, Level::Low, Speed::High); | ||
| 81 | let mut ltdc_disp_ctrl = Output::new(p.PE4, Level::Low, Speed::High); | ||
| 82 | let mut ltdc_bl_ctrl = Output::new(p.PE6, Level::Low, Speed::High); | ||
| 83 | let mut ltdc = Ltdc::new_with_pins( | ||
| 84 | p.LTDC, // PERIPHERAL | ||
| 85 | Irqs, // IRQS | ||
| 86 | p.PD3, // CLK | ||
| 87 | p.PE0, // HSYNC | ||
| 88 | p.PD13, // VSYNC | ||
| 89 | p.PB9, // B0 | ||
| 90 | p.PB2, // B1 | ||
| 91 | p.PD14, // B2 | ||
| 92 | p.PD15, // B3 | ||
| 93 | p.PD0, // B4 | ||
| 94 | p.PD1, // B5 | ||
| 95 | p.PE7, // B6 | ||
| 96 | p.PE8, // B7 | ||
| 97 | p.PC8, // G0 | ||
| 98 | p.PC9, // G1 | ||
| 99 | p.PE9, // G2 | ||
| 100 | p.PE10, // G3 | ||
| 101 | p.PE11, // G4 | ||
| 102 | p.PE12, // G5 | ||
| 103 | p.PE13, // G6 | ||
| 104 | p.PE14, // G7 | ||
| 105 | p.PC6, // R0 | ||
| 106 | p.PC7, // R1 | ||
| 107 | p.PE15, // R2 | ||
| 108 | p.PD8, // R3 | ||
| 109 | p.PD9, // R4 | ||
| 110 | p.PD10, // R5 | ||
| 111 | p.PD11, // R6 | ||
| 112 | p.PD12, // R7 | ||
| 113 | ); | ||
| 114 | ltdc.init(<dc_config); | ||
| 115 | ltdc_de.set_low(); | ||
| 116 | ltdc_bl_ctrl.set_high(); | ||
| 117 | ltdc_disp_ctrl.set_high(); | ||
| 118 | |||
| 119 | // we only need to draw on one layer for this example (not to be confused with the double buffer) | ||
| 120 | info!("enable bottom layer"); | ||
| 121 | let layer_config = LtdcLayerConfig { | ||
| 122 | pixel_format: ltdc::PixelFormat::L8, // 1 byte per pixel | ||
| 123 | layer: LtdcLayer::Layer1, | ||
| 124 | window_x0: 0, | ||
| 125 | window_x1: DISPLAY_WIDTH as _, | ||
| 126 | window_y0: 0, | ||
| 127 | window_y1: DISPLAY_HEIGHT as _, | ||
| 128 | }; | ||
| 129 | |||
| 130 | let ferris_bmp: Bmp<Rgb888> = Bmp::from_slice(include_bytes!("./ferris.bmp")).unwrap(); | ||
| 131 | let color_map = build_color_lookup_map(&ferris_bmp); | ||
| 132 | let clut = build_clut(&color_map); | ||
| 133 | |||
| 134 | // enable the bottom layer with a 256 color lookup table | ||
| 135 | ltdc.init_layer(&layer_config, Some(&clut)); | ||
| 136 | |||
| 137 | // Safety: the DoubleBuffer controls access to the statically allocated frame buffers | ||
| 138 | // and it is the only thing that mutates their content | ||
| 139 | let mut double_buffer = DoubleBuffer::new( | ||
| 140 | unsafe { FB1.as_mut() }, | ||
| 141 | unsafe { FB2.as_mut() }, | ||
| 142 | layer_config, | ||
| 143 | color_map, | ||
| 144 | ); | ||
| 145 | |||
| 146 | // this allows us to perform some simple animation for every frame | ||
| 147 | let mut bouncy_box = BouncyBox::new( | ||
| 148 | ferris_bmp.bounding_box(), | ||
| 149 | Rectangle::new(Point::zero(), Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32)), | ||
| 150 | 2, | ||
| 151 | ); | ||
| 152 | |||
| 153 | loop { | ||
| 154 | // cpu intensive drawing to the buffer that is NOT currently being copied to the LCD screen | ||
| 155 | double_buffer.clear(); | ||
| 156 | let position = bouncy_box.next_point(); | ||
| 157 | let ferris = Image::new(&ferris_bmp, position); | ||
| 158 | unwrap!(ferris.draw(&mut double_buffer)); | ||
| 159 | |||
| 160 | // perform async dma data transfer to the lcd screen | ||
| 161 | unwrap!(double_buffer.swap(&mut ltdc).await); | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | /// builds the color look-up table from all unique colors found in the bitmap. This should be a 256 color indexed bitmap to work. | ||
| 166 | fn build_color_lookup_map(bmp: &Bmp<Rgb888>) -> FnvIndexMap<u32, u8, NUM_COLORS> { | ||
| 167 | let mut color_map: FnvIndexMap<u32, u8, NUM_COLORS> = heapless::FnvIndexMap::new(); | ||
| 168 | let mut counter: u8 = 0; | ||
| 169 | |||
| 170 | // add black to position 0 | ||
| 171 | color_map.insert(Rgb888::new(0, 0, 0).into_storage(), counter).unwrap(); | ||
| 172 | counter += 1; | ||
| 173 | |||
| 174 | for Pixel(_point, color) in bmp.pixels() { | ||
| 175 | let raw = color.into_storage(); | ||
| 176 | if let Entry::Vacant(v) = color_map.entry(raw) { | ||
| 177 | v.insert(counter).expect("more than 256 colors detected"); | ||
| 178 | counter += 1; | ||
| 179 | } | ||
| 180 | } | ||
| 181 | color_map | ||
| 182 | } | ||
| 183 | |||
| 184 | /// builds the color look-up table from the color map provided | ||
| 185 | fn build_clut(color_map: &FnvIndexMap<u32, u8, NUM_COLORS>) -> [ltdc::RgbColor; NUM_COLORS] { | ||
| 186 | let mut clut = [ltdc::RgbColor::default(); NUM_COLORS]; | ||
| 187 | for (color, index) in color_map.iter() { | ||
| 188 | let color = Rgb888::from(RawU24::new(*color)); | ||
| 189 | clut[*index as usize] = ltdc::RgbColor { | ||
| 190 | red: color.r(), | ||
| 191 | green: color.g(), | ||
| 192 | blue: color.b(), | ||
| 193 | }; | ||
| 194 | } | ||
| 195 | |||
| 196 | clut | ||
| 197 | } | ||
| 198 | |||
| 199 | #[embassy_executor::task(pool_size = MY_TASK_POOL_SIZE)] | ||
| 200 | async fn led_task(mut led: Output<'static>) { | ||
| 201 | let mut counter = 0; | ||
| 202 | loop { | ||
| 203 | info!("blink: {}", counter); | ||
| 204 | counter += 1; | ||
| 205 | |||
| 206 | // on | ||
| 207 | led.set_low(); | ||
| 208 | Timer::after(Duration::from_millis(50)).await; | ||
| 209 | |||
| 210 | // off | ||
| 211 | led.set_high(); | ||
| 212 | Timer::after(Duration::from_millis(450)).await; | ||
| 213 | } | ||
| 214 | } | ||
| 215 | |||
| 216 | pub type TargetPixelType = u8; | ||
| 217 | |||
| 218 | // A simple double buffer | ||
| 219 | pub struct DoubleBuffer { | ||
| 220 | buf0: &'static mut [TargetPixelType], | ||
| 221 | buf1: &'static mut [TargetPixelType], | ||
| 222 | is_buf0: bool, | ||
| 223 | layer_config: LtdcLayerConfig, | ||
| 224 | color_map: FnvIndexMap<u32, u8, NUM_COLORS>, | ||
| 225 | } | ||
| 226 | |||
| 227 | impl DoubleBuffer { | ||
| 228 | pub fn new( | ||
| 229 | buf0: &'static mut [TargetPixelType], | ||
| 230 | buf1: &'static mut [TargetPixelType], | ||
| 231 | layer_config: LtdcLayerConfig, | ||
| 232 | color_map: FnvIndexMap<u32, u8, NUM_COLORS>, | ||
| 233 | ) -> Self { | ||
| 234 | Self { | ||
| 235 | buf0, | ||
| 236 | buf1, | ||
| 237 | is_buf0: true, | ||
| 238 | layer_config, | ||
| 239 | color_map, | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | pub fn current(&mut self) -> (&FnvIndexMap<u32, u8, NUM_COLORS>, &mut [TargetPixelType]) { | ||
| 244 | if self.is_buf0 { | ||
| 245 | (&self.color_map, self.buf0) | ||
| 246 | } else { | ||
| 247 | (&self.color_map, self.buf1) | ||
| 248 | } | ||
| 249 | } | ||
| 250 | |||
| 251 | pub async fn swap<T: ltdc::Instance>(&mut self, ltdc: &mut Ltdc<'_, T>) -> Result<(), ltdc::Error> { | ||
| 252 | let (_, buf) = self.current(); | ||
| 253 | let frame_buffer = buf.as_ptr(); | ||
| 254 | self.is_buf0 = !self.is_buf0; | ||
| 255 | ltdc.set_buffer(self.layer_config.layer, frame_buffer as *const _).await | ||
| 256 | } | ||
| 257 | |||
| 258 | /// Clears the buffer | ||
| 259 | pub fn clear(&mut self) { | ||
| 260 | let (color_map, buf) = self.current(); | ||
| 261 | let black = Rgb888::new(0, 0, 0).into_storage(); | ||
| 262 | let color_index = color_map.get(&black).expect("no black found in the color map"); | ||
| 263 | |||
| 264 | for a in buf.iter_mut() { | ||
| 265 | *a = *color_index; // solid black | ||
| 266 | } | ||
| 267 | } | ||
| 268 | } | ||
| 269 | |||
| 270 | // Implement DrawTarget for | ||
| 271 | impl DrawTarget for DoubleBuffer { | ||
| 272 | type Color = Rgb888; | ||
| 273 | type Error = (); | ||
| 274 | |||
| 275 | /// Draw a pixel | ||
| 276 | fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error> | ||
| 277 | where | ||
| 278 | I: IntoIterator<Item = Pixel<Self::Color>>, | ||
| 279 | { | ||
| 280 | let size = self.size(); | ||
| 281 | let width = size.width as i32; | ||
| 282 | let height = size.height as i32; | ||
| 283 | let (color_map, buf) = self.current(); | ||
| 284 | |||
| 285 | for pixel in pixels { | ||
| 286 | let Pixel(point, color) = pixel; | ||
| 287 | |||
| 288 | if point.x >= 0 && point.y >= 0 && point.x < width && point.y < height { | ||
| 289 | let index = point.y * width + point.x; | ||
| 290 | let raw_color = color.into_storage(); | ||
| 291 | |||
| 292 | match color_map.get(&raw_color) { | ||
| 293 | Some(x) => { | ||
| 294 | buf[index as usize] = *x; | ||
| 295 | } | ||
| 296 | None => panic!("color not found in color map: {}", raw_color), | ||
| 297 | }; | ||
| 298 | } else { | ||
| 299 | // Ignore invalid points | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | Ok(()) | ||
| 304 | } | ||
| 305 | } | ||
| 306 | |||
| 307 | impl OriginDimensions for DoubleBuffer { | ||
| 308 | /// Return the size of the display | ||
| 309 | fn size(&self) -> Size { | ||
| 310 | Size::new( | ||
| 311 | (self.layer_config.window_x1 - self.layer_config.window_x0) as _, | ||
| 312 | (self.layer_config.window_y1 - self.layer_config.window_y0) as _, | ||
| 313 | ) | ||
| 314 | } | ||
| 315 | } | ||
| 316 | |||
| 317 | mod rcc_setup { | ||
| 318 | |||
| 319 | use embassy_stm32::time::Hertz; | ||
| 320 | use embassy_stm32::{rcc, Config, Peripherals}; | ||
| 321 | |||
| 322 | /// Sets up clocks for the stm32u5g9zj mcu | ||
| 323 | /// change this if you plan to use a different microcontroller | ||
| 324 | pub fn stm32u5g9zj_init() -> Peripherals { | ||
| 325 | // setup power and clocks for an STM32U5G9J-DK2 run from an external 16 Mhz external oscillator | ||
| 326 | let mut config = Config::default(); | ||
| 327 | config.rcc.hse = Some(rcc::Hse { | ||
| 328 | freq: Hertz(16_000_000), | ||
| 329 | mode: rcc::HseMode::Oscillator, | ||
| 330 | }); | ||
| 331 | config.rcc.pll1 = Some(rcc::Pll { | ||
| 332 | source: rcc::PllSource::HSE, | ||
| 333 | prediv: rcc::PllPreDiv::DIV1, | ||
| 334 | mul: rcc::PllMul::MUL10, | ||
| 335 | divp: None, | ||
| 336 | divq: None, | ||
| 337 | divr: Some(rcc::PllDiv::DIV1), | ||
| 338 | }); | ||
| 339 | config.rcc.sys = rcc::Sysclk::PLL1_R; // 160 Mhz | ||
| 340 | config.rcc.pll3 = Some(rcc::Pll { | ||
| 341 | source: rcc::PllSource::HSE, | ||
| 342 | prediv: rcc::PllPreDiv::DIV4, // PLL_M | ||
| 343 | mul: rcc::PllMul::MUL125, // PLL_N | ||
| 344 | divp: None, | ||
| 345 | divq: None, | ||
| 346 | divr: Some(rcc::PllDiv::DIV20), | ||
| 347 | }); | ||
| 348 | config.rcc.mux.ltdcsel = rcc::mux::Ltdcsel::PLL3_R; // 25 MHz | ||
| 349 | embassy_stm32::init(config) | ||
| 350 | } | ||
| 351 | } | ||
| 352 | |||
| 353 | mod bouncy_box { | ||
| 354 | use embedded_graphics::geometry::Point; | ||
| 355 | use embedded_graphics::primitives::Rectangle; | ||
| 356 | |||
| 357 | enum Direction { | ||
| 358 | DownLeft, | ||
| 359 | DownRight, | ||
| 360 | UpLeft, | ||
| 361 | UpRight, | ||
| 362 | } | ||
| 363 | |||
| 364 | pub struct BouncyBox { | ||
| 365 | direction: Direction, | ||
| 366 | child_rect: Rectangle, | ||
| 367 | parent_rect: Rectangle, | ||
| 368 | current_point: Point, | ||
| 369 | move_by: usize, | ||
| 370 | } | ||
| 371 | |||
| 372 | // This calculates the coordinates of a chile rectangle bounced around inside a parent bounded box | ||
| 373 | impl BouncyBox { | ||
| 374 | pub fn new(child_rect: Rectangle, parent_rect: Rectangle, move_by: usize) -> Self { | ||
| 375 | let center_box = parent_rect.center(); | ||
| 376 | let center_img = child_rect.center(); | ||
| 377 | let current_point = Point::new(center_box.x - center_img.x / 2, center_box.y - center_img.y / 2); | ||
| 378 | Self { | ||
| 379 | direction: Direction::DownRight, | ||
| 380 | child_rect, | ||
| 381 | parent_rect, | ||
| 382 | current_point, | ||
| 383 | move_by, | ||
| 384 | } | ||
| 385 | } | ||
| 386 | |||
| 387 | pub fn next_point(&mut self) -> Point { | ||
| 388 | let direction = &self.direction; | ||
| 389 | let img_height = self.child_rect.size.height as i32; | ||
| 390 | let box_height = self.parent_rect.size.height as i32; | ||
| 391 | let img_width = self.child_rect.size.width as i32; | ||
| 392 | let box_width = self.parent_rect.size.width as i32; | ||
| 393 | let move_by = self.move_by as i32; | ||
| 394 | |||
| 395 | match direction { | ||
| 396 | Direction::DownLeft => { | ||
| 397 | self.current_point.x -= move_by; | ||
| 398 | self.current_point.y += move_by; | ||
| 399 | |||
| 400 | let x_out_of_bounds = self.current_point.x < 0; | ||
| 401 | let y_out_of_bounds = (self.current_point.y + img_height) > box_height; | ||
| 402 | |||
| 403 | if x_out_of_bounds && y_out_of_bounds { | ||
| 404 | self.direction = Direction::UpRight | ||
| 405 | } else if x_out_of_bounds && !y_out_of_bounds { | ||
| 406 | self.direction = Direction::DownRight | ||
| 407 | } else if !x_out_of_bounds && y_out_of_bounds { | ||
| 408 | self.direction = Direction::UpLeft | ||
| 409 | } | ||
| 410 | } | ||
| 411 | Direction::DownRight => { | ||
| 412 | self.current_point.x += move_by; | ||
| 413 | self.current_point.y += move_by; | ||
| 414 | |||
| 415 | let x_out_of_bounds = (self.current_point.x + img_width) > box_width; | ||
| 416 | let y_out_of_bounds = (self.current_point.y + img_height) > box_height; | ||
| 417 | |||
| 418 | if x_out_of_bounds && y_out_of_bounds { | ||
| 419 | self.direction = Direction::UpLeft | ||
| 420 | } else if x_out_of_bounds && !y_out_of_bounds { | ||
| 421 | self.direction = Direction::DownLeft | ||
| 422 | } else if !x_out_of_bounds && y_out_of_bounds { | ||
| 423 | self.direction = Direction::UpRight | ||
| 424 | } | ||
| 425 | } | ||
| 426 | Direction::UpLeft => { | ||
| 427 | self.current_point.x -= move_by; | ||
| 428 | self.current_point.y -= move_by; | ||
| 429 | |||
| 430 | let x_out_of_bounds = self.current_point.x < 0; | ||
| 431 | let y_out_of_bounds = self.current_point.y < 0; | ||
| 432 | |||
| 433 | if x_out_of_bounds && y_out_of_bounds { | ||
| 434 | self.direction = Direction::DownRight | ||
| 435 | } else if x_out_of_bounds && !y_out_of_bounds { | ||
| 436 | self.direction = Direction::UpRight | ||
| 437 | } else if !x_out_of_bounds && y_out_of_bounds { | ||
| 438 | self.direction = Direction::DownLeft | ||
| 439 | } | ||
| 440 | } | ||
| 441 | Direction::UpRight => { | ||
| 442 | self.current_point.x += move_by; | ||
| 443 | self.current_point.y -= move_by; | ||
| 444 | |||
| 445 | let x_out_of_bounds = (self.current_point.x + img_width) > box_width; | ||
| 446 | let y_out_of_bounds = self.current_point.y < 0; | ||
| 447 | |||
| 448 | if x_out_of_bounds && y_out_of_bounds { | ||
| 449 | self.direction = Direction::DownLeft | ||
| 450 | } else if x_out_of_bounds && !y_out_of_bounds { | ||
| 451 | self.direction = Direction::UpLeft | ||
| 452 | } else if !x_out_of_bounds && y_out_of_bounds { | ||
| 453 | self.direction = Direction::DownRight | ||
| 454 | } | ||
| 455 | } | ||
| 456 | } | ||
| 457 | |||
| 458 | self.current_point | ||
| 459 | } | ||
| 460 | } | ||
| 461 | } | ||
diff --git a/examples/stm32u5/src/bin/tsc.rs b/examples/stm32u5/src/bin/tsc.rs index eb15d275a..a85acc4c7 100644 --- a/examples/stm32u5/src/bin/tsc.rs +++ b/examples/stm32u5/src/bin/tsc.rs | |||
| @@ -2,8 +2,8 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_stm32::bind_interrupts; | ||
| 6 | use embassy_stm32::tsc::{self, *}; | 5 | use embassy_stm32::tsc::{self, *}; |
| 6 | use embassy_stm32::{bind_interrupts, peripherals}; | ||
| 7 | use embassy_time::Timer; | 7 | use embassy_time::Timer; |
| 8 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 9 | ||
| @@ -33,63 +33,52 @@ async fn main(_spawner: embassy_executor::Spawner) { | |||
| 33 | synchro_pin_polarity: false, | 33 | synchro_pin_polarity: false, |
| 34 | acquisition_mode: false, | 34 | acquisition_mode: false, |
| 35 | max_count_interrupt: false, | 35 | max_count_interrupt: false, |
| 36 | channel_ios: TscIOPin::Group2Io2 | TscIOPin::Group7Io3, | ||
| 37 | shield_ios: TscIOPin::Group1Io3.into(), | ||
| 38 | sampling_ios: TscIOPin::Group1Io2 | TscIOPin::Group2Io1 | TscIOPin::Group7Io2, | ||
| 39 | }; | 36 | }; |
| 40 | 37 | ||
| 41 | let mut g1: PinGroup<embassy_stm32::peripherals::TSC, G1> = PinGroup::new(); | 38 | let mut g1: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default(); |
| 42 | g1.set_io2(context.PB13, PinType::Sample); | 39 | g1.set_io2::<tsc::pin_roles::Sample>(context.PB13); |
| 43 | g1.set_io3(context.PB14, PinType::Shield); | 40 | g1.set_io3::<tsc::pin_roles::Shield>(context.PB14); |
| 44 | 41 | ||
| 45 | let mut g2: PinGroup<embassy_stm32::peripherals::TSC, G2> = PinGroup::new(); | 42 | let mut g2: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default(); |
| 46 | g2.set_io1(context.PB4, PinType::Sample); | 43 | g2.set_io1::<tsc::pin_roles::Sample>(context.PB4); |
| 47 | g2.set_io2(context.PB5, PinType::Channel); | 44 | let sensor0 = g2.set_io2(context.PB5); |
| 48 | 45 | ||
| 49 | let mut g7: PinGroup<embassy_stm32::peripherals::TSC, G7> = PinGroup::new(); | 46 | let mut g7: PinGroupWithRoles<peripherals::TSC, G7> = PinGroupWithRoles::default(); |
| 50 | g7.set_io2(context.PE3, PinType::Sample); | 47 | g7.set_io2::<tsc::pin_roles::Sample>(context.PE3); |
| 51 | g7.set_io3(context.PE4, PinType::Channel); | 48 | let sensor1 = g7.set_io3(context.PE4); |
| 52 | 49 | ||
| 53 | let mut touch_controller = tsc::Tsc::new_async( | 50 | let pin_groups: PinGroups<peripherals::TSC> = PinGroups { |
| 54 | context.TSC, | 51 | g1: Some(g1.pin_group), |
| 55 | Some(g1), | 52 | g2: Some(g2.pin_group), |
| 56 | Some(g2), | 53 | g7: Some(g7.pin_group), |
| 57 | None, | 54 | ..Default::default() |
| 58 | None, | 55 | }; |
| 59 | None, | 56 | |
| 60 | None, | 57 | let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, config, Irqs).unwrap(); |
| 61 | Some(g7), | ||
| 62 | None, | ||
| 63 | config, | ||
| 64 | Irqs, | ||
| 65 | ); | ||
| 66 | 58 | ||
| 67 | touch_controller.discharge_io(true); | 59 | let acquisition_bank = touch_controller.create_acquisition_bank(AcquisitionBankPins { |
| 68 | Timer::after_millis(1).await; | 60 | g2_pin: Some(sensor0), |
| 61 | g7_pin: Some(sensor1), | ||
| 62 | ..Default::default() | ||
| 63 | }); | ||
| 69 | 64 | ||
| 70 | touch_controller.start(); | 65 | touch_controller.set_active_channels_bank(&acquisition_bank); |
| 71 | 66 | ||
| 72 | let mut group_two_val = 0; | ||
| 73 | let mut group_seven_val = 0; | ||
| 74 | info!("Starting touch_controller interface"); | 67 | info!("Starting touch_controller interface"); |
| 75 | loop { | 68 | loop { |
| 69 | touch_controller.start(); | ||
| 76 | touch_controller.pend_for_acquisition().await; | 70 | touch_controller.pend_for_acquisition().await; |
| 77 | touch_controller.discharge_io(true); | 71 | touch_controller.discharge_io(true); |
| 78 | Timer::after_millis(1).await; | 72 | Timer::after_millis(1).await; |
| 79 | 73 | ||
| 80 | if touch_controller.group_get_status(Group::Two) == GroupStatus::Complete { | 74 | let status = touch_controller.get_acquisition_bank_status(&acquisition_bank); |
| 81 | group_two_val = touch_controller.group_get_value(Group::Two); | ||
| 82 | } | ||
| 83 | 75 | ||
| 84 | if touch_controller.group_get_status(Group::Seven) == GroupStatus::Complete { | 76 | if status.all_complete() { |
| 85 | group_seven_val = touch_controller.group_get_value(Group::Seven); | 77 | let read_values = touch_controller.get_acquisition_bank_values(&acquisition_bank); |
| 78 | let group2_reading = read_values.get_group_reading(Group::Two).unwrap(); | ||
| 79 | let group7_reading = read_values.get_group_reading(Group::Seven).unwrap(); | ||
| 80 | info!("group 2 value: {}", group2_reading.sensor_value); | ||
| 81 | info!("group 7 value: {}", group7_reading.sensor_value); | ||
| 86 | } | 82 | } |
| 87 | |||
| 88 | info!( | ||
| 89 | "Group Two value: {}, Group Seven value: {},", | ||
| 90 | group_two_val, group_seven_val | ||
| 91 | ); | ||
| 92 | |||
| 93 | touch_controller.start(); | ||
| 94 | } | 83 | } |
| 95 | } | 84 | } |
diff --git a/examples/stm32u5/src/bin/usb_hs_serial.rs b/examples/stm32u5/src/bin/usb_hs_serial.rs new file mode 100644 index 000000000..5549e2cbb --- /dev/null +++ b/examples/stm32u5/src/bin/usb_hs_serial.rs | |||
| @@ -0,0 +1,129 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::{panic, *}; | ||
| 5 | use defmt_rtt as _; // global logger | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_futures::join::join; | ||
| 8 | use embassy_stm32::usb::{Driver, Instance}; | ||
| 9 | use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; | ||
| 10 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 11 | use embassy_usb::driver::EndpointError; | ||
| 12 | use embassy_usb::Builder; | ||
| 13 | use panic_probe as _; | ||
| 14 | |||
| 15 | bind_interrupts!(struct Irqs { | ||
| 16 | OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>; | ||
| 17 | }); | ||
| 18 | |||
| 19 | #[embassy_executor::main] | ||
| 20 | async fn main(_spawner: Spawner) { | ||
| 21 | info!("Hello World!"); | ||
| 22 | |||
| 23 | let mut config = Config::default(); | ||
| 24 | { | ||
| 25 | use embassy_stm32::rcc::*; | ||
| 26 | use embassy_stm32::time::Hertz; | ||
| 27 | config.rcc.hse = Some(Hse { | ||
| 28 | freq: Hertz(16_000_000), | ||
| 29 | mode: HseMode::Oscillator, | ||
| 30 | }); | ||
| 31 | config.rcc.pll1 = Some(Pll { | ||
| 32 | source: PllSource::HSE, | ||
| 33 | prediv: PllPreDiv::DIV2, // HSE / 2 = 8MHz | ||
| 34 | mul: PllMul::MUL60, // 8MHz * 60 = 480MHz | ||
| 35 | divr: Some(PllDiv::DIV3), // 480MHz / 3 = 160MHz (sys_ck) | ||
| 36 | divq: Some(PllDiv::DIV10), // 480MHz / 10 = 48MHz (USB) | ||
| 37 | divp: Some(PllDiv::DIV15), // 480MHz / 15 = 32MHz (USBOTG) | ||
| 38 | }); | ||
| 39 | config.rcc.mux.otghssel = mux::Otghssel::PLL1_P; | ||
| 40 | config.rcc.voltage_range = VoltageScale::RANGE1; | ||
| 41 | config.rcc.sys = Sysclk::PLL1_R; | ||
| 42 | } | ||
| 43 | |||
| 44 | let p = embassy_stm32::init(config); | ||
| 45 | |||
| 46 | // Create the driver, from the HAL. | ||
| 47 | let mut ep_out_buffer = [0u8; 256]; | ||
| 48 | let mut config = embassy_stm32::usb::Config::default(); | ||
| 49 | // Do not enable vbus_detection. This is a safe default that works in all boards. | ||
| 50 | // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need | ||
| 51 | // to enable vbus_detection to comply with the USB spec. If you enable it, the board | ||
| 52 | // has to support it or USB won't work at all. See docs on `vbus_detection` for details. | ||
| 53 | config.vbus_detection = false; | ||
| 54 | let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); | ||
| 55 | |||
| 56 | // Create embassy-usb Config | ||
| 57 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 58 | config.manufacturer = Some("Embassy"); | ||
| 59 | config.product = Some("USB-serial example"); | ||
| 60 | config.serial_number = Some("12345678"); | ||
| 61 | |||
| 62 | // Required for windows compatibility. | ||
| 63 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 64 | config.device_class = 0xEF; | ||
| 65 | config.device_sub_class = 0x02; | ||
| 66 | config.device_protocol = 0x01; | ||
| 67 | config.composite_with_iads = true; | ||
| 68 | |||
| 69 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 70 | // It needs some buffers for building the descriptors. | ||
| 71 | let mut config_descriptor = [0; 256]; | ||
| 72 | let mut bos_descriptor = [0; 256]; | ||
| 73 | let mut control_buf = [0; 64]; | ||
| 74 | |||
| 75 | let mut state = State::new(); | ||
| 76 | |||
| 77 | let mut builder = Builder::new( | ||
| 78 | driver, | ||
| 79 | config, | ||
| 80 | &mut config_descriptor, | ||
| 81 | &mut bos_descriptor, | ||
| 82 | &mut [], // no msos descriptors | ||
| 83 | &mut control_buf, | ||
| 84 | ); | ||
| 85 | |||
| 86 | // Create classes on the builder. | ||
| 87 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 88 | |||
| 89 | // Build the builder. | ||
| 90 | let mut usb = builder.build(); | ||
| 91 | |||
| 92 | // Run the USB device. | ||
| 93 | let usb_fut = usb.run(); | ||
| 94 | |||
| 95 | // Do stuff with the class! | ||
| 96 | let echo_fut = async { | ||
| 97 | loop { | ||
| 98 | class.wait_connection().await; | ||
| 99 | info!("Connected"); | ||
| 100 | let _ = echo(&mut class).await; | ||
| 101 | info!("Disconnected"); | ||
| 102 | } | ||
| 103 | }; | ||
| 104 | |||
| 105 | // Run everything concurrently. | ||
| 106 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 107 | join(usb_fut, echo_fut).await; | ||
| 108 | } | ||
| 109 | |||
| 110 | struct Disconnected {} | ||
| 111 | |||
| 112 | impl From<EndpointError> for Disconnected { | ||
| 113 | fn from(val: EndpointError) -> Self { | ||
| 114 | match val { | ||
| 115 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 116 | EndpointError::Disabled => Disconnected {}, | ||
| 117 | } | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
| 122 | let mut buf = [0; 64]; | ||
| 123 | loop { | ||
| 124 | let n = class.read_packet(&mut buf).await?; | ||
| 125 | let data = &buf[..n]; | ||
| 126 | info!("data: {:x}", data); | ||
| 127 | class.write_packet(data).await?; | ||
| 128 | } | ||
| 129 | } | ||
diff --git a/examples/stm32u5/src/bin/usb_serial.rs b/examples/stm32u5/src/bin/usb_serial.rs index 4d56395da..4bb1a6079 100644 --- a/examples/stm32u5/src/bin/usb_serial.rs +++ b/examples/stm32u5/src/bin/usb_serial.rs | |||
| @@ -13,7 +13,7 @@ use embassy_usb::Builder; | |||
| 13 | use panic_probe as _; | 13 | use panic_probe as _; |
| 14 | 14 | ||
| 15 | bind_interrupts!(struct Irqs { | 15 | bind_interrupts!(struct Irqs { |
| 16 | OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>; | 16 | OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>; |
| 17 | }); | 17 | }); |
| 18 | 18 | ||
| 19 | #[embassy_executor::main] | 19 | #[embassy_executor::main] |
| @@ -48,7 +48,7 @@ async fn main(_spawner: Spawner) { | |||
| 48 | // to enable vbus_detection to comply with the USB spec. If you enable it, the board | 48 | // to enable vbus_detection to comply with the USB spec. If you enable it, the board |
| 49 | // has to support it or USB won't work at all. See docs on `vbus_detection` for details. | 49 | // has to support it or USB won't work at all. See docs on `vbus_detection` for details. |
| 50 | config.vbus_detection = false; | 50 | config.vbus_detection = false; |
| 51 | let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); | 51 | let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); |
| 52 | 52 | ||
| 53 | // Create embassy-usb Config | 53 | // Create embassy-usb Config |
| 54 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | 54 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); |
diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 1e1a0efe2..ecc72397b 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml | |||
| @@ -8,10 +8,10 @@ license = "MIT OR Apache-2.0" | |||
| 8 | # Change stm32wb55rg to your chip name in both dependencies, if necessary. | 8 | # Change stm32wb55rg to your chip name in both dependencies, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] } |
| 10 | embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } | 10 | embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } |
| 11 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 11 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 12 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 12 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] } |
| 13 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 13 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 14 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true } | 14 | embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true } |
| 15 | 15 | ||
| 16 | defmt = "0.3" | 16 | defmt = "0.3" |
| 17 | defmt-rtt = "0.4" | 17 | defmt-rtt = "0.4" |
diff --git a/examples/stm32wba/Cargo.toml b/examples/stm32wba/Cargo.toml index 401281c0b..7735dfdde 100644 --- a/examples/stm32wba/Cargo.toml +++ b/examples/stm32wba/Cargo.toml | |||
| @@ -6,10 +6,10 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wba52cg", "time-driver-any", "memory-x", "exti"] } | 8 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wba52cg", "time-driver-any", "memory-x", "exti"] } |
| 9 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 9 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 10 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 10 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] } |
| 11 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 11 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 12 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true } | 12 | embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true } |
| 13 | 13 | ||
| 14 | defmt = "0.3" | 14 | defmt = "0.3" |
| 15 | defmt-rtt = "0.4" | 15 | defmt-rtt = "0.4" |
diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 46af5218c..0182745e5 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml | |||
| @@ -7,8 +7,8 @@ license = "MIT OR Apache-2.0" | |||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32wl55jc-cm4 to your chip name, if necessary. | 8 | # Change stm32wl55jc-cm4 to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti", "chrono"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti", "chrono"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-4096", "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.3.2", 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.2.0", path = "../../embassy-embedded-hal" } |
| 14 | 14 | ||
diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index 75de079b7..f5dcdc0a2 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml | |||
| @@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0" | |||
| 8 | crate-type = ["cdylib"] | 8 | crate-type = ["cdylib"] |
| 9 | 9 | ||
| 10 | [dependencies] | 10 | [dependencies] |
| 11 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["log"] } | 11 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["log"] } |
| 12 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "integrated-timers"] } | 12 | embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log"] } |
| 13 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["log", "wasm", ] } | 13 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["log", "wasm", ] } |
| 14 | 14 | ||
| 15 | wasm-logger = "0.2.0" | 15 | wasm-logger = "0.2.0" |
