diff options
Diffstat (limited to 'examples')
429 files changed, 13058 insertions, 4936 deletions
diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 93e49faef..4d633e8a8 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml | |||
| @@ -5,16 +5,16 @@ 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.7.0", 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.7.0", path = "../../../../embassy-executor", features = ["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.4.0", path = "../../../../embassy-time", features = [] } |
| 11 | embassy-nrf = { version = "0.2.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } | 11 | embassy-nrf = { version = "0.3.1", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } |
| 12 | embassy-boot = { version = "0.3.0", path = "../../../../embassy-boot", features = [] } | 12 | embassy-boot = { version = "0.4.0", path = "../../../../embassy-boot", features = [] } |
| 13 | embassy-boot-nrf = { version = "0.3.0", path = "../../../../embassy-boot-nrf", features = [] } | 13 | embassy-boot-nrf = { version = "0.4.0", path = "../../../../embassy-boot-nrf", features = [] } |
| 14 | embassy-embedded-hal = { version = "0.2.0", path = "../../../../embassy-embedded-hal" } | 14 | embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } |
| 15 | 15 | ||
| 16 | defmt = { version = "0.3", optional = true } | 16 | defmt = { version = "1.0.1", optional = true } |
| 17 | defmt-rtt = { version = "0.4", optional = true } | 17 | defmt-rtt = { version = "1.0.0", optional = true } |
| 18 | panic-reset = { version = "0.1.1" } | 18 | panic-reset = { version = "0.1.1" } |
| 19 | embedded-hal = { version = "0.2.6" } | 19 | embedded-hal = { version = "0.2.6" } |
| 20 | 20 | ||
diff --git a/examples/boot/application/nrf/build.rs b/examples/boot/application/nrf/build.rs index cd1a264c4..e1da69328 100644 --- a/examples/boot/application/nrf/build.rs +++ b/examples/boot/application/nrf/build.rs | |||
| @@ -31,4 +31,7 @@ fn main() { | |||
| 31 | 31 | ||
| 32 | println!("cargo:rustc-link-arg-bins=--nmagic"); | 32 | println!("cargo:rustc-link-arg-bins=--nmagic"); |
| 33 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); | 33 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); |
| 34 | if env::var("CARGO_FEATURE_DEFMT").is_ok() { | ||
| 35 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | ||
| 36 | } | ||
| 34 | } | 37 | } |
diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index 851a3d721..2c1d1a7bb 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs | |||
| @@ -2,6 +2,9 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![macro_use] | 3 | #![macro_use] |
| 4 | 4 | ||
| 5 | #[cfg(feature = "defmt")] | ||
| 6 | use defmt_rtt as _; | ||
| 7 | use embassy_boot::State; | ||
| 5 | use embassy_boot_nrf::{FirmwareUpdater, FirmwareUpdaterConfig}; | 8 | use embassy_boot_nrf::{FirmwareUpdater, FirmwareUpdaterConfig}; |
| 6 | use embassy_embedded_hal::adapter::BlockingAsync; | 9 | use embassy_embedded_hal::adapter::BlockingAsync; |
| 7 | use embassy_executor::Spawner; | 10 | use embassy_executor::Spawner; |
| @@ -22,6 +25,7 @@ async fn main(_spawner: Spawner) { | |||
| 22 | 25 | ||
| 23 | let mut button = Input::new(p.P0_11, Pull::Up); | 26 | let mut button = Input::new(p.P0_11, Pull::Up); |
| 24 | let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); | 27 | let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); |
| 28 | let mut led_reverted = Output::new(p.P0_14, Level::High, OutputDrive::Standard); | ||
| 25 | 29 | ||
| 26 | //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); | 30 | //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); |
| 27 | //let mut button = Input::new(p.P1_02, Pull::Up); | 31 | //let mut button = Input::new(p.P1_02, Pull::Up); |
| @@ -53,6 +57,13 @@ async fn main(_spawner: Spawner) { | |||
| 53 | let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc, &nvmc); | 57 | let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc, &nvmc); |
| 54 | let mut magic = [0; 4]; | 58 | let mut magic = [0; 4]; |
| 55 | let mut updater = FirmwareUpdater::new(config, &mut magic); | 59 | let mut updater = FirmwareUpdater::new(config, &mut magic); |
| 60 | let state = updater.get_state().await.unwrap(); | ||
| 61 | if state == State::Revert { | ||
| 62 | led_reverted.set_low(); | ||
| 63 | } else { | ||
| 64 | led_reverted.set_high(); | ||
| 65 | } | ||
| 66 | |||
| 56 | loop { | 67 | loop { |
| 57 | led.set_low(); | 68 | led.set_low(); |
| 58 | button.wait_for_any_edge().await; | 69 | button.wait_for_any_edge().await; |
diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index 8bb8afdfe..be283fb27 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml | |||
| @@ -5,16 +5,16 @@ 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.7.0", 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.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] } | 10 | embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [] } |
| 11 | embassy-rp = { version = "0.2.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] } | 11 | embassy-rp = { version = "0.4.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.5.0", path = "../../../../embassy-boot-rp", features = [] } |
| 13 | embassy-embedded-hal = { version = "0.2.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = "0.3" | 15 | defmt = "1.0.1" |
| 16 | defmt-rtt = "0.4" | 16 | defmt-rtt = "1.0.0" |
| 17 | panic-probe = { version = "0.3", features = ["print-defmt"], optional = true } | 17 | panic-probe = { version = "1.0.0", features = ["print-defmt"], optional = true } |
| 18 | panic-reset = { version = "0.1.1", optional = true } | 18 | panic-reset = { version = "0.1.1", optional = true } |
| 19 | embedded-hal = { version = "0.2.6" } | 19 | embedded-hal = { version = "0.2.6" } |
| 20 | 20 | ||
diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index 1c2934298..b3466e288 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml | |||
| @@ -5,15 +5,15 @@ 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.7.0", 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.7.0", path = "../../../../embassy-executor", features = ["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.4.0", 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.2.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.3.0", path = "../../../../embassy-boot-stm32" } |
| 13 | embassy-embedded-hal = { version = "0.2.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = { version = "0.3", optional = true } | 15 | defmt = { version = "1.0.1", optional = true } |
| 16 | defmt-rtt = { version = "0.4", optional = true } | 16 | defmt-rtt = { version = "1.0.0", optional = true } |
| 17 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 18 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | 19 | ||
diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index 09e34c7df..72dbded5f 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml | |||
| @@ -5,15 +5,15 @@ 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.7.0", 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.7.0", path = "../../../../embassy-executor", features = ["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.4.0", 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.2.0", path = "../../../../embassy-stm32", features = ["stm32f767zi", "time-driver-any", "exti", "single-bank"] } |
| 12 | embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } | 12 | embassy-boot-stm32 = { version = "0.3.0", path = "../../../../embassy-boot-stm32", features = [] } |
| 13 | embassy-embedded-hal = { version = "0.2.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = { version = "0.3", optional = true } | 15 | defmt = { version = "1.0.1", optional = true } |
| 16 | defmt-rtt = { version = "0.4", optional = true } | 16 | defmt-rtt = { version = "1.0.0", optional = true } |
| 17 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 18 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | embedded-storage = "0.3.1" | 19 | embedded-storage = "0.3.1" |
diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index 5e7f4d5e7..57fb8312c 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml | |||
| @@ -5,15 +5,15 @@ 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.7.0", 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.7.0", path = "../../../../embassy-executor", features = ["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.4.0", 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.2.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.3.0", path = "../../../../embassy-boot-stm32", features = [] } |
| 13 | embassy-embedded-hal = { version = "0.2.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = { version = "0.3", optional = true } | 15 | defmt = { version = "1.0.1", optional = true } |
| 16 | defmt-rtt = { version = "0.4", optional = true } | 16 | defmt-rtt = { version = "1.0.0", optional = true } |
| 17 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 18 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | embedded-storage = "0.3.1" | 19 | embedded-storage = "0.3.1" |
diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 60fdcfafb..7dbbba138 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml | |||
| @@ -5,15 +5,15 @@ 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.7.0", 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.7.0", path = "../../../../embassy-executor", features = ["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.4.0", 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.2.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.3.0", path = "../../../../embassy-boot-stm32", features = [] } |
| 13 | embassy-embedded-hal = { version = "0.2.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = { version = "0.3", optional = true } | 15 | defmt = { version = "1.0.1", optional = true } |
| 16 | defmt-rtt = { version = "0.4", optional = true } | 16 | defmt-rtt = { version = "1.0.0", optional = true } |
| 17 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 18 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | 19 | ||
diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index fe3ab2c04..9549b2048 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml | |||
| @@ -5,15 +5,15 @@ 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.7.0", 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.7.0", path = "../../../../embassy-executor", features = ["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.4.0", 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.2.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.3.0", path = "../../../../embassy-boot-stm32", features = [] } |
| 13 | embassy-embedded-hal = { version = "0.2.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = { version = "0.3", optional = true } | 15 | defmt = { version = "1.0.1", optional = true } |
| 16 | defmt-rtt = { version = "0.4", optional = true } | 16 | defmt-rtt = { version = "1.0.0", optional = true } |
| 17 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 18 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | 19 | ||
diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 169856358..03daeb0bc 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml | |||
| @@ -5,15 +5,15 @@ 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.7.0", 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.7.0", path = "../../../../embassy-executor", features = ["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.4.0", 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.2.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.3.0", path = "../../../../embassy-boot-stm32", features = [] } |
| 13 | embassy-embedded-hal = { version = "0.2.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = { version = "0.3", optional = true } | 15 | defmt = { version = "1.0.1", optional = true } |
| 16 | defmt-rtt = { version = "0.4", optional = true } | 16 | defmt-rtt = { version = "1.0.0", optional = true } |
| 17 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 18 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | 19 | ||
diff --git a/examples/boot/application/stm32wb-dfu/Cargo.toml b/examples/boot/application/stm32wb-dfu/Cargo.toml index 7cef8fe0d..e582628aa 100644 --- a/examples/boot/application/stm32wb-dfu/Cargo.toml +++ b/examples/boot/application/stm32wb-dfu/Cargo.toml | |||
| @@ -5,17 +5,17 @@ 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.7.0", 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.7.0", path = "../../../../embassy-executor", features = ["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.4.0", 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.2.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.3.0", path = "../../../../embassy-boot-stm32", features = [] } |
| 13 | embassy-embedded-hal = { version = "0.2.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } |
| 14 | embassy-usb = { version = "0.3.0", path = "../../../../embassy-usb" } | 14 | embassy-usb = { version = "0.4.0", path = "../../../../embassy-usb" } |
| 15 | embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["application", "cortex-m"] } | 15 | embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["application", "cortex-m"] } |
| 16 | 16 | ||
| 17 | defmt = { version = "0.3", optional = true } | 17 | defmt = { version = "1.0.1", optional = true } |
| 18 | defmt-rtt = { version = "0.4", optional = true } | 18 | defmt-rtt = { version = "1.0.0", optional = true } |
| 19 | panic-reset = { version = "0.1.1" } | 19 | panic-reset = { version = "0.1.1" } |
| 20 | embedded-hal = { version = "0.2.6" } | 20 | embedded-hal = { version = "0.2.6" } |
| 21 | 21 | ||
diff --git a/examples/boot/application/stm32wb-dfu/memory.x b/examples/boot/application/stm32wb-dfu/memory.x index ff1b800d2..f1e6b053c 100644 --- a/examples/boot/application/stm32wb-dfu/memory.x +++ b/examples/boot/application/stm32wb-dfu/memory.x | |||
| @@ -1,10 +1,10 @@ | |||
| 1 | MEMORY | 1 | MEMORY |
| 2 | { | 2 | { |
| 3 | /* NOTE 1 K = 1 KiBi = 1024 bytes */ | 3 | /* NOTE 1 K = 1 KiBi = 1024 bytes */ |
| 4 | BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K | 4 | BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 48K |
| 5 | BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K | 5 | BOOTLOADER_STATE : ORIGIN = 0x0800C000, LENGTH = 4K |
| 6 | FLASH : ORIGIN = 0x08008000, LENGTH = 128K | 6 | FLASH : ORIGIN = 0x0800D000, LENGTH = 120K |
| 7 | DFU : ORIGIN = 0x08028000, LENGTH = 132K | 7 | DFU : ORIGIN = 0x0802B000, LENGTH = 120K |
| 8 | RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K | 8 | RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K |
| 9 | } | 9 | } |
| 10 | 10 | ||
diff --git a/examples/boot/application/stm32wb-dfu/secrets/key.sec b/examples/boot/application/stm32wb-dfu/secrets/key.sec new file mode 100644 index 000000000..52e7f125b --- /dev/null +++ b/examples/boot/application/stm32wb-dfu/secrets/key.sec | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | untrusted comment: signify secret key | ||
| 2 | RWRCSwAAAAATdHQF3B4jEIoNZrjADRp2LbjJjNdNNzKwTCe4IB6mDNq96pe53nbNxwbdCc/T4hrz7W+Kx1MwrZ0Yz5xebSK5Z0Kh/3Cdf039U5f+eoTDS2fIGbohyUbrtwKzjyE0qXI= | ||
diff --git a/examples/boot/application/stm32wb-dfu/src/main.rs b/examples/boot/application/stm32wb-dfu/src/main.rs index 0ab99ff90..5e7b71f5a 100644 --- a/examples/boot/application/stm32wb-dfu/src/main.rs +++ b/examples/boot/application/stm32wb-dfu/src/main.rs | |||
| @@ -13,7 +13,7 @@ use embassy_stm32::usb::{self, Driver}; | |||
| 13 | use embassy_stm32::{bind_interrupts, peripherals}; | 13 | use embassy_stm32::{bind_interrupts, peripherals}; |
| 14 | use embassy_sync::blocking_mutex::Mutex; | 14 | use embassy_sync::blocking_mutex::Mutex; |
| 15 | use embassy_time::Duration; | 15 | use embassy_time::Duration; |
| 16 | use embassy_usb::Builder; | 16 | use embassy_usb::{msos, Builder}; |
| 17 | use embassy_usb_dfu::consts::DfuAttributes; | 17 | use embassy_usb_dfu::consts::DfuAttributes; |
| 18 | use embassy_usb_dfu::{usb_dfu, Control, ResetImmediate}; | 18 | use embassy_usb_dfu::{usb_dfu, Control, ResetImmediate}; |
| 19 | use panic_reset as _; | 19 | use panic_reset as _; |
| @@ -22,6 +22,11 @@ bind_interrupts!(struct Irqs { | |||
| 22 | USB_LP => usb::InterruptHandler<peripherals::USB>; | 22 | USB_LP => usb::InterruptHandler<peripherals::USB>; |
| 23 | }); | 23 | }); |
| 24 | 24 | ||
| 25 | // This is a randomly generated GUID to allow clients on Windows to find your device. | ||
| 26 | // | ||
| 27 | // N.B. update to a custom GUID for your own device! | ||
| 28 | const DEVICE_INTERFACE_GUIDS: &[&str] = &["{EAA9A5DC-30BA-44BC-9232-606CDC875321}"]; | ||
| 29 | |||
| 25 | #[embassy_executor::main] | 30 | #[embassy_executor::main] |
| 26 | async fn main(_spawner: Spawner) { | 31 | async fn main(_spawner: Spawner) { |
| 27 | let mut config = embassy_stm32::Config::default(); | 32 | let mut config = embassy_stm32::Config::default(); |
| @@ -44,7 +49,7 @@ async fn main(_spawner: Spawner) { | |||
| 44 | let mut config_descriptor = [0; 256]; | 49 | let mut config_descriptor = [0; 256]; |
| 45 | let mut bos_descriptor = [0; 256]; | 50 | let mut bos_descriptor = [0; 256]; |
| 46 | let mut control_buf = [0; 64]; | 51 | let mut control_buf = [0; 64]; |
| 47 | let mut state = Control::new(firmware_state, DfuAttributes::CAN_DOWNLOAD); | 52 | let mut state = Control::new(firmware_state, DfuAttributes::CAN_DOWNLOAD, ResetImmediate); |
| 48 | let mut builder = Builder::new( | 53 | let mut builder = Builder::new( |
| 49 | driver, | 54 | driver, |
| 50 | config, | 55 | config, |
| @@ -54,7 +59,28 @@ async fn main(_spawner: Spawner) { | |||
| 54 | &mut control_buf, | 59 | &mut control_buf, |
| 55 | ); | 60 | ); |
| 56 | 61 | ||
| 57 | usb_dfu::<_, _, ResetImmediate>(&mut builder, &mut state, Duration::from_millis(2500)); | 62 | // We add MSOS headers so that the device automatically gets assigned the WinUSB driver on Windows. |
| 63 | // Otherwise users need to do this manually using a tool like Zadig. | ||
| 64 | // | ||
| 65 | // It seems these always need to be at added at the device level for this to work and for | ||
| 66 | // composite devices they also need to be added on the function level (as shown later). | ||
| 67 | // | ||
| 68 | builder.msos_descriptor(msos::windows_version::WIN8_1, 2); | ||
| 69 | builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); | ||
| 70 | builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new( | ||
| 71 | "DeviceInterfaceGUIDs", | ||
| 72 | msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS), | ||
| 73 | )); | ||
| 74 | |||
| 75 | usb_dfu(&mut builder, &mut state, Duration::from_millis(2500), |func| { | ||
| 76 | // You likely don't have to add these function level headers if your USB device is not composite | ||
| 77 | // (i.e. if your device does not expose another interface in addition to DFU) | ||
| 78 | func.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); | ||
| 79 | func.msos_feature(msos::RegistryPropertyFeatureDescriptor::new( | ||
| 80 | "DeviceInterfaceGUIDs", | ||
| 81 | msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS), | ||
| 82 | )); | ||
| 83 | }); | ||
| 58 | 84 | ||
| 59 | let mut dev = builder.build(); | 85 | let mut dev = builder.build(); |
| 60 | dev.run().await | 86 | dev.run().await |
diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index 860a835a9..3ed04b472 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml | |||
| @@ -5,15 +5,15 @@ 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.7.0", 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.7.0", path = "../../../../embassy-executor", features = ["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.4.0", 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.2.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.3.0", path = "../../../../embassy-boot-stm32", features = [] } |
| 13 | embassy-embedded-hal = { version = "0.2.0", path = "../../../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = { version = "0.3", optional = true } | 15 | defmt = { version = "1.0.1", optional = true } |
| 16 | defmt-rtt = { version = "0.4", optional = true } | 16 | defmt-rtt = { version = "1.0.0", optional = true } |
| 17 | panic-reset = { version = "0.1.1" } | 17 | panic-reset = { version = "0.1.1" } |
| 18 | embedded-hal = { version = "0.2.6" } | 18 | embedded-hal = { version = "0.2.6" } |
| 19 | 19 | ||
diff --git a/examples/boot/application/stm32wl/memory.x b/examples/boot/application/stm32wl/memory.x index 5af1723f5..20109e37e 100644 --- a/examples/boot/application/stm32wl/memory.x +++ b/examples/boot/application/stm32wl/memory.x | |||
| @@ -5,8 +5,8 @@ MEMORY | |||
| 5 | BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K | 5 | BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K |
| 6 | FLASH : ORIGIN = 0x08008000, LENGTH = 64K | 6 | FLASH : ORIGIN = 0x08008000, LENGTH = 64K |
| 7 | DFU : ORIGIN = 0x08018000, LENGTH = 68K | 7 | DFU : ORIGIN = 0x08018000, LENGTH = 68K |
| 8 | SHARED_RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64 | 8 | SHARED_RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128 |
| 9 | RAM (rwx) : ORIGIN = 0x20000040, LENGTH = 32K - 64 | 9 | RAM (rwx) : ORIGIN = 0x20000080, LENGTH = 32K - 128 |
| 10 | } | 10 | } |
| 11 | 11 | ||
| 12 | __bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER); | 12 | __bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER); |
| @@ -21,4 +21,4 @@ SECTIONS | |||
| 21 | { | 21 | { |
| 22 | *(.shared_data) | 22 | *(.shared_data) |
| 23 | } > SHARED_RAM | 23 | } > SHARED_RAM |
| 24 | } \ No newline at end of file | 24 | } |
diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index 127de0237..e4526927f 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs | |||
| @@ -20,7 +20,7 @@ static APP_B: &[u8] = &[0, 1, 2, 3]; | |||
| 20 | #[cfg(not(feature = "skip-include"))] | 20 | #[cfg(not(feature = "skip-include"))] |
| 21 | static APP_B: &[u8] = include_bytes!("../../b.bin"); | 21 | static APP_B: &[u8] = include_bytes!("../../b.bin"); |
| 22 | 22 | ||
| 23 | #[link_section = ".shared_data"] | 23 | #[unsafe(link_section = ".shared_data")] |
| 24 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); | 24 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); |
| 25 | 25 | ||
| 26 | #[embassy_executor::main] | 26 | #[embassy_executor::main] |
diff --git a/examples/boot/application/stm32wl/src/bin/b.rs b/examples/boot/application/stm32wl/src/bin/b.rs index 768dadf8b..6016a9555 100644 --- a/examples/boot/application/stm32wl/src/bin/b.rs +++ b/examples/boot/application/stm32wl/src/bin/b.rs | |||
| @@ -11,7 +11,7 @@ use embassy_stm32::SharedData; | |||
| 11 | use embassy_time::Timer; | 11 | use embassy_time::Timer; |
| 12 | use panic_reset as _; | 12 | use panic_reset as _; |
| 13 | 13 | ||
| 14 | #[link_section = ".shared_data"] | 14 | #[unsafe(link_section = ".shared_data")] |
| 15 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); | 15 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); |
| 16 | 16 | ||
| 17 | #[embassy_executor::main] | 17 | #[embassy_executor::main] |
diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml index 9d5d51a13..897890ca4 100644 --- a/examples/boot/bootloader/nrf/Cargo.toml +++ b/examples/boot/bootloader/nrf/Cargo.toml | |||
| @@ -6,13 +6,13 @@ description = "Bootloader for nRF chips" | |||
| 6 | license = "MIT OR Apache-2.0" | 6 | license = "MIT OR Apache-2.0" |
| 7 | 7 | ||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | defmt = { version = "0.3", optional = true } | 9 | defmt = { version = "1.0.1", optional = true } |
| 10 | defmt-rtt = { version = "0.4", optional = true } | 10 | defmt-rtt = { version = "1.0.0", optional = true } |
| 11 | 11 | ||
| 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.7.0", 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..090a581d4 100644 --- a/examples/boot/bootloader/rp/Cargo.toml +++ b/examples/boot/bootloader/rp/Cargo.toml | |||
| @@ -6,12 +6,12 @@ description = "Example bootloader for RP2040 chips" | |||
| 6 | license = "MIT OR Apache-2.0" | 6 | license = "MIT OR Apache-2.0" |
| 7 | 7 | ||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | defmt = { version = "0.3", optional = true } | 9 | defmt = { version = "1.0.1", optional = true } |
| 10 | defmt-rtt = { version = "0.4", optional = true } | 10 | defmt-rtt = { version = "1.0.0", 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.7.0", 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..67edc6a6c 100644 --- a/examples/boot/bootloader/stm32-dual-bank/Cargo.toml +++ b/examples/boot/bootloader/stm32-dual-bank/Cargo.toml | |||
| @@ -6,8 +6,8 @@ description = "Example bootloader for dual-bank flash STM32 chips" | |||
| 6 | license = "MIT OR Apache-2.0" | 6 | license = "MIT OR Apache-2.0" |
| 7 | 7 | ||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | defmt = { version = "0.3", optional = true } | 9 | defmt = { version = "1.0.1", optional = true } |
| 10 | defmt-rtt = { version = "0.4", optional = true } | 10 | defmt-rtt = { version = "1.0.0", optional = true } |
| 11 | 11 | ||
| 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" } |
| @@ -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.7.0", 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..fe81b5151 100644 --- a/examples/boot/bootloader/stm32/Cargo.toml +++ b/examples/boot/bootloader/stm32/Cargo.toml | |||
| @@ -6,13 +6,13 @@ description = "Example bootloader for STM32 chips" | |||
| 6 | license = "MIT OR Apache-2.0" | 6 | license = "MIT OR Apache-2.0" |
| 7 | 7 | ||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | defmt = { version = "0.3", optional = true } | 9 | defmt = { version = "1.0.1", optional = true } |
| 10 | defmt-rtt = { version = "0.4", optional = true } | 10 | defmt-rtt = { version = "1.0.0", optional = true } |
| 11 | 11 | ||
| 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.7.0", 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..0bb93b12e 100644 --- a/examples/boot/bootloader/stm32wb-dfu/Cargo.toml +++ b/examples/boot/bootloader/stm32wb-dfu/Cargo.toml | |||
| @@ -6,19 +6,19 @@ description = "Example USB DFUbootloader for the STM32WB series of chips" | |||
| 6 | license = "MIT OR Apache-2.0" | 6 | license = "MIT OR Apache-2.0" |
| 7 | 7 | ||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | defmt = { version = "0.3", optional = true } | 9 | defmt = { version = "1.0.1", optional = true } |
| 10 | defmt-rtt = { version = "0.4", optional = true } | 10 | defmt-rtt = { version = "1.0.0", optional = true } |
| 11 | 11 | ||
| 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.7.0", 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" |
| 19 | cfg-if = "1.0.0" | 19 | cfg-if = "1.0.0" |
| 20 | embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["dfu", "cortex-m"] } | 20 | embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["dfu", "cortex-m"] } |
| 21 | embassy-usb = { version = "0.3.0", path = "../../../../embassy-usb", default-features = false } | 21 | embassy-usb = { version = "0.4.0", path = "../../../../embassy-usb", default-features = false } |
| 22 | embassy-futures = { version = "0.1.1", path = "../../../../embassy-futures" } | 22 | embassy-futures = { version = "0.1.1", path = "../../../../embassy-futures" } |
| 23 | 23 | ||
| 24 | [features] | 24 | [features] |
| @@ -30,6 +30,7 @@ defmt = [ | |||
| 30 | "embassy-usb/defmt", | 30 | "embassy-usb/defmt", |
| 31 | "embassy-usb-dfu/defmt" | 31 | "embassy-usb-dfu/defmt" |
| 32 | ] | 32 | ] |
| 33 | verify = ["embassy-usb-dfu/ed25519-salty"] | ||
| 33 | 34 | ||
| 34 | [profile.dev] | 35 | [profile.dev] |
| 35 | debug = 2 | 36 | debug = 2 |
diff --git a/examples/boot/bootloader/stm32wb-dfu/README.md b/examples/boot/bootloader/stm32wb-dfu/README.md index d5c6ea57c..99a7002c4 100644 --- a/examples/boot/bootloader/stm32wb-dfu/README.md +++ b/examples/boot/bootloader/stm32wb-dfu/README.md | |||
| @@ -1,11 +1,63 @@ | |||
| 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 | ### 3. Sign Updates Before Flashing (Optional) | ||
| 32 | |||
| 33 | Currently, embassy-usb-dfu only supports a limited implementation of the generic support for ed25519-based update verfication in embassy-boot. This implementation assumes that a signature is simply concatenated to the end of an update binary. For more details, please see https://embassy.dev/book/#_verification and/or refer to the documentation for embassy-boot-dfu. | ||
| 34 | |||
| 35 | To sign (and then verify) application updates, you will first need to generate a key pair: | ||
| 36 | |||
| 37 | ``` | ||
| 38 | signify-openbsd -G -n -p secrets/key.pub -s secrets/key.sec | ||
| 39 | tail -n1 secrets/key.pub | base64 -d -i - | dd ibs=10 skip=1 > secrets/key.pub.short | ||
| 40 | ``` | ||
| 41 | |||
| 42 | Then you will need to sign all you binaries with the private key: | ||
| 43 | |||
| 44 | ``` | ||
| 45 | cargo objcopy --release -- -O binary fw.bin | ||
| 46 | shasum -a 512 -b fw.bin | head -c128 | xxd -p -r > target/fw-hash.txt | ||
| 47 | signify-openbsd -S -s secrets/key.sec -m target/fw-hash.txt -x target/fw-hash.sig | ||
| 48 | cp fw.bin fw-signed.bin | ||
| 49 | tail -n1 target/fw-hash.sig | base64 -d -i - | dd ibs=10 skip=1 >> fw-signed.bin | ||
| 50 | dfu-util -d c0de:cafe -w -D fw-signed.bin | ||
| 51 | ``` | ||
| 52 | |||
| 53 | Finally, as shown in this example with the `verify` feature flag enabled, you then need to embed the public key into your bootloader so that it can verify update signatures. | ||
| 54 | |||
| 55 | N.B. Please note that the exact steps above are NOT a good example of how to manage your keys securely. In a production environment, you should take great care to ensure that (at least the private key) is protected and not leaked into your version control system. | ||
| 56 | |||
| 57 | ## Troubleshooting | ||
| 58 | |||
| 59 | - Make sure your device is in DFU mode before flashing | ||
| 60 | - Verify the USB VID:PID matches your device (c0de:cafe) | ||
| 61 | - Check USB connections if the device is not detected | ||
| 62 | - 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. | ||
| 63 | - 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/memory.x b/examples/boot/bootloader/stm32wb-dfu/memory.x index 858062631..77c4d2ee2 100644 --- a/examples/boot/bootloader/stm32wb-dfu/memory.x +++ b/examples/boot/bootloader/stm32wb-dfu/memory.x | |||
| @@ -1,10 +1,10 @@ | |||
| 1 | MEMORY | 1 | MEMORY |
| 2 | { | 2 | { |
| 3 | /* NOTE 1 K = 1 KiBi = 1024 bytes */ | 3 | /* NOTE 1 K = 1 KiBi = 1024 bytes */ |
| 4 | FLASH : ORIGIN = 0x08000000, LENGTH = 24K | 4 | FLASH : ORIGIN = 0x08000000, LENGTH = 48K |
| 5 | BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K | 5 | BOOTLOADER_STATE : ORIGIN = 0x0800C000, LENGTH = 4K |
| 6 | ACTIVE : ORIGIN = 0x08008000, LENGTH = 128K | 6 | ACTIVE : ORIGIN = 0x0800D000, LENGTH = 120K |
| 7 | DFU : ORIGIN = 0x08028000, LENGTH = 132K | 7 | DFU : ORIGIN = 0x0802B000, LENGTH = 120K |
| 8 | RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 16K | 8 | RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 16K |
| 9 | } | 9 | } |
| 10 | 10 | ||
diff --git a/examples/boot/bootloader/stm32wb-dfu/secrets/key.pub.short b/examples/boot/bootloader/stm32wb-dfu/secrets/key.pub.short new file mode 100644 index 000000000..7a4de8585 --- /dev/null +++ b/examples/boot/bootloader/stm32wb-dfu/secrets/key.pub.short | |||
| @@ -0,0 +1 @@ | |||
| gB��p�M�S��z��Kg��!�F���!4�r \ No newline at end of file | |||
diff --git a/examples/boot/bootloader/stm32wb-dfu/src/main.rs b/examples/boot/bootloader/stm32wb-dfu/src/main.rs index 093b39f9d..107f243fd 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,17 @@ 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 your device. | ||
| 24 | // | ||
| 25 | // N.B. update to a custom GUID for your own device! | ||
| 26 | const DEVICE_INTERFACE_GUIDS: &[&str] = &["{EAA9A5DC-30BA-44BC-9232-606CDC875321}"]; | ||
| 27 | |||
| 28 | // This is a randomly generated example key. | ||
| 29 | // | ||
| 30 | // N.B. Please replace with your own! | ||
| 31 | #[cfg(feature = "verify")] | ||
| 32 | static PUBLIC_SIGNING_KEY: &[u8; 32] = include_bytes!("../secrets/key.pub.short"); | ||
| 33 | |||
| 23 | #[entry] | 34 | #[entry] |
| 24 | fn main() -> ! { | 35 | fn main() -> ! { |
| 25 | let mut config = embassy_stm32::Config::default(); | 36 | let mut config = embassy_stm32::Config::default(); |
| @@ -52,7 +63,13 @@ fn main() -> ! { | |||
| 52 | let mut config_descriptor = [0; 256]; | 63 | let mut config_descriptor = [0; 256]; |
| 53 | let mut bos_descriptor = [0; 256]; | 64 | let mut bos_descriptor = [0; 256]; |
| 54 | let mut control_buf = [0; 4096]; | 65 | let mut control_buf = [0; 4096]; |
| 55 | let mut state = Control::new(updater, DfuAttributes::CAN_DOWNLOAD); | 66 | |
| 67 | #[cfg(not(feature = "verify"))] | ||
| 68 | let mut state = Control::new(updater, DfuAttributes::CAN_DOWNLOAD, ResetImmediate); | ||
| 69 | |||
| 70 | #[cfg(feature = "verify")] | ||
| 71 | let mut state = Control::new(updater, DfuAttributes::CAN_DOWNLOAD, ResetImmediate, PUBLIC_SIGNING_KEY); | ||
| 72 | |||
| 56 | let mut builder = Builder::new( | 73 | let mut builder = Builder::new( |
| 57 | driver, | 74 | driver, |
| 58 | config, | 75 | config, |
| @@ -62,7 +79,28 @@ fn main() -> ! { | |||
| 62 | &mut control_buf, | 79 | &mut control_buf, |
| 63 | ); | 80 | ); |
| 64 | 81 | ||
| 65 | usb_dfu::<_, _, _, ResetImmediate, 4096>(&mut builder, &mut state); | 82 | // We add MSOS headers so that the device automatically gets assigned the WinUSB driver on Windows. |
| 83 | // Otherwise users need to do this manually using a tool like Zadig. | ||
| 84 | // | ||
| 85 | // It seems these always need to be at added at the device level for this to work and for | ||
| 86 | // composite devices they also need to be added on the function level (as shown later). | ||
| 87 | // | ||
| 88 | builder.msos_descriptor(msos::windows_version::WIN8_1, 2); | ||
| 89 | builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); | ||
| 90 | builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new( | ||
| 91 | "DeviceInterfaceGUIDs", | ||
| 92 | msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS), | ||
| 93 | )); | ||
| 94 | |||
| 95 | usb_dfu::<_, _, _, _, 4096>(&mut builder, &mut state, |func| { | ||
| 96 | // You likely don't have to add these function level headers if your USB device is not composite | ||
| 97 | // (i.e. if your device does not expose another interface in addition to DFU) | ||
| 98 | func.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); | ||
| 99 | func.msos_feature(msos::RegistryPropertyFeatureDescriptor::new( | ||
| 100 | "DeviceInterfaceGUIDs", | ||
| 101 | msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS), | ||
| 102 | )); | ||
| 103 | }); | ||
| 66 | 104 | ||
| 67 | let mut dev = builder.build(); | 105 | let mut dev = builder.build(); |
| 68 | embassy_futures::block_on(dev.run()); | 106 | embassy_futures::block_on(dev.run()); |
diff --git a/examples/rp23/.cargo/config.toml b/examples/lpc55s69/.cargo/config.toml index f77e004dc..9556de72f 100644 --- a/examples/rp23/.cargo/config.toml +++ b/examples/lpc55s69/.cargo/config.toml | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] |
| 2 | #runner = "probe-rs run --chip RP2040" | 2 | runner = "probe-rs run --chip LPC55S69JBD100" |
| 3 | runner = "elf2uf2-rs -d" | ||
| 4 | 3 | ||
| 5 | [build] | 4 | [build] |
| 6 | target = "thumbv8m.main-none-eabihf" | 5 | target = "thumbv8m.main-none-eabihf" |
diff --git a/examples/lpc55s69/Cargo.toml b/examples/lpc55s69/Cargo.toml new file mode 100644 index 000000000..7f81e9c7f --- /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.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt"] } | ||
| 11 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | ||
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt"] } | ||
| 13 | panic-halt = "1.0.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 = "1.0.1" | ||
| 17 | defmt-rtt = "1.0.0" | ||
| 18 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } | ||
| 19 | panic-semihosting = "0.6.0" | ||
| 20 | |||
| 21 | [profile.release] | ||
| 22 | debug = 2 | ||
diff --git a/examples/rp23/build.rs b/examples/lpc55s69/build.rs index 30691aa97..30691aa97 100644 --- a/examples/rp23/build.rs +++ b/examples/lpc55s69/build.rs | |||
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/mimxrt6/.cargo/config.toml b/examples/mimxrt6/.cargo/config.toml new file mode 100644 index 000000000..db42be81d --- /dev/null +++ b/examples/mimxrt6/.cargo/config.toml | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | [target.thumbv8m.main-none-eabihf] | ||
| 2 | runner = 'probe-rs run --chip MIMXRT685SFVKB' | ||
| 3 | |||
| 4 | rustflags = [ | ||
| 5 | "-C", "linker=flip-link", | ||
| 6 | "-C", "link-arg=-Tlink.x", | ||
| 7 | "-C", "link-arg=-Tdefmt.x", | ||
| 8 | # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x | ||
| 9 | # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95 | ||
| 10 | "-C", "link-arg=--nmagic", | ||
| 11 | ] | ||
| 12 | |||
| 13 | [build] | ||
| 14 | target = "thumbv8m.main-none-eabihf" # Cortex-M33 | ||
| 15 | |||
| 16 | [env] | ||
| 17 | DEFMT_LOG = "trace" | ||
diff --git a/examples/mimxrt6/.gitignore b/examples/mimxrt6/.gitignore new file mode 100644 index 000000000..418e01907 --- /dev/null +++ b/examples/mimxrt6/.gitignore | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # Generated by Cargo | ||
| 2 | # will have compiled files and executables | ||
| 3 | /debug | ||
| 4 | /target | ||
| 5 | |||
| 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries | ||
| 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html | ||
| 8 | Cargo.lock | ||
| 9 | |||
| 10 | # These are backup files generated by rustfmt | ||
| 11 | **/*.rs.bk | ||
| 12 | |||
| 13 | # MSVC Windows builds of rustc generate these, which store debugging information | ||
| 14 | *.pdb | ||
diff --git a/examples/mimxrt6/Cargo.toml b/examples/mimxrt6/Cargo.toml new file mode 100644 index 000000000..65cb9e3ca --- /dev/null +++ b/examples/mimxrt6/Cargo.toml | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | [package] | ||
| 2 | name = "embassy-imxrt-examples" | ||
| 3 | version = "0.1.0" | ||
| 4 | edition = "2021" | ||
| 5 | license = "MIT or Apache-2.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | cortex-m = { version = "0.7.7", features = ["inline-asm", "critical-section-single-core"] } | ||
| 9 | cortex-m-rt = "0.7.3" | ||
| 10 | defmt = "1.0.1" | ||
| 11 | defmt-rtt = "1.0.0" | ||
| 12 | |||
| 13 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | ||
| 14 | embassy-futures = { version = "0.1.1", path = "../../embassy-futures" } | ||
| 15 | embassy-imxrt = { version = "0.1.0", path = "../../embassy-imxrt", features = ["defmt", "mimxrt685s", "unstable-pac", "time", "time-driver-os-timer"] } | ||
| 16 | embassy-time = { version = "0.4", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | ||
| 17 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | ||
| 18 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | ||
| 19 | embedded-hal-async = "1.0.0" | ||
| 20 | |||
| 21 | mimxrt600-fcb = "0.2.2" | ||
| 22 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } | ||
| 23 | |||
| 24 | # cargo build/run | ||
| 25 | [profile.dev] | ||
| 26 | codegen-units = 1 | ||
| 27 | debug = 2 | ||
| 28 | debug-assertions = true # <- | ||
| 29 | incremental = false | ||
| 30 | opt-level = 3 # <- | ||
| 31 | overflow-checks = true # <- | ||
| 32 | |||
| 33 | # cargo test | ||
| 34 | [profile.test] | ||
| 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 build/run --release | ||
| 43 | [profile.release] | ||
| 44 | codegen-units = 1 | ||
| 45 | debug = 2 | ||
| 46 | debug-assertions = false # <- | ||
| 47 | incremental = false | ||
| 48 | lto = 'fat' | ||
| 49 | opt-level = 3 # <- | ||
| 50 | overflow-checks = false # <- | ||
| 51 | |||
| 52 | # cargo test --release | ||
| 53 | [profile.bench] | ||
| 54 | codegen-units = 1 | ||
| 55 | debug = 2 | ||
| 56 | debug-assertions = false # <- | ||
| 57 | incremental = false | ||
| 58 | lto = 'fat' | ||
| 59 | opt-level = 3 # <- | ||
| 60 | overflow-checks = false # <- | ||
diff --git a/examples/mimxrt6/README.md b/examples/mimxrt6/README.md new file mode 100644 index 000000000..6d5031cf9 --- /dev/null +++ b/examples/mimxrt6/README.md | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | # embassy-imxrt-examples | ||
| 2 | |||
| 3 | ## Introduction | ||
| 4 | |||
| 5 | These examples illustrates how to use the embassy-imxrt HAL. | ||
| 6 | |||
| 7 | ## Adding Examples | ||
| 8 | Add uniquely named example to `src/bin` like `adc.rs` | ||
| 9 | |||
| 10 | ## Build | ||
| 11 | `cd` to examples folder | ||
| 12 | `cargo build --bin <example_name>` for example, `cargo build --bin adc` | ||
| 13 | |||
| 14 | ## Run | ||
| 15 | Assuming RT685 is powered and connected to Jlink debug probe and the latest probe-rs is installed via | ||
| 16 | `$ cargo install probe-rs-tools --git https://github.com/probe-rs/probe-rs --locked` | ||
| 17 | `cd` to examples folder | ||
| 18 | `cargo run --bin <example_name>` for example, `cargo run --bin adc` \ No newline at end of file | ||
diff --git a/examples/mimxrt6/build.rs b/examples/mimxrt6/build.rs new file mode 100644 index 000000000..56010dfd6 --- /dev/null +++ b/examples/mimxrt6/build.rs | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | use std::env; | ||
| 2 | use std::fs::File; | ||
| 3 | use std::io::Write; | ||
| 4 | use std::path::PathBuf; | ||
| 5 | |||
| 6 | fn main() { | ||
| 7 | // Put `memory.x` in our output directory and ensure it's | ||
| 8 | // on the linker search path. | ||
| 9 | let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); | ||
| 10 | File::create(out.join("memory.x")) | ||
| 11 | .unwrap() | ||
| 12 | .write_all(include_bytes!("memory.x")) | ||
| 13 | .unwrap(); | ||
| 14 | println!("cargo:rustc-link-search={}", out.display()); | ||
| 15 | |||
| 16 | // By default, Cargo will re-run a build script whenever | ||
| 17 | // any file in the project changes. By specifying `memory.x` | ||
| 18 | // here, we ensure the build script is only re-run when | ||
| 19 | // `memory.x` is changed. | ||
| 20 | println!("cargo:rerun-if-changed=memory.x"); | ||
| 21 | |||
| 22 | // Inject crate version into the .biv section. | ||
| 23 | File::create(out.join("biv.rs")) | ||
| 24 | .unwrap() | ||
| 25 | .write_all( | ||
| 26 | format!( | ||
| 27 | r##" | ||
| 28 | #[unsafe(link_section = ".biv")] | ||
| 29 | #[used] | ||
| 30 | static BOOT_IMAGE_VERSION: u32 = 0x{:02x}{:02x}{:02x}00; | ||
| 31 | "##, | ||
| 32 | env!("CARGO_PKG_VERSION_MAJOR") | ||
| 33 | .parse::<u8>() | ||
| 34 | .expect("should have major version"), | ||
| 35 | env!("CARGO_PKG_VERSION_MINOR") | ||
| 36 | .parse::<u8>() | ||
| 37 | .expect("should have minor version"), | ||
| 38 | env!("CARGO_PKG_VERSION_PATCH") | ||
| 39 | .parse::<u8>() | ||
| 40 | .expect("should have patch version"), | ||
| 41 | ) | ||
| 42 | .as_bytes(), | ||
| 43 | ) | ||
| 44 | .unwrap(); | ||
| 45 | } | ||
diff --git a/examples/mimxrt6/memory.x b/examples/mimxrt6/memory.x new file mode 100644 index 000000000..6c4410f57 --- /dev/null +++ b/examples/mimxrt6/memory.x | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | MEMORY { | ||
| 2 | OTFAD : ORIGIN = 0x08000000, LENGTH = 256 | ||
| 3 | FCB : ORIGIN = 0x08000400, LENGTH = 512 | ||
| 4 | BIV : ORIGIN = 0x08000600, LENGTH = 4 | ||
| 5 | KEYSTORE : ORIGIN = 0x08000800, LENGTH = 2K | ||
| 6 | FLASH : ORIGIN = 0x08001000, LENGTH = 1M | ||
| 7 | RAM : ORIGIN = 0x20080000, LENGTH = 1536K | ||
| 8 | } | ||
| 9 | |||
| 10 | SECTIONS { | ||
| 11 | .otfad : { | ||
| 12 | . = ALIGN(4); | ||
| 13 | KEEP(* (.otfad)) | ||
| 14 | . = ALIGN(4); | ||
| 15 | } > OTFAD | ||
| 16 | |||
| 17 | .fcb : { | ||
| 18 | . = ALIGN(4); | ||
| 19 | KEEP(* (.fcb)) | ||
| 20 | . = ALIGN(4); | ||
| 21 | } > FCB | ||
| 22 | |||
| 23 | .biv : { | ||
| 24 | . = ALIGN(4); | ||
| 25 | KEEP(* (.biv)) | ||
| 26 | . = ALIGN(4); | ||
| 27 | } > BIV | ||
| 28 | |||
| 29 | .keystore : { | ||
| 30 | . = ALIGN(4); | ||
| 31 | KEEP(* (.keystore)) | ||
| 32 | . = ALIGN(4); | ||
| 33 | } > KEYSTORE | ||
| 34 | } | ||
diff --git a/examples/mimxrt6/src/bin/blinky.rs b/examples/mimxrt6/src/bin/blinky.rs new file mode 100644 index 000000000..de079d505 --- /dev/null +++ b/examples/mimxrt6/src/bin/blinky.rs | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | extern crate embassy_imxrt_examples; | ||
| 5 | |||
| 6 | use defmt::info; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_imxrt::gpio; | ||
| 9 | use embassy_time::Timer; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let p = embassy_imxrt::init(Default::default()); | ||
| 14 | |||
| 15 | info!("Initializing GPIO"); | ||
| 16 | |||
| 17 | let mut led = gpio::Output::new( | ||
| 18 | p.PIO0_26, | ||
| 19 | gpio::Level::Low, | ||
| 20 | gpio::DriveMode::PushPull, | ||
| 21 | gpio::DriveStrength::Normal, | ||
| 22 | gpio::SlewRate::Standard, | ||
| 23 | ); | ||
| 24 | |||
| 25 | loop { | ||
| 26 | info!("Toggling LED"); | ||
| 27 | led.toggle(); | ||
| 28 | Timer::after_secs(1).await; | ||
| 29 | } | ||
| 30 | } | ||
diff --git a/examples/mimxrt6/src/bin/button.rs b/examples/mimxrt6/src/bin/button.rs new file mode 100644 index 000000000..efb7f14af --- /dev/null +++ b/examples/mimxrt6/src/bin/button.rs | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::info; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_futures::select::{select, Either}; | ||
| 7 | use embassy_imxrt::gpio; | ||
| 8 | use {defmt_rtt as _, embassy_imxrt_examples as _, panic_probe as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::main] | ||
| 11 | async fn main(_spawner: Spawner) { | ||
| 12 | let p = embassy_imxrt::init(Default::default()); | ||
| 13 | |||
| 14 | let mut user1 = gpio::Input::new(p.PIO1_1, gpio::Pull::None, gpio::Inverter::Disabled); | ||
| 15 | let mut user2 = gpio::Input::new(p.PIO0_10, gpio::Pull::None, gpio::Inverter::Disabled); | ||
| 16 | |||
| 17 | loop { | ||
| 18 | let res = select(user1.wait_for_falling_edge(), user2.wait_for_falling_edge()).await; | ||
| 19 | |||
| 20 | match res { | ||
| 21 | Either::First(()) => { | ||
| 22 | info!("Button `USER1' pressed"); | ||
| 23 | } | ||
| 24 | Either::Second(()) => { | ||
| 25 | info!("Button `USER2' pressed"); | ||
| 26 | } | ||
| 27 | } | ||
| 28 | } | ||
| 29 | } | ||
diff --git a/examples/mimxrt6/src/bin/crc.rs b/examples/mimxrt6/src/bin/crc.rs new file mode 100644 index 000000000..005a250e5 --- /dev/null +++ b/examples/mimxrt6/src/bin/crc.rs | |||
| @@ -0,0 +1,175 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | extern crate embassy_imxrt_examples; | ||
| 5 | |||
| 6 | use defmt::*; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_imxrt::crc::{Config, Crc, Polynomial}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let mut p = embassy_imxrt::init(Default::default()); | ||
| 14 | let data = b"123456789"; | ||
| 15 | |||
| 16 | info!("Initializing CRC"); | ||
| 17 | |||
| 18 | // CRC-CCITT | ||
| 19 | let mut crc = Crc::new(p.CRC.reborrow(), Default::default()); | ||
| 20 | let output = crc.feed_bytes(data); | ||
| 21 | defmt::assert_eq!(output, 0x29b1); | ||
| 22 | |||
| 23 | // CRC16-ARC | ||
| 24 | let mut crc = Crc::new( | ||
| 25 | p.CRC.reborrow(), | ||
| 26 | Config { | ||
| 27 | polynomial: Polynomial::Crc16, | ||
| 28 | reverse_in: true, | ||
| 29 | reverse_out: true, | ||
| 30 | complement_out: false, | ||
| 31 | seed: 0, | ||
| 32 | ..Default::default() | ||
| 33 | }, | ||
| 34 | ); | ||
| 35 | let output = crc.feed_bytes(data); | ||
| 36 | defmt::assert_eq!(output, 0xbb3d); | ||
| 37 | |||
| 38 | // CRC16-CMS | ||
| 39 | let mut crc = Crc::new( | ||
| 40 | p.CRC.reborrow(), | ||
| 41 | Config { | ||
| 42 | polynomial: Polynomial::Crc16, | ||
| 43 | reverse_in: false, | ||
| 44 | reverse_out: false, | ||
| 45 | complement_out: false, | ||
| 46 | seed: 0xffff, | ||
| 47 | ..Default::default() | ||
| 48 | }, | ||
| 49 | ); | ||
| 50 | let output = crc.feed_bytes(data); | ||
| 51 | defmt::assert_eq!(output, 0xaee7); | ||
| 52 | |||
| 53 | // CRC16-DDS-110 | ||
| 54 | let mut crc = Crc::new( | ||
| 55 | p.CRC.reborrow(), | ||
| 56 | Config { | ||
| 57 | polynomial: Polynomial::Crc16, | ||
| 58 | reverse_in: false, | ||
| 59 | reverse_out: false, | ||
| 60 | complement_out: false, | ||
| 61 | seed: 0x800d, | ||
| 62 | ..Default::default() | ||
| 63 | }, | ||
| 64 | ); | ||
| 65 | let output = crc.feed_bytes(data); | ||
| 66 | defmt::assert_eq!(output, 0x9ecf); | ||
| 67 | |||
| 68 | // CRC16-MAXIM-DOW | ||
| 69 | let mut crc = Crc::new( | ||
| 70 | p.CRC.reborrow(), | ||
| 71 | Config { | ||
| 72 | polynomial: Polynomial::Crc16, | ||
| 73 | reverse_in: true, | ||
| 74 | reverse_out: true, | ||
| 75 | complement_out: true, | ||
| 76 | seed: 0, | ||
| 77 | ..Default::default() | ||
| 78 | }, | ||
| 79 | ); | ||
| 80 | let output = crc.feed_bytes(data); | ||
| 81 | defmt::assert_eq!(output, 0x44c2); | ||
| 82 | |||
| 83 | // CRC16-MODBUS | ||
| 84 | let mut crc = Crc::new( | ||
| 85 | p.CRC.reborrow(), | ||
| 86 | Config { | ||
| 87 | polynomial: Polynomial::Crc16, | ||
| 88 | reverse_in: true, | ||
| 89 | reverse_out: true, | ||
| 90 | complement_out: false, | ||
| 91 | seed: 0xffff, | ||
| 92 | ..Default::default() | ||
| 93 | }, | ||
| 94 | ); | ||
| 95 | let output = crc.feed_bytes(data); | ||
| 96 | defmt::assert_eq!(output, 0x4b37); | ||
| 97 | |||
| 98 | // CRC32-BZIP2 | ||
| 99 | let mut crc = Crc::new( | ||
| 100 | p.CRC.reborrow(), | ||
| 101 | Config { | ||
| 102 | polynomial: Polynomial::Crc32, | ||
| 103 | reverse_in: false, | ||
| 104 | reverse_out: false, | ||
| 105 | complement_out: true, | ||
| 106 | seed: 0xffff_ffff, | ||
| 107 | ..Default::default() | ||
| 108 | }, | ||
| 109 | ); | ||
| 110 | let output = crc.feed_bytes(data); | ||
| 111 | defmt::assert_eq!(output, 0xfc89_1918); | ||
| 112 | |||
| 113 | // CRC32-CKSUM | ||
| 114 | let mut crc = Crc::new( | ||
| 115 | p.CRC.reborrow(), | ||
| 116 | Config { | ||
| 117 | polynomial: Polynomial::Crc32, | ||
| 118 | reverse_in: false, | ||
| 119 | reverse_out: false, | ||
| 120 | complement_out: true, | ||
| 121 | seed: 0, | ||
| 122 | ..Default::default() | ||
| 123 | }, | ||
| 124 | ); | ||
| 125 | let output = crc.feed_bytes(data); | ||
| 126 | defmt::assert_eq!(output, 0x765e_7680); | ||
| 127 | |||
| 128 | // CRC32-ISO-HDLC | ||
| 129 | let mut crc = Crc::new( | ||
| 130 | p.CRC.reborrow(), | ||
| 131 | Config { | ||
| 132 | polynomial: Polynomial::Crc32, | ||
| 133 | reverse_in: true, | ||
| 134 | reverse_out: true, | ||
| 135 | complement_out: true, | ||
| 136 | seed: 0xffff_ffff, | ||
| 137 | ..Default::default() | ||
| 138 | }, | ||
| 139 | ); | ||
| 140 | let output = crc.feed_bytes(data); | ||
| 141 | defmt::assert_eq!(output, 0xcbf4_3926); | ||
| 142 | |||
| 143 | // CRC32-JAMCRC | ||
| 144 | let mut crc = Crc::new( | ||
| 145 | p.CRC.reborrow(), | ||
| 146 | Config { | ||
| 147 | polynomial: Polynomial::Crc32, | ||
| 148 | reverse_in: true, | ||
| 149 | reverse_out: true, | ||
| 150 | complement_out: false, | ||
| 151 | seed: 0xffff_ffff, | ||
| 152 | ..Default::default() | ||
| 153 | }, | ||
| 154 | ); | ||
| 155 | let output = crc.feed_bytes(data); | ||
| 156 | defmt::assert_eq!(output, 0x340b_c6d9); | ||
| 157 | |||
| 158 | // CRC32-MPEG-2 | ||
| 159 | let mut crc = Crc::new( | ||
| 160 | p.CRC.reborrow(), | ||
| 161 | Config { | ||
| 162 | polynomial: Polynomial::Crc32, | ||
| 163 | reverse_in: false, | ||
| 164 | reverse_out: false, | ||
| 165 | complement_out: false, | ||
| 166 | seed: 0xffff_ffff, | ||
| 167 | ..Default::default() | ||
| 168 | }, | ||
| 169 | ); | ||
| 170 | let output = crc.feed_bytes(data); | ||
| 171 | defmt::assert_eq!(output, 0x0376_e6e7); | ||
| 172 | |||
| 173 | info!("end program"); | ||
| 174 | cortex_m::asm::bkpt(); | ||
| 175 | } | ||
diff --git a/examples/mimxrt6/src/bin/hello.rs b/examples/mimxrt6/src/bin/hello.rs new file mode 100644 index 000000000..c640241ce --- /dev/null +++ b/examples/mimxrt6/src/bin/hello.rs | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | extern crate embassy_imxrt_examples; | ||
| 5 | |||
| 6 | use defmt::info; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::main] | ||
| 11 | async fn main(_spawner: Spawner) -> ! { | ||
| 12 | let _p = embassy_imxrt::init(Default::default()); | ||
| 13 | loop { | ||
| 14 | info!("Hello"); | ||
| 15 | cortex_m::asm::delay(5_000_000); | ||
| 16 | } | ||
| 17 | } | ||
diff --git a/examples/mimxrt6/src/bin/rng.rs b/examples/mimxrt6/src/bin/rng.rs new file mode 100644 index 000000000..9468dd109 --- /dev/null +++ b/examples/mimxrt6/src/bin/rng.rs | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | extern crate embassy_imxrt_examples; | ||
| 5 | |||
| 6 | use defmt::*; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_imxrt::rng::Rng; | ||
| 9 | use embassy_imxrt::{bind_interrupts, peripherals, rng}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | bind_interrupts!(struct Irqs { | ||
| 13 | RNG => rng::InterruptHandler<peripherals::RNG>; | ||
| 14 | }); | ||
| 15 | |||
| 16 | #[embassy_executor::main] | ||
| 17 | async fn main(_spawner: Spawner) { | ||
| 18 | let p = embassy_imxrt::init(Default::default()); | ||
| 19 | |||
| 20 | info!("Initializing RNG"); | ||
| 21 | let mut rng = Rng::new(p.RNG, Irqs); | ||
| 22 | let mut buf = [0u8; 65]; | ||
| 23 | |||
| 24 | // Async interface | ||
| 25 | unwrap!(rng.async_fill_bytes(&mut buf).await); | ||
| 26 | info!("random bytes: {:02x}", buf); | ||
| 27 | |||
| 28 | // RngCore interface | ||
| 29 | let mut random_bytes = [0; 16]; | ||
| 30 | |||
| 31 | let random_u32 = rng.blocking_next_u32(); | ||
| 32 | let random_u64 = rng.blocking_next_u64(); | ||
| 33 | |||
| 34 | rng.blocking_fill_bytes(&mut random_bytes); | ||
| 35 | |||
| 36 | info!("random_u32 {}", random_u32); | ||
| 37 | info!("random_u64 {}", random_u64); | ||
| 38 | info!("random_bytes {}", random_bytes); | ||
| 39 | } | ||
diff --git a/examples/mimxrt6/src/bin/uart-async.rs b/examples/mimxrt6/src/bin/uart-async.rs new file mode 100644 index 000000000..58e31f379 --- /dev/null +++ b/examples/mimxrt6/src/bin/uart-async.rs | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | extern crate embassy_imxrt_examples; | ||
| 5 | |||
| 6 | use defmt::info; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_imxrt::flexcomm::uart::{self, Async, Uart}; | ||
| 9 | use embassy_imxrt::{bind_interrupts, peripherals}; | ||
| 10 | use embassy_time::Timer; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | bind_interrupts!(struct Irqs { | ||
| 14 | FLEXCOMM2 => uart::InterruptHandler<peripherals::FLEXCOMM2>; | ||
| 15 | FLEXCOMM4 => uart::InterruptHandler<peripherals::FLEXCOMM4>; | ||
| 16 | }); | ||
| 17 | |||
| 18 | const BUFLEN: usize = 16; | ||
| 19 | |||
| 20 | #[embassy_executor::task] | ||
| 21 | async fn usart4_task(mut uart: Uart<'static, Async>) { | ||
| 22 | info!("RX Task"); | ||
| 23 | |||
| 24 | loop { | ||
| 25 | let mut rx_buf = [0; BUFLEN]; | ||
| 26 | uart.read(&mut rx_buf).await.unwrap(); | ||
| 27 | info!("usart4: rx_buf {:02x}", rx_buf); | ||
| 28 | |||
| 29 | Timer::after_millis(10).await; | ||
| 30 | |||
| 31 | let tx_buf = [0xaa; BUFLEN]; | ||
| 32 | uart.write(&tx_buf).await.unwrap(); | ||
| 33 | info!("usart4: tx_buf {:02x}", tx_buf); | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | #[embassy_executor::task] | ||
| 38 | async fn usart2_task(mut uart: Uart<'static, Async>) { | ||
| 39 | info!("TX Task"); | ||
| 40 | |||
| 41 | loop { | ||
| 42 | let tx_buf = [0x55; BUFLEN]; | ||
| 43 | uart.write(&tx_buf).await.unwrap(); | ||
| 44 | info!("usart2: tx_buf {:02x}", tx_buf); | ||
| 45 | |||
| 46 | Timer::after_millis(10).await; | ||
| 47 | |||
| 48 | let mut rx_buf = [0x00; BUFLEN]; | ||
| 49 | uart.read(&mut rx_buf).await.unwrap(); | ||
| 50 | info!("usart2: rx_buf {:02x}", rx_buf); | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | #[embassy_executor::main] | ||
| 55 | async fn main(spawner: Spawner) { | ||
| 56 | let p = embassy_imxrt::init(Default::default()); | ||
| 57 | |||
| 58 | info!("UART test start"); | ||
| 59 | |||
| 60 | let usart4 = Uart::new_with_rtscts( | ||
| 61 | p.FLEXCOMM4, | ||
| 62 | p.PIO0_29, | ||
| 63 | p.PIO0_30, | ||
| 64 | p.PIO1_0, | ||
| 65 | p.PIO0_31, | ||
| 66 | Irqs, | ||
| 67 | p.DMA0_CH9, | ||
| 68 | p.DMA0_CH8, | ||
| 69 | Default::default(), | ||
| 70 | ) | ||
| 71 | .unwrap(); | ||
| 72 | spawner.must_spawn(usart4_task(usart4)); | ||
| 73 | |||
| 74 | let usart2 = Uart::new_with_rtscts( | ||
| 75 | p.FLEXCOMM2, | ||
| 76 | p.PIO0_15, | ||
| 77 | p.PIO0_16, | ||
| 78 | p.PIO0_18, | ||
| 79 | p.PIO0_17, | ||
| 80 | Irqs, | ||
| 81 | p.DMA0_CH5, | ||
| 82 | p.DMA0_CH4, | ||
| 83 | Default::default(), | ||
| 84 | ) | ||
| 85 | .unwrap(); | ||
| 86 | spawner.must_spawn(usart2_task(usart2)); | ||
| 87 | } | ||
diff --git a/examples/mimxrt6/src/bin/uart.rs b/examples/mimxrt6/src/bin/uart.rs new file mode 100644 index 000000000..d6a75f85d --- /dev/null +++ b/examples/mimxrt6/src/bin/uart.rs | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | extern crate embassy_imxrt_examples; | ||
| 5 | |||
| 6 | use defmt::info; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_imxrt::flexcomm::uart::{Blocking, Uart, UartRx, UartTx}; | ||
| 9 | use embassy_time::Timer; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::task] | ||
| 13 | async fn usart4_task(mut uart: UartRx<'static, Blocking>) { | ||
| 14 | info!("RX Task"); | ||
| 15 | |||
| 16 | loop { | ||
| 17 | let mut buf = [0; 8]; | ||
| 18 | |||
| 19 | Timer::after_millis(10).await; | ||
| 20 | |||
| 21 | uart.blocking_read(&mut buf).unwrap(); | ||
| 22 | |||
| 23 | let s = core::str::from_utf8(&buf).unwrap(); | ||
| 24 | |||
| 25 | info!("Received '{}'", s); | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | #[embassy_executor::task] | ||
| 30 | async fn usart2_task(mut uart: UartTx<'static, Blocking>) { | ||
| 31 | info!("TX Task"); | ||
| 32 | |||
| 33 | loop { | ||
| 34 | let buf = "Testing\0".as_bytes(); | ||
| 35 | |||
| 36 | uart.blocking_write(buf).unwrap(); | ||
| 37 | |||
| 38 | Timer::after_millis(10).await; | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | #[embassy_executor::main] | ||
| 43 | async fn main(spawner: Spawner) { | ||
| 44 | let p = embassy_imxrt::init(Default::default()); | ||
| 45 | |||
| 46 | info!("UART test start"); | ||
| 47 | |||
| 48 | let usart4 = Uart::new_blocking(p.FLEXCOMM4, p.PIO0_29, p.PIO0_30, Default::default()).unwrap(); | ||
| 49 | |||
| 50 | let (_, usart4) = usart4.split(); | ||
| 51 | spawner.must_spawn(usart4_task(usart4)); | ||
| 52 | |||
| 53 | let usart2 = UartTx::new_blocking(p.FLEXCOMM2, p.PIO0_15, Default::default()).unwrap(); | ||
| 54 | spawner.must_spawn(usart2_task(usart2)); | ||
| 55 | } | ||
diff --git a/examples/mimxrt6/src/lib.rs b/examples/mimxrt6/src/lib.rs new file mode 100644 index 000000000..3c3ea1981 --- /dev/null +++ b/examples/mimxrt6/src/lib.rs | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | #![no_std] | ||
| 2 | |||
| 3 | use mimxrt600_fcb::FlexSPIFlashConfigurationBlock; | ||
| 4 | use {defmt_rtt as _, panic_probe as _}; | ||
| 5 | |||
| 6 | // auto-generated version information from Cargo.toml | ||
| 7 | include!(concat!(env!("OUT_DIR"), "/biv.rs")); | ||
| 8 | |||
| 9 | #[unsafe(link_section = ".otfad")] | ||
| 10 | #[used] | ||
| 11 | static OTFAD: [u8; 256] = [0; 256]; | ||
| 12 | |||
| 13 | #[rustfmt::skip] | ||
| 14 | #[unsafe(link_section = ".fcb")] | ||
| 15 | #[used] | ||
| 16 | static FCB: FlexSPIFlashConfigurationBlock = FlexSPIFlashConfigurationBlock::build(); | ||
| 17 | |||
| 18 | #[unsafe(link_section = ".keystore")] | ||
| 19 | #[used] | ||
| 20 | static KEYSTORE: [u8; 2048] = [0; 2048]; | ||
diff --git a/examples/mspm0c1104/.cargo/config.toml b/examples/mspm0c1104/.cargo/config.toml new file mode 100644 index 000000000..204a56b1c --- /dev/null +++ b/examples/mspm0c1104/.cargo/config.toml | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | ||
| 2 | # replace MSPM0C1104 with your chip as listed in `probe-rs chip list` | ||
| 3 | runner = "probe-rs run --chip MSPM0C1104 --protocol=swd" | ||
| 4 | |||
| 5 | [build] | ||
| 6 | target = "thumbv6m-none-eabi" | ||
| 7 | |||
| 8 | [env] | ||
| 9 | DEFMT_LOG = "debug" | ||
| 10 | # defmt's buffer needs to be shrunk since the MSPM0C1104 only has 1KB of ram. | ||
| 11 | DEFMT_RTT_BUFFER_SIZE = "72" | ||
diff --git a/examples/mspm0c1104/Cargo.toml b/examples/mspm0c1104/Cargo.toml new file mode 100644 index 000000000..79f9c0959 --- /dev/null +++ b/examples/mspm0c1104/Cargo.toml | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-mspm0-c1104-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | embassy-mspm0 = { version = "0.1.0", path = "../../embassy-mspm0", features = ["mspm0c1104dgs20", "defmt", "rt", "time-driver-any"] } | ||
| 9 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt"] } | ||
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | ||
| 11 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt"] } | ||
| 12 | panic-halt = "1.0.0" | ||
| 13 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||
| 14 | cortex-m-rt = { version = "0.7.0"} | ||
| 15 | defmt = "1.0.1" | ||
| 16 | defmt-rtt = "1.0.0" | ||
| 17 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } | ||
| 18 | panic-semihosting = "0.6.0" | ||
| 19 | |||
| 20 | # The chip only has 1KB of ram, so we must optimize binaries regardless | ||
| 21 | [profile.dev] | ||
| 22 | debug = 0 | ||
| 23 | opt-level = "z" | ||
| 24 | lto = true | ||
| 25 | codegen-units = 1 | ||
| 26 | # strip = true | ||
| 27 | |||
| 28 | [profile.release] | ||
| 29 | debug = 0 | ||
| 30 | opt-level = "z" | ||
| 31 | lto = true | ||
| 32 | codegen-units = 1 | ||
diff --git a/examples/mspm0c1104/README.md b/examples/mspm0c1104/README.md new file mode 100644 index 000000000..86b6c3918 --- /dev/null +++ b/examples/mspm0c1104/README.md | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | # Examples for MSPM0C1104 | ||
| 2 | |||
| 3 | Run individual examples with | ||
| 4 | ``` | ||
| 5 | cargo run --bin <module-name> | ||
| 6 | ``` | ||
| 7 | for example | ||
| 8 | ``` | ||
| 9 | cargo run --bin blinky | ||
| 10 | ``` | ||
| 11 | |||
| 12 | ## Checklist before running examples | ||
| 13 | A large number of the examples are written for the [LP-MSPM0C1104](https://www.ti.com/tool/LP-MSPM0C1104) board. | ||
| 14 | |||
| 15 | 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. | ||
| 16 | |||
| 17 | * [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for C1104 it should be `probe-rs run --chip MSPM0C1104`. (use `probe-rs chip list` to find your chip) | ||
| 18 | * [ ] Update Cargo.toml to have the correct `embassy-mspm0` feature. For the LP-MSPM0C1104 it should be `mspm0c1104dgs20`. Look in the `Cargo.toml` file of the `embassy-mspm0` project to find the correct feature flag for your chip. | ||
| 19 | * [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately. | ||
| 20 | * [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic | ||
| 21 | |||
| 22 | If you are unsure, please drop by the Embassy Matrix chat for support, and let us know: | ||
| 23 | |||
| 24 | * Which example you are trying to run | ||
| 25 | * Which chip and board you are using | ||
| 26 | |||
| 27 | Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org | ||
diff --git a/examples/mspm0c1104/build.rs b/examples/mspm0c1104/build.rs new file mode 100644 index 000000000..2d777c2d3 --- /dev/null +++ b/examples/mspm0c1104/build.rs | |||
| @@ -0,0 +1,37 @@ | |||
| 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 | // You must tell cargo to link interrupt groups if the rt feature is enabled. | ||
| 36 | println!("cargo:rustc-link-arg-bins=-Tinterrupt_group.x"); | ||
| 37 | } | ||
diff --git a/examples/mspm0c1104/memory.x b/examples/mspm0c1104/memory.x new file mode 100644 index 000000000..a9108835a --- /dev/null +++ b/examples/mspm0c1104/memory.x | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | MEMORY | ||
| 2 | { | ||
| 3 | FLASH : ORIGIN = 0x00000000, LENGTH = 16K | ||
| 4 | RAM : ORIGIN = 0x20000000, LENGTH = 1K | ||
| 5 | } | ||
diff --git a/examples/mspm0c1104/src/bin/blinky.rs b/examples/mspm0c1104/src/bin/blinky.rs new file mode 100644 index 000000000..0d974cc5e --- /dev/null +++ b/examples/mspm0c1104/src/bin/blinky.rs | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_mspm0::gpio::{Level, Output}; | ||
| 7 | use embassy_mspm0::Config; | ||
| 8 | use embassy_time::Timer; | ||
| 9 | use {defmt_rtt as _, panic_halt as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) -> ! { | ||
| 13 | info!("Hello world!"); | ||
| 14 | let p = embassy_mspm0::init(Config::default()); | ||
| 15 | |||
| 16 | let mut led1 = Output::new(p.PA22, Level::Low); | ||
| 17 | led1.set_inversion(true); | ||
| 18 | |||
| 19 | loop { | ||
| 20 | Timer::after_millis(400).await; | ||
| 21 | |||
| 22 | info!("Toggle"); | ||
| 23 | led1.toggle(); | ||
| 24 | } | ||
| 25 | } | ||
diff --git a/examples/mspm0c1104/src/bin/button.rs b/examples/mspm0c1104/src/bin/button.rs new file mode 100644 index 000000000..7face1618 --- /dev/null +++ b/examples/mspm0c1104/src/bin/button.rs | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_mspm0::gpio::{Input, Level, Output, Pull}; | ||
| 7 | use embassy_mspm0::Config; | ||
| 8 | use {defmt_rtt as _, panic_halt as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::main] | ||
| 11 | async fn main(_spawner: Spawner) -> ! { | ||
| 12 | info!("Hello world!"); | ||
| 13 | |||
| 14 | let p = embassy_mspm0::init(Config::default()); | ||
| 15 | |||
| 16 | let led1 = p.PA22; | ||
| 17 | let s2 = p.PA16; | ||
| 18 | |||
| 19 | let mut led1 = Output::new(led1, Level::Low); | ||
| 20 | |||
| 21 | let mut s2 = Input::new(s2, Pull::Up); | ||
| 22 | |||
| 23 | // led1 is active low | ||
| 24 | led1.set_high(); | ||
| 25 | |||
| 26 | loop { | ||
| 27 | s2.wait_for_falling_edge().await; | ||
| 28 | |||
| 29 | info!("Switch 2 was pressed"); | ||
| 30 | |||
| 31 | led1.toggle(); | ||
| 32 | } | ||
| 33 | } | ||
diff --git a/examples/mspm0c1104/src/bin/uart.rs b/examples/mspm0c1104/src/bin/uart.rs new file mode 100644 index 000000000..da611aaac --- /dev/null +++ b/examples/mspm0c1104/src/bin/uart.rs | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | //! Example of using blocking uart | ||
| 2 | //! | ||
| 3 | //! This uses the virtual COM port provided on the LP-MSPM0C1104 board. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_mspm0::uart::{Config, Uart}; | ||
| 11 | use {defmt_rtt as _, panic_halt as _}; | ||
| 12 | |||
| 13 | #[embassy_executor::main] | ||
| 14 | async fn main(_spawner: Spawner) -> ! { | ||
| 15 | info!("Hello world!"); | ||
| 16 | |||
| 17 | let p = embassy_mspm0::init(Default::default()); | ||
| 18 | |||
| 19 | let instance = p.UART0; | ||
| 20 | let tx = p.PA27; | ||
| 21 | let rx = p.PA26; | ||
| 22 | |||
| 23 | let config = Config::default(); | ||
| 24 | let mut uart = unwrap!(Uart::new_blocking(instance, rx, tx, config)); | ||
| 25 | |||
| 26 | unwrap!(uart.blocking_write(b"Hello Embassy World!\r\n")); | ||
| 27 | info!("wrote Hello, starting echo"); | ||
| 28 | |||
| 29 | let mut buf = [0u8; 1]; | ||
| 30 | |||
| 31 | loop { | ||
| 32 | unwrap!(uart.blocking_read(&mut buf)); | ||
| 33 | unwrap!(uart.blocking_write(&buf)); | ||
| 34 | } | ||
| 35 | } | ||
diff --git a/examples/mspm0g3507/.cargo/config.toml b/examples/mspm0g3507/.cargo/config.toml new file mode 100644 index 000000000..34c720cdd --- /dev/null +++ b/examples/mspm0g3507/.cargo/config.toml | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | ||
| 2 | # replace MSPM0G3507 with your chip as listed in `probe-rs chip list` | ||
| 3 | runner = "probe-rs run --chip MSPM0G3507 --protocol=swd" | ||
| 4 | |||
| 5 | [build] | ||
| 6 | target = "thumbv6m-none-eabi" | ||
| 7 | |||
| 8 | [env] | ||
| 9 | DEFMT_LOG = "debug" | ||
diff --git a/examples/mspm0g3507/Cargo.toml b/examples/mspm0g3507/Cargo.toml new file mode 100644 index 000000000..b6621c9c5 --- /dev/null +++ b/examples/mspm0g3507/Cargo.toml | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-mspm0-g3507-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | embassy-mspm0 = { version = "0.1.0", path = "../../embassy-mspm0", features = ["mspm0g3507pm", "defmt", "rt", "time-driver-any"] } | ||
| 9 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt"] } | ||
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | ||
| 11 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt"] } | ||
| 12 | panic-halt = "1.0.0" | ||
| 13 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||
| 14 | cortex-m-rt = { version = "0.7.0"} | ||
| 15 | defmt = "1.0.1" | ||
| 16 | defmt-rtt = "1.0.0" | ||
| 17 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } | ||
| 18 | panic-semihosting = "0.6.0" | ||
| 19 | |||
| 20 | [profile.release] | ||
| 21 | debug = 2 | ||
diff --git a/examples/mspm0g3507/README.md b/examples/mspm0g3507/README.md new file mode 100644 index 000000000..be91dc5a0 --- /dev/null +++ b/examples/mspm0g3507/README.md | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | # Examples for MSPM0M3507 | ||
| 2 | |||
| 3 | Run individual examples with | ||
| 4 | ``` | ||
| 5 | cargo run --bin <module-name> | ||
| 6 | ``` | ||
| 7 | for example | ||
| 8 | ``` | ||
| 9 | cargo run --bin blinky | ||
| 10 | ``` | ||
| 11 | |||
| 12 | ## Checklist before running examples | ||
| 13 | A large number of the examples are written for the [LP-MSPM0G3507](https://www.ti.com/tool/LP-MSPM0G3507) board. | ||
| 14 | |||
| 15 | 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. | ||
| 16 | |||
| 17 | * [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for G3507 it should be `probe-rs run --chip MSPM0G3507`. (use `probe-rs chip list` to find your chip) | ||
| 18 | * [ ] Update Cargo.toml to have the correct `embassy-mspm0` feature. For the LP-MSPM0G3507 it should be `mspm0g3507pm`. Look in the `Cargo.toml` file of the `embassy-mspm0` project to find the correct feature flag for your chip. | ||
| 19 | * [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately. | ||
| 20 | * [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic | ||
| 21 | |||
| 22 | If you are unsure, please drop by the Embassy Matrix chat for support, and let us know: | ||
| 23 | |||
| 24 | * Which example you are trying to run | ||
| 25 | * Which chip and board you are using | ||
| 26 | |||
| 27 | Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org | ||
diff --git a/examples/mspm0g3507/build.rs b/examples/mspm0g3507/build.rs new file mode 100644 index 000000000..2d777c2d3 --- /dev/null +++ b/examples/mspm0g3507/build.rs | |||
| @@ -0,0 +1,37 @@ | |||
| 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 | // You must tell cargo to link interrupt groups if the rt feature is enabled. | ||
| 36 | println!("cargo:rustc-link-arg-bins=-Tinterrupt_group.x"); | ||
| 37 | } | ||
diff --git a/examples/mspm0g3507/memory.x b/examples/mspm0g3507/memory.x new file mode 100644 index 000000000..37e381fbd --- /dev/null +++ b/examples/mspm0g3507/memory.x | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | MEMORY | ||
| 2 | { | ||
| 3 | FLASH : ORIGIN = 0x00000000, LENGTH = 128K | ||
| 4 | /* Select non-parity range of SRAM due to SRAM_ERR_01 errata in SLAZ758 */ | ||
| 5 | RAM : ORIGIN = 0x20200000, LENGTH = 32K | ||
| 6 | } | ||
diff --git a/examples/mspm0g3507/src/bin/blinky.rs b/examples/mspm0g3507/src/bin/blinky.rs new file mode 100644 index 000000000..055a5cd81 --- /dev/null +++ b/examples/mspm0g3507/src/bin/blinky.rs | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_mspm0::gpio::{Level, Output}; | ||
| 7 | use embassy_mspm0::Config; | ||
| 8 | use embassy_time::Timer; | ||
| 9 | use {defmt_rtt as _, panic_halt as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) -> ! { | ||
| 13 | info!("Hello world!"); | ||
| 14 | let p = embassy_mspm0::init(Config::default()); | ||
| 15 | |||
| 16 | let mut led1 = Output::new(p.PA0, Level::Low); | ||
| 17 | led1.set_inversion(true); | ||
| 18 | |||
| 19 | loop { | ||
| 20 | Timer::after_millis(400).await; | ||
| 21 | |||
| 22 | info!("Toggle"); | ||
| 23 | led1.toggle(); | ||
| 24 | } | ||
| 25 | } | ||
diff --git a/examples/mspm0g3507/src/bin/button.rs b/examples/mspm0g3507/src/bin/button.rs new file mode 100644 index 000000000..cde1f2892 --- /dev/null +++ b/examples/mspm0g3507/src/bin/button.rs | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_mspm0::gpio::{Input, Level, Output, Pull}; | ||
| 7 | use embassy_mspm0::Config; | ||
| 8 | use {defmt_rtt as _, panic_halt as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::main] | ||
| 11 | async fn main(_spawner: Spawner) -> ! { | ||
| 12 | info!("Hello world!"); | ||
| 13 | |||
| 14 | let p = embassy_mspm0::init(Config::default()); | ||
| 15 | |||
| 16 | let led1 = p.PA0; | ||
| 17 | let s2 = p.PB21; | ||
| 18 | |||
| 19 | let mut led1 = Output::new(led1, Level::Low); | ||
| 20 | |||
| 21 | let mut s2 = Input::new(s2, Pull::Up); | ||
| 22 | |||
| 23 | // led1 is active low | ||
| 24 | led1.set_high(); | ||
| 25 | |||
| 26 | loop { | ||
| 27 | s2.wait_for_falling_edge().await; | ||
| 28 | |||
| 29 | info!("Switch 2 was pressed"); | ||
| 30 | |||
| 31 | led1.toggle(); | ||
| 32 | } | ||
| 33 | } | ||
diff --git a/examples/mspm0g3507/src/bin/uart.rs b/examples/mspm0g3507/src/bin/uart.rs new file mode 100644 index 000000000..7e7e6db0e --- /dev/null +++ b/examples/mspm0g3507/src/bin/uart.rs | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | //! Example of using blocking uart | ||
| 2 | //! | ||
| 3 | //! This uses the virtual COM port provided on the LP-MSPM0G3507 board. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_mspm0::uart::{Config, Uart}; | ||
| 11 | use {defmt_rtt as _, panic_halt as _}; | ||
| 12 | |||
| 13 | #[embassy_executor::main] | ||
| 14 | async fn main(_spawner: Spawner) -> ! { | ||
| 15 | info!("Hello world!"); | ||
| 16 | |||
| 17 | let p = embassy_mspm0::init(Default::default()); | ||
| 18 | |||
| 19 | let instance = p.UART0; | ||
| 20 | let tx = p.PA10; | ||
| 21 | let rx = p.PA11; | ||
| 22 | |||
| 23 | let config = Config::default(); | ||
| 24 | let mut uart = unwrap!(Uart::new_blocking(instance, rx, tx, config)); | ||
| 25 | |||
| 26 | unwrap!(uart.blocking_write(b"Hello Embassy World!\r\n")); | ||
| 27 | info!("wrote Hello, starting echo"); | ||
| 28 | |||
| 29 | let mut buf = [0u8; 1]; | ||
| 30 | |||
| 31 | loop { | ||
| 32 | unwrap!(uart.blocking_read(&mut buf)); | ||
| 33 | unwrap!(uart.blocking_write(&buf)); | ||
| 34 | } | ||
| 35 | } | ||
diff --git a/examples/mspm0g3519/.cargo/config.toml b/examples/mspm0g3519/.cargo/config.toml new file mode 100644 index 000000000..7bba4646f --- /dev/null +++ b/examples/mspm0g3519/.cargo/config.toml | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | ||
| 2 | # replace MSPM0G3519 with your chip as listed in `probe-rs chip list` | ||
| 3 | runner = "probe-rs run --restore-unwritten --verify --chip MSPM0G3519 --protocol=swd" | ||
| 4 | |||
| 5 | [build] | ||
| 6 | target = "thumbv6m-none-eabi" | ||
| 7 | |||
| 8 | [env] | ||
| 9 | DEFMT_LOG = "trace" | ||
diff --git a/examples/mspm0g3519/Cargo.toml b/examples/mspm0g3519/Cargo.toml new file mode 100644 index 000000000..fd0e97c01 --- /dev/null +++ b/examples/mspm0g3519/Cargo.toml | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-mspm0-g3519-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | embassy-mspm0 = { version = "0.1.0", path = "../../embassy-mspm0", features = ["mspm0g3519pz", "defmt", "rt", "time-driver-any"] } | ||
| 9 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt"] } | ||
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | ||
| 11 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt"] } | ||
| 12 | panic-halt = "1.0.0" | ||
| 13 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||
| 14 | cortex-m-rt = { version = "0.7.0"} | ||
| 15 | defmt = "1.0.1" | ||
| 16 | defmt-rtt = "1.0.0" | ||
| 17 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } | ||
| 18 | panic-semihosting = "0.6.0" | ||
| 19 | |||
| 20 | [profile.release] | ||
| 21 | debug = 2 | ||
diff --git a/examples/mspm0g3519/README.md b/examples/mspm0g3519/README.md new file mode 100644 index 000000000..c392c9e25 --- /dev/null +++ b/examples/mspm0g3519/README.md | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | # Examples for MSPM0G3519 | ||
| 2 | |||
| 3 | Run individual examples with | ||
| 4 | ``` | ||
| 5 | cargo run --bin <module-name> | ||
| 6 | ``` | ||
| 7 | for example | ||
| 8 | ``` | ||
| 9 | cargo run --bin blinky | ||
| 10 | ``` | ||
| 11 | |||
| 12 | ## Checklist before running examples | ||
| 13 | A large number of the examples are written for the [LP-MSPM0G3519](https://www.ti.com/tool/LP-MSPM0G3519) board. | ||
| 14 | |||
| 15 | 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. | ||
| 16 | |||
| 17 | * [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for G3519 it should be `probe-rs run --chip MSPM0G3519`. (use `probe-rs chip list` to find your chip) | ||
| 18 | * [ ] Update Cargo.toml to have the correct `embassy-mspm0` feature. For the LP-MSPM0G3519 it should be `mspm0g3519pz`. Look in the `Cargo.toml` file of the `embassy-mspm0` project to find the correct feature flag for your chip. | ||
| 19 | * [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately. | ||
| 20 | * [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic | ||
| 21 | |||
| 22 | If you are unsure, please drop by the Embassy Matrix chat for support, and let us know: | ||
| 23 | |||
| 24 | * Which example you are trying to run | ||
| 25 | * Which chip and board you are using | ||
| 26 | |||
| 27 | Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org | ||
diff --git a/examples/mspm0g3519/build.rs b/examples/mspm0g3519/build.rs new file mode 100644 index 000000000..2d777c2d3 --- /dev/null +++ b/examples/mspm0g3519/build.rs | |||
| @@ -0,0 +1,37 @@ | |||
| 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 | // You must tell cargo to link interrupt groups if the rt feature is enabled. | ||
| 36 | println!("cargo:rustc-link-arg-bins=-Tinterrupt_group.x"); | ||
| 37 | } | ||
diff --git a/examples/mspm0g3519/memory.x b/examples/mspm0g3519/memory.x new file mode 100644 index 000000000..e6e0ec9e9 --- /dev/null +++ b/examples/mspm0g3519/memory.x | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | MEMORY | ||
| 2 | { | ||
| 3 | FLASH : ORIGIN = 0x00000000, LENGTH = 512K | ||
| 4 | /* Select non-parity range of SRAM due to SRAM_ERR_01 errata in SLAZ758 */ | ||
| 5 | RAM : ORIGIN = 0x20200000, LENGTH = 128K | ||
| 6 | } | ||
diff --git a/examples/mspm0g3519/src/bin/blinky.rs b/examples/mspm0g3519/src/bin/blinky.rs new file mode 100644 index 000000000..055a5cd81 --- /dev/null +++ b/examples/mspm0g3519/src/bin/blinky.rs | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_mspm0::gpio::{Level, Output}; | ||
| 7 | use embassy_mspm0::Config; | ||
| 8 | use embassy_time::Timer; | ||
| 9 | use {defmt_rtt as _, panic_halt as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) -> ! { | ||
| 13 | info!("Hello world!"); | ||
| 14 | let p = embassy_mspm0::init(Config::default()); | ||
| 15 | |||
| 16 | let mut led1 = Output::new(p.PA0, Level::Low); | ||
| 17 | led1.set_inversion(true); | ||
| 18 | |||
| 19 | loop { | ||
| 20 | Timer::after_millis(400).await; | ||
| 21 | |||
| 22 | info!("Toggle"); | ||
| 23 | led1.toggle(); | ||
| 24 | } | ||
| 25 | } | ||
diff --git a/examples/mspm0g3519/src/bin/button.rs b/examples/mspm0g3519/src/bin/button.rs new file mode 100644 index 000000000..c81cc2918 --- /dev/null +++ b/examples/mspm0g3519/src/bin/button.rs | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_mspm0::gpio::{Input, Level, Output, Pull}; | ||
| 7 | use embassy_mspm0::Config; | ||
| 8 | use {defmt_rtt as _, panic_halt as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::main] | ||
| 11 | async fn main(_spawner: Spawner) -> ! { | ||
| 12 | info!("Hello world!"); | ||
| 13 | |||
| 14 | let p = embassy_mspm0::init(Config::default()); | ||
| 15 | |||
| 16 | let led1 = p.PA0; | ||
| 17 | let s2 = p.PB3; | ||
| 18 | |||
| 19 | let mut led1 = Output::new(led1, Level::Low); | ||
| 20 | |||
| 21 | let mut s2 = Input::new(s2, Pull::Up); | ||
| 22 | |||
| 23 | // led1 is active low | ||
| 24 | led1.set_high(); | ||
| 25 | |||
| 26 | loop { | ||
| 27 | s2.wait_for_falling_edge().await; | ||
| 28 | |||
| 29 | info!("Switch 2 was pressed"); | ||
| 30 | |||
| 31 | led1.toggle(); | ||
| 32 | } | ||
| 33 | } | ||
diff --git a/examples/mspm0g3519/src/bin/uart.rs b/examples/mspm0g3519/src/bin/uart.rs new file mode 100644 index 000000000..498377c61 --- /dev/null +++ b/examples/mspm0g3519/src/bin/uart.rs | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | //! Example of using blocking uart | ||
| 2 | //! | ||
| 3 | //! This uses the virtual COM port provided on the LP-MSPM0G3519 board. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_mspm0::uart::{Config, Uart}; | ||
| 11 | use {defmt_rtt as _, panic_halt as _}; | ||
| 12 | |||
| 13 | #[embassy_executor::main] | ||
| 14 | async fn main(_spawner: Spawner) -> ! { | ||
| 15 | info!("Hello world!"); | ||
| 16 | |||
| 17 | let p = embassy_mspm0::init(Default::default()); | ||
| 18 | |||
| 19 | let instance = p.UART0; | ||
| 20 | let tx = p.PA10; | ||
| 21 | let rx = p.PA11; | ||
| 22 | |||
| 23 | let config = Config::default(); | ||
| 24 | let mut uart = unwrap!(Uart::new_blocking(instance, rx, tx, config)); | ||
| 25 | |||
| 26 | unwrap!(uart.blocking_write(b"Hello Embassy World!\r\n")); | ||
| 27 | info!("wrote Hello, starting echo"); | ||
| 28 | |||
| 29 | let mut buf = [0u8; 1]; | ||
| 30 | |||
| 31 | loop { | ||
| 32 | unwrap!(uart.blocking_read(&mut buf)); | ||
| 33 | unwrap!(uart.blocking_write(&buf)); | ||
| 34 | } | ||
| 35 | } | ||
diff --git a/examples/mspm0l1306/.cargo/config.toml b/examples/mspm0l1306/.cargo/config.toml new file mode 100644 index 000000000..93f148a71 --- /dev/null +++ b/examples/mspm0l1306/.cargo/config.toml | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | ||
| 2 | # replace MSPM0L1306 with your chip as listed in `probe-rs chip list` | ||
| 3 | runner = "probe-rs run --chip MSPM0L1306 --protocol=swd" | ||
| 4 | |||
| 5 | [build] | ||
| 6 | target = "thumbv6m-none-eabi" | ||
| 7 | |||
| 8 | [env] | ||
| 9 | DEFMT_LOG = "trace" | ||
diff --git a/examples/mspm0l1306/Cargo.toml b/examples/mspm0l1306/Cargo.toml new file mode 100644 index 000000000..6b1125810 --- /dev/null +++ b/examples/mspm0l1306/Cargo.toml | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-mspm0-l1306-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | embassy-mspm0 = { version = "0.1.0", path = "../../embassy-mspm0", features = ["mspm0l1306rhb", "defmt", "rt", "time-driver-any"] } | ||
| 9 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt"] } | ||
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | ||
| 11 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt"] } | ||
| 12 | panic-halt = "1.0.0" | ||
| 13 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||
| 14 | cortex-m-rt = { version = "0.7.0"} | ||
| 15 | defmt = "1.0.1" | ||
| 16 | defmt-rtt = "1.0.0" | ||
| 17 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } | ||
| 18 | panic-semihosting = "0.6.0" | ||
| 19 | |||
| 20 | [profile.release] | ||
| 21 | debug = 2 | ||
diff --git a/examples/mspm0l1306/README.md b/examples/mspm0l1306/README.md new file mode 100644 index 000000000..4d698e0fa --- /dev/null +++ b/examples/mspm0l1306/README.md | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | # Examples for MSPM0L1306 | ||
| 2 | |||
| 3 | Run individual examples with | ||
| 4 | ``` | ||
| 5 | cargo run --bin <module-name> | ||
| 6 | ``` | ||
| 7 | for example | ||
| 8 | ``` | ||
| 9 | cargo run --bin blinky | ||
| 10 | ``` | ||
| 11 | |||
| 12 | ## Checklist before running examples | ||
| 13 | A large number of the examples are written for the [LP-MSPM0L1306](https://www.ti.com/tool/LP-MSPM0L1306) board. | ||
| 14 | |||
| 15 | 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. | ||
| 16 | |||
| 17 | * [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L1306 it should be `probe-rs run --chip MSPM0L1306`. (use `probe-rs chip list` to find your chip) | ||
| 18 | * [ ] Update Cargo.toml to have the correct `embassy-mspm0` feature. For the LP-MSPM0L1306 it should be `mspm0l1306rhb`. Look in the `Cargo.toml` file of the `embassy-mspm0` project to find the correct feature flag for your chip. | ||
| 19 | * [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately. | ||
| 20 | * [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic | ||
| 21 | |||
| 22 | If you are unsure, please drop by the Embassy Matrix chat for support, and let us know: | ||
| 23 | |||
| 24 | * Which example you are trying to run | ||
| 25 | * Which chip and board you are using | ||
| 26 | |||
| 27 | Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org | ||
diff --git a/examples/mspm0l1306/build.rs b/examples/mspm0l1306/build.rs new file mode 100644 index 000000000..2d777c2d3 --- /dev/null +++ b/examples/mspm0l1306/build.rs | |||
| @@ -0,0 +1,37 @@ | |||
| 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 | // You must tell cargo to link interrupt groups if the rt feature is enabled. | ||
| 36 | println!("cargo:rustc-link-arg-bins=-Tinterrupt_group.x"); | ||
| 37 | } | ||
diff --git a/examples/mspm0l1306/memory.x b/examples/mspm0l1306/memory.x new file mode 100644 index 000000000..d93b61f44 --- /dev/null +++ b/examples/mspm0l1306/memory.x | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | MEMORY | ||
| 2 | { | ||
| 3 | FLASH : ORIGIN = 0x00000000, LENGTH = 64K | ||
| 4 | RAM : ORIGIN = 0x20000000, LENGTH = 4K | ||
| 5 | } | ||
diff --git a/examples/mspm0l1306/src/bin/blinky.rs b/examples/mspm0l1306/src/bin/blinky.rs new file mode 100644 index 000000000..055a5cd81 --- /dev/null +++ b/examples/mspm0l1306/src/bin/blinky.rs | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_mspm0::gpio::{Level, Output}; | ||
| 7 | use embassy_mspm0::Config; | ||
| 8 | use embassy_time::Timer; | ||
| 9 | use {defmt_rtt as _, panic_halt as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) -> ! { | ||
| 13 | info!("Hello world!"); | ||
| 14 | let p = embassy_mspm0::init(Config::default()); | ||
| 15 | |||
| 16 | let mut led1 = Output::new(p.PA0, Level::Low); | ||
| 17 | led1.set_inversion(true); | ||
| 18 | |||
| 19 | loop { | ||
| 20 | Timer::after_millis(400).await; | ||
| 21 | |||
| 22 | info!("Toggle"); | ||
| 23 | led1.toggle(); | ||
| 24 | } | ||
| 25 | } | ||
diff --git a/examples/mspm0l1306/src/bin/button.rs b/examples/mspm0l1306/src/bin/button.rs new file mode 100644 index 000000000..d8c85947f --- /dev/null +++ b/examples/mspm0l1306/src/bin/button.rs | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_mspm0::gpio::{Input, Level, Output, Pull}; | ||
| 7 | use embassy_mspm0::Config; | ||
| 8 | use {defmt_rtt as _, panic_halt as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::main] | ||
| 11 | async fn main(_spawner: Spawner) -> ! { | ||
| 12 | info!("Hello world!"); | ||
| 13 | |||
| 14 | let p = embassy_mspm0::init(Config::default()); | ||
| 15 | |||
| 16 | let led1 = p.PA0; | ||
| 17 | let s2 = p.PA14; | ||
| 18 | |||
| 19 | let mut led1 = Output::new(led1, Level::Low); | ||
| 20 | |||
| 21 | let mut s2 = Input::new(s2, Pull::Up); | ||
| 22 | |||
| 23 | // led1 is active low | ||
| 24 | led1.set_high(); | ||
| 25 | |||
| 26 | loop { | ||
| 27 | s2.wait_for_falling_edge().await; | ||
| 28 | |||
| 29 | info!("Switch 2 was pressed"); | ||
| 30 | |||
| 31 | led1.toggle(); | ||
| 32 | } | ||
| 33 | } | ||
diff --git a/examples/mspm0l1306/src/bin/uart.rs b/examples/mspm0l1306/src/bin/uart.rs new file mode 100644 index 000000000..95c56fdd3 --- /dev/null +++ b/examples/mspm0l1306/src/bin/uart.rs | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | //! Example of using blocking uart | ||
| 2 | //! | ||
| 3 | //! This uses the virtual COM port provided on the LP-MSPM0L1306 board. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_mspm0::uart::{Config, Uart}; | ||
| 11 | use {defmt_rtt as _, panic_halt as _}; | ||
| 12 | |||
| 13 | #[embassy_executor::main] | ||
| 14 | async fn main(_spawner: Spawner) -> ! { | ||
| 15 | info!("Hello world!"); | ||
| 16 | |||
| 17 | let p = embassy_mspm0::init(Default::default()); | ||
| 18 | |||
| 19 | let instance = p.UART0; | ||
| 20 | let tx = p.PA8; | ||
| 21 | let rx = p.PA9; | ||
| 22 | |||
| 23 | let config = Config::default(); | ||
| 24 | let mut uart = unwrap!(Uart::new_blocking(instance, rx, tx, config)); | ||
| 25 | |||
| 26 | unwrap!(uart.blocking_write(b"Hello Embassy World!\r\n")); | ||
| 27 | info!("wrote Hello, starting echo"); | ||
| 28 | |||
| 29 | let mut buf = [0u8; 1]; | ||
| 30 | |||
| 31 | loop { | ||
| 32 | unwrap!(uart.blocking_read(&mut buf)); | ||
| 33 | unwrap!(uart.blocking_write(&buf)); | ||
| 34 | } | ||
| 35 | } | ||
diff --git a/examples/mspm0l2228/.cargo/config.toml b/examples/mspm0l2228/.cargo/config.toml new file mode 100644 index 000000000..f383afd9e --- /dev/null +++ b/examples/mspm0l2228/.cargo/config.toml | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | ||
| 2 | # replace MSPM0L2228 with your chip as listed in `probe-rs chip list` | ||
| 3 | runner = "probe-rs run --restore-unwritten --verify --chip MSPM0L2228 --protocol=swd" | ||
| 4 | |||
| 5 | [build] | ||
| 6 | target = "thumbv6m-none-eabi" | ||
| 7 | |||
| 8 | [env] | ||
| 9 | DEFMT_LOG = "trace" | ||
diff --git a/examples/mspm0l2228/Cargo.toml b/examples/mspm0l2228/Cargo.toml new file mode 100644 index 000000000..08dfd5ff6 --- /dev/null +++ b/examples/mspm0l2228/Cargo.toml | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-mspm0-l2228-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | embassy-mspm0 = { version = "0.1.0", path = "../../embassy-mspm0", features = ["mspm0l2228pn", "defmt", "rt", "time-driver-any"] } | ||
| 9 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt"] } | ||
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | ||
| 11 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt"] } | ||
| 12 | panic-halt = "1.0.0" | ||
| 13 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||
| 14 | cortex-m-rt = { version = "0.7.0"} | ||
| 15 | defmt = "1.0.1" | ||
| 16 | defmt-rtt = "1.0.0" | ||
| 17 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } | ||
| 18 | panic-semihosting = "0.6.0" | ||
| 19 | |||
| 20 | [profile.release] | ||
| 21 | debug = 2 | ||
diff --git a/examples/mspm0l2228/README.md b/examples/mspm0l2228/README.md new file mode 100644 index 000000000..191022258 --- /dev/null +++ b/examples/mspm0l2228/README.md | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | # Examples for MSPM0L2228 | ||
| 2 | |||
| 3 | Run individual examples with | ||
| 4 | ``` | ||
| 5 | cargo run --bin <module-name> | ||
| 6 | ``` | ||
| 7 | for example | ||
| 8 | ``` | ||
| 9 | cargo run --bin blinky | ||
| 10 | ``` | ||
| 11 | |||
| 12 | ## Checklist before running examples | ||
| 13 | A large number of the examples are written for the [LP-MSPM0L2228](https://www.ti.com/tool/LP-MSPM0L2228) board. | ||
| 14 | |||
| 15 | 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. | ||
| 16 | |||
| 17 | * [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L2228 it should be `probe-rs run --chip MSPM0L2228`. (use `probe-rs chip list` to find your chip) | ||
| 18 | * [ ] Update Cargo.toml to have the correct `embassy-mspm0` feature. For example for LP-MSPM0L2228 it should be `mspm0l2228pn`. Look in the `Cargo.toml` file of the `embassy-mspm0` project to find the correct feature flag for your chip. | ||
| 19 | * [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately. | ||
| 20 | * [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic | ||
| 21 | |||
| 22 | If you are unsure, please drop by the Embassy Matrix chat for support, and let us know: | ||
| 23 | |||
| 24 | * Which example you are trying to run | ||
| 25 | * Which chip and board you are using | ||
| 26 | |||
| 27 | Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org | ||
diff --git a/examples/mspm0l2228/build.rs b/examples/mspm0l2228/build.rs new file mode 100644 index 000000000..2d777c2d3 --- /dev/null +++ b/examples/mspm0l2228/build.rs | |||
| @@ -0,0 +1,37 @@ | |||
| 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 | // You must tell cargo to link interrupt groups if the rt feature is enabled. | ||
| 36 | println!("cargo:rustc-link-arg-bins=-Tinterrupt_group.x"); | ||
| 37 | } | ||
diff --git a/examples/mspm0l2228/memory.x b/examples/mspm0l2228/memory.x new file mode 100644 index 000000000..aba414a88 --- /dev/null +++ b/examples/mspm0l2228/memory.x | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | MEMORY | ||
| 2 | { | ||
| 3 | FLASH : ORIGIN = 0x00000000, LENGTH = 256K | ||
| 4 | /* Select non-parity range of SRAM due to SRAM_ERR_01 errata in SLAZ758 */ | ||
| 5 | RAM : ORIGIN = 0x20200000, LENGTH = 32K | ||
| 6 | } | ||
diff --git a/examples/mspm0l2228/src/bin/blinky.rs b/examples/mspm0l2228/src/bin/blinky.rs new file mode 100644 index 000000000..055a5cd81 --- /dev/null +++ b/examples/mspm0l2228/src/bin/blinky.rs | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_mspm0::gpio::{Level, Output}; | ||
| 7 | use embassy_mspm0::Config; | ||
| 8 | use embassy_time::Timer; | ||
| 9 | use {defmt_rtt as _, panic_halt as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) -> ! { | ||
| 13 | info!("Hello world!"); | ||
| 14 | let p = embassy_mspm0::init(Config::default()); | ||
| 15 | |||
| 16 | let mut led1 = Output::new(p.PA0, Level::Low); | ||
| 17 | led1.set_inversion(true); | ||
| 18 | |||
| 19 | loop { | ||
| 20 | Timer::after_millis(400).await; | ||
| 21 | |||
| 22 | info!("Toggle"); | ||
| 23 | led1.toggle(); | ||
| 24 | } | ||
| 25 | } | ||
diff --git a/examples/mspm0l2228/src/bin/button.rs b/examples/mspm0l2228/src/bin/button.rs new file mode 100644 index 000000000..47bfd274b --- /dev/null +++ b/examples/mspm0l2228/src/bin/button.rs | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_mspm0::gpio::{Input, Level, Output, Pull}; | ||
| 7 | use embassy_mspm0::Config; | ||
| 8 | use {defmt_rtt as _, panic_halt as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::main] | ||
| 11 | async fn main(_spawner: Spawner) -> ! { | ||
| 12 | info!("Hello world!"); | ||
| 13 | |||
| 14 | let p = embassy_mspm0::init(Config::default()); | ||
| 15 | |||
| 16 | let led1 = p.PA0; | ||
| 17 | let s2 = p.PB8; | ||
| 18 | |||
| 19 | let mut led1 = Output::new(led1, Level::Low); | ||
| 20 | |||
| 21 | let mut s2 = Input::new(s2, Pull::Up); | ||
| 22 | |||
| 23 | // led1 is active low | ||
| 24 | led1.set_high(); | ||
| 25 | |||
| 26 | loop { | ||
| 27 | s2.wait_for_falling_edge().await; | ||
| 28 | |||
| 29 | info!("Switch 2 was pressed"); | ||
| 30 | |||
| 31 | led1.toggle(); | ||
| 32 | } | ||
| 33 | } | ||
diff --git a/examples/mspm0l2228/src/bin/uart.rs b/examples/mspm0l2228/src/bin/uart.rs new file mode 100644 index 000000000..a266add47 --- /dev/null +++ b/examples/mspm0l2228/src/bin/uart.rs | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | //! Example of using blocking uart | ||
| 2 | //! | ||
| 3 | //! This uses the virtual COM port provided on the LP-MSPM0L2228 board. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_mspm0::uart::{Config, Uart}; | ||
| 11 | use {defmt_rtt as _, panic_halt as _}; | ||
| 12 | |||
| 13 | #[embassy_executor::main] | ||
| 14 | async fn main(_spawner: Spawner) -> ! { | ||
| 15 | info!("Hello world!"); | ||
| 16 | |||
| 17 | let p = embassy_mspm0::init(Default::default()); | ||
| 18 | |||
| 19 | let instance = p.UART0; | ||
| 20 | let tx = p.PA10; | ||
| 21 | let rx = p.PA11; | ||
| 22 | |||
| 23 | let config = Config::default(); | ||
| 24 | let mut uart = unwrap!(Uart::new_blocking(instance, rx, tx, config)); | ||
| 25 | |||
| 26 | unwrap!(uart.blocking_write(b"Hello Embassy World!\r\n")); | ||
| 27 | info!("wrote Hello, starting echo"); | ||
| 28 | |||
| 29 | let mut buf = [0u8; 1]; | ||
| 30 | |||
| 31 | loop { | ||
| 32 | unwrap!(uart.blocking_read(&mut buf)); | ||
| 33 | unwrap!(uart.blocking_write(&buf)); | ||
| 34 | } | ||
| 35 | } | ||
diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index 98a678815..d9e8ca2f9 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml | |||
| @@ -15,15 +15,14 @@ log = [ | |||
| 15 | ] | 15 | ] |
| 16 | 16 | ||
| 17 | [dependencies] | 17 | [dependencies] |
| 18 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync" } | 18 | embassy-sync = { version = "0.7.0", 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.7.0", 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.4.0", 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.3.1", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } |
| 22 | 22 | ||
| 23 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 23 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 24 | cortex-m-rt = "0.7.0" | 24 | cortex-m-rt = "0.7.0" |
| 25 | panic-probe = { version = "0.3" } | 25 | panic-probe = "1.0.0" |
| 26 | rand = { version = "0.8.4", default-features = false } | ||
| 27 | serde = { version = "1.0.136", default-features = false } | 26 | serde = { version = "1.0.136", default-features = false } |
| 28 | rtos-trace = "0.1.3" | 27 | rtos-trace = "0.1.3" |
| 29 | systemview-target = { version = "0.1.2", features = ["callbacks-app", "callbacks-os", "log", "cortex-m"] } | 28 | systemview-target = { version = "0.1.2", features = ["callbacks-app", "callbacks-os", "log", "cortex-m"] } |
diff --git a/examples/nrf51/Cargo.toml b/examples/nrf51/Cargo.toml index 93a19bea7..91f78737f 100644 --- a/examples/nrf51/Cargo.toml +++ b/examples/nrf51/Cargo.toml | |||
| @@ -5,16 +5,16 @@ 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.7.0", path = "../../embassy-executor", features = ["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.4.0", 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.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] } |
| 11 | 11 | ||
| 12 | defmt = "0.3" | 12 | defmt = "1.0.1" |
| 13 | defmt-rtt = "0.4" | 13 | defmt-rtt = "1.0.0" |
| 14 | 14 | ||
| 15 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 15 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 16 | cortex-m-rt = "0.7" | 16 | cortex-m-rt = "0.7" |
| 17 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 17 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 18 | 18 | ||
| 19 | [profile.release] | 19 | [profile.release] |
| 20 | debug = 2 | 20 | debug = 2 |
diff --git a/examples/nrf52810/Cargo.toml b/examples/nrf52810/Cargo.toml index 0e3e81c3f..87da89efe 100644 --- a/examples/nrf52810/Cargo.toml +++ b/examples/nrf52810/Cargo.toml | |||
| @@ -6,19 +6,19 @@ 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.7.0", 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.7.0", path = "../../embassy-executor", features = ["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.4.0", 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.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } |
| 13 | 13 | ||
| 14 | defmt = "0.3" | 14 | defmt = "1.0.1" |
| 15 | defmt-rtt = "0.4" | 15 | defmt-rtt = "1.0.0" |
| 16 | 16 | ||
| 17 | fixed = "1.10.0" | 17 | fixed = "1.10.0" |
| 18 | static_cell = { version = "2" } | 18 | static_cell = { version = "2" } |
| 19 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 19 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 20 | cortex-m-rt = "0.7.0" | 20 | cortex-m-rt = "0.7.0" |
| 21 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 21 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 22 | 22 | ||
| 23 | [profile.release] | 23 | [profile.release] |
| 24 | debug = 2 | 24 | debug = 2 |
diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml index 7fae7aefc..afd269f72 100644 --- a/examples/nrf52840-rtic/Cargo.toml +++ b/examples/nrf52840-rtic/Cargo.toml | |||
| @@ -8,16 +8,17 @@ 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.7.0", 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.4.0", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime"] } |
| 13 | embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | 13 | embassy-time-queue-utils = { version = "0.1", path = "../../embassy-time-queue-utils", features = ["generic-queue-8"] } |
| 14 | embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | ||
| 14 | 15 | ||
| 15 | defmt = "0.3" | 16 | defmt = "1.0.1" |
| 16 | defmt-rtt = "0.4" | 17 | defmt-rtt = "1.0.0" |
| 17 | 18 | ||
| 18 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 19 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 19 | cortex-m-rt = "0.7.0" | 20 | cortex-m-rt = "0.7.0" |
| 20 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 21 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 21 | 22 | ||
| 22 | [profile.release] | 23 | [profile.release] |
| 23 | debug = 2 | 24 | debug = 2 |
diff --git a/examples/nrf52840-rtic/src/bin/blinky.rs b/examples/nrf52840-rtic/src/bin/blinky.rs index 060bb9ebc..719e22729 100644 --- a/examples/nrf52840-rtic/src/bin/blinky.rs +++ b/examples/nrf52840-rtic/src/bin/blinky.rs | |||
| @@ -4,11 +4,11 @@ | |||
| 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}; |
| 11 | use embassy_nrf::peripherals; | 11 | use embassy_nrf::{peripherals, Peri}; |
| 12 | use embassy_time::Timer; | 12 | use embassy_time::Timer; |
| 13 | 13 | ||
| 14 | #[shared] | 14 | #[shared] |
| @@ -28,7 +28,7 @@ mod app { | |||
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | #[task(priority = 1)] | 30 | #[task(priority = 1)] |
| 31 | async fn blink(_cx: blink::Context, pin: peripherals::P0_13) { | 31 | async fn blink(_cx: blink::Context, pin: Peri<'static, peripherals::P0_13>) { |
| 32 | let mut led = Output::new(pin, Level::Low, OutputDrive::Standard); | 32 | let mut led = Output::new(pin, Level::Low, OutputDrive::Standard); |
| 33 | 33 | ||
| 34 | loop { | 34 | loop { |
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 17fa6234d..4140e49d2 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml | |||
| @@ -6,26 +6,26 @@ 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.7.0", 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.7.0", path = "../../embassy-executor", features = ["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.4.0", 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.3.1", 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.7.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.4.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"] } |
| 17 | embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } | 17 | embassy-net-esp-hosted = { version = "0.2.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } |
| 18 | embassy-net-enc28j60 = { version = "0.1.0", path = "../../embassy-net-enc28j60", features = ["defmt"] } | 18 | embassy-net-enc28j60 = { version = "0.2.0", path = "../../embassy-net-enc28j60", features = ["defmt"] } |
| 19 | 19 | ||
| 20 | defmt = "0.3" | 20 | defmt = "1.0.1" |
| 21 | defmt-rtt = "0.4" | 21 | defmt-rtt = "1.0.0" |
| 22 | 22 | ||
| 23 | fixed = "1.10.0" | 23 | fixed = "1.10.0" |
| 24 | static_cell = { version = "2" } | 24 | static_cell = { version = "2" } |
| 25 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 25 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 26 | cortex-m-rt = "0.7.0" | 26 | cortex-m-rt = "0.7.0" |
| 27 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 27 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 28 | rand = { version = "0.8.4", default-features = false } | 28 | rand = { version = "0.9.0", default-features = false } |
| 29 | embedded-storage = "0.3.1" | 29 | embedded-storage = "0.3.1" |
| 30 | usbd-hid = "0.8.1" | 30 | usbd-hid = "0.8.1" |
| 31 | serde = { version = "1.0.136", default-features = false } | 31 | serde = { version = "1.0.136", default-features = false } |
diff --git a/examples/nrf52840/src/bin/buffered_uart.rs b/examples/nrf52840/src/bin/buffered_uart.rs index 6ac72bcaf..f0a066818 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] |
| @@ -28,9 +28,9 @@ async fn main(_spawner: Spawner) { | |||
| 28 | p.PPI_CH0, | 28 | p.PPI_CH0, |
| 29 | p.PPI_CH1, | 29 | p.PPI_CH1, |
| 30 | p.PPI_GROUP0, | 30 | p.PPI_GROUP0, |
| 31 | Irqs, | ||
| 32 | p.P0_08, | 31 | p.P0_08, |
| 33 | p.P0_06, | 32 | p.P0_06, |
| 33 | Irqs, | ||
| 34 | config, | 34 | config, |
| 35 | &mut rx_buffer, | 35 | &mut rx_buffer, |
| 36 | &mut tx_buffer, | 36 | &mut tx_buffer, |
diff --git a/examples/nrf52840/src/bin/channel.rs b/examples/nrf52840/src/bin/channel.rs index 7fcea9dbd..e06ba1c73 100644 --- a/examples/nrf52840/src/bin/channel.rs +++ b/examples/nrf52840/src/bin/channel.rs | |||
| @@ -35,8 +35,8 @@ async fn main(spawner: Spawner) { | |||
| 35 | 35 | ||
| 36 | loop { | 36 | loop { |
| 37 | match CHANNEL.receive().await { | 37 | match CHANNEL.receive().await { |
| 38 | LedState::On => led.set_high(), | 38 | LedState::On => led.set_low(), |
| 39 | LedState::Off => led.set_low(), | 39 | LedState::Off => led.set_high(), |
| 40 | } | 40 | } |
| 41 | } | 41 | } |
| 42 | } | 42 | } |
diff --git a/examples/nrf52840/src/bin/channel_sender_receiver.rs b/examples/nrf52840/src/bin/channel_sender_receiver.rs index 3095a04ec..74c62ca20 100644 --- a/examples/nrf52840/src/bin/channel_sender_receiver.rs +++ b/examples/nrf52840/src/bin/channel_sender_receiver.rs | |||
| @@ -3,7 +3,8 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::unwrap; | 4 | use defmt::unwrap; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_nrf::gpio::{AnyPin, Level, Output, OutputDrive, Pin}; | 6 | use embassy_nrf::gpio::{AnyPin, Level, Output, OutputDrive}; |
| 7 | use embassy_nrf::Peri; | ||
| 7 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | 8 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; |
| 8 | use embassy_sync::channel::{Channel, Receiver, Sender}; | 9 | use embassy_sync::channel::{Channel, Receiver, Sender}; |
| 9 | use embassy_time::Timer; | 10 | use embassy_time::Timer; |
| @@ -28,13 +29,13 @@ async fn send_task(sender: Sender<'static, NoopRawMutex, LedState, 1>) { | |||
| 28 | } | 29 | } |
| 29 | 30 | ||
| 30 | #[embassy_executor::task] | 31 | #[embassy_executor::task] |
| 31 | async fn recv_task(led: AnyPin, receiver: Receiver<'static, NoopRawMutex, LedState, 1>) { | 32 | async fn recv_task(led: Peri<'static, AnyPin>, receiver: Receiver<'static, NoopRawMutex, LedState, 1>) { |
| 32 | let mut led = Output::new(led, Level::Low, OutputDrive::Standard); | 33 | let mut led = Output::new(led, Level::Low, OutputDrive::Standard); |
| 33 | 34 | ||
| 34 | loop { | 35 | loop { |
| 35 | match receiver.receive().await { | 36 | match receiver.receive().await { |
| 36 | LedState::On => led.set_high(), | 37 | LedState::On => led.set_low(), |
| 37 | LedState::Off => led.set_low(), | 38 | LedState::Off => led.set_high(), |
| 38 | } | 39 | } |
| 39 | } | 40 | } |
| 40 | } | 41 | } |
| @@ -45,5 +46,5 @@ async fn main(spawner: Spawner) { | |||
| 45 | let channel = CHANNEL.init(Channel::new()); | 46 | let channel = CHANNEL.init(Channel::new()); |
| 46 | 47 | ||
| 47 | unwrap!(spawner.spawn(send_task(channel.sender()))); | 48 | unwrap!(spawner.spawn(send_task(channel.sender()))); |
| 48 | unwrap!(spawner.spawn(recv_task(p.P0_13.degrade(), channel.receiver()))); | 49 | unwrap!(spawner.spawn(recv_task(p.P0_13.into(), channel.receiver()))); |
| 49 | } | 50 | } |
diff --git a/examples/nrf52840/src/bin/ethernet_enc28j60.rs b/examples/nrf52840/src/bin/ethernet_enc28j60.rs index 94cf09c88..0946492fe 100644 --- a/examples/nrf52840/src/bin/ethernet_enc28j60.rs +++ b/examples/nrf52840/src/bin/ethernet_enc28j60.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_net::tcp::TcpSocket; | 6 | use embassy_net::tcp::TcpSocket; |
| 7 | use embassy_net::{Stack, StackResources}; | 7 | use embassy_net::StackResources; |
| 8 | use embassy_net_enc28j60::Enc28j60; | 8 | use embassy_net_enc28j60::Enc28j60; |
| 9 | use embassy_nrf::gpio::{Level, Output, OutputDrive}; | 9 | use embassy_nrf::gpio::{Level, Output, OutputDrive}; |
| 10 | use embassy_nrf::rng::Rng; | 10 | use embassy_nrf::rng::Rng; |
| @@ -23,11 +23,12 @@ bind_interrupts!(struct Irqs { | |||
| 23 | 23 | ||
| 24 | #[embassy_executor::task] | 24 | #[embassy_executor::task] |
| 25 | async fn net_task( | 25 | async fn net_task( |
| 26 | stack: &'static Stack< | 26 | mut runner: embassy_net::Runner< |
| 27 | 'static, | ||
| 27 | Enc28j60<ExclusiveDevice<Spim<'static, peripherals::SPI3>, Output<'static>, Delay>, Output<'static>>, | 28 | Enc28j60<ExclusiveDevice<Spim<'static, peripherals::SPI3>, Output<'static>, Delay>, Output<'static>>, |
| 28 | >, | 29 | >, |
| 29 | ) -> ! { | 30 | ) -> ! { |
| 30 | stack.run().await | 31 | runner.run().await |
| 31 | } | 32 | } |
| 32 | 33 | ||
| 33 | #[embassy_executor::main] | 34 | #[embassy_executor::main] |
| @@ -67,12 +68,9 @@ async fn main(spawner: Spawner) { | |||
| 67 | 68 | ||
| 68 | // Init network stack | 69 | // Init network stack |
| 69 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 70 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 70 | static STACK: StaticCell< | 71 | let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); |
| 71 | Stack<Enc28j60<ExclusiveDevice<Spim<'static, peripherals::SPI3>, Output<'static>, Delay>, Output<'static>>>, | ||
| 72 | > = StaticCell::new(); | ||
| 73 | let stack = STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); | ||
| 74 | 72 | ||
| 75 | unwrap!(spawner.spawn(net_task(stack))); | 73 | unwrap!(spawner.spawn(net_task(runner))); |
| 76 | 74 | ||
| 77 | // And now we can use it! | 75 | // And now we can use it! |
| 78 | 76 | ||
diff --git a/examples/nrf52840/src/bin/ieee802154_receive.rs b/examples/nrf52840/src/bin/ieee802154_receive.rs new file mode 100644 index 000000000..ede8fca65 --- /dev/null +++ b/examples/nrf52840/src/bin/ieee802154_receive.rs | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_nrf::config::{Config, HfclkSource}; | ||
| 6 | use embassy_nrf::gpio::{Level, Output, OutputDrive}; | ||
| 7 | use embassy_nrf::radio::ieee802154::{self, Packet}; | ||
| 8 | use embassy_nrf::{peripherals, radio}; | ||
| 9 | use embassy_time::Timer; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | embassy_nrf::bind_interrupts!(struct Irqs { | ||
| 13 | RADIO => radio::InterruptHandler<peripherals::RADIO>; | ||
| 14 | }); | ||
| 15 | |||
| 16 | #[embassy_executor::main] | ||
| 17 | async fn main(_spawner: Spawner) { | ||
| 18 | let mut config = Config::default(); | ||
| 19 | config.hfclk_source = HfclkSource::ExternalXtal; | ||
| 20 | let peripherals = embassy_nrf::init(config); | ||
| 21 | |||
| 22 | // assumes LED on P0_15 with active-high polarity | ||
| 23 | let mut gpo_led = Output::new(peripherals.P0_15, Level::Low, OutputDrive::Standard); | ||
| 24 | |||
| 25 | let mut radio = ieee802154::Radio::new(peripherals.RADIO, Irqs); | ||
| 26 | let mut packet = Packet::new(); | ||
| 27 | |||
| 28 | loop { | ||
| 29 | gpo_led.set_low(); | ||
| 30 | let rv = radio.receive(&mut packet).await; | ||
| 31 | gpo_led.set_high(); | ||
| 32 | match rv { | ||
| 33 | Err(_) => defmt::error!("receive() Err"), | ||
| 34 | Ok(_) => defmt::info!("receive() {:?}", *packet), | ||
| 35 | } | ||
| 36 | Timer::after_millis(100u64).await; | ||
| 37 | } | ||
| 38 | } | ||
diff --git a/examples/nrf52840/src/bin/ieee802154_send.rs b/examples/nrf52840/src/bin/ieee802154_send.rs new file mode 100644 index 000000000..7af9d1d06 --- /dev/null +++ b/examples/nrf52840/src/bin/ieee802154_send.rs | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_nrf::config::{Config, HfclkSource}; | ||
| 6 | use embassy_nrf::gpio::{Level, Output, OutputDrive}; | ||
| 7 | use embassy_nrf::radio::ieee802154::{self, Packet}; | ||
| 8 | use embassy_nrf::{peripherals, radio}; | ||
| 9 | use embassy_time::Timer; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | embassy_nrf::bind_interrupts!(struct Irqs { | ||
| 13 | RADIO => radio::InterruptHandler<peripherals::RADIO>; | ||
| 14 | }); | ||
| 15 | |||
| 16 | #[embassy_executor::main] | ||
| 17 | async fn main(_spawner: Spawner) { | ||
| 18 | let mut config = Config::default(); | ||
| 19 | config.hfclk_source = HfclkSource::ExternalXtal; | ||
| 20 | let peripherals = embassy_nrf::init(config); | ||
| 21 | |||
| 22 | // assumes LED on P0_15 with active-high polarity | ||
| 23 | let mut gpo_led = Output::new(peripherals.P0_15, Level::Low, OutputDrive::Standard); | ||
| 24 | |||
| 25 | let mut radio = ieee802154::Radio::new(peripherals.RADIO, Irqs); | ||
| 26 | let mut packet = Packet::new(); | ||
| 27 | |||
| 28 | loop { | ||
| 29 | packet.copy_from_slice(&[0_u8; 16]); | ||
| 30 | gpo_led.set_high(); | ||
| 31 | let rv = radio.try_send(&mut packet).await; | ||
| 32 | gpo_led.set_low(); | ||
| 33 | match rv { | ||
| 34 | Err(_) => defmt::error!("try_send() Err"), | ||
| 35 | Ok(_) => defmt::info!("try_send() {:?}", *packet), | ||
| 36 | } | ||
| 37 | Timer::after_millis(1000u64).await; | ||
| 38 | } | ||
| 39 | } | ||
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/pdm_continuous.rs b/examples/nrf52840/src/bin/pdm_continuous.rs index e948203a5..0d76636b0 100644 --- a/examples/nrf52840/src/bin/pdm_continuous.rs +++ b/examples/nrf52840/src/bin/pdm_continuous.rs | |||
| @@ -20,14 +20,14 @@ bind_interrupts!(struct Irqs { | |||
| 20 | 20 | ||
| 21 | #[embassy_executor::main] | 21 | #[embassy_executor::main] |
| 22 | async fn main(_p: Spawner) { | 22 | async fn main(_p: Spawner) { |
| 23 | let mut p = embassy_nrf::init(Default::default()); | 23 | let p = embassy_nrf::init(Default::default()); |
| 24 | let mut config = Config::default(); | 24 | let mut config = Config::default(); |
| 25 | // Pins are correct for the onboard microphone on the Feather nRF52840 Sense. | 25 | // Pins are correct for the onboard microphone on the Feather nRF52840 Sense. |
| 26 | config.frequency = Frequency::_1280K; // 16 kHz sample rate | 26 | config.frequency = Frequency::_1280K; // 16 kHz sample rate |
| 27 | config.ratio = Ratio::RATIO80; | 27 | config.ratio = Ratio::RATIO80; |
| 28 | config.operation_mode = OperationMode::Mono; | 28 | config.operation_mode = OperationMode::Mono; |
| 29 | config.gain_left = I7F1::from_bits(5); // 2.5 dB | 29 | config.gain_left = I7F1::from_bits(5); // 2.5 dB |
| 30 | let mut pdm = Pdm::new(p.PDM, Irqs, &mut p.P0_00, &mut p.P0_01, config); | 30 | let mut pdm = Pdm::new(p.PDM, Irqs, p.P0_00, p.P0_01, config); |
| 31 | 31 | ||
| 32 | let mut bufs = [[0; 1024]; 2]; | 32 | let mut bufs = [[0; 1024]; 2]; |
| 33 | 33 | ||
diff --git a/examples/nrf52840/src/bin/pwm_sequence_ws2812b.rs b/examples/nrf52840/src/bin/pwm_sequence_ws2812b.rs index 751cf4425..df8da8800 100644 --- a/examples/nrf52840/src/bin/pwm_sequence_ws2812b.rs +++ b/examples/nrf52840/src/bin/pwm_sequence_ws2812b.rs | |||
| @@ -14,6 +14,16 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 14 | // https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf. | 14 | // https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf. |
| 15 | // This demo lights up a single LED in blue. It then proceeds | 15 | // This demo lights up a single LED in blue. It then proceeds |
| 16 | // to pulsate the LED rapidly. | 16 | // to pulsate the LED rapidly. |
| 17 | // | ||
| 18 | // /!\ NOTE FOR nRF52840-DK users /!\ | ||
| 19 | // | ||
| 20 | // If you're using the nRF52840-DK, the default "Vdd" power source | ||
| 21 | // will set the GPIO I/O voltage to 3.0v, using the onboard regulator. | ||
| 22 | // This can sometimes not be enough to drive the WS2812B signal if you | ||
| 23 | // are not using an external level shifter. If you set the board to "USB" | ||
| 24 | // power instead (and provide power via the "nRF USB" connector), the board | ||
| 25 | // will instead power the I/Os at 3.3v, which is often enough (but still | ||
| 26 | // out of official spec) for the WS2812Bs to work properly. | ||
| 17 | 27 | ||
| 18 | // In the following declarations, setting the high bit tells the PWM | 28 | // In the following declarations, setting the high bit tells the PWM |
| 19 | // to reverse polarity, which is what the WS2812B expects. | 29 | // to reverse polarity, which is what the WS2812B expects. |
diff --git a/examples/nrf52840/src/bin/qspi_lowpower.rs b/examples/nrf52840/src/bin/qspi_lowpower.rs index 516c9b481..238a0d941 100644 --- a/examples/nrf52840/src/bin/qspi_lowpower.rs +++ b/examples/nrf52840/src/bin/qspi_lowpower.rs | |||
| @@ -37,14 +37,14 @@ async fn main(_p: Spawner) { | |||
| 37 | }); | 37 | }); |
| 38 | 38 | ||
| 39 | let mut q = qspi::Qspi::new( | 39 | let mut q = qspi::Qspi::new( |
| 40 | &mut p.QSPI, | 40 | p.QSPI.reborrow(), |
| 41 | Irqs, | 41 | Irqs, |
| 42 | &mut p.P0_19, | 42 | p.P0_19.reborrow(), |
| 43 | &mut p.P0_17, | 43 | p.P0_17.reborrow(), |
| 44 | &mut p.P0_20, | 44 | p.P0_20.reborrow(), |
| 45 | &mut p.P0_21, | 45 | p.P0_21.reborrow(), |
| 46 | &mut p.P0_22, | 46 | p.P0_22.reborrow(), |
| 47 | &mut p.P0_23, | 47 | p.P0_23.reborrow(), |
| 48 | config, | 48 | config, |
| 49 | ); | 49 | ); |
| 50 | 50 | ||
diff --git a/examples/nrf52840/src/bin/rng.rs b/examples/nrf52840/src/bin/rng.rs index 326054c9a..f32d17cd9 100644..100755 --- a/examples/nrf52840/src/bin/rng.rs +++ b/examples/nrf52840/src/bin/rng.rs | |||
| @@ -22,7 +22,7 @@ async fn main(_spawner: Spawner) { | |||
| 22 | defmt::info!("Some random bytes: {:?}", bytes); | 22 | defmt::info!("Some random bytes: {:?}", bytes); |
| 23 | 23 | ||
| 24 | // Sync API with `rand` | 24 | // Sync API with `rand` |
| 25 | defmt::info!("A random number from 1 to 10: {:?}", rng.gen_range(1..=10)); | 25 | defmt::info!("A random number from 1 to 10: {:?}", rng.random_range(1..=10)); |
| 26 | 26 | ||
| 27 | let mut bytes = [0; 1024]; | 27 | let mut bytes = [0; 1024]; |
| 28 | rng.fill_bytes(&mut bytes).await; | 28 | rng.fill_bytes(&mut bytes).await; |
diff --git a/examples/nrf52840/src/bin/saadc.rs b/examples/nrf52840/src/bin/saadc.rs index 653b7d606..cf2d860ab 100644 --- a/examples/nrf52840/src/bin/saadc.rs +++ b/examples/nrf52840/src/bin/saadc.rs | |||
| @@ -16,7 +16,7 @@ bind_interrupts!(struct Irqs { | |||
| 16 | async fn main(_p: Spawner) { | 16 | async fn main(_p: Spawner) { |
| 17 | let mut p = embassy_nrf::init(Default::default()); | 17 | let mut p = embassy_nrf::init(Default::default()); |
| 18 | let config = Config::default(); | 18 | let config = Config::default(); |
| 19 | let channel_config = ChannelConfig::single_ended(&mut p.P0_02); | 19 | let channel_config = ChannelConfig::single_ended(p.P0_02.reborrow()); |
| 20 | let mut saadc = Saadc::new(p.SAADC, Irqs, config, [channel_config]); | 20 | let mut saadc = Saadc::new(p.SAADC, Irqs, config, [channel_config]); |
| 21 | 21 | ||
| 22 | loop { | 22 | loop { |
diff --git a/examples/nrf52840/src/bin/saadc_continuous.rs b/examples/nrf52840/src/bin/saadc_continuous.rs index f76fa3570..e8f169c8c 100644 --- a/examples/nrf52840/src/bin/saadc_continuous.rs +++ b/examples/nrf52840/src/bin/saadc_continuous.rs | |||
| @@ -18,9 +18,9 @@ bind_interrupts!(struct Irqs { | |||
| 18 | async fn main(_p: Spawner) { | 18 | async fn main(_p: Spawner) { |
| 19 | let mut p = embassy_nrf::init(Default::default()); | 19 | let mut p = embassy_nrf::init(Default::default()); |
| 20 | let config = Config::default(); | 20 | let config = Config::default(); |
| 21 | let channel_1_config = ChannelConfig::single_ended(&mut p.P0_02); | 21 | let channel_1_config = ChannelConfig::single_ended(p.P0_02.reborrow()); |
| 22 | let channel_2_config = ChannelConfig::single_ended(&mut p.P0_03); | 22 | let channel_2_config = ChannelConfig::single_ended(p.P0_03.reborrow()); |
| 23 | let channel_3_config = ChannelConfig::single_ended(&mut p.P0_04); | 23 | let channel_3_config = ChannelConfig::single_ended(p.P0_04.reborrow()); |
| 24 | let mut saadc = Saadc::new( | 24 | let mut saadc = Saadc::new( |
| 25 | p.SAADC, | 25 | p.SAADC, |
| 26 | Irqs, | 26 | Irqs, |
| @@ -40,9 +40,9 @@ async fn main(_p: Spawner) { | |||
| 40 | 40 | ||
| 41 | saadc | 41 | saadc |
| 42 | .run_task_sampler( | 42 | .run_task_sampler( |
| 43 | &mut p.TIMER0, | 43 | p.TIMER0.reborrow(), |
| 44 | &mut p.PPI_CH0, | 44 | p.PPI_CH0.reborrow(), |
| 45 | &mut p.PPI_CH1, | 45 | p.PPI_CH1.reborrow(), |
| 46 | Frequency::F1MHz, | 46 | Frequency::F1MHz, |
| 47 | 1000, // We want to sample at 1KHz | 47 | 1000, // We want to sample at 1KHz |
| 48 | &mut bufs, | 48 | &mut bufs, |
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..e30a3855d 100644 --- a/examples/nrf52840/src/bin/twim.rs +++ b/examples/nrf52840/src/bin/twim.rs | |||
| @@ -9,12 +9,13 @@ use defmt::*; | |||
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_nrf::twim::{self, Twim}; | 10 | use embassy_nrf::twim::{self, Twim}; |
| 11 | use embassy_nrf::{bind_interrupts, peripherals}; | 11 | use embassy_nrf::{bind_interrupts, peripherals}; |
| 12 | use static_cell::ConstStaticCell; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 14 | ||
| 14 | const ADDRESS: u8 = 0x50; | 15 | const ADDRESS: u8 = 0x50; |
| 15 | 16 | ||
| 16 | bind_interrupts!(struct Irqs { | 17 | bind_interrupts!(struct Irqs { |
| 17 | SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twim::InterruptHandler<peripherals::TWISPI0>; | 18 | TWISPI0 => twim::InterruptHandler<peripherals::TWISPI0>; |
| 18 | }); | 19 | }); |
| 19 | 20 | ||
| 20 | #[embassy_executor::main] | 21 | #[embassy_executor::main] |
| @@ -22,7 +23,8 @@ async fn main(_spawner: Spawner) { | |||
| 22 | let p = embassy_nrf::init(Default::default()); | 23 | let p = embassy_nrf::init(Default::default()); |
| 23 | info!("Initializing TWI..."); | 24 | info!("Initializing TWI..."); |
| 24 | let config = twim::Config::default(); | 25 | let config = twim::Config::default(); |
| 25 | let mut twi = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config); | 26 | static RAM_BUFFER: ConstStaticCell<[u8; 16]> = ConstStaticCell::new([0; 16]); |
| 27 | let mut twi = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config, RAM_BUFFER.take()); | ||
| 26 | 28 | ||
| 27 | info!("Reading..."); | 29 | info!("Reading..."); |
| 28 | 30 | ||
diff --git a/examples/nrf52840/src/bin/twim_lowpower.rs b/examples/nrf52840/src/bin/twim_lowpower.rs index c743614b8..f7380e20d 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] |
| @@ -30,9 +30,17 @@ async fn main(_p: Spawner) { | |||
| 30 | loop { | 30 | loop { |
| 31 | info!("Initializing TWI..."); | 31 | info!("Initializing TWI..."); |
| 32 | let config = twim::Config::default(); | 32 | let config = twim::Config::default(); |
| 33 | let mut ram_buffer = [0u8; 16]; | ||
| 33 | 34 | ||
| 34 | // Create the TWIM instance with borrowed singletons, so they're not consumed. | 35 | // Create the TWIM instance with borrowed singletons, so they're not consumed. |
| 35 | let mut twi = Twim::new(&mut p.TWISPI0, Irqs, &mut p.P0_03, &mut p.P0_04, config); | 36 | let mut twi = Twim::new( |
| 37 | p.TWISPI0.reborrow(), | ||
| 38 | Irqs, | ||
| 39 | p.P0_03.reborrow(), | ||
| 40 | p.P0_04.reborrow(), | ||
| 41 | config, | ||
| 42 | &mut ram_buffer, | ||
| 43 | ); | ||
| 36 | 44 | ||
| 37 | info!("Reading..."); | 45 | info!("Reading..."); |
| 38 | 46 | ||
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..f9f8d74ab 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] |
| @@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) { | |||
| 17 | config.parity = uarte::Parity::EXCLUDED; | 17 | config.parity = uarte::Parity::EXCLUDED; |
| 18 | config.baudrate = uarte::Baudrate::BAUD115200; | 18 | config.baudrate = uarte::Baudrate::BAUD115200; |
| 19 | 19 | ||
| 20 | let mut uart = uarte::Uarte::new(p.UARTE0, Irqs, p.P0_08, p.P0_06, config); | 20 | let mut uart = uarte::Uarte::new(p.UARTE0, p.P0_08, p.P0_06, Irqs, config); |
| 21 | 21 | ||
| 22 | info!("uarte initialized!"); | 22 | info!("uarte initialized!"); |
| 23 | 23 | ||
diff --git a/examples/nrf52840/src/bin/uart_idle.rs b/examples/nrf52840/src/bin/uart_idle.rs index fa93bcf21..00e3ae904 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] |
| @@ -18,7 +18,7 @@ async fn main(_spawner: Spawner) { | |||
| 18 | config.parity = uarte::Parity::EXCLUDED; | 18 | config.parity = uarte::Parity::EXCLUDED; |
| 19 | config.baudrate = uarte::Baudrate::BAUD115200; | 19 | config.baudrate = uarte::Baudrate::BAUD115200; |
| 20 | 20 | ||
| 21 | let uart = uarte::Uarte::new(p.UARTE0, Irqs, p.P0_08, p.P0_06, config); | 21 | let uart = uarte::Uarte::new(p.UARTE0, p.P0_08, p.P0_06, Irqs, config); |
| 22 | let (mut tx, mut rx) = uart.split_with_idle(p.TIMER0, p.PPI_CH0, p.PPI_CH1); | 22 | let (mut tx, mut rx) = uart.split_with_idle(p.TIMER0, p.PPI_CH0, p.PPI_CH1); |
| 23 | 23 | ||
| 24 | info!("uarte initialized!"); | 24 | info!("uarte initialized!"); |
diff --git a/examples/nrf52840/src/bin/uart_split.rs b/examples/nrf52840/src/bin/uart_split.rs index c7510a9a8..46be8f636 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] |
| @@ -23,7 +23,7 @@ async fn main(spawner: Spawner) { | |||
| 23 | config.parity = uarte::Parity::EXCLUDED; | 23 | config.parity = uarte::Parity::EXCLUDED; |
| 24 | config.baudrate = uarte::Baudrate::BAUD115200; | 24 | config.baudrate = uarte::Baudrate::BAUD115200; |
| 25 | 25 | ||
| 26 | let uart = uarte::Uarte::new(p.UARTE0, Irqs, p.P0_08, p.P0_06, config); | 26 | let uart = uarte::Uarte::new(p.UARTE0, p.P0_08, p.P0_06, Irqs, config); |
| 27 | let (mut tx, rx) = uart.split(); | 27 | let (mut tx, rx) = uart.split(); |
| 28 | 28 | ||
| 29 | info!("uarte initialized!"); | 29 | info!("uarte initialized!"); |
diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index e56b215e3..49856012d 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs | |||
| @@ -1,12 +1,10 @@ | |||
| 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; |
| 9 | use embassy_net::{Stack, StackResources}; | 7 | use embassy_net::StackResources; |
| 10 | use embassy_nrf::rng::Rng; | 8 | use embassy_nrf::rng::Rng; |
| 11 | use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; | 9 | use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; |
| 12 | use embassy_nrf::usb::Driver; | 10 | use embassy_nrf::usb::Driver; |
| @@ -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 | ||
| @@ -39,18 +37,17 @@ async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { | |||
| 39 | } | 37 | } |
| 40 | 38 | ||
| 41 | #[embassy_executor::task] | 39 | #[embassy_executor::task] |
| 42 | async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! { | 40 | async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static, MTU>>) -> ! { |
| 43 | stack.run().await | 41 | runner.run().await |
| 44 | } | 42 | } |
| 45 | 43 | ||
| 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)); |
| @@ -63,12 +60,6 @@ async fn main(spawner: Spawner) { | |||
| 63 | config.max_power = 100; | 60 | config.max_power = 100; |
| 64 | config.max_packet_size_0 = 64; | 61 | config.max_packet_size_0 = 64; |
| 65 | 62 | ||
| 66 | // Required for Windows support. | ||
| 67 | config.composite_with_iads = true; | ||
| 68 | config.device_class = 0xEF; | ||
| 69 | config.device_sub_class = 0x02; | ||
| 70 | config.device_protocol = 0x01; | ||
| 71 | |||
| 72 | // Create embassy-usb DeviceBuilder using the driver and config. | 63 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 73 | static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new(); | 64 | static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new(); |
| 74 | static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new(); | 65 | static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new(); |
| @@ -116,10 +107,9 @@ async fn main(spawner: Spawner) { | |||
| 116 | 107 | ||
| 117 | // Init network stack | 108 | // Init network stack |
| 118 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 109 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 119 | static STACK: StaticCell<Stack<Device<'static, MTU>>> = StaticCell::new(); | 110 | let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); |
| 120 | let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); | ||
| 121 | 111 | ||
| 122 | unwrap!(spawner.spawn(net_task(stack))); | 112 | unwrap!(spawner.spawn(net_task(runner))); |
| 123 | 113 | ||
| 124 | // And now we can use it! | 114 | // And now we can use it! |
| 125 | 115 | ||
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..8d05df791 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)); |
| @@ -39,13 +36,6 @@ async fn main(_spawner: Spawner) { | |||
| 39 | config.max_power = 100; | 36 | config.max_power = 100; |
| 40 | config.max_packet_size_0 = 64; | 37 | config.max_packet_size_0 = 64; |
| 41 | 38 | ||
| 42 | // Required for windows compatibility. | ||
| 43 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 44 | config.device_class = 0xEF; | ||
| 45 | config.device_sub_class = 0x02; | ||
| 46 | config.device_protocol = 0x01; | ||
| 47 | config.composite_with_iads = true; | ||
| 48 | |||
| 49 | // Create embassy-usb DeviceBuilder using the driver and config. | 39 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 50 | // It needs some buffers for building the descriptors. | 40 | // It needs some buffers for building the descriptors. |
| 51 | let mut config_descriptor = [0; 256]; | 41 | let mut config_descriptor = [0; 256]; |
diff --git a/examples/nrf52840/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs index 895cca8b9..5e5b4de35 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)); |
| @@ -56,13 +53,6 @@ async fn main(spawner: Spawner) { | |||
| 56 | config.max_power = 100; | 53 | config.max_power = 100; |
| 57 | config.max_packet_size_0 = 64; | 54 | config.max_packet_size_0 = 64; |
| 58 | 55 | ||
| 59 | // Required for windows compatibility. | ||
| 60 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 61 | config.device_class = 0xEF; | ||
| 62 | config.device_sub_class = 0x02; | ||
| 63 | config.device_protocol = 0x01; | ||
| 64 | config.composite_with_iads = true; | ||
| 65 | |||
| 66 | static STATE: StaticCell<State> = StaticCell::new(); | 56 | static STATE: StaticCell<State> = StaticCell::new(); |
| 67 | let state = STATE.init(State::new()); | 57 | let state = STATE.init(State::new()); |
| 68 | 58 | ||
diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs index c6675a3d3..8a20ce673 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)); |
| @@ -44,13 +41,6 @@ async fn main(_spawner: Spawner) { | |||
| 44 | config.max_power = 100; | 41 | config.max_power = 100; |
| 45 | config.max_packet_size_0 = 64; | 42 | config.max_packet_size_0 = 64; |
| 46 | 43 | ||
| 47 | // Required for windows compatibility. | ||
| 48 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 49 | config.device_class = 0xEF; | ||
| 50 | config.device_sub_class = 0x02; | ||
| 51 | config.device_protocol = 0x01; | ||
| 52 | config.composite_with_iads = true; | ||
| 53 | |||
| 54 | // Create embassy-usb DeviceBuilder using the driver and config. | 44 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 55 | // It needs some buffers for building the descriptors. | 45 | // It needs some buffers for building the descriptors. |
| 56 | let mut config_descriptor = [0; 256]; | 46 | let mut config_descriptor = [0; 256]; |
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/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs index a3b69a99b..26eaf485e 100644 --- a/examples/nrf52840/src/bin/wifi_esp_hosted.rs +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | use defmt::{info, unwrap, warn}; | 4 | use defmt::{info, unwrap, warn}; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_net::tcp::TcpSocket; | 6 | use embassy_net::tcp::TcpSocket; |
| 7 | use embassy_net::{Stack, StackResources}; | 7 | use embassy_net::StackResources; |
| 8 | use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull}; | 8 | use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull}; |
| 9 | use embassy_nrf::rng::Rng; | 9 | use embassy_nrf::rng::Rng; |
| 10 | use embassy_nrf::spim::{self, Spim}; | 10 | use embassy_nrf::spim::{self, Spim}; |
| @@ -36,8 +36,8 @@ async fn wifi_task( | |||
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | #[embassy_executor::task] | 38 | #[embassy_executor::task] |
| 39 | async fn net_task(stack: &'static Stack<hosted::NetDriver<'static>>) -> ! { | 39 | async fn net_task(mut runner: embassy_net::Runner<'static, hosted::NetDriver<'static>>) -> ! { |
| 40 | stack.run().await | 40 | runner.run().await |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | #[embassy_executor::main] | 43 | #[embassy_executor::main] |
| @@ -90,10 +90,9 @@ async fn main(spawner: Spawner) { | |||
| 90 | 90 | ||
| 91 | // Init network stack | 91 | // Init network stack |
| 92 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 92 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 93 | static STACK: StaticCell<Stack<hosted::NetDriver<'static>>> = StaticCell::new(); | 93 | let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); |
| 94 | let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); | ||
| 95 | 94 | ||
| 96 | unwrap!(spawner.spawn(net_task(stack))); | 95 | unwrap!(spawner.spawn(net_task(runner))); |
| 97 | 96 | ||
| 98 | // And now we can use it! | 97 | // And now we can use it! |
| 99 | 98 | ||
diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 0da85be07..dc4fba4fd 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml | |||
| @@ -6,22 +6,21 @@ 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.7.0", 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.7.0", path = "../../embassy-executor", features = ["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.4.0", 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.3.1", 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.7.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.4.0", path = "../../embassy-usb", features = ["defmt"] } |
| 15 | embedded-io-async = { version = "0.6.1" } | 15 | embedded-io-async = { version = "0.6.1" } |
| 16 | 16 | ||
| 17 | defmt = "0.3" | 17 | defmt = "1.0.1" |
| 18 | defmt-rtt = "0.4" | 18 | defmt-rtt = "1.0.0" |
| 19 | 19 | ||
| 20 | static_cell = "2" | 20 | static_cell = "2" |
| 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 22 | cortex-m-rt = "0.7.0" | 22 | cortex-m-rt = "0.7.0" |
| 23 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 23 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 24 | rand = { version = "0.8.4", default-features = false } | ||
| 25 | embedded-storage = "0.3.1" | 24 | embedded-storage = "0.3.1" |
| 26 | usbd-hid = "0.8.1" | 25 | usbd-hid = "0.8.1" |
| 27 | serde = { version = "1.0.136", default-features = false } | 26 | serde = { version = "1.0.136", default-features = false } |
diff --git a/examples/nrf5340/src/bin/uart.rs b/examples/nrf5340/src/bin/uart.rs index 7b41d7463..7e8b8d418 100644 --- a/examples/nrf5340/src/bin/uart.rs +++ b/examples/nrf5340/src/bin/uart.rs | |||
| @@ -18,7 +18,7 @@ async fn main(_spawner: Spawner) { | |||
| 18 | config.parity = uarte::Parity::EXCLUDED; | 18 | config.parity = uarte::Parity::EXCLUDED; |
| 19 | config.baudrate = uarte::Baudrate::BAUD115200; | 19 | config.baudrate = uarte::Baudrate::BAUD115200; |
| 20 | 20 | ||
| 21 | let mut uart = uarte::Uarte::new(p.SERIAL0, Irqs, p.P1_00, p.P1_01, config); | 21 | let mut uart = uarte::Uarte::new(p.SERIAL0, p.P1_00, p.P1_01, Irqs, config); |
| 22 | 22 | ||
| 23 | info!("uarte initialized!"); | 23 | info!("uarte initialized!"); |
| 24 | 24 | ||
diff --git a/examples/nrf54l15/.cargo/config.toml b/examples/nrf54l15/.cargo/config.toml new file mode 100644 index 000000000..443bd7418 --- /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 = "probe-rs run --chip nrf54l15" | ||
| 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..4b229d06d --- /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.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | ||
| 9 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | ||
| 10 | embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | ||
| 11 | |||
| 12 | defmt = "1.0.1" | ||
| 13 | defmt-rtt = "1.0.0" | ||
| 14 | panic-probe = { version = "1.0.0", 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..a083aa5e7 100644 --- a/examples/nrf9151/ns/Cargo.toml +++ b/examples/nrf9151/ns/Cargo.toml | |||
| @@ -5,16 +5,16 @@ 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.7.0", path = "../../../embassy-executor", features = ["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.4.0", 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.3.1", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } |
| 11 | 11 | ||
| 12 | defmt = "0.3" | 12 | defmt = "1.0.1" |
| 13 | defmt-rtt = "0.4" | 13 | defmt-rtt = "1.0.0" |
| 14 | 14 | ||
| 15 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 15 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 16 | cortex-m-rt = "0.7.0" | 16 | cortex-m-rt = "0.7.0" |
| 17 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 17 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 18 | 18 | ||
| 19 | [profile.release] | 19 | [profile.release] |
| 20 | debug = 2 | 20 | debug = 2 |
diff --git a/examples/nrf9151/ns/src/bin/uart.rs b/examples/nrf9151/ns/src/bin/uart.rs index 2220dccfb..6fd377978 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] |
| @@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) { | |||
| 17 | config.parity = uarte::Parity::EXCLUDED; | 17 | config.parity = uarte::Parity::EXCLUDED; |
| 18 | config.baudrate = uarte::Baudrate::BAUD115200; | 18 | config.baudrate = uarte::Baudrate::BAUD115200; |
| 19 | 19 | ||
| 20 | let mut uart = uarte::Uarte::new(p.SERIAL0, Irqs, p.P0_26, p.P0_27, config); | 20 | let mut uart = uarte::Uarte::new(p.SERIAL0, p.P0_26, p.P0_27, Irqs, config); |
| 21 | 21 | ||
| 22 | info!("uarte initialized!"); | 22 | info!("uarte initialized!"); |
| 23 | 23 | ||
diff --git a/examples/nrf9151/s/Cargo.toml b/examples/nrf9151/s/Cargo.toml index 7253fc4be..ae98631ef 100644 --- a/examples/nrf9151/s/Cargo.toml +++ b/examples/nrf9151/s/Cargo.toml | |||
| @@ -5,16 +5,16 @@ 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.7.0", path = "../../../embassy-executor", features = ["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.4.0", 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.3.1", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } |
| 11 | 11 | ||
| 12 | defmt = "0.3" | 12 | defmt = "1.0.1" |
| 13 | defmt-rtt = "0.4" | 13 | defmt-rtt = "1.0.0" |
| 14 | 14 | ||
| 15 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 15 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 16 | cortex-m-rt = "0.7.0" | 16 | cortex-m-rt = "0.7.0" |
| 17 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 17 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 18 | 18 | ||
| 19 | [profile.release] | 19 | [profile.release] |
| 20 | debug = 2 | 20 | debug = 2 |
diff --git a/examples/nrf9160/.cargo/config.toml b/examples/nrf9160/.cargo/config.toml index f64c63966..6072b8595 100644 --- a/examples/nrf9160/.cargo/config.toml +++ b/examples/nrf9160/.cargo/config.toml | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] |
| 2 | runner = "probe-rs run --chip nRF9160_xxAA" | 2 | # runner = "probe-rs run --chip nRF9160_xxAA" |
| 3 | runner = [ "probe-rs", "run", "--chip=nRF9160_xxAA", "--always-print-stacktrace", "--log-format={t} {[{L}]%bold} {s} {{c} {ff}:{l:1}%dimmed}" ] | ||
| 3 | 4 | ||
| 4 | [build] | 5 | [build] |
| 5 | target = "thumbv8m.main-none-eabihf" | 6 | target = "thumbv8m.main-none-eabihf" |
diff --git a/examples/nrf9160/Cargo.toml b/examples/nrf9160/Cargo.toml index c30b54ebd..25aedf624 100644 --- a/examples/nrf9160/Cargo.toml +++ b/examples/nrf9160/Cargo.toml | |||
| @@ -5,16 +5,22 @@ 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.7.0", path = "../../embassy-executor", features = ["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.4.0", 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.3.1", 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"] } | ||
| 12 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "proto-ipv4", "medium-ip"] } | ||
| 11 | 13 | ||
| 12 | defmt = "0.3" | 14 | defmt = "1.0.1" |
| 13 | defmt-rtt = "0.4" | 15 | defmt-rtt = "1.0.0" |
| 14 | 16 | ||
| 17 | heapless = "0.8" | ||
| 15 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 18 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 16 | cortex-m-rt = "0.7.0" | 19 | cortex-m-rt = "0.7.0" |
| 17 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 20 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 21 | static_cell = { version = "2" } | ||
| 22 | embedded-io = "0.6.1" | ||
| 23 | embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } | ||
| 18 | 24 | ||
| 19 | [profile.release] | 25 | [profile.release] |
| 20 | debug = 2 | 26 | debug = 2 |
diff --git a/examples/nrf9160/memory.x b/examples/nrf9160/memory.x index 4c7d4ebf0..e33498773 100644 --- a/examples/nrf9160/memory.x +++ b/examples/nrf9160/memory.x | |||
| @@ -1,5 +1,9 @@ | |||
| 1 | MEMORY | 1 | MEMORY |
| 2 | { | 2 | { |
| 3 | FLASH : ORIGIN = 0x00000000, LENGTH = 1024K | 3 | FLASH : ORIGIN = 0x00000000, LENGTH = 1024K |
| 4 | RAM : ORIGIN = 0x20018000, LENGTH = 160K | 4 | RAM : ORIGIN = 0x20010000, LENGTH = 192K |
| 5 | IPC : ORIGIN = 0x20000000, LENGTH = 64K | ||
| 5 | } | 6 | } |
| 7 | |||
| 8 | PROVIDE(__start_ipc = ORIGIN(IPC)); | ||
| 9 | PROVIDE(__end_ipc = ORIGIN(IPC) + LENGTH(IPC)); | ||
diff --git a/examples/nrf9160/src/bin/modem_tcp_client.rs b/examples/nrf9160/src/bin/modem_tcp_client.rs new file mode 100644 index 000000000..a36b14626 --- /dev/null +++ b/examples/nrf9160/src/bin/modem_tcp_client.rs | |||
| @@ -0,0 +1,197 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use core::mem::MaybeUninit; | ||
| 5 | use core::net::IpAddr; | ||
| 6 | use core::ptr::addr_of_mut; | ||
| 7 | use core::slice; | ||
| 8 | use core::str::FromStr; | ||
| 9 | |||
| 10 | use defmt::{info, unwrap, warn}; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_net::{Ipv4Cidr, Stack, StackResources}; | ||
| 13 | use embassy_net_nrf91::context::Status; | ||
| 14 | use embassy_net_nrf91::{context, Runner, State, TraceBuffer, TraceReader}; | ||
| 15 | use embassy_nrf::buffered_uarte::{self, BufferedUarteTx}; | ||
| 16 | use embassy_nrf::gpio::{AnyPin, Level, Output, OutputDrive}; | ||
| 17 | use embassy_nrf::uarte::Baudrate; | ||
| 18 | use embassy_nrf::{bind_interrupts, interrupt, peripherals, uarte, Peri}; | ||
| 19 | use embassy_time::{Duration, Timer}; | ||
| 20 | use embedded_io_async::Write; | ||
| 21 | use heapless::Vec; | ||
| 22 | use static_cell::StaticCell; | ||
| 23 | use {defmt_rtt as _, panic_probe as _}; | ||
| 24 | |||
| 25 | #[interrupt] | ||
| 26 | fn IPC() { | ||
| 27 | embassy_net_nrf91::on_ipc_irq(); | ||
| 28 | } | ||
| 29 | |||
| 30 | bind_interrupts!(struct Irqs { | ||
| 31 | SERIAL0 => buffered_uarte::InterruptHandler<peripherals::SERIAL0>; | ||
| 32 | }); | ||
| 33 | |||
| 34 | #[embassy_executor::task] | ||
| 35 | async fn trace_task(mut uart: BufferedUarteTx<'static, peripherals::SERIAL0>, reader: TraceReader<'static>) -> ! { | ||
| 36 | let mut rx = [0u8; 1024]; | ||
| 37 | loop { | ||
| 38 | let n = reader.read(&mut rx[..]).await; | ||
| 39 | unwrap!(uart.write_all(&rx[..n]).await); | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | #[embassy_executor::task] | ||
| 44 | async fn modem_task(runner: Runner<'static>) -> ! { | ||
| 45 | runner.run().await | ||
| 46 | } | ||
| 47 | |||
| 48 | #[embassy_executor::task] | ||
| 49 | async fn net_task(mut runner: embassy_net::Runner<'static, embassy_net_nrf91::NetDriver<'static>>) -> ! { | ||
| 50 | runner.run().await | ||
| 51 | } | ||
| 52 | |||
| 53 | #[embassy_executor::task] | ||
| 54 | async fn control_task( | ||
| 55 | control: &'static context::Control<'static>, | ||
| 56 | config: context::Config<'static>, | ||
| 57 | stack: Stack<'static>, | ||
| 58 | ) { | ||
| 59 | unwrap!(control.configure(&config).await); | ||
| 60 | unwrap!( | ||
| 61 | control | ||
| 62 | .run(|status| { | ||
| 63 | stack.set_config_v4(status_to_config(status)); | ||
| 64 | }) | ||
| 65 | .await | ||
| 66 | ); | ||
| 67 | } | ||
| 68 | |||
| 69 | fn status_to_config(status: &Status) -> embassy_net::ConfigV4 { | ||
| 70 | let Some(IpAddr::V4(addr)) = status.ip else { | ||
| 71 | panic!("Unexpected IP address"); | ||
| 72 | }; | ||
| 73 | |||
| 74 | let gateway = match status.gateway { | ||
| 75 | Some(IpAddr::V4(addr)) => Some(addr), | ||
| 76 | _ => None, | ||
| 77 | }; | ||
| 78 | |||
| 79 | let mut dns_servers = Vec::new(); | ||
| 80 | for dns in status.dns.iter() { | ||
| 81 | if let IpAddr::V4(ip) = dns { | ||
| 82 | unwrap!(dns_servers.push(*ip)); | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | embassy_net::ConfigV4::Static(embassy_net::StaticConfigV4 { | ||
| 87 | address: Ipv4Cidr::new(addr, 32), | ||
| 88 | gateway, | ||
| 89 | dns_servers, | ||
| 90 | }) | ||
| 91 | } | ||
| 92 | |||
| 93 | #[embassy_executor::task] | ||
| 94 | async fn blink_task(pin: Peri<'static, AnyPin>) { | ||
| 95 | let mut led = Output::new(pin, Level::Low, OutputDrive::Standard); | ||
| 96 | loop { | ||
| 97 | led.set_high(); | ||
| 98 | Timer::after_millis(1000).await; | ||
| 99 | led.set_low(); | ||
| 100 | Timer::after_millis(1000).await; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | extern "C" { | ||
| 105 | static __start_ipc: u8; | ||
| 106 | static __end_ipc: u8; | ||
| 107 | } | ||
| 108 | |||
| 109 | #[embassy_executor::main] | ||
| 110 | async fn main(spawner: Spawner) { | ||
| 111 | let p = embassy_nrf::init(Default::default()); | ||
| 112 | |||
| 113 | info!("Hello World!"); | ||
| 114 | |||
| 115 | unwrap!(spawner.spawn(blink_task(p.P0_02.into()))); | ||
| 116 | |||
| 117 | let ipc_mem = unsafe { | ||
| 118 | let ipc_start = &__start_ipc as *const u8 as *mut MaybeUninit<u8>; | ||
| 119 | let ipc_end = &__end_ipc as *const u8 as *mut MaybeUninit<u8>; | ||
| 120 | let ipc_len = ipc_end.offset_from(ipc_start) as usize; | ||
| 121 | slice::from_raw_parts_mut(ipc_start, ipc_len) | ||
| 122 | }; | ||
| 123 | |||
| 124 | static mut TRACE_BUF: [u8; 4096] = [0u8; 4096]; | ||
| 125 | let mut config = uarte::Config::default(); | ||
| 126 | config.baudrate = Baudrate::BAUD1M; | ||
| 127 | let uart = BufferedUarteTx::new( | ||
| 128 | //let trace_uart = BufferedUarteTx::new( | ||
| 129 | unsafe { peripherals::SERIAL0::steal() }, | ||
| 130 | unsafe { peripherals::P0_01::steal() }, | ||
| 131 | Irqs, | ||
| 132 | //unsafe { peripherals::P0_14::steal() }, | ||
| 133 | config, | ||
| 134 | unsafe { &mut *addr_of_mut!(TRACE_BUF) }, | ||
| 135 | ); | ||
| 136 | |||
| 137 | static STATE: StaticCell<State> = StaticCell::new(); | ||
| 138 | static TRACE: StaticCell<TraceBuffer> = StaticCell::new(); | ||
| 139 | let (device, control, runner, tracer) = | ||
| 140 | embassy_net_nrf91::new_with_trace(STATE.init(State::new()), ipc_mem, TRACE.init(TraceBuffer::new())).await; | ||
| 141 | unwrap!(spawner.spawn(modem_task(runner))); | ||
| 142 | unwrap!(spawner.spawn(trace_task(uart, tracer))); | ||
| 143 | |||
| 144 | let config = embassy_net::Config::default(); | ||
| 145 | |||
| 146 | // Generate "random" seed. nRF91 has no RNG, TODO figure out something... | ||
| 147 | let seed = 123456; | ||
| 148 | |||
| 149 | // Init network stack | ||
| 150 | static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new(); | ||
| 151 | let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::<2>::new()), seed); | ||
| 152 | |||
| 153 | unwrap!(spawner.spawn(net_task(runner))); | ||
| 154 | |||
| 155 | static CONTROL: StaticCell<context::Control<'static>> = StaticCell::new(); | ||
| 156 | let control = CONTROL.init(context::Control::new(control, 0).await); | ||
| 157 | |||
| 158 | unwrap!(spawner.spawn(control_task( | ||
| 159 | control, | ||
| 160 | context::Config { | ||
| 161 | apn: b"iot.nat.es", | ||
| 162 | auth_prot: context::AuthProt::Pap, | ||
| 163 | auth: Some((b"orange", b"orange")), | ||
| 164 | pin: None, | ||
| 165 | }, | ||
| 166 | stack | ||
| 167 | ))); | ||
| 168 | |||
| 169 | stack.wait_config_up().await; | ||
| 170 | |||
| 171 | let mut rx_buffer = [0; 4096]; | ||
| 172 | let mut tx_buffer = [0; 4096]; | ||
| 173 | loop { | ||
| 174 | let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 175 | socket.set_timeout(Some(Duration::from_secs(10))); | ||
| 176 | |||
| 177 | info!("Connecting..."); | ||
| 178 | let host_addr = embassy_net::Ipv4Address::from_str("45.79.112.203").unwrap(); | ||
| 179 | if let Err(e) = socket.connect((host_addr, 4242)).await { | ||
| 180 | warn!("connect error: {:?}", e); | ||
| 181 | Timer::after_secs(10).await; | ||
| 182 | continue; | ||
| 183 | } | ||
| 184 | info!("Connected to {:?}", socket.remote_endpoint()); | ||
| 185 | |||
| 186 | let msg = b"Hello world!\n"; | ||
| 187 | for _ in 0..10 { | ||
| 188 | if let Err(e) = socket.write_all(msg).await { | ||
| 189 | warn!("write error: {:?}", e); | ||
| 190 | break; | ||
| 191 | } | ||
| 192 | info!("txd: {}", core::str::from_utf8(msg).unwrap()); | ||
| 193 | Timer::after_secs(1).await; | ||
| 194 | } | ||
| 195 | Timer::after_secs(4).await; | ||
| 196 | } | ||
| 197 | } | ||
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 83d5792b6..c8a132a5e 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml | |||
| @@ -6,43 +6,43 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 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.3.0", path = "../../embassy-embedded-hal", features = ["defmt"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.7.0", 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"] } | 12 | embassy-time = { version = "0.4.0", 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.4.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.4.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.7.0", path = "../../embassy-net", features = ["defmt", "icmp", "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.2.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.4.0", path = "../../embassy-usb-logger" } |
| 19 | cyw43 = { version = "0.2.0", path = "../../cyw43", features = ["defmt", "firmware-logs", "bluetooth"] } | 19 | cyw43 = { version = "0.3.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } |
| 20 | cyw43-pio = { version = "0.2.0", path = "../../cyw43-pio", features = ["defmt"] } | 20 | cyw43-pio = { version = "0.4.0", path = "../../cyw43-pio", features = ["defmt"] } |
| 21 | 21 | ||
| 22 | defmt = "0.3" | 22 | defmt = "1.0.1" |
| 23 | defmt-rtt = "0.4" | 23 | defmt-rtt = "1.0.0" |
| 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 | 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 | ||
| 32 | # for assign resources example | 32 | # for assign resources example |
| 33 | assign-resources = { git = "https://github.com/adamgreig/assign-resources", rev = "94ad10e2729afdf0fd5a77cd12e68409a982f58a" } | 33 | assign-resources = { git = "https://github.com/adamgreig/assign-resources", rev = "bd22cb7a92031fb16f74a5da42469d466c33383e" } |
| 34 | 34 | ||
| 35 | #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 35 | #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 36 | cortex-m = { version = "0.7.6", features = ["inline-asm"] } | 36 | 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 = "1.0.0", 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 | 48 | ||
| @@ -54,14 +54,9 @@ embedded-storage = { version = "0.3" } | |||
| 54 | static_cell = "2.1" | 54 | static_cell = "2.1" |
| 55 | portable-atomic = { version = "1.5", features = ["critical-section"] } | 55 | portable-atomic = { version = "1.5", features = ["critical-section"] } |
| 56 | log = "0.4" | 56 | log = "0.4" |
| 57 | pio-proc = "0.2" | 57 | rand = { version = "0.9.0", default-features = false } |
| 58 | pio = "0.2.1" | ||
| 59 | rand = { version = "0.8.5", default-features = false } | ||
| 60 | embedded-sdmmc = "0.7.0" | 58 | embedded-sdmmc = "0.7.0" |
| 61 | 59 | ||
| 62 | bt-hci = { version = "0.1.0", default-features = false, features = ["defmt"] } | ||
| 63 | trouble-host = { version = "0.1.0", features = ["defmt", "gatt"] } | ||
| 64 | |||
| 65 | [profile.release] | 60 | [profile.release] |
| 66 | debug = 2 | 61 | debug = 2 |
| 67 | lto = true | 62 | lto = true |
| @@ -71,13 +66,3 @@ opt-level = 'z' | |||
| 71 | debug = 2 | 66 | debug = 2 |
| 72 | lto = true | 67 | lto = true |
| 73 | opt-level = "z" | 68 | opt-level = "z" |
| 74 | |||
| 75 | [patch.crates-io] | ||
| 76 | trouble-host = { git = "https://github.com/embassy-rs/trouble.git", rev = "4b8c0f499b34e46ca23a56e2d1640ede371722cf" } | ||
| 77 | bt-hci = { git = "https://github.com/alexmoon/bt-hci.git", rev = "b9cd5954f6bd89b535cad9c418e9fdf12812d7c3" } | ||
| 78 | embassy-executor = { path = "../../embassy-executor" } | ||
| 79 | embassy-sync = { path = "../../embassy-sync" } | ||
| 80 | embassy-futures = { path = "../../embassy-futures" } | ||
| 81 | embassy-time = { path = "../../embassy-time" } | ||
| 82 | embassy-time-driver = { path = "../../embassy-time-driver" } | ||
| 83 | embassy-embedded-hal = { path = "../../embassy-embedded-hal" } | ||
diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs index 1bb7c2249..015915586 100644 --- a/examples/rp/src/bin/adc.rs +++ b/examples/rp/src/bin/adc.rs | |||
| @@ -12,9 +12,12 @@ use embassy_rp::gpio::Pull; | |||
| 12 | use embassy_time::Timer; | 12 | use embassy_time::Timer; |
| 13 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 14 | 14 | ||
| 15 | bind_interrupts!(struct Irqs { | 15 | bind_interrupts!( |
| 16 | ADC_IRQ_FIFO => InterruptHandler; | 16 | /// Binds the ADC interrupts. |
| 17 | }); | 17 | struct Irqs { |
| 18 | ADC_IRQ_FIFO => InterruptHandler; | ||
| 19 | } | ||
| 20 | ); | ||
| 18 | 21 | ||
| 19 | #[embassy_executor::main] | 22 | #[embassy_executor::main] |
| 20 | async fn main(_spawner: Spawner) { | 23 | async fn main(_spawner: Spawner) { |
diff --git a/examples/rp/src/bin/adc_dma.rs b/examples/rp/src/bin/adc_dma.rs index f755cf5bf..b42c13fde 100644 --- a/examples/rp/src/bin/adc_dma.rs +++ b/examples/rp/src/bin/adc_dma.rs | |||
| @@ -38,13 +38,13 @@ async fn main(_spawner: Spawner) { | |||
| 38 | // Read 100 samples from a single channel | 38 | // Read 100 samples from a single channel |
| 39 | let mut buf = [0_u16; BLOCK_SIZE]; | 39 | let mut buf = [0_u16; BLOCK_SIZE]; |
| 40 | let div = 479; // 100kHz sample rate (48Mhz / 100kHz - 1) | 40 | let div = 479; // 100kHz sample rate (48Mhz / 100kHz - 1) |
| 41 | adc.read_many(&mut pin, &mut buf, div, &mut dma).await.unwrap(); | 41 | adc.read_many(&mut pin, &mut buf, div, dma.reborrow()).await.unwrap(); |
| 42 | info!("single: {:?} ...etc", buf[..8]); | 42 | info!("single: {:?} ...etc", buf[..8]); |
| 43 | 43 | ||
| 44 | // Read 100 samples from 4 channels interleaved | 44 | // Read 100 samples from 4 channels interleaved |
| 45 | let mut buf = [0_u16; { BLOCK_SIZE * NUM_CHANNELS }]; | 45 | let mut buf = [0_u16; { BLOCK_SIZE * NUM_CHANNELS }]; |
| 46 | let div = 119; // 100kHz sample rate (48Mhz / 100kHz * 4ch - 1) | 46 | let div = 119; // 100kHz sample rate (48Mhz / 100kHz * 4ch - 1) |
| 47 | adc.read_many_multichannel(&mut pins, &mut buf, div, &mut dma) | 47 | adc.read_many_multichannel(&mut pins, &mut buf, div, dma.reborrow()) |
| 48 | .await | 48 | .await |
| 49 | .unwrap(); | 49 | .unwrap(); |
| 50 | info!("multi: {:?} ...etc", buf[..NUM_CHANNELS * 2]); | 50 | info!("multi: {:?} ...etc", buf[..NUM_CHANNELS * 2]); |
diff --git a/examples/rp/src/bin/assign_resources.rs b/examples/rp/src/bin/assign_resources.rs index ff6eff4a2..341f54d22 100644 --- a/examples/rp/src/bin/assign_resources.rs +++ b/examples/rp/src/bin/assign_resources.rs | |||
| @@ -16,6 +16,7 @@ use defmt::*; | |||
| 16 | use embassy_executor::Spawner; | 16 | use embassy_executor::Spawner; |
| 17 | use embassy_rp::gpio::{Level, Output}; | 17 | use embassy_rp::gpio::{Level, Output}; |
| 18 | use embassy_rp::peripherals::{self, PIN_20, PIN_21}; | 18 | use embassy_rp::peripherals::{self, PIN_20, PIN_21}; |
| 19 | use embassy_rp::Peri; | ||
| 19 | use embassy_time::Timer; | 20 | use embassy_time::Timer; |
| 20 | use {defmt_rtt as _, panic_probe as _}; | 21 | use {defmt_rtt as _, panic_probe as _}; |
| 21 | 22 | ||
| @@ -38,7 +39,11 @@ async fn main(spawner: Spawner) { | |||
| 38 | 39 | ||
| 39 | // 1) Assigning a resource to a task by passing parts of the peripherals. | 40 | // 1) Assigning a resource to a task by passing parts of the peripherals. |
| 40 | #[embassy_executor::task] | 41 | #[embassy_executor::task] |
| 41 | async fn double_blinky_manually_assigned(_spawner: Spawner, pin_20: PIN_20, pin_21: PIN_21) { | 42 | async fn double_blinky_manually_assigned( |
| 43 | _spawner: Spawner, | ||
| 44 | pin_20: Peri<'static, PIN_20>, | ||
| 45 | pin_21: Peri<'static, PIN_21>, | ||
| 46 | ) { | ||
| 42 | let mut led_20 = Output::new(pin_20, Level::Low); | 47 | let mut led_20 = Output::new(pin_20, Level::Low); |
| 43 | let mut led_21 = Output::new(pin_21, Level::High); | 48 | let mut led_21 = Output::new(pin_21, Level::High); |
| 44 | 49 | ||
diff --git a/examples/rp/src/bin/blinky_two_channels.rs b/examples/rp/src/bin/blinky_two_channels.rs index b2eec2a21..51e139e94 100644 --- a/examples/rp/src/bin/blinky_two_channels.rs +++ b/examples/rp/src/bin/blinky_two_channels.rs | |||
| @@ -11,7 +11,7 @@ use embassy_rp::gpio; | |||
| 11 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | 11 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; |
| 12 | use embassy_sync::channel::{Channel, Sender}; | 12 | use embassy_sync::channel::{Channel, Sender}; |
| 13 | use embassy_time::{Duration, Ticker}; | 13 | use embassy_time::{Duration, Ticker}; |
| 14 | use gpio::{AnyPin, Level, Output}; | 14 | use gpio::{Level, Output}; |
| 15 | use {defmt_rtt as _, panic_probe as _}; | 15 | use {defmt_rtt as _, panic_probe as _}; |
| 16 | 16 | ||
| 17 | enum LedState { | 17 | enum LedState { |
| @@ -22,7 +22,7 @@ static CHANNEL: Channel<ThreadModeRawMutex, LedState, 64> = Channel::new(); | |||
| 22 | #[embassy_executor::main] | 22 | #[embassy_executor::main] |
| 23 | async fn main(spawner: Spawner) { | 23 | async fn main(spawner: Spawner) { |
| 24 | let p = embassy_rp::init(Default::default()); | 24 | let p = embassy_rp::init(Default::default()); |
| 25 | let mut led = Output::new(AnyPin::from(p.PIN_25), Level::High); | 25 | let mut led = Output::new(p.PIN_25, Level::High); |
| 26 | 26 | ||
| 27 | let dt = 100 * 1_000_000; | 27 | let dt = 100 * 1_000_000; |
| 28 | let k = 1.003; | 28 | let k = 1.003; |
diff --git a/examples/rp/src/bin/blinky_two_tasks.rs b/examples/rp/src/bin/blinky_two_tasks.rs index a57b513d6..67a9108c0 100644 --- a/examples/rp/src/bin/blinky_two_tasks.rs +++ b/examples/rp/src/bin/blinky_two_tasks.rs | |||
| @@ -11,7 +11,7 @@ use embassy_rp::gpio; | |||
| 11 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | 11 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; |
| 12 | use embassy_sync::mutex::Mutex; | 12 | use embassy_sync::mutex::Mutex; |
| 13 | use embassy_time::{Duration, Ticker}; | 13 | use embassy_time::{Duration, Ticker}; |
| 14 | use gpio::{AnyPin, Level, Output}; | 14 | use gpio::{Level, Output}; |
| 15 | use {defmt_rtt as _, panic_probe as _}; | 15 | use {defmt_rtt as _, panic_probe as _}; |
| 16 | 16 | ||
| 17 | type LedType = Mutex<ThreadModeRawMutex, Option<Output<'static>>>; | 17 | type LedType = Mutex<ThreadModeRawMutex, Option<Output<'static>>>; |
| @@ -21,7 +21,7 @@ static LED: LedType = Mutex::new(None); | |||
| 21 | async fn main(spawner: Spawner) { | 21 | async fn main(spawner: Spawner) { |
| 22 | let p = embassy_rp::init(Default::default()); | 22 | let p = embassy_rp::init(Default::default()); |
| 23 | // set the content of the global LED reference to the real LED pin | 23 | // set the content of the global LED reference to the real LED pin |
| 24 | let led = Output::new(AnyPin::from(p.PIN_25), Level::High); | 24 | let led = Output::new(p.PIN_25, Level::High); |
| 25 | // inner scope is so that once the mutex is written to, the MutexGuard is dropped, thus the | 25 | // inner scope is so that once the mutex is written to, the MutexGuard is dropped, thus the |
| 26 | // Mutex is released | 26 | // Mutex is released |
| 27 | { | 27 | { |
diff --git a/examples/rp/src/bin/bluetooth.rs b/examples/rp/src/bin/bluetooth.rs deleted file mode 100644 index 901521b60..000000000 --- a/examples/rp/src/bin/bluetooth.rs +++ /dev/null | |||
| @@ -1,148 +0,0 @@ | |||
| 1 | //! This example test the RP Pico W on board LED. | ||
| 2 | //! | ||
| 3 | //! It does not work with the RP Pico board. See blinky.rs. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | |||
| 8 | use bt_hci::controller::ExternalController; | ||
| 9 | use cyw43_pio::PioSpi; | ||
| 10 | use defmt::*; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_futures::join::join3; | ||
| 13 | use embassy_rp::bind_interrupts; | ||
| 14 | use embassy_rp::gpio::{Level, Output}; | ||
| 15 | use embassy_rp::peripherals::{DMA_CH0, PIO0}; | ||
| 16 | use embassy_rp::pio::{InterruptHandler, Pio}; | ||
| 17 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
| 18 | use embassy_time::{Duration, Timer}; | ||
| 19 | use static_cell::StaticCell; | ||
| 20 | use trouble_host::advertise::{AdStructure, Advertisement, BR_EDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE}; | ||
| 21 | use trouble_host::attribute::{AttributeTable, CharacteristicProp, Service, Uuid}; | ||
| 22 | use trouble_host::gatt::GattEvent; | ||
| 23 | use trouble_host::{Address, BleHost, BleHostResources, PacketQos}; | ||
| 24 | use {defmt_rtt as _, embassy_time as _, panic_probe as _}; | ||
| 25 | |||
| 26 | bind_interrupts!(struct Irqs { | ||
| 27 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 28 | }); | ||
| 29 | |||
| 30 | #[embassy_executor::task] | ||
| 31 | async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { | ||
| 32 | runner.run().await | ||
| 33 | } | ||
| 34 | |||
| 35 | #[embassy_executor::main] | ||
| 36 | async fn main(spawner: Spawner) { | ||
| 37 | let p = embassy_rp::init(Default::default()); | ||
| 38 | let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin"); | ||
| 39 | let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin"); | ||
| 40 | let btfw = include_bytes!("../../../../cyw43-firmware/43439A0_btfw.bin"); | ||
| 41 | |||
| 42 | // To make flashing faster for development, you may want to flash the firmwares independently | ||
| 43 | // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: | ||
| 44 | // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 | ||
| 45 | // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 | ||
| 46 | //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; | ||
| 47 | //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; | ||
| 48 | |||
| 49 | let pwr = Output::new(p.PIN_23, Level::Low); | ||
| 50 | let cs = Output::new(p.PIN_25, Level::High); | ||
| 51 | let mut pio = Pio::new(p.PIO0, Irqs); | ||
| 52 | let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); | ||
| 53 | |||
| 54 | static STATE: StaticCell<cyw43::State> = StaticCell::new(); | ||
| 55 | let state = STATE.init(cyw43::State::new()); | ||
| 56 | let (_net_device, bt_device, mut control, runner) = cyw43::new_with_bluetooth(state, pwr, spi, fw, btfw).await; | ||
| 57 | unwrap!(spawner.spawn(cyw43_task(runner))); | ||
| 58 | control.init(clm).await; | ||
| 59 | |||
| 60 | let controller: ExternalController<_, 10> = ExternalController::new(bt_device); | ||
| 61 | static HOST_RESOURCES: StaticCell<BleHostResources<4, 32, 27>> = StaticCell::new(); | ||
| 62 | let host_resources = HOST_RESOURCES.init(BleHostResources::new(PacketQos::None)); | ||
| 63 | |||
| 64 | let mut ble: BleHost<'_, _> = BleHost::new(controller, host_resources); | ||
| 65 | |||
| 66 | ble.set_random_address(Address::random([0xff, 0x9f, 0x1a, 0x05, 0xe4, 0xff])); | ||
| 67 | let mut table: AttributeTable<'_, NoopRawMutex, 10> = AttributeTable::new(); | ||
| 68 | |||
| 69 | // Generic Access Service (mandatory) | ||
| 70 | let id = b"Pico W Bluetooth"; | ||
| 71 | let appearance = [0x80, 0x07]; | ||
| 72 | let mut bat_level = [0; 1]; | ||
| 73 | let handle = { | ||
| 74 | let mut svc = table.add_service(Service::new(0x1800)); | ||
| 75 | let _ = svc.add_characteristic_ro(0x2a00, id); | ||
| 76 | let _ = svc.add_characteristic_ro(0x2a01, &appearance[..]); | ||
| 77 | svc.build(); | ||
| 78 | |||
| 79 | // Generic attribute service (mandatory) | ||
| 80 | table.add_service(Service::new(0x1801)); | ||
| 81 | |||
| 82 | // Battery service | ||
| 83 | let mut svc = table.add_service(Service::new(0x180f)); | ||
| 84 | |||
| 85 | svc.add_characteristic( | ||
| 86 | 0x2a19, | ||
| 87 | &[CharacteristicProp::Read, CharacteristicProp::Notify], | ||
| 88 | &mut bat_level, | ||
| 89 | ) | ||
| 90 | .build() | ||
| 91 | }; | ||
| 92 | |||
| 93 | let mut adv_data = [0; 31]; | ||
| 94 | AdStructure::encode_slice( | ||
| 95 | &[ | ||
| 96 | AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), | ||
| 97 | AdStructure::ServiceUuids16(&[Uuid::Uuid16([0x0f, 0x18])]), | ||
| 98 | AdStructure::CompleteLocalName(b"Pico W Bluetooth"), | ||
| 99 | ], | ||
| 100 | &mut adv_data[..], | ||
| 101 | ) | ||
| 102 | .unwrap(); | ||
| 103 | |||
| 104 | let server = ble.gatt_server(&table); | ||
| 105 | |||
| 106 | info!("Starting advertising and GATT service"); | ||
| 107 | let _ = join3( | ||
| 108 | ble.run(), | ||
| 109 | async { | ||
| 110 | loop { | ||
| 111 | match server.next().await { | ||
| 112 | Ok(GattEvent::Write { handle, connection: _ }) => { | ||
| 113 | let _ = table.get(handle, |value| { | ||
| 114 | info!("Write event. Value written: {:?}", value); | ||
| 115 | }); | ||
| 116 | } | ||
| 117 | Ok(GattEvent::Read { .. }) => { | ||
| 118 | info!("Read event"); | ||
| 119 | } | ||
| 120 | Err(e) => { | ||
| 121 | error!("Error processing GATT events: {:?}", e); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | } | ||
| 125 | }, | ||
| 126 | async { | ||
| 127 | let mut advertiser = ble | ||
| 128 | .advertise( | ||
| 129 | &Default::default(), | ||
| 130 | Advertisement::ConnectableScannableUndirected { | ||
| 131 | adv_data: &adv_data[..], | ||
| 132 | scan_data: &[], | ||
| 133 | }, | ||
| 134 | ) | ||
| 135 | .await | ||
| 136 | .unwrap(); | ||
| 137 | let conn = advertiser.accept().await.unwrap(); | ||
| 138 | // Keep connection alive | ||
| 139 | let mut tick: u8 = 0; | ||
| 140 | loop { | ||
| 141 | Timer::after(Duration::from_secs(10)).await; | ||
| 142 | tick += 1; | ||
| 143 | server.notify(handle, &conn, &[tick]).await.unwrap(); | ||
| 144 | } | ||
| 145 | }, | ||
| 146 | ) | ||
| 147 | .await; | ||
| 148 | } | ||
diff --git a/examples/rp/src/bin/ethernet_w5500_icmp.rs b/examples/rp/src/bin/ethernet_w5500_icmp.rs new file mode 100644 index 000000000..e434b3bbc --- /dev/null +++ b/examples/rp/src/bin/ethernet_w5500_icmp.rs | |||
| @@ -0,0 +1,143 @@ | |||
| 1 | //! This example implements an echo (ping) with an ICMP Socket and using defmt to report the results. | ||
| 2 | //! | ||
| 3 | //! Although there is a better way to execute pings using the child module ping of the icmp module, | ||
| 4 | //! this example allows for other icmp messages like `Destination unreachable` to be sent aswell. | ||
| 5 | //! | ||
| 6 | //! Example written for the [`WIZnet W5500-EVB-Pico`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico) board. | ||
| 7 | |||
| 8 | #![no_std] | ||
| 9 | #![no_main] | ||
| 10 | |||
| 11 | use defmt::*; | ||
| 12 | use embassy_executor::Spawner; | ||
| 13 | use embassy_futures::yield_now; | ||
| 14 | use embassy_net::icmp::{ChecksumCapabilities, IcmpEndpoint, IcmpSocket, Icmpv4Packet, Icmpv4Repr, PacketMetadata}; | ||
| 15 | use embassy_net::{Stack, StackResources}; | ||
| 16 | use embassy_net_wiznet::chip::W5500; | ||
| 17 | use embassy_net_wiznet::*; | ||
| 18 | use embassy_rp::clocks::RoscRng; | ||
| 19 | use embassy_rp::gpio::{Input, Level, Output, Pull}; | ||
| 20 | use embassy_rp::peripherals::SPI0; | ||
| 21 | use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; | ||
| 22 | use embassy_time::{Delay, Instant, Timer}; | ||
| 23 | use embedded_hal_bus::spi::ExclusiveDevice; | ||
| 24 | use static_cell::StaticCell; | ||
| 25 | use {defmt_rtt as _, panic_probe as _}; | ||
| 26 | |||
| 27 | type ExclusiveSpiDevice = ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static>, Delay>; | ||
| 28 | |||
| 29 | #[embassy_executor::task] | ||
| 30 | async fn ethernet_task(runner: Runner<'static, W5500, ExclusiveSpiDevice, Input<'static>, Output<'static>>) -> ! { | ||
| 31 | runner.run().await | ||
| 32 | } | ||
| 33 | |||
| 34 | #[embassy_executor::task] | ||
| 35 | async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! { | ||
| 36 | runner.run().await | ||
| 37 | } | ||
| 38 | |||
| 39 | #[embassy_executor::main] | ||
| 40 | async fn main(spawner: Spawner) { | ||
| 41 | let p = embassy_rp::init(Default::default()); | ||
| 42 | let mut rng = RoscRng; | ||
| 43 | |||
| 44 | let mut spi_cfg = SpiConfig::default(); | ||
| 45 | spi_cfg.frequency = 50_000_000; | ||
| 46 | let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18); | ||
| 47 | let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); | ||
| 48 | let cs = Output::new(p.PIN_17, Level::High); | ||
| 49 | let w5500_int = Input::new(p.PIN_21, Pull::Up); | ||
| 50 | let w5500_reset = Output::new(p.PIN_20, Level::High); | ||
| 51 | |||
| 52 | let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; | ||
| 53 | static STATE: StaticCell<State<8, 8>> = StaticCell::new(); | ||
| 54 | let state = STATE.init(State::<8, 8>::new()); | ||
| 55 | let (device, runner) = embassy_net_wiznet::new( | ||
| 56 | mac_addr, | ||
| 57 | state, | ||
| 58 | ExclusiveDevice::new(spi, cs, Delay), | ||
| 59 | w5500_int, | ||
| 60 | w5500_reset, | ||
| 61 | ) | ||
| 62 | .await | ||
| 63 | .unwrap(); | ||
| 64 | unwrap!(spawner.spawn(ethernet_task(runner))); | ||
| 65 | |||
| 66 | // Generate random seed | ||
| 67 | let seed = rng.next_u64(); | ||
| 68 | |||
| 69 | // Init network stack | ||
| 70 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | ||
| 71 | let (stack, runner) = embassy_net::new( | ||
| 72 | device, | ||
| 73 | embassy_net::Config::dhcpv4(Default::default()), | ||
| 74 | RESOURCES.init(StackResources::new()), | ||
| 75 | seed, | ||
| 76 | ); | ||
| 77 | |||
| 78 | // Launch network task | ||
| 79 | unwrap!(spawner.spawn(net_task(runner))); | ||
| 80 | |||
| 81 | info!("Waiting for DHCP..."); | ||
| 82 | let cfg = wait_for_config(stack).await; | ||
| 83 | let local_addr = cfg.address.address(); | ||
| 84 | info!("IP address: {:?}", local_addr); | ||
| 85 | |||
| 86 | // Then we can use it! | ||
| 87 | let mut rx_buffer = [0; 256]; | ||
| 88 | let mut tx_buffer = [0; 256]; | ||
| 89 | let mut rx_meta = [PacketMetadata::EMPTY]; | ||
| 90 | let mut tx_meta = [PacketMetadata::EMPTY]; | ||
| 91 | |||
| 92 | // Identifier used for the ICMP socket | ||
| 93 | let ident = 42; | ||
| 94 | |||
| 95 | // Create and bind the socket | ||
| 96 | let mut socket = IcmpSocket::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer); | ||
| 97 | socket.bind(IcmpEndpoint::Ident(ident)).unwrap(); | ||
| 98 | |||
| 99 | // Create the repr for the packet | ||
| 100 | let icmp_repr = Icmpv4Repr::EchoRequest { | ||
| 101 | ident, | ||
| 102 | seq_no: 0, | ||
| 103 | data: b"Hello, icmp!", | ||
| 104 | }; | ||
| 105 | |||
| 106 | // Send the packet and store the starting instant to mesure latency later | ||
| 107 | let start = socket | ||
| 108 | .send_to_with(icmp_repr.buffer_len(), cfg.gateway.unwrap(), |buf| { | ||
| 109 | // Create and populate the packet buffer allocated by `send_to_with` | ||
| 110 | let mut icmp_packet = Icmpv4Packet::new_unchecked(buf); | ||
| 111 | icmp_repr.emit(&mut icmp_packet, &ChecksumCapabilities::default()); | ||
| 112 | Instant::now() // Return the instant where the packet was sent | ||
| 113 | }) | ||
| 114 | .await | ||
| 115 | .unwrap(); | ||
| 116 | |||
| 117 | // Recieve and log the data of the reply | ||
| 118 | socket | ||
| 119 | .recv_from_with(|(buf, addr)| { | ||
| 120 | let packet = Icmpv4Packet::new_checked(buf).unwrap(); | ||
| 121 | info!( | ||
| 122 | "Recieved {:?} from {} in {}ms", | ||
| 123 | packet.data(), | ||
| 124 | addr, | ||
| 125 | start.elapsed().as_millis() | ||
| 126 | ); | ||
| 127 | }) | ||
| 128 | .await | ||
| 129 | .unwrap(); | ||
| 130 | |||
| 131 | loop { | ||
| 132 | Timer::after_secs(10).await; | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 { | ||
| 137 | loop { | ||
| 138 | if let Some(config) = stack.config_v4() { | ||
| 139 | return config.clone(); | ||
| 140 | } | ||
| 141 | yield_now().await; | ||
| 142 | } | ||
| 143 | } | ||
diff --git a/examples/rp/src/bin/ethernet_w5500_icmp_ping.rs b/examples/rp/src/bin/ethernet_w5500_icmp_ping.rs new file mode 100644 index 000000000..0ec594fd5 --- /dev/null +++ b/examples/rp/src/bin/ethernet_w5500_icmp_ping.rs | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | //! This example implements a LAN ping scan with the ping utilities in the icmp module of embassy-net. | ||
| 2 | //! | ||
| 3 | //! Example written for the [`WIZnet W5500-EVB-Pico`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico) board. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | |||
| 8 | use core::net::Ipv4Addr; | ||
| 9 | use core::ops::Not; | ||
| 10 | use core::str::FromStr; | ||
| 11 | |||
| 12 | use defmt::*; | ||
| 13 | use embassy_executor::Spawner; | ||
| 14 | use embassy_futures::yield_now; | ||
| 15 | use embassy_net::icmp::ping::{PingManager, PingParams}; | ||
| 16 | use embassy_net::icmp::PacketMetadata; | ||
| 17 | use embassy_net::{Ipv4Cidr, Stack, StackResources}; | ||
| 18 | use embassy_net_wiznet::chip::W5500; | ||
| 19 | use embassy_net_wiznet::*; | ||
| 20 | use embassy_rp::clocks::RoscRng; | ||
| 21 | use embassy_rp::gpio::{Input, Level, Output, Pull}; | ||
| 22 | use embassy_rp::peripherals::SPI0; | ||
| 23 | use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; | ||
| 24 | use embassy_time::{Delay, Duration}; | ||
| 25 | use embedded_hal_bus::spi::ExclusiveDevice; | ||
| 26 | use static_cell::StaticCell; | ||
| 27 | use {defmt_rtt as _, panic_probe as _}; | ||
| 28 | |||
| 29 | type ExclusiveSpiDevice = ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static>, Delay>; | ||
| 30 | |||
| 31 | #[embassy_executor::task] | ||
| 32 | async fn ethernet_task(runner: Runner<'static, W5500, ExclusiveSpiDevice, Input<'static>, Output<'static>>) -> ! { | ||
| 33 | runner.run().await | ||
| 34 | } | ||
| 35 | |||
| 36 | #[embassy_executor::task] | ||
| 37 | async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! { | ||
| 38 | runner.run().await | ||
| 39 | } | ||
| 40 | |||
| 41 | #[embassy_executor::main] | ||
| 42 | async fn main(spawner: Spawner) { | ||
| 43 | let p = embassy_rp::init(Default::default()); | ||
| 44 | let mut rng = RoscRng; | ||
| 45 | |||
| 46 | let mut spi_cfg = SpiConfig::default(); | ||
| 47 | spi_cfg.frequency = 50_000_000; | ||
| 48 | let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18); | ||
| 49 | let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); | ||
| 50 | let cs = Output::new(p.PIN_17, Level::High); | ||
| 51 | let w5500_int = Input::new(p.PIN_21, Pull::Up); | ||
| 52 | let w5500_reset = Output::new(p.PIN_20, Level::High); | ||
| 53 | |||
| 54 | let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; | ||
| 55 | static STATE: StaticCell<State<8, 8>> = StaticCell::new(); | ||
| 56 | let state = STATE.init(State::<8, 8>::new()); | ||
| 57 | let (device, runner) = embassy_net_wiznet::new( | ||
| 58 | mac_addr, | ||
| 59 | state, | ||
| 60 | ExclusiveDevice::new(spi, cs, Delay), | ||
| 61 | w5500_int, | ||
| 62 | w5500_reset, | ||
| 63 | ) | ||
| 64 | .await | ||
| 65 | .unwrap(); | ||
| 66 | unwrap!(spawner.spawn(ethernet_task(runner))); | ||
| 67 | |||
| 68 | // Generate random seed | ||
| 69 | let seed = rng.next_u64(); | ||
| 70 | |||
| 71 | // Init network stack | ||
| 72 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | ||
| 73 | let (stack, runner) = embassy_net::new( | ||
| 74 | device, | ||
| 75 | embassy_net::Config::dhcpv4(Default::default()), | ||
| 76 | RESOURCES.init(StackResources::new()), | ||
| 77 | seed, | ||
| 78 | ); | ||
| 79 | |||
| 80 | // Launch network task | ||
| 81 | unwrap!(spawner.spawn(net_task(runner))); | ||
| 82 | |||
| 83 | info!("Waiting for DHCP..."); | ||
| 84 | let cfg = wait_for_config(stack).await; | ||
| 85 | let local_addr = cfg.address.address(); | ||
| 86 | info!("IP address: {:?}", local_addr); | ||
| 87 | let gateway = cfg.gateway.unwrap(); | ||
| 88 | let mask = cfg.address.netmask(); | ||
| 89 | let lower_bound = (gateway.to_bits() & mask.to_bits()) + 1; | ||
| 90 | let upper_bound = gateway.to_bits() | mask.to_bits().not(); | ||
| 91 | let addr_range = lower_bound..=upper_bound; | ||
| 92 | |||
| 93 | // Then we can use it! | ||
| 94 | let mut rx_buffer = [0; 256]; | ||
| 95 | let mut tx_buffer = [0; 256]; | ||
| 96 | let mut rx_meta = [PacketMetadata::EMPTY]; | ||
| 97 | let mut tx_meta = [PacketMetadata::EMPTY]; | ||
| 98 | |||
| 99 | // Create the ping manager instance | ||
| 100 | let mut ping_manager = PingManager::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer); | ||
| 101 | let addr = "192.168.8.1"; // Address to ping to | ||
| 102 | // Create the PingParams with the target address | ||
| 103 | let mut ping_params = PingParams::new(Ipv4Addr::from_str(addr).unwrap()); | ||
| 104 | // (optional) Set custom properties of the ping | ||
| 105 | ping_params.set_payload(b"Hello, Ping!"); // custom payload | ||
| 106 | ping_params.set_count(1); // ping 1 times per ping call | ||
| 107 | ping_params.set_timeout(Duration::from_millis(500)); // wait .5 seconds instead of 4 | ||
| 108 | |||
| 109 | info!("Online hosts in {}:", Ipv4Cidr::from_netmask(gateway, mask).unwrap()); | ||
| 110 | let mut total_online_hosts = 0u32; | ||
| 111 | for addr in addr_range { | ||
| 112 | let ip_addr = Ipv4Addr::from_bits(addr); | ||
| 113 | // Set the target address in the ping params | ||
| 114 | ping_params.set_target(ip_addr); | ||
| 115 | // Execute the ping with the given parameters and wait for the reply | ||
| 116 | match ping_manager.ping(&ping_params).await { | ||
| 117 | Ok(time) => { | ||
| 118 | info!("{} is online\n- latency: {}ms\n", ip_addr, time.as_millis()); | ||
| 119 | total_online_hosts += 1; | ||
| 120 | } | ||
| 121 | _ => continue, | ||
| 122 | } | ||
| 123 | } | ||
| 124 | info!("Ping scan complete, total online hosts: {}", total_online_hosts); | ||
| 125 | } | ||
| 126 | |||
| 127 | async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 { | ||
| 128 | loop { | ||
| 129 | if let Some(config) = stack.config_v4() { | ||
| 130 | return config.clone(); | ||
| 131 | } | ||
| 132 | yield_now().await; | ||
| 133 | } | ||
| 134 | } | ||
diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs index aaa035a72..27e2f3c30 100644 --- a/examples/rp/src/bin/ethernet_w5500_multisocket.rs +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | //! This example shows how you can allow multiple simultaneous TCP connections, by having multiple sockets listening on the same port. | 1 | //! This example shows how you can allow multiple simultaneous TCP connections, by having multiple sockets listening on the same port. |
| 2 | //! | 2 | //! |
| 3 | //! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. | 3 | //! Example written for the [`WIZnet W5500-EVB-Pico`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico) board. |
| 4 | 4 | ||
| 5 | #![no_std] | 5 | #![no_std] |
| 6 | #![no_main] | 6 | #![no_main] |
| @@ -18,7 +18,6 @@ use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; | |||
| 18 | use embassy_time::{Delay, Duration}; | 18 | use embassy_time::{Delay, Duration}; |
| 19 | use embedded_hal_bus::spi::ExclusiveDevice; | 19 | use embedded_hal_bus::spi::ExclusiveDevice; |
| 20 | use embedded_io_async::Write; | 20 | use embedded_io_async::Write; |
| 21 | use rand::RngCore; | ||
| 22 | use static_cell::StaticCell; | 21 | use static_cell::StaticCell; |
| 23 | use {defmt_rtt as _, panic_probe as _}; | 22 | use {defmt_rtt as _, panic_probe as _}; |
| 24 | 23 | ||
| @@ -36,8 +35,8 @@ async fn ethernet_task( | |||
| 36 | } | 35 | } |
| 37 | 36 | ||
| 38 | #[embassy_executor::task] | 37 | #[embassy_executor::task] |
| 39 | async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { | 38 | async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! { |
| 40 | stack.run().await | 39 | runner.run().await |
| 41 | } | 40 | } |
| 42 | 41 | ||
| 43 | #[embassy_executor::main] | 42 | #[embassy_executor::main] |
| @@ -71,17 +70,16 @@ async fn main(spawner: Spawner) { | |||
| 71 | let seed = rng.next_u64(); | 70 | let seed = rng.next_u64(); |
| 72 | 71 | ||
| 73 | // Init network stack | 72 | // Init network stack |
| 74 | static STACK: StaticCell<Stack<Device<'static>>> = StaticCell::new(); | ||
| 75 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 73 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 76 | let stack = &*STACK.init(Stack::new( | 74 | let (stack, runner) = embassy_net::new( |
| 77 | device, | 75 | device, |
| 78 | embassy_net::Config::dhcpv4(Default::default()), | 76 | embassy_net::Config::dhcpv4(Default::default()), |
| 79 | RESOURCES.init(StackResources::new()), | 77 | RESOURCES.init(StackResources::new()), |
| 80 | seed, | 78 | seed, |
| 81 | )); | 79 | ); |
| 82 | 80 | ||
| 83 | // Launch network task | 81 | // Launch network task |
| 84 | unwrap!(spawner.spawn(net_task(&stack))); | 82 | unwrap!(spawner.spawn(net_task(runner))); |
| 85 | 83 | ||
| 86 | info!("Waiting for DHCP..."); | 84 | info!("Waiting for DHCP..."); |
| 87 | let cfg = wait_for_config(stack).await; | 85 | let cfg = wait_for_config(stack).await; |
| @@ -89,12 +87,12 @@ async fn main(spawner: Spawner) { | |||
| 89 | info!("IP address: {:?}", local_addr); | 87 | info!("IP address: {:?}", local_addr); |
| 90 | 88 | ||
| 91 | // Create two sockets listening to the same port, to handle simultaneous connections | 89 | // Create two sockets listening to the same port, to handle simultaneous connections |
| 92 | unwrap!(spawner.spawn(listen_task(&stack, 0, 1234))); | 90 | unwrap!(spawner.spawn(listen_task(stack, 0, 1234))); |
| 93 | unwrap!(spawner.spawn(listen_task(&stack, 1, 1234))); | 91 | unwrap!(spawner.spawn(listen_task(stack, 1, 1234))); |
| 94 | } | 92 | } |
| 95 | 93 | ||
| 96 | #[embassy_executor::task(pool_size = 2)] | 94 | #[embassy_executor::task(pool_size = 2)] |
| 97 | async fn listen_task(stack: &'static Stack<Device<'static>>, id: u8, port: u16) { | 95 | async fn listen_task(stack: Stack<'static>, id: u8, port: u16) { |
| 98 | let mut rx_buffer = [0; 4096]; | 96 | let mut rx_buffer = [0; 4096]; |
| 99 | let mut tx_buffer = [0; 4096]; | 97 | let mut tx_buffer = [0; 4096]; |
| 100 | let mut buf = [0; 4096]; | 98 | let mut buf = [0; 4096]; |
| @@ -131,7 +129,7 @@ async fn listen_task(stack: &'static Stack<Device<'static>>, id: u8, port: u16) | |||
| 131 | } | 129 | } |
| 132 | } | 130 | } |
| 133 | 131 | ||
| 134 | async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { | 132 | async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 { |
| 135 | loop { | 133 | loop { |
| 136 | if let Some(config) = stack.config_v4() { | 134 | if let Some(config) = stack.config_v4() { |
| 137 | return config.clone(); | 135 | return config.clone(); |
diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs index 8e96a114c..ba82f2a60 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | //! This example implements a TCP client that attempts to connect to a host on port 1234 and send it some data once per second. | 1 | //! This example implements a TCP client that attempts to connect to a host on port 1234 and send it some data once per second. |
| 2 | //! | 2 | //! |
| 3 | //! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. | 3 | //! Example written for the [`WIZnet W5500-EVB-Pico`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico) board. |
| 4 | 4 | ||
| 5 | #![no_std] | 5 | #![no_std] |
| 6 | #![no_main] | 6 | #![no_main] |
| @@ -20,7 +20,6 @@ use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; | |||
| 20 | use embassy_time::{Delay, Duration, Timer}; | 20 | use embassy_time::{Delay, Duration, Timer}; |
| 21 | use embedded_hal_bus::spi::ExclusiveDevice; | 21 | use embedded_hal_bus::spi::ExclusiveDevice; |
| 22 | use embedded_io_async::Write; | 22 | use embedded_io_async::Write; |
| 23 | use rand::RngCore; | ||
| 24 | use static_cell::StaticCell; | 23 | use static_cell::StaticCell; |
| 25 | use {defmt_rtt as _, panic_probe as _}; | 24 | use {defmt_rtt as _, panic_probe as _}; |
| 26 | 25 | ||
| @@ -38,8 +37,8 @@ async fn ethernet_task( | |||
| 38 | } | 37 | } |
| 39 | 38 | ||
| 40 | #[embassy_executor::task] | 39 | #[embassy_executor::task] |
| 41 | async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { | 40 | async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! { |
| 42 | stack.run().await | 41 | runner.run().await |
| 43 | } | 42 | } |
| 44 | 43 | ||
| 45 | #[embassy_executor::main] | 44 | #[embassy_executor::main] |
| @@ -74,17 +73,16 @@ async fn main(spawner: Spawner) { | |||
| 74 | let seed = rng.next_u64(); | 73 | let seed = rng.next_u64(); |
| 75 | 74 | ||
| 76 | // Init network stack | 75 | // Init network stack |
| 77 | static STACK: StaticCell<Stack<Device<'static>>> = StaticCell::new(); | ||
| 78 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 76 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 79 | let stack = &*STACK.init(Stack::new( | 77 | let (stack, runner) = embassy_net::new( |
| 80 | device, | 78 | device, |
| 81 | embassy_net::Config::dhcpv4(Default::default()), | 79 | embassy_net::Config::dhcpv4(Default::default()), |
| 82 | RESOURCES.init(StackResources::new()), | 80 | RESOURCES.init(StackResources::new()), |
| 83 | seed, | 81 | seed, |
| 84 | )); | 82 | ); |
| 85 | 83 | ||
| 86 | // Launch network task | 84 | // Launch network task |
| 87 | unwrap!(spawner.spawn(net_task(&stack))); | 85 | unwrap!(spawner.spawn(net_task(runner))); |
| 88 | 86 | ||
| 89 | info!("Waiting for DHCP..."); | 87 | info!("Waiting for DHCP..."); |
| 90 | let cfg = wait_for_config(stack).await; | 88 | let cfg = wait_for_config(stack).await; |
| @@ -119,7 +117,7 @@ async fn main(spawner: Spawner) { | |||
| 119 | } | 117 | } |
| 120 | } | 118 | } |
| 121 | 119 | ||
| 122 | async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { | 120 | async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 { |
| 123 | loop { | 121 | loop { |
| 124 | if let Some(config) = stack.config_v4() { | 122 | if let Some(config) = stack.config_v4() { |
| 125 | return config.clone(); | 123 | return config.clone(); |
diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs index 40736bf3c..5c56dcafa 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | //! This example implements a TCP echo server on port 1234 and using DHCP. | 1 | //! This example implements a TCP echo server on port 1234 and using DHCP. |
| 2 | //! Send it some data, you should see it echoed back and printed in the console. | 2 | //! Send it some data, you should see it echoed back and printed in the console. |
| 3 | //! | 3 | //! |
| 4 | //! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. | 4 | //! Example written for the [`WIZnet W5500-EVB-Pico`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico) board. |
| 5 | 5 | ||
| 6 | #![no_std] | 6 | #![no_std] |
| 7 | #![no_main] | 7 | #![no_main] |
| @@ -19,7 +19,6 @@ use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; | |||
| 19 | use embassy_time::{Delay, Duration}; | 19 | use embassy_time::{Delay, Duration}; |
| 20 | use embedded_hal_bus::spi::ExclusiveDevice; | 20 | use embedded_hal_bus::spi::ExclusiveDevice; |
| 21 | use embedded_io_async::Write; | 21 | use embedded_io_async::Write; |
| 22 | use rand::RngCore; | ||
| 23 | use static_cell::StaticCell; | 22 | use static_cell::StaticCell; |
| 24 | use {defmt_rtt as _, panic_probe as _}; | 23 | use {defmt_rtt as _, panic_probe as _}; |
| 25 | 24 | ||
| @@ -37,8 +36,8 @@ async fn ethernet_task( | |||
| 37 | } | 36 | } |
| 38 | 37 | ||
| 39 | #[embassy_executor::task] | 38 | #[embassy_executor::task] |
| 40 | async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { | 39 | async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! { |
| 41 | stack.run().await | 40 | runner.run().await |
| 42 | } | 41 | } |
| 43 | 42 | ||
| 44 | #[embassy_executor::main] | 43 | #[embassy_executor::main] |
| @@ -73,17 +72,16 @@ async fn main(spawner: Spawner) { | |||
| 73 | let seed = rng.next_u64(); | 72 | let seed = rng.next_u64(); |
| 74 | 73 | ||
| 75 | // Init network stack | 74 | // Init network stack |
| 76 | static STACK: StaticCell<Stack<Device<'static>>> = StaticCell::new(); | ||
| 77 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 75 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 78 | let stack = &*STACK.init(Stack::new( | 76 | let (stack, runner) = embassy_net::new( |
| 79 | device, | 77 | device, |
| 80 | embassy_net::Config::dhcpv4(Default::default()), | 78 | embassy_net::Config::dhcpv4(Default::default()), |
| 81 | RESOURCES.init(StackResources::new()), | 79 | RESOURCES.init(StackResources::new()), |
| 82 | seed, | 80 | seed, |
| 83 | )); | 81 | ); |
| 84 | 82 | ||
| 85 | // Launch network task | 83 | // Launch network task |
| 86 | unwrap!(spawner.spawn(net_task(&stack))); | 84 | unwrap!(spawner.spawn(net_task(runner))); |
| 87 | 85 | ||
| 88 | info!("Waiting for DHCP..."); | 86 | info!("Waiting for DHCP..."); |
| 89 | let cfg = wait_for_config(stack).await; | 87 | let cfg = wait_for_config(stack).await; |
| @@ -128,7 +126,7 @@ async fn main(spawner: Spawner) { | |||
| 128 | } | 126 | } |
| 129 | } | 127 | } |
| 130 | 128 | ||
| 131 | async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { | 129 | async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 { |
| 132 | loop { | 130 | loop { |
| 133 | if let Some(config) = stack.config_v4() { | 131 | if let Some(config) = stack.config_v4() { |
| 134 | return config.clone(); | 132 | return config.clone(); |
diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs index c79f01538..c5fc8de1d 100644 --- a/examples/rp/src/bin/ethernet_w5500_udp.rs +++ b/examples/rp/src/bin/ethernet_w5500_udp.rs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | //! This example implements a UDP server listening on port 1234 and echoing back the data. | 1 | //! This example implements a UDP server listening on port 1234 and echoing back the data. |
| 2 | //! | 2 | //! |
| 3 | //! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. | 3 | //! Example written for the [`WIZnet W5500-EVB-Pico`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico) board. |
| 4 | 4 | ||
| 5 | #![no_std] | 5 | #![no_std] |
| 6 | #![no_main] | 6 | #![no_main] |
| @@ -18,7 +18,6 @@ use embassy_rp::peripherals::SPI0; | |||
| 18 | use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; | 18 | use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; |
| 19 | use embassy_time::Delay; | 19 | use embassy_time::Delay; |
| 20 | use embedded_hal_bus::spi::ExclusiveDevice; | 20 | use embedded_hal_bus::spi::ExclusiveDevice; |
| 21 | use rand::RngCore; | ||
| 22 | use static_cell::StaticCell; | 21 | use static_cell::StaticCell; |
| 23 | use {defmt_rtt as _, panic_probe as _}; | 22 | use {defmt_rtt as _, panic_probe as _}; |
| 24 | 23 | ||
| @@ -36,8 +35,8 @@ async fn ethernet_task( | |||
| 36 | } | 35 | } |
| 37 | 36 | ||
| 38 | #[embassy_executor::task] | 37 | #[embassy_executor::task] |
| 39 | async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { | 38 | async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! { |
| 40 | stack.run().await | 39 | runner.run().await |
| 41 | } | 40 | } |
| 42 | 41 | ||
| 43 | #[embassy_executor::main] | 42 | #[embassy_executor::main] |
| @@ -71,17 +70,16 @@ async fn main(spawner: Spawner) { | |||
| 71 | let seed = rng.next_u64(); | 70 | let seed = rng.next_u64(); |
| 72 | 71 | ||
| 73 | // Init network stack | 72 | // Init network stack |
| 74 | static STACK: StaticCell<Stack<Device<'static>>> = StaticCell::new(); | ||
| 75 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 73 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 76 | let stack = &*STACK.init(Stack::new( | 74 | let (stack, runner) = embassy_net::new( |
| 77 | device, | 75 | device, |
| 78 | embassy_net::Config::dhcpv4(Default::default()), | 76 | embassy_net::Config::dhcpv4(Default::default()), |
| 79 | RESOURCES.init(StackResources::new()), | 77 | RESOURCES.init(StackResources::new()), |
| 80 | seed, | 78 | seed, |
| 81 | )); | 79 | ); |
| 82 | 80 | ||
| 83 | // Launch network task | 81 | // Launch network task |
| 84 | unwrap!(spawner.spawn(net_task(&stack))); | 82 | unwrap!(spawner.spawn(net_task(runner))); |
| 85 | 83 | ||
| 86 | info!("Waiting for DHCP..."); | 84 | info!("Waiting for DHCP..."); |
| 87 | let cfg = wait_for_config(stack).await; | 85 | let cfg = wait_for_config(stack).await; |
| @@ -108,7 +106,7 @@ async fn main(spawner: Spawner) { | |||
| 108 | } | 106 | } |
| 109 | } | 107 | } |
| 110 | 108 | ||
| 111 | async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { | 109 | async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 { |
| 112 | loop { | 110 | loop { |
| 113 | if let Some(config) = stack.config_v4() { | 111 | if let Some(config) = stack.config_v4() { |
| 114 | return config.clone(); | 112 | return config.clone(); |
diff --git a/examples/rp/src/bin/i2c_slave.rs b/examples/rp/src/bin/i2c_slave.rs index 9fffb4646..08f31001b 100644 --- a/examples/rp/src/bin/i2c_slave.rs +++ b/examples/rp/src/bin/i2c_slave.rs | |||
| @@ -99,19 +99,19 @@ async fn main(spawner: Spawner) { | |||
| 99 | let p = embassy_rp::init(Default::default()); | 99 | let p = embassy_rp::init(Default::default()); |
| 100 | info!("Hello World!"); | 100 | info!("Hello World!"); |
| 101 | 101 | ||
| 102 | let d_sda = p.PIN_3; | 102 | let d_sda = p.PIN_2; |
| 103 | let d_scl = p.PIN_2; | 103 | let d_scl = p.PIN_3; |
| 104 | let mut config = i2c_slave::Config::default(); | 104 | let mut config = i2c_slave::Config::default(); |
| 105 | config.addr = DEV_ADDR as u16; | 105 | config.addr = DEV_ADDR as u16; |
| 106 | let device = i2c_slave::I2cSlave::new(p.I2C1, d_sda, d_scl, Irqs, config); | 106 | let device = i2c_slave::I2cSlave::new(p.I2C1, d_scl, d_sda, Irqs, config); |
| 107 | 107 | ||
| 108 | unwrap!(spawner.spawn(device_task(device))); | 108 | unwrap!(spawner.spawn(device_task(device))); |
| 109 | 109 | ||
| 110 | let c_sda = p.PIN_1; | 110 | let c_sda = p.PIN_0; |
| 111 | let c_scl = p.PIN_0; | 111 | let c_scl = p.PIN_1; |
| 112 | let mut config = i2c::Config::default(); | 112 | let mut config = i2c::Config::default(); |
| 113 | config.frequency = 1_000_000; | 113 | config.frequency = 1_000_000; |
| 114 | let controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, config); | 114 | let controller = i2c::I2c::new_async(p.I2C0, c_scl, c_sda, Irqs, config); |
| 115 | 115 | ||
| 116 | unwrap!(spawner.spawn(controller_task(controller))); | 116 | unwrap!(spawner.spawn(controller_task(controller))); |
| 117 | } | 117 | } |
diff --git a/examples/rp/src/bin/interrupt.rs b/examples/rp/src/bin/interrupt.rs index 5b9d7027e..787cdc112 100644 --- a/examples/rp/src/bin/interrupt.rs +++ b/examples/rp/src/bin/interrupt.rs | |||
| @@ -32,7 +32,6 @@ static ADC_VALUES: Channel<CriticalSectionRawMutex, u16, 2048> = Channel::new(); | |||
| 32 | 32 | ||
| 33 | #[embassy_executor::main] | 33 | #[embassy_executor::main] |
| 34 | async fn main(spawner: Spawner) { | 34 | async fn main(spawner: Spawner) { |
| 35 | embassy_rp::pac::SIO.spinlock(31).write_value(1); | ||
| 36 | let p = embassy_rp::init(Default::default()); | 35 | let p = embassy_rp::init(Default::default()); |
| 37 | 36 | ||
| 38 | let adc = Adc::new_blocking(p.ADC, Default::default()); | 37 | let adc = Adc::new_blocking(p.ADC, Default::default()); |
diff --git a/examples/rp/src/bin/orchestrate_tasks.rs b/examples/rp/src/bin/orchestrate_tasks.rs new file mode 100644 index 000000000..c35679251 --- /dev/null +++ b/examples/rp/src/bin/orchestrate_tasks.rs | |||
| @@ -0,0 +1,318 @@ | |||
| 1 | //! This example demonstrates some approaches to communicate between tasks in order to orchestrate the state of the system. | ||
| 2 | //! | ||
| 3 | //! The system consists of several tasks: | ||
| 4 | //! - Three tasks that generate random numbers at different intervals (simulating i.e. sensor readings) | ||
| 5 | //! - A task that monitors USB power connection (hardware event handling) | ||
| 6 | //! - A task that reads system voltage (ADC sampling) | ||
| 7 | //! - A consumer task that processes all this information | ||
| 8 | //! | ||
| 9 | //! The system maintains state in a single place, wrapped in a Mutex. | ||
| 10 | //! | ||
| 11 | //! We demonstrate how to: | ||
| 12 | //! - use a mutex to maintain shared state between tasks | ||
| 13 | //! - use a channel to send events between tasks | ||
| 14 | //! - use an orchestrator task to coordinate tasks and handle state transitions | ||
| 15 | //! - use signals to notify about state changes and terminate tasks | ||
| 16 | |||
| 17 | #![no_std] | ||
| 18 | #![no_main] | ||
| 19 | |||
| 20 | use assign_resources::assign_resources; | ||
| 21 | use defmt::*; | ||
| 22 | use embassy_executor::Spawner; | ||
| 23 | use embassy_futures::select::{select, Either}; | ||
| 24 | use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler}; | ||
| 25 | use embassy_rp::clocks::RoscRng; | ||
| 26 | use embassy_rp::gpio::{Input, Pull}; | ||
| 27 | use embassy_rp::{bind_interrupts, peripherals, Peri}; | ||
| 28 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 29 | use embassy_sync::mutex::Mutex; | ||
| 30 | use embassy_sync::{channel, signal}; | ||
| 31 | use embassy_time::{Duration, Timer}; | ||
| 32 | use {defmt_rtt as _, panic_probe as _}; | ||
| 33 | |||
| 34 | // Hardware resource assignment. See other examples for different ways of doing this. | ||
| 35 | assign_resources! { | ||
| 36 | vsys: Vsys { | ||
| 37 | adc: ADC, | ||
| 38 | pin_29: PIN_29, | ||
| 39 | }, | ||
| 40 | vbus: Vbus { | ||
| 41 | pin_24: PIN_24, | ||
| 42 | }, | ||
| 43 | } | ||
| 44 | |||
| 45 | // Interrupt binding - required for hardware peripherals like ADC | ||
| 46 | bind_interrupts!(struct Irqs { | ||
| 47 | ADC_IRQ_FIFO => InterruptHandler; | ||
| 48 | }); | ||
| 49 | |||
| 50 | /// Events that worker tasks send to the orchestrator | ||
| 51 | enum Events { | ||
| 52 | UsbPowered(bool), // USB connection state changed | ||
| 53 | VsysVoltage(f32), // New voltage reading | ||
| 54 | FirstRandomSeed(u32), // Random number from 30s timer | ||
| 55 | SecondRandomSeed(u32), // Random number from 60s timer | ||
| 56 | ThirdRandomSeed(u32), // Random number from 90s timer | ||
| 57 | ResetFirstRandomSeed, // Signal to reset the first counter | ||
| 58 | } | ||
| 59 | |||
| 60 | /// Commands that can control task behavior. | ||
| 61 | /// Currently only used to stop tasks, but could be extended for other controls. | ||
| 62 | enum Commands { | ||
| 63 | /// Signals a task to stop execution | ||
| 64 | Stop, | ||
| 65 | } | ||
| 66 | |||
| 67 | /// The central state of our system, shared between tasks. | ||
| 68 | #[derive(Clone, Format)] | ||
| 69 | struct State { | ||
| 70 | usb_powered: bool, | ||
| 71 | vsys_voltage: f32, | ||
| 72 | first_random_seed: u32, | ||
| 73 | second_random_seed: u32, | ||
| 74 | third_random_seed: u32, | ||
| 75 | first_random_seed_task_running: bool, | ||
| 76 | times_we_got_first_random_seed: u8, | ||
| 77 | maximum_times_we_want_first_random_seed: u8, | ||
| 78 | } | ||
| 79 | |||
| 80 | /// A formatted view of the system status, used for logging. Used for the below `get_system_summary` fn. | ||
| 81 | #[derive(Format)] | ||
| 82 | struct SystemStatus { | ||
| 83 | power_source: &'static str, | ||
| 84 | voltage: f32, | ||
| 85 | } | ||
| 86 | |||
| 87 | impl State { | ||
| 88 | const fn new() -> Self { | ||
| 89 | Self { | ||
| 90 | usb_powered: false, | ||
| 91 | vsys_voltage: 0.0, | ||
| 92 | first_random_seed: 0, | ||
| 93 | second_random_seed: 0, | ||
| 94 | third_random_seed: 0, | ||
| 95 | first_random_seed_task_running: false, | ||
| 96 | times_we_got_first_random_seed: 0, | ||
| 97 | maximum_times_we_want_first_random_seed: 3, | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | /// Returns a formatted summary of power state and voltage. | ||
| 102 | /// Shows how to create methods that work with shared state. | ||
| 103 | fn get_system_summary(&self) -> SystemStatus { | ||
| 104 | SystemStatus { | ||
| 105 | power_source: if self.usb_powered { | ||
| 106 | "USB powered" | ||
| 107 | } else { | ||
| 108 | "Battery powered" | ||
| 109 | }, | ||
| 110 | voltage: self.vsys_voltage, | ||
| 111 | } | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | /// The shared state protected by a mutex | ||
| 116 | static SYSTEM_STATE: Mutex<CriticalSectionRawMutex, State> = Mutex::new(State::new()); | ||
| 117 | |||
| 118 | /// Channel for events from worker tasks to the orchestrator | ||
| 119 | static EVENT_CHANNEL: channel::Channel<CriticalSectionRawMutex, Events, 10> = channel::Channel::new(); | ||
| 120 | |||
| 121 | /// Signal used to stop the first random number task | ||
| 122 | static STOP_FIRST_RANDOM_SIGNAL: signal::Signal<CriticalSectionRawMutex, Commands> = signal::Signal::new(); | ||
| 123 | |||
| 124 | /// Signal for notifying about state changes | ||
| 125 | static STATE_CHANGED: signal::Signal<CriticalSectionRawMutex, ()> = signal::Signal::new(); | ||
| 126 | |||
| 127 | #[embassy_executor::main] | ||
| 128 | async fn main(spawner: Spawner) { | ||
| 129 | let p = embassy_rp::init(Default::default()); | ||
| 130 | let r = split_resources! {p}; | ||
| 131 | |||
| 132 | spawner.spawn(orchestrate(spawner)).unwrap(); | ||
| 133 | spawner.spawn(random_60s(spawner)).unwrap(); | ||
| 134 | spawner.spawn(random_90s(spawner)).unwrap(); | ||
| 135 | // `random_30s` is not spawned here, butin the orchestrate task depending on state | ||
| 136 | spawner.spawn(usb_power(spawner, r.vbus)).unwrap(); | ||
| 137 | spawner.spawn(vsys_voltage(spawner, r.vsys)).unwrap(); | ||
| 138 | spawner.spawn(consumer(spawner)).unwrap(); | ||
| 139 | } | ||
| 140 | |||
| 141 | /// Main task that processes all events and updates system state. | ||
| 142 | #[embassy_executor::task] | ||
| 143 | async fn orchestrate(spawner: Spawner) { | ||
| 144 | let receiver = EVENT_CHANNEL.receiver(); | ||
| 145 | |||
| 146 | loop { | ||
| 147 | // Do nothing until we receive any event | ||
| 148 | let event = receiver.receive().await; | ||
| 149 | |||
| 150 | // Scope in which we want to lock the system state. As an alternative we could also call `drop` on the state | ||
| 151 | { | ||
| 152 | let mut state = SYSTEM_STATE.lock().await; | ||
| 153 | |||
| 154 | match event { | ||
| 155 | Events::UsbPowered(usb_powered) => { | ||
| 156 | state.usb_powered = usb_powered; | ||
| 157 | info!("Usb powered: {}", usb_powered); | ||
| 158 | info!("System summary: {}", state.get_system_summary()); | ||
| 159 | } | ||
| 160 | Events::VsysVoltage(voltage) => { | ||
| 161 | state.vsys_voltage = voltage; | ||
| 162 | info!("Vsys voltage: {}", voltage); | ||
| 163 | } | ||
| 164 | Events::FirstRandomSeed(seed) => { | ||
| 165 | state.first_random_seed = seed; | ||
| 166 | state.times_we_got_first_random_seed += 1; | ||
| 167 | info!( | ||
| 168 | "First random seed: {}, and that was iteration {} of receiving this.", | ||
| 169 | seed, &state.times_we_got_first_random_seed | ||
| 170 | ); | ||
| 171 | } | ||
| 172 | Events::SecondRandomSeed(seed) => { | ||
| 173 | state.second_random_seed = seed; | ||
| 174 | info!("Second random seed: {}", seed); | ||
| 175 | } | ||
| 176 | Events::ThirdRandomSeed(seed) => { | ||
| 177 | state.third_random_seed = seed; | ||
| 178 | info!("Third random seed: {}", seed); | ||
| 179 | } | ||
| 180 | Events::ResetFirstRandomSeed => { | ||
| 181 | state.times_we_got_first_random_seed = 0; | ||
| 182 | state.first_random_seed = 0; | ||
| 183 | info!("Resetting the first random seed counter"); | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | // Handle task orchestration based on state | ||
| 188 | // Just placed as an example here, could be hooked into the event system, puton a timer, ... | ||
| 189 | match state.times_we_got_first_random_seed { | ||
| 190 | max if max == state.maximum_times_we_want_first_random_seed => { | ||
| 191 | info!("Stopping the first random signal task"); | ||
| 192 | STOP_FIRST_RANDOM_SIGNAL.signal(Commands::Stop); | ||
| 193 | EVENT_CHANNEL.sender().send(Events::ResetFirstRandomSeed).await; | ||
| 194 | } | ||
| 195 | 0 => { | ||
| 196 | let respawn_first_random_seed_task = !state.first_random_seed_task_running; | ||
| 197 | // Deliberately dropping the Mutex lock here to release it before a lengthy operation | ||
| 198 | drop(state); | ||
| 199 | if respawn_first_random_seed_task { | ||
| 200 | info!("(Re)-Starting the first random signal task"); | ||
| 201 | spawner.spawn(random_30s(spawner)).unwrap(); | ||
| 202 | } | ||
| 203 | } | ||
| 204 | _ => {} | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | STATE_CHANGED.signal(()); | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | /// Task that monitors state changes and logs system status. | ||
| 213 | #[embassy_executor::task] | ||
| 214 | async fn consumer(_spawner: Spawner) { | ||
| 215 | loop { | ||
| 216 | // Wait for state change notification | ||
| 217 | STATE_CHANGED.wait().await; | ||
| 218 | |||
| 219 | let state = SYSTEM_STATE.lock().await; | ||
| 220 | info!( | ||
| 221 | "State update - {} | Seeds - First: {} (count: {}/{}, running: {}), Second: {}, Third: {}", | ||
| 222 | state.get_system_summary(), | ||
| 223 | state.first_random_seed, | ||
| 224 | state.times_we_got_first_random_seed, | ||
| 225 | state.maximum_times_we_want_first_random_seed, | ||
| 226 | state.first_random_seed_task_running, | ||
| 227 | state.second_random_seed, | ||
| 228 | state.third_random_seed | ||
| 229 | ); | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | /// Task that generates random numbers every 30 seconds until stopped. | ||
| 234 | /// Shows how to handle both timer events and stop signals. | ||
| 235 | /// As an example of some routine we want to be on or off depending on other needs. | ||
| 236 | #[embassy_executor::task] | ||
| 237 | async fn random_30s(_spawner: Spawner) { | ||
| 238 | { | ||
| 239 | let mut state = SYSTEM_STATE.lock().await; | ||
| 240 | state.first_random_seed_task_running = true; | ||
| 241 | } | ||
| 242 | |||
| 243 | let mut rng = RoscRng; | ||
| 244 | let sender = EVENT_CHANNEL.sender(); | ||
| 245 | |||
| 246 | loop { | ||
| 247 | // Wait for either 30s timer or stop signal (like select() in Go) | ||
| 248 | match select(Timer::after(Duration::from_secs(30)), STOP_FIRST_RANDOM_SIGNAL.wait()).await { | ||
| 249 | Either::First(_) => { | ||
| 250 | info!("30s are up, generating random number"); | ||
| 251 | let random_number = rng.next_u32(); | ||
| 252 | sender.send(Events::FirstRandomSeed(random_number)).await; | ||
| 253 | } | ||
| 254 | Either::Second(_) => { | ||
| 255 | info!("Received signal to stop, goodbye!"); | ||
| 256 | |||
| 257 | let mut state = SYSTEM_STATE.lock().await; | ||
| 258 | state.first_random_seed_task_running = false; | ||
| 259 | |||
| 260 | break; | ||
| 261 | } | ||
| 262 | } | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | /// Task that generates random numbers every 60 seconds. As an example of some routine. | ||
| 267 | #[embassy_executor::task] | ||
| 268 | async fn random_60s(_spawner: Spawner) { | ||
| 269 | let mut rng = RoscRng; | ||
| 270 | let sender = EVENT_CHANNEL.sender(); | ||
| 271 | |||
| 272 | loop { | ||
| 273 | Timer::after(Duration::from_secs(60)).await; | ||
| 274 | let random_number = rng.next_u32(); | ||
| 275 | sender.send(Events::SecondRandomSeed(random_number)).await; | ||
| 276 | } | ||
| 277 | } | ||
| 278 | |||
| 279 | /// Task that generates random numbers every 90 seconds. . As an example of some routine. | ||
| 280 | #[embassy_executor::task] | ||
| 281 | async fn random_90s(_spawner: Spawner) { | ||
| 282 | let mut rng = RoscRng; | ||
| 283 | let sender = EVENT_CHANNEL.sender(); | ||
| 284 | |||
| 285 | loop { | ||
| 286 | Timer::after(Duration::from_secs(90)).await; | ||
| 287 | let random_number = rng.next_u32(); | ||
| 288 | sender.send(Events::ThirdRandomSeed(random_number)).await; | ||
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 292 | /// Task that monitors USB power connection. As an example of some Interrupt somewhere. | ||
| 293 | #[embassy_executor::task] | ||
| 294 | pub async fn usb_power(_spawner: Spawner, r: Vbus) { | ||
| 295 | let mut vbus_in = Input::new(r.pin_24, Pull::None); | ||
| 296 | let sender = EVENT_CHANNEL.sender(); | ||
| 297 | |||
| 298 | loop { | ||
| 299 | sender.send(Events::UsbPowered(vbus_in.is_high())).await; | ||
| 300 | vbus_in.wait_for_any_edge().await; | ||
| 301 | } | ||
| 302 | } | ||
| 303 | |||
| 304 | /// Task that reads system voltage through ADC. As an example of some continuous sensor reading. | ||
| 305 | #[embassy_executor::task] | ||
| 306 | pub async fn vsys_voltage(_spawner: Spawner, r: Vsys) { | ||
| 307 | let mut adc = Adc::new(r.adc, Irqs, Config::default()); | ||
| 308 | let vsys_in = r.pin_29; | ||
| 309 | let mut channel = Channel::new_pin(vsys_in, Pull::None); | ||
| 310 | let sender = EVENT_CHANNEL.sender(); | ||
| 311 | |||
| 312 | loop { | ||
| 313 | Timer::after(Duration::from_secs(30)).await; | ||
| 314 | let adc_value = adc.read(&mut channel).await.unwrap(); | ||
| 315 | let voltage = (adc_value as f32) * 3.3 * 3.0 / 4096.0; | ||
| 316 | sender.send(Events::VsysVoltage(voltage)).await; | ||
| 317 | } | ||
| 318 | } | ||
diff --git a/examples/rp/src/bin/overclock.rs b/examples/rp/src/bin/overclock.rs new file mode 100644 index 000000000..83b17308b --- /dev/null +++ b/examples/rp/src/bin/overclock.rs | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | //! # Overclocking the RP2040 to 200 MHz | ||
| 2 | //! | ||
| 3 | //! This example demonstrates how to configure the RP2040 to run at 200 MHz. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_rp::clocks::{clk_sys_freq, core_voltage, ClockConfig}; | ||
| 11 | use embassy_rp::config::Config; | ||
| 12 | use embassy_rp::gpio::{Level, Output}; | ||
| 13 | use embassy_time::{Duration, Instant, Timer}; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | ||
| 15 | |||
| 16 | const COUNT_TO: i64 = 10_000_000; | ||
| 17 | |||
| 18 | #[embassy_executor::main] | ||
| 19 | async fn main(_spawner: Spawner) -> ! { | ||
| 20 | // Set up for clock frequency of 200 MHz, setting all necessary defaults. | ||
| 21 | let config = Config::new(ClockConfig::system_freq(200_000_000).unwrap()); | ||
| 22 | |||
| 23 | // Initialize the peripherals | ||
| 24 | let p = embassy_rp::init(config); | ||
| 25 | |||
| 26 | // Show CPU frequency for verification | ||
| 27 | let sys_freq = clk_sys_freq(); | ||
| 28 | info!("System clock frequency: {} MHz", sys_freq / 1_000_000); | ||
| 29 | // Show core voltage for verification | ||
| 30 | let core_voltage = core_voltage().unwrap(); | ||
| 31 | info!("Core voltage: {}", core_voltage); | ||
| 32 | |||
| 33 | // LED to indicate the system is running | ||
| 34 | let mut led = Output::new(p.PIN_25, Level::Low); | ||
| 35 | |||
| 36 | loop { | ||
| 37 | // Reset the counter at the start of measurement period | ||
| 38 | let mut counter = 0; | ||
| 39 | |||
| 40 | // Turn LED on while counting | ||
| 41 | led.set_high(); | ||
| 42 | |||
| 43 | let start = Instant::now(); | ||
| 44 | |||
| 45 | // This is a busy loop that will take some time to complete | ||
| 46 | while counter < COUNT_TO { | ||
| 47 | counter += 1; | ||
| 48 | } | ||
| 49 | |||
| 50 | let elapsed = Instant::now() - start; | ||
| 51 | |||
| 52 | // Report the elapsed time | ||
| 53 | led.set_low(); | ||
| 54 | info!( | ||
| 55 | "At {}Mhz: Elapsed time to count to {}: {}ms", | ||
| 56 | sys_freq / 1_000_000, | ||
| 57 | counter, | ||
| 58 | elapsed.as_millis() | ||
| 59 | ); | ||
| 60 | |||
| 61 | // Wait 2 seconds before starting the next measurement | ||
| 62 | Timer::after(Duration::from_secs(2)).await; | ||
| 63 | } | ||
| 64 | } | ||
diff --git a/examples/rp/src/bin/overclock_manual.rs b/examples/rp/src/bin/overclock_manual.rs new file mode 100644 index 000000000..dea5cfb3c --- /dev/null +++ b/examples/rp/src/bin/overclock_manual.rs | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | //! # Overclocking the RP2040 to 200 MHz manually | ||
| 2 | //! | ||
| 3 | //! This example demonstrates how to manually configure the RP2040 to run at 200 MHz. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_rp::clocks::{clk_sys_freq, core_voltage, ClockConfig, CoreVoltage, PllConfig}; | ||
| 11 | use embassy_rp::config::Config; | ||
| 12 | use embassy_rp::gpio::{Level, Output}; | ||
| 13 | use embassy_time::{Duration, Instant, Timer}; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | ||
| 15 | |||
| 16 | const COUNT_TO: i64 = 10_000_000; | ||
| 17 | |||
| 18 | /// Configure the RP2040 for 200 MHz operation by manually specifying the PLL settings. | ||
| 19 | fn configure_manual_overclock() -> Config { | ||
| 20 | // Set the PLL configuration manually, starting from default values | ||
| 21 | let mut config = Config::default(); | ||
| 22 | |||
| 23 | // Set the system clock to 200 MHz | ||
| 24 | config.clocks = ClockConfig::manual_pll( | ||
| 25 | 12_000_000, // Crystal frequency, 12 MHz is common. If using custom, set to your value. | ||
| 26 | PllConfig { | ||
| 27 | refdiv: 1, // Reference divider | ||
| 28 | fbdiv: 100, // Feedback divider | ||
| 29 | post_div1: 3, // Post divider 1 | ||
| 30 | post_div2: 2, // Post divider 2 | ||
| 31 | }, | ||
| 32 | CoreVoltage::V1_15, // Core voltage, should be set to V1_15 for 200 MHz | ||
| 33 | ); | ||
| 34 | |||
| 35 | config | ||
| 36 | } | ||
| 37 | |||
| 38 | #[embassy_executor::main] | ||
| 39 | async fn main(_spawner: Spawner) -> ! { | ||
| 40 | // Initialize with our manual overclock configuration | ||
| 41 | let p = embassy_rp::init(configure_manual_overclock()); | ||
| 42 | |||
| 43 | // Show CPU frequency for verification | ||
| 44 | let sys_freq = clk_sys_freq(); | ||
| 45 | info!("System clock frequency: {} MHz", sys_freq / 1_000_000); | ||
| 46 | // Show core voltage for verification | ||
| 47 | let core_voltage = core_voltage().unwrap(); | ||
| 48 | info!("Core voltage: {}", core_voltage); | ||
| 49 | |||
| 50 | // LED to indicate the system is running | ||
| 51 | let mut led = Output::new(p.PIN_25, Level::Low); | ||
| 52 | |||
| 53 | loop { | ||
| 54 | // Reset the counter at the start of measurement period | ||
| 55 | let mut counter = 0; | ||
| 56 | |||
| 57 | // Turn LED on while counting | ||
| 58 | led.set_high(); | ||
| 59 | |||
| 60 | let start = Instant::now(); | ||
| 61 | |||
| 62 | // This is a busy loop that will take some time to complete | ||
| 63 | while counter < COUNT_TO { | ||
| 64 | counter += 1; | ||
| 65 | } | ||
| 66 | |||
| 67 | let elapsed = Instant::now() - start; | ||
| 68 | |||
| 69 | // Report the elapsed time | ||
| 70 | led.set_low(); | ||
| 71 | info!( | ||
| 72 | "At {}Mhz: Elapsed time to count to {}: {}ms", | ||
| 73 | sys_freq / 1_000_000, | ||
| 74 | counter, | ||
| 75 | elapsed.as_millis() | ||
| 76 | ); | ||
| 77 | |||
| 78 | // Wait 2 seconds before starting the next measurement | ||
| 79 | Timer::after(Duration::from_secs(2)).await; | ||
| 80 | } | ||
| 81 | } | ||
diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index ee248591b..bf6dbee69 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs | |||
| @@ -4,9 +4,10 @@ | |||
| 4 | #![no_main] | 4 | #![no_main] |
| 5 | use defmt::info; | 5 | use defmt::info; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_rp::bind_interrupts; | ||
| 8 | use embassy_rp::peripherals::PIO0; | 7 | use embassy_rp::peripherals::PIO0; |
| 8 | use embassy_rp::pio::program::pio_asm; | ||
| 9 | use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine}; | 9 | use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine}; |
| 10 | use embassy_rp::{bind_interrupts, Peri}; | ||
| 10 | use fixed::traits::ToFixed; | 11 | use fixed::traits::ToFixed; |
| 11 | use fixed_macro::types::U56F8; | 12 | use fixed_macro::types::U56F8; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -15,11 +16,11 @@ bind_interrupts!(struct Irqs { | |||
| 15 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | 16 | PIO0_IRQ_0 => InterruptHandler<PIO0>; |
| 16 | }); | 17 | }); |
| 17 | 18 | ||
| 18 | fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 0>, pin: impl PioPin) { | 19 | fn setup_pio_task_sm0<'d>(pio: &mut Common<'d, PIO0>, sm: &mut StateMachine<'d, PIO0, 0>, pin: Peri<'d, impl PioPin>) { |
| 19 | // Setup sm0 | 20 | // Setup sm0 |
| 20 | 21 | ||
| 21 | // Send data serially to pin | 22 | // Send data serially to pin |
| 22 | let prg = pio_proc::pio_asm!( | 23 | let prg = pio_asm!( |
| 23 | ".origin 16", | 24 | ".origin 16", |
| 24 | "set pindirs, 1", | 25 | "set pindirs, 1", |
| 25 | ".wrap_target", | 26 | ".wrap_target", |
| @@ -49,11 +50,11 @@ async fn pio_task_sm0(mut sm: StateMachine<'static, PIO0, 0>) { | |||
| 49 | } | 50 | } |
| 50 | } | 51 | } |
| 51 | 52 | ||
| 52 | fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 1>) { | 53 | fn setup_pio_task_sm1<'d>(pio: &mut Common<'d, PIO0>, sm: &mut StateMachine<'d, PIO0, 1>) { |
| 53 | // Setupm sm1 | 54 | // Setupm sm1 |
| 54 | 55 | ||
| 55 | // Read 0b10101 repeatedly until ISR is full | 56 | // Read 0b10101 repeatedly until ISR is full |
| 56 | let prg = pio_proc::pio_asm!( | 57 | let prg = pio_asm!( |
| 57 | // | 58 | // |
| 58 | ".origin 8", | 59 | ".origin 8", |
| 59 | "set x, 0x15", | 60 | "set x, 0x15", |
| @@ -79,11 +80,11 @@ async fn pio_task_sm1(mut sm: StateMachine<'static, PIO0, 1>) { | |||
| 79 | } | 80 | } |
| 80 | } | 81 | } |
| 81 | 82 | ||
| 82 | fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 2>) { | 83 | fn setup_pio_task_sm2<'d>(pio: &mut Common<'d, PIO0>, sm: &mut StateMachine<'d, PIO0, 2>) { |
| 83 | // Setup sm2 | 84 | // Setup sm2 |
| 84 | 85 | ||
| 85 | // Repeatedly trigger IRQ 3 | 86 | // Repeatedly trigger IRQ 3 |
| 86 | let prg = pio_proc::pio_asm!( | 87 | let prg = pio_asm!( |
| 87 | ".origin 0", | 88 | ".origin 0", |
| 88 | ".wrap_target", | 89 | ".wrap_target", |
| 89 | "set x,10", | 90 | "set x,10", |
diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index 02700269c..64d603ba4 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs | |||
| @@ -5,9 +5,10 @@ | |||
| 5 | use defmt::info; | 5 | use defmt::info; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_futures::join::join; | 7 | use embassy_futures::join::join; |
| 8 | use embassy_rp::bind_interrupts; | ||
| 8 | use embassy_rp::peripherals::PIO0; | 9 | use embassy_rp::peripherals::PIO0; |
| 10 | use embassy_rp::pio::program::pio_asm; | ||
| 9 | use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; | 11 | use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; |
| 10 | use embassy_rp::{bind_interrupts, Peripheral}; | ||
| 11 | use fixed::traits::ToFixed; | 12 | use fixed::traits::ToFixed; |
| 12 | use fixed_macro::types::U56F8; | 13 | use fixed_macro::types::U56F8; |
| 13 | use {defmt_rtt as _, panic_probe as _}; | 14 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -32,7 +33,7 @@ async fn main(_spawner: Spawner) { | |||
| 32 | .. | 33 | .. |
| 33 | } = Pio::new(pio, Irqs); | 34 | } = Pio::new(pio, Irqs); |
| 34 | 35 | ||
| 35 | let prg = pio_proc::pio_asm!( | 36 | let prg = pio_asm!( |
| 36 | ".origin 0", | 37 | ".origin 0", |
| 37 | "set pindirs,1", | 38 | "set pindirs,1", |
| 38 | ".wrap_target", | 39 | ".wrap_target", |
| @@ -61,8 +62,8 @@ async fn main(_spawner: Spawner) { | |||
| 61 | sm.set_config(&cfg); | 62 | sm.set_config(&cfg); |
| 62 | sm.set_enable(true); | 63 | sm.set_enable(true); |
| 63 | 64 | ||
| 64 | let mut dma_out_ref = p.DMA_CH0.into_ref(); | 65 | let mut dma_out_ref = p.DMA_CH0; |
| 65 | let mut dma_in_ref = p.DMA_CH1.into_ref(); | 66 | let mut dma_in_ref = p.DMA_CH1; |
| 66 | let mut dout = [0x12345678u32; 29]; | 67 | let mut dout = [0x12345678u32; 29]; |
| 67 | for i in 1..dout.len() { | 68 | for i in 1..dout.len() { |
| 68 | dout[i] = (dout[i - 1] & 0x0fff_ffff) * 13 + 7; | 69 | dout[i] = (dout[i - 1] & 0x0fff_ffff) * 13 + 7; |
| @@ -71,8 +72,8 @@ async fn main(_spawner: Spawner) { | |||
| 71 | loop { | 72 | loop { |
| 72 | let (rx, tx) = sm.rx_tx(); | 73 | let (rx, tx) = sm.rx_tx(); |
| 73 | join( | 74 | join( |
| 74 | tx.dma_push(dma_out_ref.reborrow(), &dout), | 75 | tx.dma_push(dma_out_ref.reborrow(), &dout, false), |
| 75 | rx.dma_pull(dma_in_ref.reborrow(), &mut din), | 76 | rx.dma_pull(dma_in_ref.reborrow(), &mut din, false), |
| 76 | ) | 77 | ) |
| 77 | .await; | 78 | .await; |
| 78 | for i in 0..din.len() { | 79 | for i in 0..din.len() { |
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..695a74cc3 100644 --- a/examples/rp/src/bin/pio_i2s.rs +++ b/examples/rp/src/bin/pio_i2s.rs | |||
| @@ -13,10 +13,11 @@ | |||
| 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; | ||
| 17 | use embassy_rp::bootsel::is_bootsel_pressed; | ||
| 16 | use embassy_rp::peripherals::PIO0; | 18 | use embassy_rp::peripherals::PIO0; |
| 17 | use embassy_rp::pio::{Config, FifoJoin, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; | 19 | use embassy_rp::pio::{InterruptHandler, Pio}; |
| 18 | use embassy_rp::{bind_interrupts, Peripheral}; | 20 | use embassy_rp::pio_programs::i2s::{PioI2sOut, PioI2sOutProgram}; |
| 19 | use fixed::traits::ToFixed; | ||
| 20 | use static_cell::StaticCell; | 21 | use static_cell::StaticCell; |
| 21 | use {defmt_rtt as _, panic_probe as _}; | 22 | use {defmt_rtt as _, panic_probe as _}; |
| 22 | 23 | ||
| @@ -25,61 +26,30 @@ bind_interrupts!(struct Irqs { | |||
| 25 | }); | 26 | }); |
| 26 | 27 | ||
| 27 | const SAMPLE_RATE: u32 = 48_000; | 28 | const SAMPLE_RATE: u32 = 48_000; |
| 29 | const BIT_DEPTH: u32 = 16; | ||
| 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 | &program, |
| 66 | const BIT_DEPTH: u32 = 16; | ||
| 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 | ); | 53 | ); |
| 84 | 54 | ||
| 85 | // create two audio buffers (back and front) which will take turns being | 55 | // create two audio buffers (back and front) which will take turns being |
| @@ -90,20 +60,20 @@ async fn main(_spawner: Spawner) { | |||
| 90 | let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE); | 60 | let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE); |
| 91 | 61 | ||
| 92 | // start pio state machine | 62 | // 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; | 63 | let mut fade_value: i32 = 0; |
| 98 | let mut phase: i32 = 0; | 64 | let mut phase: i32 = 0; |
| 99 | 65 | ||
| 100 | loop { | 66 | loop { |
| 101 | // trigger transfer of front buffer data to the pio fifo | 67 | // trigger transfer of front buffer data to the pio fifo |
| 102 | // but don't await the returned future, yet | 68 | // but don't await the returned future, yet |
| 103 | let dma_future = tx.dma_push(dma_ref.reborrow(), front_buffer); | 69 | let dma_future = i2s.write(front_buffer); |
| 104 | 70 | ||
| 105 | // fade in audio when bootsel is pressed | 71 | // fade in audio when bootsel is pressed |
| 106 | let fade_target = if p.BOOTSEL.is_pressed() { i32::MAX } else { 0 }; | 72 | let fade_target = if is_bootsel_pressed(p.BOOTSEL.reborrow()) { |
| 73 | i32::MAX | ||
| 74 | } else { | ||
| 75 | 0 | ||
| 76 | }; | ||
| 107 | 77 | ||
| 108 | // fill back buffer with fresh audio samples before awaiting the dma future | 78 | // fill back buffer with fresh audio samples before awaiting the dma future |
| 109 | for s in back_buffer.iter_mut() { | 79 | for s in back_buffer.iter_mut() { |
diff --git a/examples/rp/src/bin/pio_onewire.rs b/examples/rp/src/bin/pio_onewire.rs new file mode 100644 index 000000000..379e2b8f9 --- /dev/null +++ b/examples/rp/src/bin/pio_onewire.rs | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | //! This example shows how you can use PIO to read one or more `DS18B20` one-wire temperature sensors. | ||
| 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::peripherals::PIO0; | ||
| 9 | use embassy_rp::pio::{InterruptHandler, Pio}; | ||
| 10 | use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram, PioOneWireSearch}; | ||
| 11 | use embassy_time::Timer; | ||
| 12 | use heapless::Vec; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | bind_interrupts!(struct Irqs { | ||
| 16 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 17 | }); | ||
| 18 | |||
| 19 | #[embassy_executor::main] | ||
| 20 | async fn main(_spawner: Spawner) { | ||
| 21 | let p = embassy_rp::init(Default::default()); | ||
| 22 | let mut pio = Pio::new(p.PIO0, Irqs); | ||
| 23 | |||
| 24 | let prg = PioOneWireProgram::new(&mut pio.common); | ||
| 25 | let mut onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg); | ||
| 26 | |||
| 27 | info!("Starting onewire search"); | ||
| 28 | |||
| 29 | let mut devices = Vec::<u64, 10>::new(); | ||
| 30 | let mut search = PioOneWireSearch::new(); | ||
| 31 | for _ in 0..10 { | ||
| 32 | if !search.is_finished() { | ||
| 33 | if let Some(address) = search.next(&mut onewire).await { | ||
| 34 | if crc8(&address.to_le_bytes()) == 0 { | ||
| 35 | info!("Found addres: {:x}", address); | ||
| 36 | let _ = devices.push(address); | ||
| 37 | } else { | ||
| 38 | warn!("Found invalid address: {:x}", address); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | info!("Search done, found {} devices", devices.len()); | ||
| 45 | |||
| 46 | loop { | ||
| 47 | onewire.reset().await; | ||
| 48 | // Skip rom and trigger conversion, we can trigger all devices on the bus immediately | ||
| 49 | onewire.write_bytes(&[0xCC, 0x44]).await; | ||
| 50 | |||
| 51 | Timer::after_secs(1).await; // Allow 1s for the measurement to finish | ||
| 52 | |||
| 53 | // Read all devices one by one | ||
| 54 | for device in &devices { | ||
| 55 | onewire.reset().await; | ||
| 56 | onewire.write_bytes(&[0x55]).await; // Match rom | ||
| 57 | onewire.write_bytes(&device.to_le_bytes()).await; | ||
| 58 | onewire.write_bytes(&[0xBE]).await; // Read scratchpad | ||
| 59 | |||
| 60 | let mut data = [0; 9]; | ||
| 61 | onewire.read_bytes(&mut data).await; | ||
| 62 | if crc8(&data) == 0 { | ||
| 63 | let temp = ((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.; | ||
| 64 | info!("Read device {:x}: {} deg C", device, temp); | ||
| 65 | } else { | ||
| 66 | warn!("Reading device {:x} failed", device); | ||
| 67 | } | ||
| 68 | } | ||
| 69 | Timer::after_secs(1).await; | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | fn crc8(data: &[u8]) -> u8 { | ||
| 74 | let mut crc = 0; | ||
| 75 | for b in data { | ||
| 76 | let mut data_byte = *b; | ||
| 77 | for _ in 0..8 { | ||
| 78 | let temp = (crc ^ data_byte) & 0x01; | ||
| 79 | crc >>= 1; | ||
| 80 | if temp != 0 { | ||
| 81 | crc ^= 0x8C; | ||
| 82 | } | ||
| 83 | data_byte >>= 1; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | crc | ||
| 87 | } | ||
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..485c65204 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] |
| @@ -51,13 +49,6 @@ async fn main(_spawner: Spawner) { | |||
| 51 | config.max_power = 100; | 49 | config.max_power = 100; |
| 52 | config.max_packet_size_0 = 64; | 50 | config.max_packet_size_0 = 64; |
| 53 | 51 | ||
| 54 | // Required for windows compatibility. | ||
| 55 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 56 | config.device_class = 0xEF; | ||
| 57 | config.device_sub_class = 0x02; | ||
| 58 | config.device_protocol = 0x01; | ||
| 59 | config.composite_with_iads = true; | ||
| 60 | |||
| 61 | // Create embassy-usb DeviceBuilder using the driver and config. | 52 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 62 | // It needs some buffers for building the descriptors. | 53 | // It needs some buffers for building the descriptors. |
| 63 | let mut config_descriptor = [0; 256]; | 54 | let mut config_descriptor = [0; 256]; |
| @@ -85,8 +76,15 @@ async fn main(_spawner: Spawner) { | |||
| 85 | let usb_fut = usb.run(); | 76 | let usb_fut = usb.run(); |
| 86 | 77 | ||
| 87 | // PIO UART setup | 78 | // PIO UART setup |
| 88 | let uart = PioUart::new(9600, p.PIO0, p.PIN_4, p.PIN_5); | 79 | let pio::Pio { |
| 89 | let (mut uart_tx, mut uart_rx) = uart.split(); | 80 | mut common, sm0, sm1, .. |
| 81 | } = pio::Pio::new(p.PIO0, Irqs); | ||
| 82 | |||
| 83 | let tx_program = PioUartTxProgram::new(&mut common); | ||
| 84 | let mut uart_tx = PioUartTx::new(9600, &mut common, sm0, p.PIN_4, &tx_program); | ||
| 85 | |||
| 86 | let rx_program = PioUartRxProgram::new(&mut common); | ||
| 87 | let mut uart_rx = PioUartRx::new(9600, &mut common, sm1, p.PIN_5, &rx_program); | ||
| 90 | 88 | ||
| 91 | // Pipe setup | 89 | // Pipe setup |
| 92 | let mut usb_pipe: Pipe<NoopRawMutex, 20> = Pipe::new(); | 90 | let mut usb_pipe: Pipe<NoopRawMutex, 20> = Pipe::new(); |
| @@ -163,8 +161,8 @@ async fn usb_write<'d, T: Instance + 'd>( | |||
| 163 | } | 161 | } |
| 164 | 162 | ||
| 165 | /// Read from the UART and write it to the USB TX pipe | 163 | /// Read from the UART and write it to the USB TX pipe |
| 166 | async fn uart_read( | 164 | async fn uart_read<PIO: pio::Instance, const SM: usize>( |
| 167 | uart_rx: &mut PioUartRx<'_>, | 165 | uart_rx: &mut PioUartRx<'_, PIO, SM>, |
| 168 | usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>, | 166 | usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>, |
| 169 | ) -> ! { | 167 | ) -> ! { |
| 170 | let mut buf = [0; 64]; | 168 | let mut buf = [0; 64]; |
| @@ -180,8 +178,8 @@ async fn uart_read( | |||
| 180 | } | 178 | } |
| 181 | 179 | ||
| 182 | /// Read from the UART TX pipe and write it to the UART | 180 | /// Read from the UART TX pipe and write it to the UART |
| 183 | async fn uart_write( | 181 | async fn uart_write<PIO: pio::Instance, const SM: usize>( |
| 184 | uart_tx: &mut PioUartTx<'_>, | 182 | uart_tx: &mut PioUartTx<'_, PIO, SM>, |
| 185 | uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>, | 183 | uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>, |
| 186 | ) -> ! { | 184 | ) -> ! { |
| 187 | let mut buf = [0; 64]; | 185 | let mut buf = [0; 64]; |
| @@ -192,197 +190,3 @@ async fn uart_write( | |||
| 192 | let _ = uart_tx.write(&data).await; | 190 | let _ = uart_tx.write(&data).await; |
| 193 | } | 191 | } |
| 194 | } | 192 | } |
| 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..04374323d 100644 --- a/examples/rp/src/bin/pwm.rs +++ b/examples/rp/src/bin/pwm.rs | |||
| @@ -1,24 +1,37 @@ | |||
| 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}; | ||
| 14 | use embassy_rp::Peri; | ||
| 11 | use embassy_time::Timer; | 15 | use embassy_time::Timer; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 16 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 17 | ||
| 14 | #[embassy_executor::main] | 18 | #[embassy_executor::main] |
| 15 | async fn main(_spawner: Spawner) { | 19 | async fn main(spawner: Spawner) { |
| 16 | let p = embassy_rp::init(Default::default()); | 20 | let p = embassy_rp::init(Default::default()); |
| 21 | spawner.spawn(pwm_set_config(p.PWM_SLICE4, p.PIN_25)).unwrap(); | ||
| 22 | spawner.spawn(pwm_set_dutycycle(p.PWM_SLICE2, p.PIN_4)).unwrap(); | ||
| 23 | } | ||
| 17 | 24 | ||
| 18 | let mut c: Config = Default::default(); | 25 | /// Demonstrate PWM by modifying & applying the config |
| 19 | c.top = 0x8000; | 26 | /// |
| 27 | /// Using the onboard led, if You are using a different Board than plain Pico2 (i.e. W variant) | ||
| 28 | /// you must use another slice & pin and an appropriate resistor. | ||
| 29 | #[embassy_executor::task] | ||
| 30 | async fn pwm_set_config(slice4: Peri<'static, PWM_SLICE4>, pin25: Peri<'static, PIN_25>) { | ||
| 31 | let mut c = Config::default(); | ||
| 32 | c.top = 32_768; | ||
| 20 | c.compare_b = 8; | 33 | c.compare_b = 8; |
| 21 | let mut pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, c.clone()); | 34 | let mut pwm = Pwm::new_output_b(slice4, pin25, c.clone()); |
| 22 | 35 | ||
| 23 | loop { | 36 | loop { |
| 24 | info!("current LED duty cycle: {}/32768", c.compare_b); | 37 | info!("current LED duty cycle: {}/32768", c.compare_b); |
| @@ -27,3 +40,41 @@ async fn main(_spawner: Spawner) { | |||
| 27 | pwm.set_config(&c); | 40 | pwm.set_config(&c); |
| 28 | } | 41 | } |
| 29 | } | 42 | } |
| 43 | |||
| 44 | /// Demonstrate PWM by setting duty cycle | ||
| 45 | /// | ||
| 46 | /// Using GP4 in Slice2, make sure to use an appropriate resistor. | ||
| 47 | #[embassy_executor::task] | ||
| 48 | async fn pwm_set_dutycycle(slice2: Peri<'static, PWM_SLICE2>, pin4: Peri<'static, PIN_4>) { | ||
| 49 | // If we aim for a specific frequency, here is how we can calculate the top value. | ||
| 50 | // The top value sets the period of the PWM cycle, so a counter goes from 0 to top and then wraps around to 0. | ||
| 51 | // Every such wraparound is one PWM cycle. So here is how we get 25KHz: | ||
| 52 | let desired_freq_hz = 25_000; | ||
| 53 | let clock_freq_hz = embassy_rp::clocks::clk_sys_freq(); | ||
| 54 | let divider = 16u8; | ||
| 55 | let period = (clock_freq_hz / (desired_freq_hz * divider as u32)) as u16 - 1; | ||
| 56 | |||
| 57 | let mut c = Config::default(); | ||
| 58 | c.top = period; | ||
| 59 | c.divider = divider.into(); | ||
| 60 | |||
| 61 | let mut pwm = Pwm::new_output_a(slice2, pin4, c.clone()); | ||
| 62 | |||
| 63 | loop { | ||
| 64 | // 100% duty cycle, fully on | ||
| 65 | pwm.set_duty_cycle_fully_on().unwrap(); | ||
| 66 | Timer::after_secs(1).await; | ||
| 67 | |||
| 68 | // 66% duty cycle. Expressed as simple percentage. | ||
| 69 | pwm.set_duty_cycle_percent(66).unwrap(); | ||
| 70 | Timer::after_secs(1).await; | ||
| 71 | |||
| 72 | // 25% duty cycle. Expressed as 32768/4 = 8192. | ||
| 73 | pwm.set_duty_cycle(c.top / 4).unwrap(); | ||
| 74 | Timer::after_secs(1).await; | ||
| 75 | |||
| 76 | // 0% duty cycle, fully off. | ||
| 77 | pwm.set_duty_cycle_fully_off().unwrap(); | ||
| 78 | Timer::after_secs(1).await; | ||
| 79 | } | ||
| 80 | } | ||
diff --git a/examples/rp/src/bin/shared_bus.rs b/examples/rp/src/bin/shared_bus.rs index c6cb5d64c..9267dfccb 100644 --- a/examples/rp/src/bin/shared_bus.rs +++ b/examples/rp/src/bin/shared_bus.rs | |||
| @@ -8,7 +8,7 @@ use embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice; | |||
| 8 | use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice; | 8 | use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_rp::bind_interrupts; | 10 | use embassy_rp::bind_interrupts; |
| 11 | use embassy_rp::gpio::{AnyPin, Level, Output}; | 11 | use embassy_rp::gpio::{Level, Output}; |
| 12 | use embassy_rp::i2c::{self, I2c, InterruptHandler}; | 12 | use embassy_rp::i2c::{self, I2c, InterruptHandler}; |
| 13 | use embassy_rp::peripherals::{I2C1, SPI1}; | 13 | use embassy_rp::peripherals::{I2C1, SPI1}; |
| 14 | use embassy_rp::spi::{self, Spi}; | 14 | use embassy_rp::spi::{self, Spi}; |
| @@ -45,8 +45,8 @@ async fn main(spawner: Spawner) { | |||
| 45 | let spi_bus = SPI_BUS.init(Mutex::new(spi)); | 45 | let spi_bus = SPI_BUS.init(Mutex::new(spi)); |
| 46 | 46 | ||
| 47 | // Chip select pins for the SPI devices | 47 | // Chip select pins for the SPI devices |
| 48 | let cs_a = Output::new(AnyPin::from(p.PIN_0), Level::High); | 48 | let cs_a = Output::new(p.PIN_0, Level::High); |
| 49 | let cs_b = Output::new(AnyPin::from(p.PIN_1), Level::High); | 49 | let cs_b = Output::new(p.PIN_1, Level::High); |
| 50 | 50 | ||
| 51 | spawner.must_spawn(spi_task_a(spi_bus, cs_a)); | 51 | spawner.must_spawn(spi_task_a(spi_bus, cs_a)); |
| 52 | spawner.must_spawn(spi_task_b(spi_bus, cs_b)); | 52 | spawner.must_spawn(spi_task_b(spi_bus, cs_b)); |
diff --git a/examples/rp/src/bin/sharing.rs b/examples/rp/src/bin/sharing.rs index 5416e20ce..856be6ace 100644 --- a/examples/rp/src/bin/sharing.rs +++ b/examples/rp/src/bin/sharing.rs | |||
| @@ -27,11 +27,10 @@ use embassy_rp::{bind_interrupts, interrupt}; | |||
| 27 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 27 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 28 | use embassy_sync::{blocking_mutex, mutex}; | 28 | use embassy_sync::{blocking_mutex, mutex}; |
| 29 | use embassy_time::{Duration, Ticker}; | 29 | use embassy_time::{Duration, Ticker}; |
| 30 | use rand::RngCore; | ||
| 31 | use static_cell::{ConstStaticCell, StaticCell}; | 30 | use static_cell::{ConstStaticCell, StaticCell}; |
| 32 | use {defmt_rtt as _, panic_probe as _}; | 31 | use {defmt_rtt as _, panic_probe as _}; |
| 33 | 32 | ||
| 34 | type UartAsyncMutex = mutex::Mutex<CriticalSectionRawMutex, UartTx<'static, UART0, uart::Async>>; | 33 | type UartAsyncMutex = mutex::Mutex<CriticalSectionRawMutex, UartTx<'static, uart::Async>>; |
| 35 | 34 | ||
| 36 | struct MyType { | 35 | struct MyType { |
| 37 | inner: u32, | 36 | inner: u32, |
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..fdef09d4b --- /dev/null +++ b/examples/rp/src/bin/spi_gc9a01.rs | |||
| @@ -0,0 +1,125 @@ | |||
| 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 {defmt_rtt as _, panic_probe as _}; | ||
| 30 | |||
| 31 | const DISPLAY_FREQ: u32 = 64_000_000; | ||
| 32 | const LCD_X_RES: i32 = 240; | ||
| 33 | const LCD_Y_RES: i32 = 240; | ||
| 34 | const FERRIS_WIDTH: u32 = 86; | ||
| 35 | const FERRIS_HEIGHT: u32 = 64; | ||
| 36 | |||
| 37 | #[embassy_executor::main] | ||
| 38 | async fn main(_spawner: Spawner) { | ||
| 39 | let p = embassy_rp::init(Default::default()); | ||
| 40 | let mut rng = RoscRng; | ||
| 41 | |||
| 42 | info!("Hello World!"); | ||
| 43 | |||
| 44 | let bl = p.PIN_25; | ||
| 45 | let rst = p.PIN_12; | ||
| 46 | let display_cs = p.PIN_9; | ||
| 47 | let dcx = p.PIN_8; | ||
| 48 | let mosi = p.PIN_11; | ||
| 49 | let clk = p.PIN_10; | ||
| 50 | |||
| 51 | // create SPI | ||
| 52 | let mut display_config = spi::Config::default(); | ||
| 53 | display_config.frequency = DISPLAY_FREQ; | ||
| 54 | display_config.phase = spi::Phase::CaptureOnSecondTransition; | ||
| 55 | display_config.polarity = spi::Polarity::IdleHigh; | ||
| 56 | |||
| 57 | let spi: Spi<'_, _, Blocking> = Spi::new_blocking_txonly(p.SPI1, clk, mosi, display_config.clone()); | ||
| 58 | let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi)); | ||
| 59 | |||
| 60 | let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config); | ||
| 61 | let dcx = Output::new(dcx, Level::Low); | ||
| 62 | let rst = Output::new(rst, Level::Low); | ||
| 63 | // dcx: 0 = command, 1 = data | ||
| 64 | |||
| 65 | // Enable LCD backlight | ||
| 66 | let _bl = Output::new(bl, Level::High); | ||
| 67 | |||
| 68 | // display interface abstraction from SPI and DC | ||
| 69 | let di = SPIInterface::new(display_spi, dcx); | ||
| 70 | |||
| 71 | // Define the display from the display interface and initialize it | ||
| 72 | let mut display = Builder::new(GC9A01, di) | ||
| 73 | .display_size(240, 240) | ||
| 74 | .reset_pin(rst) | ||
| 75 | .color_order(ColorOrder::Bgr) | ||
| 76 | .invert_colors(ColorInversion::Inverted) | ||
| 77 | .init(&mut Delay) | ||
| 78 | .unwrap(); | ||
| 79 | display.clear(Rgb565::BLACK).unwrap(); | ||
| 80 | |||
| 81 | let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), FERRIS_WIDTH); | ||
| 82 | let mut ferris = Image::new(&raw_image_data, Point::zero()); | ||
| 83 | |||
| 84 | let r = rng.next_u32(); | ||
| 85 | let mut delta = Point { | ||
| 86 | x: ((r % 10) + 5) as i32, | ||
| 87 | y: (((r >> 8) % 10) + 5) as i32, | ||
| 88 | }; | ||
| 89 | loop { | ||
| 90 | // Move Ferris | ||
| 91 | let bb = ferris.bounding_box(); | ||
| 92 | let tl = bb.top_left; | ||
| 93 | let br = bb.bottom_right().unwrap(); | ||
| 94 | if tl.x < 0 || br.x > LCD_X_RES { | ||
| 95 | delta.x = -delta.x; | ||
| 96 | } | ||
| 97 | if tl.y < 0 || br.y > LCD_Y_RES { | ||
| 98 | delta.y = -delta.y; | ||
| 99 | } | ||
| 100 | |||
| 101 | // Erase ghosting | ||
| 102 | let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::BLACK).build(); | ||
| 103 | let mut off = Point { x: 0, y: 0 }; | ||
| 104 | if delta.x < 0 { | ||
| 105 | off.x = FERRIS_WIDTH as i32; | ||
| 106 | } | ||
| 107 | Rectangle::new(tl + off, Size::new(delta.x as u32, FERRIS_HEIGHT)) | ||
| 108 | .into_styled(style) | ||
| 109 | .draw(&mut display) | ||
| 110 | .unwrap(); | ||
| 111 | off = Point { x: 0, y: 0 }; | ||
| 112 | if delta.y < 0 { | ||
| 113 | off.y = FERRIS_HEIGHT as i32; | ||
| 114 | } | ||
| 115 | Rectangle::new(tl + off, Size::new(FERRIS_WIDTH, delta.y as u32)) | ||
| 116 | .into_styled(style) | ||
| 117 | .draw(&mut display) | ||
| 118 | .unwrap(); | ||
| 119 | // Translate Ferris | ||
| 120 | ferris.translate_mut(delta); | ||
| 121 | // Display the image | ||
| 122 | ferris.draw(&mut display).unwrap(); | ||
| 123 | Timer::after(Duration::from_millis(50)).await; | ||
| 124 | } | ||
| 125 | } | ||
diff --git a/examples/rp/src/bin/spi_sdmmc.rs b/examples/rp/src/bin/spi_sdmmc.rs index 4cbc82f7b..4e3c2f199 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}; |
| @@ -33,7 +32,6 @@ impl embedded_sdmmc::TimeSource for DummyTimesource { | |||
| 33 | 32 | ||
| 34 | #[embassy_executor::main] | 33 | #[embassy_executor::main] |
| 35 | async fn main(_spawner: Spawner) { | 34 | async fn main(_spawner: Spawner) { |
| 36 | embassy_rp::pac::SIO.spinlock(31).write_value(1); | ||
| 37 | let p = embassy_rp::init(Default::default()); | 35 | let p = embassy_rp::init(Default::default()); |
| 38 | 36 | ||
| 39 | // SPI clock needs to be running at <= 400kHz during initialization | 37 | // SPI clock needs to be running at <= 400kHz during initialization |
| @@ -51,7 +49,7 @@ async fn main(_spawner: Spawner) { | |||
| 51 | // Now that the card is initialized, the SPI clock can go faster | 49 | // Now that the card is initialized, the SPI clock can go faster |
| 52 | let mut config = spi::Config::default(); | 50 | let mut config = spi::Config::default(); |
| 53 | config.frequency = 16_000_000; | 51 | config.frequency = 16_000_000; |
| 54 | sdcard.spi(|dev| dev.bus_mut().set_config(&config)).ok(); | 52 | sdcard.spi(|dev| dev.bus_mut().set_config(&config)); |
| 55 | 53 | ||
| 56 | // Now let's look for volumes (also known as partitions) on our block device. | 54 | // 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. | 55 | // To do this we need a Volume Manager. It will take ownership of the block device. |
diff --git a/examples/rp/src/bin/uart_buffered_split.rs b/examples/rp/src/bin/uart_buffered_split.rs index 468d2b61a..3adbc18ab 100644 --- a/examples/rp/src/bin/uart_buffered_split.rs +++ b/examples/rp/src/bin/uart_buffered_split.rs | |||
| @@ -30,7 +30,7 @@ async fn main(spawner: Spawner) { | |||
| 30 | let tx_buf = &mut TX_BUF.init([0; 16])[..]; | 30 | let tx_buf = &mut TX_BUF.init([0; 16])[..]; |
| 31 | static RX_BUF: StaticCell<[u8; 16]> = StaticCell::new(); | 31 | static RX_BUF: StaticCell<[u8; 16]> = StaticCell::new(); |
| 32 | let rx_buf = &mut RX_BUF.init([0; 16])[..]; | 32 | let rx_buf = &mut RX_BUF.init([0; 16])[..]; |
| 33 | let uart = BufferedUart::new(uart, Irqs, tx_pin, rx_pin, tx_buf, rx_buf, Config::default()); | 33 | let uart = BufferedUart::new(uart, tx_pin, rx_pin, Irqs, tx_buf, rx_buf, Config::default()); |
| 34 | let (mut tx, rx) = uart.split(); | 34 | let (mut tx, rx) = uart.split(); |
| 35 | 35 | ||
| 36 | unwrap!(spawner.spawn(reader(rx))); | 36 | unwrap!(spawner.spawn(reader(rx))); |
| @@ -48,7 +48,7 @@ async fn main(spawner: Spawner) { | |||
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | #[embassy_executor::task] | 50 | #[embassy_executor::task] |
| 51 | async fn reader(mut rx: BufferedUartRx<'static, UART0>) { | 51 | async fn reader(mut rx: BufferedUartRx) { |
| 52 | info!("Reading..."); | 52 | info!("Reading..."); |
| 53 | loop { | 53 | loop { |
| 54 | let mut buf = [0; 31]; | 54 | let mut buf = [0; 31]; |
diff --git a/examples/rp/src/bin/uart_unidir.rs b/examples/rp/src/bin/uart_unidir.rs index a45f40756..c2c8dfad8 100644 --- a/examples/rp/src/bin/uart_unidir.rs +++ b/examples/rp/src/bin/uart_unidir.rs | |||
| @@ -39,7 +39,7 @@ async fn main(spawner: Spawner) { | |||
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | #[embassy_executor::task] | 41 | #[embassy_executor::task] |
| 42 | async fn reader(mut rx: UartRx<'static, UART1, Async>) { | 42 | async fn reader(mut rx: UartRx<'static, Async>) { |
| 43 | info!("Reading..."); | 43 | info!("Reading..."); |
| 44 | loop { | 44 | loop { |
| 45 | // read a total of 4 transmissions (32 / 8) and then print the result | 45 | // read a total of 4 transmissions (32 / 8) and then print the result |
diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 03c510f37..171f21a75 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs | |||
| @@ -8,7 +8,7 @@ | |||
| 8 | use defmt::*; | 8 | use defmt::*; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_net::tcp::TcpSocket; | 10 | use embassy_net::tcp::TcpSocket; |
| 11 | use embassy_net::{Stack, StackResources}; | 11 | use embassy_net::StackResources; |
| 12 | use embassy_rp::clocks::RoscRng; | 12 | use embassy_rp::clocks::RoscRng; |
| 13 | use embassy_rp::peripherals::USB; | 13 | use embassy_rp::peripherals::USB; |
| 14 | use embassy_rp::usb::{Driver, InterruptHandler}; | 14 | use embassy_rp::usb::{Driver, InterruptHandler}; |
| @@ -17,7 +17,6 @@ use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState | |||
| 17 | use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; | 17 | use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; |
| 18 | use embassy_usb::{Builder, Config, UsbDevice}; | 18 | use embassy_usb::{Builder, Config, UsbDevice}; |
| 19 | use embedded_io_async::Write; | 19 | use embedded_io_async::Write; |
| 20 | use rand::RngCore; | ||
| 21 | use static_cell::StaticCell; | 20 | use static_cell::StaticCell; |
| 22 | use {defmt_rtt as _, panic_probe as _}; | 21 | use {defmt_rtt as _, panic_probe as _}; |
| 23 | 22 | ||
| @@ -40,8 +39,8 @@ async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { | |||
| 40 | } | 39 | } |
| 41 | 40 | ||
| 42 | #[embassy_executor::task] | 41 | #[embassy_executor::task] |
| 43 | async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! { | 42 | async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static, MTU>>) -> ! { |
| 44 | stack.run().await | 43 | runner.run().await |
| 45 | } | 44 | } |
| 46 | 45 | ||
| 47 | #[embassy_executor::main] | 46 | #[embassy_executor::main] |
| @@ -60,12 +59,6 @@ async fn main(spawner: Spawner) { | |||
| 60 | config.max_power = 100; | 59 | config.max_power = 100; |
| 61 | config.max_packet_size_0 = 64; | 60 | config.max_packet_size_0 = 64; |
| 62 | 61 | ||
| 63 | // Required for Windows support. | ||
| 64 | config.composite_with_iads = true; | ||
| 65 | config.device_class = 0xEF; | ||
| 66 | config.device_sub_class = 0x02; | ||
| 67 | config.device_protocol = 0x01; | ||
| 68 | |||
| 69 | // Create embassy-usb DeviceBuilder using the driver and config. | 62 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 70 | static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new(); | 63 | static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new(); |
| 71 | static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new(); | 64 | static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new(); |
| @@ -108,11 +101,10 @@ async fn main(spawner: Spawner) { | |||
| 108 | let seed = rng.next_u64(); | 101 | let seed = rng.next_u64(); |
| 109 | 102 | ||
| 110 | // Init network stack | 103 | // Init network stack |
| 111 | static STACK: StaticCell<Stack<Device<'static, MTU>>> = StaticCell::new(); | ||
| 112 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 104 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 113 | let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); | 105 | let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); |
| 114 | 106 | ||
| 115 | unwrap!(spawner.spawn(net_task(stack))); | 107 | unwrap!(spawner.spawn(net_task(runner))); |
| 116 | 108 | ||
| 117 | // And now we can use it! | 109 | // And now we can use it! |
| 118 | 110 | ||
diff --git a/examples/rp/src/bin/usb_hid_mouse.rs b/examples/rp/src/bin/usb_hid_mouse.rs index cce344fb0..4454c593c 100644..100755 --- a/examples/rp/src/bin/usb_hid_mouse.rs +++ b/examples/rp/src/bin/usb_hid_mouse.rs | |||
| @@ -8,7 +8,6 @@ use embassy_executor::Spawner; | |||
| 8 | use embassy_futures::join::join; | 8 | use embassy_futures::join::join; |
| 9 | use embassy_rp::bind_interrupts; | 9 | use embassy_rp::bind_interrupts; |
| 10 | use embassy_rp::clocks::RoscRng; | 10 | use embassy_rp::clocks::RoscRng; |
| 11 | use embassy_rp::gpio::{Input, Pull}; | ||
| 12 | use embassy_rp::peripherals::USB; | 11 | use embassy_rp::peripherals::USB; |
| 13 | use embassy_rp::usb::{Driver, InterruptHandler}; | 12 | use embassy_rp::usb::{Driver, InterruptHandler}; |
| 14 | use embassy_time::Timer; | 13 | use embassy_time::Timer; |
| @@ -75,12 +74,6 @@ async fn main(_spawner: Spawner) { | |||
| 75 | // Run the USB device. | 74 | // Run the USB device. |
| 76 | let usb_fut = usb.run(); | 75 | let usb_fut = usb.run(); |
| 77 | 76 | ||
| 78 | // Set up the signal pin that will be used to trigger the keyboard. | ||
| 79 | let mut signal_pin = Input::new(p.PIN_16, Pull::None); | ||
| 80 | |||
| 81 | // Enable the schmitt trigger to slightly debounce. | ||
| 82 | signal_pin.set_schmitt(true); | ||
| 83 | |||
| 84 | let (reader, mut writer) = hid.split(); | 77 | let (reader, mut writer) = hid.split(); |
| 85 | 78 | ||
| 86 | // Do stuff with the class! | 79 | // Do stuff with the class! |
| @@ -92,8 +85,8 @@ async fn main(_spawner: Spawner) { | |||
| 92 | _ = Timer::after_secs(1).await; | 85 | _ = Timer::after_secs(1).await; |
| 93 | let report = MouseReport { | 86 | let report = MouseReport { |
| 94 | buttons: 0, | 87 | buttons: 0, |
| 95 | x: rng.gen_range(-100..100), // random small x movement | 88 | x: rng.random_range(-100..100), // random small x movement |
| 96 | y: rng.gen_range(-100..100), // random small y movement | 89 | y: rng.random_range(-100..100), // random small y movement |
| 97 | wheel: 0, | 90 | wheel: 0, |
| 98 | pan: 0, | 91 | pan: 0, |
| 99 | }; | 92 | }; |
diff --git a/examples/rp/src/bin/usb_midi.rs b/examples/rp/src/bin/usb_midi.rs index 11db1b2e1..3b7910f8b 100644 --- a/examples/rp/src/bin/usb_midi.rs +++ b/examples/rp/src/bin/usb_midi.rs | |||
| @@ -37,13 +37,6 @@ async fn main(_spawner: Spawner) { | |||
| 37 | config.max_power = 100; | 37 | config.max_power = 100; |
| 38 | config.max_packet_size_0 = 64; | 38 | config.max_packet_size_0 = 64; |
| 39 | 39 | ||
| 40 | // Required for windows compatibility. | ||
| 41 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 42 | config.device_class = 0xEF; | ||
| 43 | config.device_sub_class = 0x02; | ||
| 44 | config.device_protocol = 0x01; | ||
| 45 | config.composite_with_iads = true; | ||
| 46 | |||
| 47 | // Create embassy-usb DeviceBuilder using the driver and config. | 40 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 48 | // It needs some buffers for building the descriptors. | 41 | // It needs some buffers for building the descriptors. |
| 49 | let mut config_descriptor = [0; 256]; | 42 | let mut config_descriptor = [0; 256]; |
diff --git a/examples/rp/src/bin/usb_raw.rs b/examples/rp/src/bin/usb_raw.rs index 97e7e0244..5974c04c0 100644 --- a/examples/rp/src/bin/usb_raw.rs +++ b/examples/rp/src/bin/usb_raw.rs | |||
| @@ -84,13 +84,6 @@ async fn main(_spawner: Spawner) { | |||
| 84 | config.max_power = 100; | 84 | config.max_power = 100; |
| 85 | config.max_packet_size_0 = 64; | 85 | config.max_packet_size_0 = 64; |
| 86 | 86 | ||
| 87 | // // Required for windows compatibility. | ||
| 88 | // // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 89 | config.device_class = 0xEF; | ||
| 90 | config.device_sub_class = 0x02; | ||
| 91 | config.device_protocol = 0x01; | ||
| 92 | config.composite_with_iads = true; | ||
| 93 | |||
| 94 | // Create embassy-usb DeviceBuilder using the driver and config. | 87 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 95 | // It needs some buffers for building the descriptors. | 88 | // It needs some buffers for building the descriptors. |
| 96 | let mut config_descriptor = [0; 256]; | 89 | let mut config_descriptor = [0; 256]; |
diff --git a/examples/rp/src/bin/usb_raw_bulk.rs b/examples/rp/src/bin/usb_raw_bulk.rs index 331c3da4c..103269791 100644 --- a/examples/rp/src/bin/usb_raw_bulk.rs +++ b/examples/rp/src/bin/usb_raw_bulk.rs | |||
| @@ -62,13 +62,6 @@ async fn main(_spawner: Spawner) { | |||
| 62 | config.max_power = 100; | 62 | config.max_power = 100; |
| 63 | config.max_packet_size_0 = 64; | 63 | config.max_packet_size_0 = 64; |
| 64 | 64 | ||
| 65 | // // Required for windows compatibility. | ||
| 66 | // // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 67 | config.device_class = 0xEF; | ||
| 68 | config.device_sub_class = 0x02; | ||
| 69 | config.device_protocol = 0x01; | ||
| 70 | config.composite_with_iads = true; | ||
| 71 | |||
| 72 | // Create embassy-usb DeviceBuilder using the driver and config. | 65 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 73 | // It needs some buffers for building the descriptors. | 66 | // It needs some buffers for building the descriptors. |
| 74 | let mut config_descriptor = [0; 256]; | 67 | let mut config_descriptor = [0; 256]; |
diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index 4a802994a..5e3f0f378 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs | |||
| @@ -37,13 +37,6 @@ async fn main(spawner: Spawner) { | |||
| 37 | config.serial_number = Some("12345678"); | 37 | config.serial_number = Some("12345678"); |
| 38 | config.max_power = 100; | 38 | config.max_power = 100; |
| 39 | config.max_packet_size_0 = 64; | 39 | config.max_packet_size_0 = 64; |
| 40 | |||
| 41 | // Required for windows compatibility. | ||
| 42 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 43 | config.device_class = 0xEF; | ||
| 44 | config.device_sub_class = 0x02; | ||
| 45 | config.device_protocol = 0x01; | ||
| 46 | config.composite_with_iads = true; | ||
| 47 | config | 40 | config |
| 48 | }; | 41 | }; |
| 49 | 42 | ||
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/usb_serial_with_logger.rs b/examples/rp/src/bin/usb_serial_with_logger.rs index f9cfdef94..ea13a1e27 100644 --- a/examples/rp/src/bin/usb_serial_with_logger.rs +++ b/examples/rp/src/bin/usb_serial_with_logger.rs | |||
| @@ -37,13 +37,6 @@ async fn main(_spawner: Spawner) { | |||
| 37 | config.max_power = 100; | 37 | config.max_power = 100; |
| 38 | config.max_packet_size_0 = 64; | 38 | config.max_packet_size_0 = 64; |
| 39 | 39 | ||
| 40 | // Required for windows compatibility. | ||
| 41 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 42 | config.device_class = 0xEF; | ||
| 43 | config.device_sub_class = 0x02; | ||
| 44 | config.device_protocol = 0x01; | ||
| 45 | config.composite_with_iads = true; | ||
| 46 | |||
| 47 | // Create embassy-usb DeviceBuilder using the driver and config. | 40 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 48 | // It needs some buffers for building the descriptors. | 41 | // It needs some buffers for building the descriptors. |
| 49 | let mut config_descriptor = [0; 256]; | 42 | let mut config_descriptor = [0; 256]; |
diff --git a/examples/rp/src/bin/usb_webusb.rs b/examples/rp/src/bin/usb_webusb.rs index e73938ac9..a5dc94d5b 100644 --- a/examples/rp/src/bin/usb_webusb.rs +++ b/examples/rp/src/bin/usb_webusb.rs | |||
| @@ -51,12 +51,6 @@ async fn main(_spawner: Spawner) { | |||
| 51 | config.max_power = 100; | 51 | config.max_power = 100; |
| 52 | config.max_packet_size_0 = 64; | 52 | config.max_packet_size_0 = 64; |
| 53 | 53 | ||
| 54 | // Required for windows compatibility. | ||
| 55 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 56 | config.device_class = 0xff; | ||
| 57 | config.device_sub_class = 0x00; | ||
| 58 | config.device_protocol = 0x00; | ||
| 59 | |||
| 60 | // Create embassy-usb DeviceBuilder using the driver and config. | 54 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 61 | // It needs some buffers for building the descriptors. | 55 | // It needs some buffers for building the descriptors. |
| 62 | let mut config_descriptor = [0; 256]; | 56 | let mut config_descriptor = [0; 256]; |
diff --git a/examples/rp/src/bin/wifi_ap_tcp_server.rs b/examples/rp/src/bin/wifi_ap_tcp_server.rs index 00f404a9b..856838a8c 100644 --- a/examples/rp/src/bin/wifi_ap_tcp_server.rs +++ b/examples/rp/src/bin/wifi_ap_tcp_server.rs | |||
| @@ -7,11 +7,11 @@ | |||
| 7 | 7 | ||
| 8 | use core::str::from_utf8; | 8 | use core::str::from_utf8; |
| 9 | 9 | ||
| 10 | use cyw43_pio::PioSpi; | 10 | use cyw43_pio::{PioSpi, DEFAULT_CLOCK_DIVIDER}; |
| 11 | use defmt::*; | 11 | use defmt::*; |
| 12 | use embassy_executor::Spawner; | 12 | use embassy_executor::Spawner; |
| 13 | use embassy_net::tcp::TcpSocket; | 13 | use embassy_net::tcp::TcpSocket; |
| 14 | use embassy_net::{Config, Stack, StackResources}; | 14 | use embassy_net::{Config, StackResources}; |
| 15 | use embassy_rp::bind_interrupts; | 15 | use embassy_rp::bind_interrupts; |
| 16 | use embassy_rp::clocks::RoscRng; | 16 | use embassy_rp::clocks::RoscRng; |
| 17 | use embassy_rp::gpio::{Level, Output}; | 17 | use embassy_rp::gpio::{Level, Output}; |
| @@ -19,7 +19,6 @@ use embassy_rp::peripherals::{DMA_CH0, PIO0}; | |||
| 19 | use embassy_rp::pio::{InterruptHandler, Pio}; | 19 | use embassy_rp::pio::{InterruptHandler, Pio}; |
| 20 | use embassy_time::Duration; | 20 | use embassy_time::Duration; |
| 21 | use embedded_io_async::Write; | 21 | use embedded_io_async::Write; |
| 22 | use rand::RngCore; | ||
| 23 | use static_cell::StaticCell; | 22 | use static_cell::StaticCell; |
| 24 | use {defmt_rtt as _, panic_probe as _}; | 23 | use {defmt_rtt as _, panic_probe as _}; |
| 25 | 24 | ||
| @@ -33,8 +32,8 @@ async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'stat | |||
| 33 | } | 32 | } |
| 34 | 33 | ||
| 35 | #[embassy_executor::task] | 34 | #[embassy_executor::task] |
| 36 | async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! { | 35 | async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! { |
| 37 | stack.run().await | 36 | runner.run().await |
| 38 | } | 37 | } |
| 39 | 38 | ||
| 40 | #[embassy_executor::main] | 39 | #[embassy_executor::main] |
| @@ -57,7 +56,16 @@ async fn main(spawner: Spawner) { | |||
| 57 | let pwr = Output::new(p.PIN_23, Level::Low); | 56 | let pwr = Output::new(p.PIN_23, Level::Low); |
| 58 | let cs = Output::new(p.PIN_25, Level::High); | 57 | let cs = Output::new(p.PIN_25, Level::High); |
| 59 | let mut pio = Pio::new(p.PIO0, Irqs); | 58 | let mut pio = Pio::new(p.PIO0, Irqs); |
| 60 | let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); | 59 | let spi = PioSpi::new( |
| 60 | &mut pio.common, | ||
| 61 | pio.sm0, | ||
| 62 | DEFAULT_CLOCK_DIVIDER, | ||
| 63 | pio.irq0, | ||
| 64 | cs, | ||
| 65 | p.PIN_24, | ||
| 66 | p.PIN_29, | ||
| 67 | p.DMA_CH0, | ||
| 68 | ); | ||
| 61 | 69 | ||
| 62 | static STATE: StaticCell<cyw43::State> = StaticCell::new(); | 70 | static STATE: StaticCell<cyw43::State> = StaticCell::new(); |
| 63 | let state = STATE.init(cyw43::State::new()); | 71 | let state = STATE.init(cyw43::State::new()); |
| @@ -80,16 +88,10 @@ async fn main(spawner: Spawner) { | |||
| 80 | let seed = rng.next_u64(); | 88 | let seed = rng.next_u64(); |
| 81 | 89 | ||
| 82 | // Init network stack | 90 | // Init network stack |
| 83 | static STACK: StaticCell<Stack<cyw43::NetDriver<'static>>> = StaticCell::new(); | ||
| 84 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 91 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 85 | let stack = &*STACK.init(Stack::new( | 92 | let (stack, runner) = embassy_net::new(net_device, config, RESOURCES.init(StackResources::new()), seed); |
| 86 | net_device, | 93 | |
| 87 | config, | 94 | unwrap!(spawner.spawn(net_task(runner))); |
| 88 | RESOURCES.init(StackResources::new()), | ||
| 89 | seed, | ||
| 90 | )); | ||
| 91 | |||
| 92 | unwrap!(spawner.spawn(net_task(stack))); | ||
| 93 | 95 | ||
| 94 | //control.start_ap_open("cyw43", 5).await; | 96 | //control.start_ap_open("cyw43", 5).await; |
| 95 | control.start_ap_wpa2("cyw43", "password", 5).await; | 97 | control.start_ap_wpa2("cyw43", "password", 5).await; |
diff --git a/examples/rp/src/bin/wifi_blinky.rs b/examples/rp/src/bin/wifi_blinky.rs index 04a61bbd5..6e91ce167 100644 --- a/examples/rp/src/bin/wifi_blinky.rs +++ b/examples/rp/src/bin/wifi_blinky.rs | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | #![no_std] | 5 | #![no_std] |
| 6 | #![no_main] | 6 | #![no_main] |
| 7 | 7 | ||
| 8 | use cyw43_pio::PioSpi; | 8 | use cyw43_pio::{PioSpi, DEFAULT_CLOCK_DIVIDER}; |
| 9 | use defmt::*; | 9 | use defmt::*; |
| 10 | use embassy_executor::Spawner; | 10 | use embassy_executor::Spawner; |
| 11 | use embassy_rp::bind_interrupts; | 11 | use embassy_rp::bind_interrupts; |
| @@ -33,15 +33,24 @@ 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 | ||
| 41 | let pwr = Output::new(p.PIN_23, Level::Low); | 41 | let pwr = Output::new(p.PIN_23, Level::Low); |
| 42 | let cs = Output::new(p.PIN_25, Level::High); | 42 | let cs = Output::new(p.PIN_25, Level::High); |
| 43 | let mut pio = Pio::new(p.PIO0, Irqs); | 43 | let mut pio = Pio::new(p.PIO0, Irqs); |
| 44 | let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); | 44 | let spi = PioSpi::new( |
| 45 | &mut pio.common, | ||
| 46 | pio.sm0, | ||
| 47 | DEFAULT_CLOCK_DIVIDER, | ||
| 48 | pio.irq0, | ||
| 49 | cs, | ||
| 50 | p.PIN_24, | ||
| 51 | p.PIN_29, | ||
| 52 | p.DMA_CH0, | ||
| 53 | ); | ||
| 45 | 54 | ||
| 46 | static STATE: StaticCell<cyw43::State> = StaticCell::new(); | 55 | static STATE: StaticCell<cyw43::State> = StaticCell::new(); |
| 47 | let state = STATE.init(cyw43::State::new()); | 56 | let state = STATE.init(cyw43::State::new()); |
diff --git a/examples/rp/src/bin/wifi_scan.rs b/examples/rp/src/bin/wifi_scan.rs index ab3529112..fe9c363d9 100644 --- a/examples/rp/src/bin/wifi_scan.rs +++ b/examples/rp/src/bin/wifi_scan.rs | |||
| @@ -7,10 +7,9 @@ | |||
| 7 | 7 | ||
| 8 | use core::str; | 8 | use core::str; |
| 9 | 9 | ||
| 10 | use cyw43_pio::PioSpi; | 10 | use cyw43_pio::{PioSpi, DEFAULT_CLOCK_DIVIDER}; |
| 11 | use defmt::*; | 11 | use defmt::*; |
| 12 | use embassy_executor::Spawner; | 12 | use embassy_executor::Spawner; |
| 13 | use embassy_net::Stack; | ||
| 14 | use embassy_rp::bind_interrupts; | 13 | use embassy_rp::bind_interrupts; |
| 15 | use embassy_rp::gpio::{Level, Output}; | 14 | use embassy_rp::gpio::{Level, Output}; |
| 16 | use embassy_rp::peripherals::{DMA_CH0, PIO0}; | 15 | use embassy_rp::peripherals::{DMA_CH0, PIO0}; |
| @@ -27,11 +26,6 @@ async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'stat | |||
| 27 | runner.run().await | 26 | runner.run().await |
| 28 | } | 27 | } |
| 29 | 28 | ||
| 30 | #[embassy_executor::task] | ||
| 31 | async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! { | ||
| 32 | stack.run().await | ||
| 33 | } | ||
| 34 | |||
| 35 | #[embassy_executor::main] | 29 | #[embassy_executor::main] |
| 36 | async fn main(spawner: Spawner) { | 30 | async fn main(spawner: Spawner) { |
| 37 | info!("Hello World!"); | 31 | info!("Hello World!"); |
| @@ -51,7 +45,16 @@ async fn main(spawner: Spawner) { | |||
| 51 | let pwr = Output::new(p.PIN_23, Level::Low); | 45 | let pwr = Output::new(p.PIN_23, Level::Low); |
| 52 | let cs = Output::new(p.PIN_25, Level::High); | 46 | let cs = Output::new(p.PIN_25, Level::High); |
| 53 | let mut pio = Pio::new(p.PIO0, Irqs); | 47 | let mut pio = Pio::new(p.PIO0, Irqs); |
| 54 | let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); | 48 | let spi = PioSpi::new( |
| 49 | &mut pio.common, | ||
| 50 | pio.sm0, | ||
| 51 | DEFAULT_CLOCK_DIVIDER, | ||
| 52 | pio.irq0, | ||
| 53 | cs, | ||
| 54 | p.PIN_24, | ||
| 55 | p.PIN_29, | ||
| 56 | p.DMA_CH0, | ||
| 57 | ); | ||
| 55 | 58 | ||
| 56 | static STATE: StaticCell<cyw43::State> = StaticCell::new(); | 59 | static STATE: StaticCell<cyw43::State> = StaticCell::new(); |
| 57 | let state = STATE.init(cyw43::State::new()); | 60 | let state = STATE.init(cyw43::State::new()); |
diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs index 61eeb82f7..fbc957e0e 100644 --- a/examples/rp/src/bin/wifi_tcp_server.rs +++ b/examples/rp/src/bin/wifi_tcp_server.rs | |||
| @@ -7,11 +7,12 @@ | |||
| 7 | 7 | ||
| 8 | use core::str::from_utf8; | 8 | use core::str::from_utf8; |
| 9 | 9 | ||
| 10 | use cyw43_pio::PioSpi; | 10 | use cyw43::JoinOptions; |
| 11 | use cyw43_pio::{PioSpi, DEFAULT_CLOCK_DIVIDER}; | ||
| 11 | use defmt::*; | 12 | use defmt::*; |
| 12 | use embassy_executor::Spawner; | 13 | use embassy_executor::Spawner; |
| 13 | use embassy_net::tcp::TcpSocket; | 14 | use embassy_net::tcp::TcpSocket; |
| 14 | use embassy_net::{Config, Stack, StackResources}; | 15 | use embassy_net::{Config, StackResources}; |
| 15 | use embassy_rp::bind_interrupts; | 16 | use embassy_rp::bind_interrupts; |
| 16 | use embassy_rp::clocks::RoscRng; | 17 | use embassy_rp::clocks::RoscRng; |
| 17 | use embassy_rp::gpio::{Level, Output}; | 18 | use embassy_rp::gpio::{Level, Output}; |
| @@ -19,7 +20,6 @@ use embassy_rp::peripherals::{DMA_CH0, PIO0}; | |||
| 19 | use embassy_rp::pio::{InterruptHandler, Pio}; | 20 | use embassy_rp::pio::{InterruptHandler, Pio}; |
| 20 | use embassy_time::{Duration, Timer}; | 21 | use embassy_time::{Duration, Timer}; |
| 21 | use embedded_io_async::Write; | 22 | use embedded_io_async::Write; |
| 22 | use rand::RngCore; | ||
| 23 | use static_cell::StaticCell; | 23 | use static_cell::StaticCell; |
| 24 | use {defmt_rtt as _, panic_probe as _}; | 24 | use {defmt_rtt as _, panic_probe as _}; |
| 25 | 25 | ||
| @@ -27,8 +27,8 @@ bind_interrupts!(struct Irqs { | |||
| 27 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | 27 | PIO0_IRQ_0 => InterruptHandler<PIO0>; |
| 28 | }); | 28 | }); |
| 29 | 29 | ||
| 30 | const WIFI_NETWORK: &str = "LadronDeWifi"; | 30 | const WIFI_NETWORK: &str = "ssid"; // change to your network SSID |
| 31 | const WIFI_PASSWORD: &str = "MBfcaedHmyRFE4kaQ1O5SsY8"; | 31 | const WIFI_PASSWORD: &str = "pwd"; // change to your network password |
| 32 | 32 | ||
| 33 | #[embassy_executor::task] | 33 | #[embassy_executor::task] |
| 34 | async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { | 34 | async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { |
| @@ -36,8 +36,8 @@ async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'stat | |||
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | #[embassy_executor::task] | 38 | #[embassy_executor::task] |
| 39 | async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! { | 39 | async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! { |
| 40 | stack.run().await | 40 | runner.run().await |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | #[embassy_executor::main] | 43 | #[embassy_executor::main] |
| @@ -60,7 +60,16 @@ async fn main(spawner: Spawner) { | |||
| 60 | let pwr = Output::new(p.PIN_23, Level::Low); | 60 | let pwr = Output::new(p.PIN_23, Level::Low); |
| 61 | let cs = Output::new(p.PIN_25, Level::High); | 61 | let cs = Output::new(p.PIN_25, Level::High); |
| 62 | let mut pio = Pio::new(p.PIO0, Irqs); | 62 | let mut pio = Pio::new(p.PIO0, Irqs); |
| 63 | let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); | 63 | let spi = PioSpi::new( |
| 64 | &mut pio.common, | ||
| 65 | pio.sm0, | ||
| 66 | DEFAULT_CLOCK_DIVIDER, | ||
| 67 | pio.irq0, | ||
| 68 | cs, | ||
| 69 | p.PIN_24, | ||
| 70 | p.PIN_29, | ||
| 71 | p.DMA_CH0, | ||
| 72 | ); | ||
| 64 | 73 | ||
| 65 | static STATE: StaticCell<cyw43::State> = StaticCell::new(); | 74 | static STATE: StaticCell<cyw43::State> = StaticCell::new(); |
| 66 | let state = STATE.init(cyw43::State::new()); | 75 | let state = STATE.init(cyw43::State::new()); |
| @@ -83,20 +92,16 @@ async fn main(spawner: Spawner) { | |||
| 83 | let seed = rng.next_u64(); | 92 | let seed = rng.next_u64(); |
| 84 | 93 | ||
| 85 | // Init network stack | 94 | // Init network stack |
| 86 | static STACK: StaticCell<Stack<cyw43::NetDriver<'static>>> = StaticCell::new(); | ||
| 87 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 95 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 88 | let stack = &*STACK.init(Stack::new( | 96 | let (stack, runner) = embassy_net::new(net_device, config, RESOURCES.init(StackResources::new()), seed); |
| 89 | net_device, | ||
| 90 | config, | ||
| 91 | RESOURCES.init(StackResources::new()), | ||
| 92 | seed, | ||
| 93 | )); | ||
| 94 | 97 | ||
| 95 | unwrap!(spawner.spawn(net_task(stack))); | 98 | unwrap!(spawner.spawn(net_task(runner))); |
| 96 | 99 | ||
| 97 | loop { | 100 | loop { |
| 98 | //control.join_open(WIFI_NETWORK).await; | 101 | match control |
| 99 | match control.join_wpa2(WIFI_NETWORK, WIFI_PASSWORD).await { | 102 | .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes())) |
| 103 | .await | ||
| 104 | { | ||
| 100 | Ok(_) => break, | 105 | Ok(_) => break, |
| 101 | Err(err) => { | 106 | Err(err) => { |
| 102 | info!("join failed with status={}", err.status); | 107 | info!("join failed with status={}", err.status); |
diff --git a/examples/rp/src/bin/wifi_webrequest.rs b/examples/rp/src/bin/wifi_webrequest.rs index 889371241..1efd1cd28 100644 --- a/examples/rp/src/bin/wifi_webrequest.rs +++ b/examples/rp/src/bin/wifi_webrequest.rs | |||
| @@ -7,19 +7,19 @@ | |||
| 7 | 7 | ||
| 8 | use core::str::from_utf8; | 8 | use core::str::from_utf8; |
| 9 | 9 | ||
| 10 | use cyw43_pio::PioSpi; | 10 | use cyw43::JoinOptions; |
| 11 | use cyw43_pio::{PioSpi, DEFAULT_CLOCK_DIVIDER}; | ||
| 11 | use defmt::*; | 12 | use defmt::*; |
| 12 | use embassy_executor::Spawner; | 13 | use embassy_executor::Spawner; |
| 13 | use embassy_net::dns::DnsSocket; | 14 | use embassy_net::dns::DnsSocket; |
| 14 | use embassy_net::tcp::client::{TcpClient, TcpClientState}; | 15 | use embassy_net::tcp::client::{TcpClient, TcpClientState}; |
| 15 | use embassy_net::{Config, Stack, StackResources}; | 16 | use embassy_net::{Config, StackResources}; |
| 16 | use embassy_rp::bind_interrupts; | 17 | use embassy_rp::bind_interrupts; |
| 17 | use embassy_rp::clocks::RoscRng; | 18 | use embassy_rp::clocks::RoscRng; |
| 18 | use embassy_rp::gpio::{Level, Output}; | 19 | use embassy_rp::gpio::{Level, Output}; |
| 19 | use embassy_rp::peripherals::{DMA_CH0, PIO0}; | 20 | use embassy_rp::peripherals::{DMA_CH0, PIO0}; |
| 20 | use embassy_rp::pio::{InterruptHandler, Pio}; | 21 | use embassy_rp::pio::{InterruptHandler, Pio}; |
| 21 | use embassy_time::{Duration, Timer}; | 22 | use embassy_time::{Duration, Timer}; |
| 22 | use rand::RngCore; | ||
| 23 | use reqwless::client::{HttpClient, TlsConfig, TlsVerify}; | 23 | use reqwless::client::{HttpClient, TlsConfig, TlsVerify}; |
| 24 | use reqwless::request::Method; | 24 | use reqwless::request::Method; |
| 25 | use serde::Deserialize; | 25 | use serde::Deserialize; |
| @@ -39,8 +39,8 @@ async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'stat | |||
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | #[embassy_executor::task] | 41 | #[embassy_executor::task] |
| 42 | async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! { | 42 | async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! { |
| 43 | stack.run().await | 43 | runner.run().await |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | #[embassy_executor::main] | 46 | #[embassy_executor::main] |
| @@ -62,7 +62,16 @@ async fn main(spawner: Spawner) { | |||
| 62 | let pwr = Output::new(p.PIN_23, Level::Low); | 62 | let pwr = Output::new(p.PIN_23, Level::Low); |
| 63 | let cs = Output::new(p.PIN_25, Level::High); | 63 | let cs = Output::new(p.PIN_25, Level::High); |
| 64 | let mut pio = Pio::new(p.PIO0, Irqs); | 64 | let mut pio = Pio::new(p.PIO0, Irqs); |
| 65 | let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); | 65 | let spi = PioSpi::new( |
| 66 | &mut pio.common, | ||
| 67 | pio.sm0, | ||
| 68 | DEFAULT_CLOCK_DIVIDER, | ||
| 69 | pio.irq0, | ||
| 70 | cs, | ||
| 71 | p.PIN_24, | ||
| 72 | p.PIN_29, | ||
| 73 | p.DMA_CH0, | ||
| 74 | ); | ||
| 66 | 75 | ||
| 67 | static STATE: StaticCell<cyw43::State> = StaticCell::new(); | 76 | static STATE: StaticCell<cyw43::State> = StaticCell::new(); |
| 68 | let state = STATE.init(cyw43::State::new()); | 77 | let state = STATE.init(cyw43::State::new()); |
| @@ -86,20 +95,16 @@ async fn main(spawner: Spawner) { | |||
| 86 | let seed = rng.next_u64(); | 95 | let seed = rng.next_u64(); |
| 87 | 96 | ||
| 88 | // Init network stack | 97 | // Init network stack |
| 89 | static STACK: StaticCell<Stack<cyw43::NetDriver<'static>>> = StaticCell::new(); | ||
| 90 | static RESOURCES: StaticCell<StackResources<5>> = StaticCell::new(); | 98 | static RESOURCES: StaticCell<StackResources<5>> = StaticCell::new(); |
| 91 | let stack = &*STACK.init(Stack::new( | 99 | let (stack, runner) = embassy_net::new(net_device, config, RESOURCES.init(StackResources::new()), seed); |
| 92 | net_device, | ||
| 93 | config, | ||
| 94 | RESOURCES.init(StackResources::new()), | ||
| 95 | seed, | ||
| 96 | )); | ||
| 97 | 100 | ||
| 98 | unwrap!(spawner.spawn(net_task(stack))); | 101 | unwrap!(spawner.spawn(net_task(runner))); |
| 99 | 102 | ||
| 100 | loop { | 103 | loop { |
| 101 | //match control.join_open(WIFI_NETWORK).await { // for open networks | 104 | match control |
| 102 | match control.join_wpa2(WIFI_NETWORK, WIFI_PASSWORD).await { | 105 | .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes())) |
| 106 | .await | ||
| 107 | { | ||
| 103 | Ok(_) => break, | 108 | Ok(_) => break, |
| 104 | Err(err) => { | 109 | Err(err) => { |
| 105 | info!("join failed with status={}", err.status); | 110 | info!("join failed with status={}", err.status); |
diff --git a/examples/rp/src/bin/zerocopy.rs b/examples/rp/src/bin/zerocopy.rs index 39f03c8e4..d1fb0eb00 100644 --- a/examples/rp/src/bin/zerocopy.rs +++ b/examples/rp/src/bin/zerocopy.rs | |||
| @@ -9,9 +9,9 @@ use core::sync::atomic::{AtomicU16, Ordering}; | |||
| 9 | use defmt::*; | 9 | use defmt::*; |
| 10 | use embassy_executor::Spawner; | 10 | use embassy_executor::Spawner; |
| 11 | use embassy_rp::adc::{self, Adc, Async, Config, InterruptHandler}; | 11 | use embassy_rp::adc::{self, Adc, Async, Config, InterruptHandler}; |
| 12 | use embassy_rp::bind_interrupts; | ||
| 13 | use embassy_rp::gpio::Pull; | 12 | use embassy_rp::gpio::Pull; |
| 14 | use embassy_rp::peripherals::DMA_CH0; | 13 | use embassy_rp::peripherals::DMA_CH0; |
| 14 | use embassy_rp::{bind_interrupts, Peri}; | ||
| 15 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | 15 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; |
| 16 | use embassy_sync::zerocopy_channel::{Channel, Receiver, Sender}; | 16 | use embassy_sync::zerocopy_channel::{Channel, Receiver, Sender}; |
| 17 | use embassy_time::{Duration, Ticker, Timer}; | 17 | use embassy_time::{Duration, Ticker, Timer}; |
| @@ -31,7 +31,7 @@ static MAX: AtomicU16 = AtomicU16::new(0); | |||
| 31 | struct AdcParts { | 31 | struct AdcParts { |
| 32 | adc: Adc<'static, Async>, | 32 | adc: Adc<'static, Async>, |
| 33 | pin: adc::Channel<'static>, | 33 | pin: adc::Channel<'static>, |
| 34 | dma: DMA_CH0, | 34 | dma: Peri<'static, DMA_CH0>, |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | #[embassy_executor::main] | 37 | #[embassy_executor::main] |
| @@ -70,7 +70,10 @@ async fn producer(mut sender: Sender<'static, NoopRawMutex, SampleBuffer>, mut a | |||
| 70 | let buf = sender.send().await; | 70 | let buf = sender.send().await; |
| 71 | 71 | ||
| 72 | // Fill it with data | 72 | // Fill it with data |
| 73 | adc.adc.read_many(&mut adc.pin, buf, 1, &mut adc.dma).await.unwrap(); | 73 | adc.adc |
| 74 | .read_many(&mut adc.pin, buf, 1, adc.dma.reborrow()) | ||
| 75 | .await | ||
| 76 | .unwrap(); | ||
| 74 | 77 | ||
| 75 | // Notify the channel that the buffer is now ready to be received | 78 | // Notify the channel that the buffer is now ready to be received |
| 76 | sender.send_done(); | 79 | sender.send_done(); |
diff --git a/examples/rp23/Cargo.toml b/examples/rp23/Cargo.toml deleted file mode 100644 index 8f8d6ff10..000000000 --- a/examples/rp23/Cargo.toml +++ /dev/null | |||
| @@ -1,80 +0,0 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-rp2350-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | |||
| 8 | [dependencies] | ||
| 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"] } | ||
| 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"] } | ||
| 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"] } | ||
| 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"] } | ||
| 16 | embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } | ||
| 17 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | ||
| 18 | embassy-usb-logger = { version = "0.2.0", path = "../../embassy-usb-logger" } | ||
| 19 | cyw43 = { version = "0.2.0", path = "../../cyw43", features = ["defmt", "firmware-logs", "bluetooth"] } | ||
| 20 | cyw43-pio = { version = "0.2.0", path = "../../cyw43-pio", features = ["defmt"] } | ||
| 21 | |||
| 22 | defmt = "0.3" | ||
| 23 | defmt-rtt = "0.4" | ||
| 24 | fixed = "1.23.1" | ||
| 25 | fixed-macro = "1.2" | ||
| 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"] } | ||
| 30 | serde-json-core = "0.5.1" | ||
| 31 | |||
| 32 | # for assign resources example | ||
| 33 | assign-resources = { git = "https://github.com/adamgreig/assign-resources", rev = "94ad10e2729afdf0fd5a77cd12e68409a982f58a" } | ||
| 34 | |||
| 35 | #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | ||
| 36 | cortex-m = { version = "0.7.6", features = ["inline-asm"] } | ||
| 37 | cortex-m-rt = "0.7.0" | ||
| 38 | critical-section = "1.1" | ||
| 39 | panic-probe = { version = "0.3", features = ["print-defmt"] } | ||
| 40 | display-interface-spi = "0.4.1" | ||
| 41 | embedded-graphics = "0.7.1" | ||
| 42 | st7789 = "0.6.1" | ||
| 43 | display-interface = "0.4.1" | ||
| 44 | byte-slice-cast = { version = "1.2.0", default-features = false } | ||
| 45 | smart-leds = "0.3.0" | ||
| 46 | heapless = "0.8" | ||
| 47 | usbd-hid = "0.8.1" | ||
| 48 | |||
| 49 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | ||
| 50 | embedded-hal-async = "1.0" | ||
| 51 | embedded-hal-bus = { version = "0.1", features = ["async"] } | ||
| 52 | embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } | ||
| 53 | embedded-storage = { version = "0.3" } | ||
| 54 | static_cell = "2.1" | ||
| 55 | portable-atomic = { version = "1.5", features = ["critical-section"] } | ||
| 56 | log = "0.4" | ||
| 57 | pio-proc = "0.2" | ||
| 58 | pio = "0.2.1" | ||
| 59 | rand = { version = "0.8.5", default-features = false } | ||
| 60 | embedded-sdmmc = "0.7.0" | ||
| 61 | |||
| 62 | bt-hci = { version = "0.1.0", default-features = false, features = ["defmt"] } | ||
| 63 | trouble-host = { version = "0.1.0", features = ["defmt", "gatt"] } | ||
| 64 | |||
| 65 | [profile.release] | ||
| 66 | debug = 2 | ||
| 67 | |||
| 68 | [profile.dev] | ||
| 69 | lto = true | ||
| 70 | opt-level = "z" | ||
| 71 | |||
| 72 | [patch.crates-io] | ||
| 73 | trouble-host = { git = "https://github.com/embassy-rs/trouble.git", rev = "4b8c0f499b34e46ca23a56e2d1640ede371722cf" } | ||
| 74 | bt-hci = { git = "https://github.com/alexmoon/bt-hci.git", rev = "b9cd5954f6bd89b535cad9c418e9fdf12812d7c3" } | ||
| 75 | embassy-executor = { path = "../../embassy-executor" } | ||
| 76 | embassy-sync = { path = "../../embassy-sync" } | ||
| 77 | embassy-futures = { path = "../../embassy-futures" } | ||
| 78 | embassy-time = { path = "../../embassy-time" } | ||
| 79 | embassy-time-driver = { path = "../../embassy-time-driver" } | ||
| 80 | embassy-embedded-hal = { path = "../../embassy-embedded-hal" } | ||
diff --git a/examples/rp23/src/bin/pio_hd44780.rs b/examples/rp23/src/bin/pio_hd44780.rs deleted file mode 100644 index fc658267d..000000000 --- a/examples/rp23/src/bin/pio_hd44780.rs +++ /dev/null | |||
| @@ -1,255 +0,0 @@ | |||
| 1 | //! This example shows powerful PIO module in the RP2040 chip to communicate with a HD44780 display. | ||
| 2 | //! See (https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | |||
| 7 | use core::fmt::Write; | ||
| 8 | |||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_rp::block::ImageDef; | ||
| 11 | use embassy_rp::dma::{AnyChannel, Channel}; | ||
| 12 | use embassy_rp::peripherals::PIO0; | ||
| 13 | use embassy_rp::pio::{ | ||
| 14 | Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, | ||
| 15 | }; | ||
| 16 | use embassy_rp::pwm::{self, Pwm}; | ||
| 17 | use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef}; | ||
| 18 | use embassy_time::{Instant, Timer}; | ||
| 19 | use {defmt_rtt as _, panic_probe as _}; | ||
| 20 | |||
| 21 | #[link_section = ".start_block"] | ||
| 22 | #[used] | ||
| 23 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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 { | ||
| 36 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 37 | }); | ||
| 38 | |||
| 39 | #[embassy_executor::main] | ||
| 40 | async fn main(_spawner: Spawner) { | ||
| 41 | // this test assumes a 2x16 HD44780 display attached as follow: | ||
| 42 | // rs = PIN0 | ||
| 43 | // rw = PIN1 | ||
| 44 | // e = PIN2 | ||
| 45 | // db4 = PIN3 | ||
| 46 | // db5 = PIN4 | ||
| 47 | // db6 = PIN5 | ||
| 48 | // db7 = PIN6 | ||
| 49 | // additionally a pwm signal for a bias voltage charge pump is provided on pin 15, | ||
| 50 | // allowing direct connection of the display to the RP2040 without level shifters. | ||
| 51 | let p = embassy_rp::init(Default::default()); | ||
| 52 | |||
| 53 | let _pwm = Pwm::new_output_b(p.PWM_SLICE7, p.PIN_15, { | ||
| 54 | let mut c = pwm::Config::default(); | ||
| 55 | c.divider = 125.into(); | ||
| 56 | c.top = 100; | ||
| 57 | c.compare_b = 50; | ||
| 58 | c | ||
| 59 | }); | ||
| 60 | |||
| 61 | let mut hd = HD44780::new( | ||
| 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, | ||
| 63 | ) | ||
| 64 | .await; | ||
| 65 | |||
| 66 | loop { | ||
| 67 | struct Buf<const N: usize>([u8; N], usize); | ||
| 68 | impl<const N: usize> Write for Buf<N> { | ||
| 69 | fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { | ||
| 70 | for b in s.as_bytes() { | ||
| 71 | if self.1 >= N { | ||
| 72 | return Err(core::fmt::Error); | ||
| 73 | } | ||
| 74 | self.0[self.1] = *b; | ||
| 75 | self.1 += 1; | ||
| 76 | } | ||
| 77 | Ok(()) | ||
| 78 | } | ||
| 79 | } | ||
| 80 | let mut buf = Buf([0; 16], 0); | ||
| 81 | write!(buf, "up {}s", Instant::now().as_micros() as f32 / 1e6).unwrap(); | ||
| 82 | hd.add_line(&buf.0[0..buf.1]).await; | ||
| 83 | Timer::after_secs(1).await; | ||
| 84 | } | ||
| 85 | } | ||
| 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 deleted file mode 100644 index 5a3bde759..000000000 --- a/examples/rp23/src/bin/pio_i2s.rs +++ /dev/null | |||
| @@ -1,140 +0,0 @@ | |||
| 1 | //! This example shows generating audio and sending it to a connected i2s DAC using the PIO | ||
| 2 | //! module of the RP2040. | ||
| 3 | //! | ||
| 4 | //! Connect the i2s DAC as follows: | ||
| 5 | //! bclk : GPIO 18 | ||
| 6 | //! lrc : GPIO 19 | ||
| 7 | //! din : GPIO 20 | ||
| 8 | //! Then hold down the boot select button to trigger a rising triangle waveform. | ||
| 9 | |||
| 10 | #![no_std] | ||
| 11 | #![no_main] | ||
| 12 | |||
| 13 | use core::mem; | ||
| 14 | |||
| 15 | use embassy_executor::Spawner; | ||
| 16 | use embassy_rp::block::ImageDef; | ||
| 17 | use embassy_rp::peripherals::PIO0; | ||
| 18 | use embassy_rp::pio::{Config, FifoJoin, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; | ||
| 19 | use embassy_rp::{bind_interrupts, Peripheral}; | ||
| 20 | use fixed::traits::ToFixed; | ||
| 21 | use static_cell::StaticCell; | ||
| 22 | use {defmt_rtt as _, panic_probe as _}; | ||
| 23 | |||
| 24 | #[link_section = ".start_block"] | ||
| 25 | #[used] | ||
| 26 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 27 | |||
| 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_cargo_bin_name!(), | ||
| 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 { | ||
| 39 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 40 | }); | ||
| 41 | |||
| 42 | const SAMPLE_RATE: u32 = 48_000; | ||
| 43 | |||
| 44 | #[embassy_executor::main] | ||
| 45 | async fn main(_spawner: Spawner) { | ||
| 46 | let p = embassy_rp::init(Default::default()); | ||
| 47 | |||
| 48 | // Setup pio state machine for i2s output | ||
| 49 | let mut pio = 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 | |||
| 66 | let bit_clock_pin = p.PIN_18; | ||
| 67 | let left_right_clock_pin = p.PIN_19; | ||
| 68 | let data_pin = p.PIN_20; | ||
| 69 | |||
| 70 | let data_pin = pio.common.make_pio_pin(data_pin); | ||
| 71 | let bit_clock_pin = pio.common.make_pio_pin(bit_clock_pin); | ||
| 72 | let left_right_clock_pin = pio.common.make_pio_pin(left_right_clock_pin); | ||
| 73 | |||
| 74 | let cfg = { | ||
| 75 | let mut cfg = Config::default(); | ||
| 76 | cfg.use_program( | ||
| 77 | &pio.common.load_program(&pio_program.program), | ||
| 78 | &[&bit_clock_pin, &left_right_clock_pin], | ||
| 79 | ); | ||
| 80 | cfg.set_out_pins(&[&data_pin]); | ||
| 81 | const BIT_DEPTH: u32 = 16; | ||
| 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 | ); | ||
| 99 | |||
| 100 | // 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 | ||
| 102 | const BUFFER_SIZE: usize = 960; | ||
| 103 | static DMA_BUFFER: StaticCell<[u32; BUFFER_SIZE * 2]> = StaticCell::new(); | ||
| 104 | let dma_buffer = DMA_BUFFER.init_with(|| [0u32; BUFFER_SIZE * 2]); | ||
| 105 | let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE); | ||
| 106 | |||
| 107 | // 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; | ||
| 113 | let mut phase: i32 = 0; | ||
| 114 | |||
| 115 | loop { | ||
| 116 | // trigger transfer of front buffer data to the pio fifo | ||
| 117 | // but don't await the returned future, yet | ||
| 118 | let dma_future = tx.dma_push(dma_ref.reborrow(), front_buffer); | ||
| 119 | |||
| 120 | // fade in audio | ||
| 121 | let fade_target = i32::MAX; | ||
| 122 | |||
| 123 | // fill back buffer with fresh audio samples before awaiting the dma future | ||
| 124 | for s in back_buffer.iter_mut() { | ||
| 125 | // exponential approach of fade_value => fade_target | ||
| 126 | fade_value += (fade_target - fade_value) >> 14; | ||
| 127 | // generate triangle wave with amplitude and frequency based on fade value | ||
| 128 | phase = (phase + (fade_value >> 22)) & 0xffff; | ||
| 129 | let triangle_sample = (phase as i16 as i32).abs() - 16384; | ||
| 130 | let sample = (triangle_sample * (fade_value >> 15)) >> 16; | ||
| 131 | // duplicate mono sample into lower and upper half of dma word | ||
| 132 | *s = (sample as u16 as u32) * 0x10001; | ||
| 133 | } | ||
| 134 | |||
| 135 | // now await the dma future. once the dma finishes, the next buffer needs to be queued | ||
| 136 | // within DMA_DEPTH / SAMPLE_RATE = 8 / 48000 seconds = 166us | ||
| 137 | dma_future.await; | ||
| 138 | mem::swap(&mut back_buffer, &mut front_buffer); | ||
| 139 | } | ||
| 140 | } | ||
diff --git a/examples/rp23/src/bin/pio_pwm.rs b/examples/rp23/src/bin/pio_pwm.rs deleted file mode 100644 index 7c5eefc45..000000000 --- a/examples/rp23/src/bin/pio_pwm.rs +++ /dev/null | |||
| @@ -1,133 +0,0 @@ | |||
| 1 | //! This example shows how to create a pwm using the PIO module in the RP2040 chip. | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | use core::time::Duration; | ||
| 6 | |||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_rp::block::ImageDef; | ||
| 9 | use embassy_rp::gpio::Level; | ||
| 10 | use embassy_rp::peripherals::PIO0; | ||
| 11 | use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine}; | ||
| 12 | use embassy_rp::{bind_interrupts, clocks}; | ||
| 13 | use embassy_time::Timer; | ||
| 14 | use pio::InstructionOperands; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | #[link_section = ".start_block"] | ||
| 18 | #[used] | ||
| 19 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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; | ||
| 32 | |||
| 33 | bind_interrupts!(struct Irqs { | ||
| 34 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 35 | }); | ||
| 36 | |||
| 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] | ||
| 118 | async fn main(_spawner: Spawner) { | ||
| 119 | let p = embassy_rp::init(Default::default()); | ||
| 120 | let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); | ||
| 121 | |||
| 122 | // Note that PIN_25 is the led pin on the Pico | ||
| 123 | let mut pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_25); | ||
| 124 | pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL)); | ||
| 125 | pwm_pio.start(); | ||
| 126 | |||
| 127 | let mut duration = 0; | ||
| 128 | loop { | ||
| 129 | duration = (duration + 1) % 1000; | ||
| 130 | pwm_pio.write(Duration::from_micros(duration)); | ||
| 131 | Timer::after_millis(1).await; | ||
| 132 | } | ||
| 133 | } | ||
diff --git a/examples/rp23/src/bin/pio_stepper.rs b/examples/rp23/src/bin/pio_stepper.rs deleted file mode 100644 index 8b52dc37a..000000000 --- a/examples/rp23/src/bin/pio_stepper.rs +++ /dev/null | |||
| @@ -1,183 +0,0 @@ | |||
| 1 | //! This example shows how to use the PIO module in the RP2040 to implement a stepper motor driver | ||
| 2 | //! for a 5-wire stepper such as the 28BYJ-48. You can halt an ongoing rotation by dropping the future. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | use core::mem::{self, MaybeUninit}; | ||
| 7 | |||
| 8 | use defmt::info; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_rp::bind_interrupts; | ||
| 11 | use embassy_rp::block::ImageDef; | ||
| 12 | use embassy_rp::peripherals::PIO0; | ||
| 13 | use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Irq, Pio, PioPin, StateMachine}; | ||
| 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 _}; | ||
| 19 | |||
| 20 | #[link_section = ".start_block"] | ||
| 21 | #[used] | ||
| 22 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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 { | ||
| 35 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 36 | }); | ||
| 37 | |||
| 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] | ||
| 159 | async fn main(_spawner: Spawner) { | ||
| 160 | let p = embassy_rp::init(Default::default()); | ||
| 161 | let Pio { | ||
| 162 | mut common, irq0, sm0, .. | ||
| 163 | } = Pio::new(p.PIO0, Irqs); | ||
| 164 | |||
| 165 | let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7); | ||
| 166 | stepper.set_frequency(120); | ||
| 167 | loop { | ||
| 168 | info!("CW full steps"); | ||
| 169 | stepper.step(1000).await; | ||
| 170 | |||
| 171 | info!("CCW full steps, drop after 1 sec"); | ||
| 172 | if let Err(_) = with_timeout(Duration::from_secs(1), stepper.step(i32::MIN)).await { | ||
| 173 | info!("Time's up!"); | ||
| 174 | Timer::after(Duration::from_secs(1)).await; | ||
| 175 | } | ||
| 176 | |||
| 177 | info!("CW half steps"); | ||
| 178 | stepper.step_half(1000).await; | ||
| 179 | |||
| 180 | info!("CCW half steps"); | ||
| 181 | stepper.step_half(-1000).await; | ||
| 182 | } | ||
| 183 | } | ||
diff --git a/examples/rp23/src/bin/pio_ws2812.rs b/examples/rp23/src/bin/pio_ws2812.rs deleted file mode 100644 index 99d9cf7e6..000000000 --- a/examples/rp23/src/bin/pio_ws2812.rs +++ /dev/null | |||
| @@ -1,176 +0,0 @@ | |||
| 1 | //! This example shows powerful PIO module in the RP2040 chip to communicate with WS2812 LED modules. | ||
| 2 | //! See (https://www.sparkfun.com/categories/tags/ws2812) | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | |||
| 7 | use defmt::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_rp::block::ImageDef; | ||
| 10 | use embassy_rp::dma::{AnyChannel, Channel}; | ||
| 11 | use embassy_rp::peripherals::PIO0; | ||
| 12 | use embassy_rp::pio::{ | ||
| 13 | Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, | ||
| 14 | }; | ||
| 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; | ||
| 20 | use {defmt_rtt as _, panic_probe as _}; | ||
| 21 | |||
| 22 | #[link_section = ".start_block"] | ||
| 23 | #[used] | ||
| 24 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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 { | ||
| 37 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 38 | }); | ||
| 39 | |||
| 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 | ||
| 131 | /// The colours are a transition r - g - b - back to r. | ||
| 132 | fn wheel(mut wheel_pos: u8) -> RGB8 { | ||
| 133 | wheel_pos = 255 - wheel_pos; | ||
| 134 | if wheel_pos < 85 { | ||
| 135 | return (255 - wheel_pos * 3, 0, wheel_pos * 3).into(); | ||
| 136 | } | ||
| 137 | if wheel_pos < 170 { | ||
| 138 | wheel_pos -= 85; | ||
| 139 | return (0, wheel_pos * 3, 255 - wheel_pos * 3).into(); | ||
| 140 | } | ||
| 141 | wheel_pos -= 170; | ||
| 142 | (wheel_pos * 3, 255 - wheel_pos * 3, 0).into() | ||
| 143 | } | ||
| 144 | |||
| 145 | #[embassy_executor::main] | ||
| 146 | async fn main(_spawner: Spawner) { | ||
| 147 | info!("Start"); | ||
| 148 | let p = embassy_rp::init(Default::default()); | ||
| 149 | |||
| 150 | let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); | ||
| 151 | |||
| 152 | // This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit | ||
| 153 | // feather boards for the 2040 both have one built in. | ||
| 154 | const NUM_LEDS: usize = 1; | ||
| 155 | let mut data = [RGB8::default(); NUM_LEDS]; | ||
| 156 | |||
| 157 | // Common neopixel pins: | ||
| 158 | // Thing plus: 8 | ||
| 159 | // Adafruit Feather: 16; Adafruit Feather+RFM95: 4 | ||
| 160 | let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16); | ||
| 161 | |||
| 162 | // Loop forever making RGB values and pushing them out to the WS2812. | ||
| 163 | let mut ticker = Ticker::every(Duration::from_millis(10)); | ||
| 164 | loop { | ||
| 165 | for j in 0..(256 * 5) { | ||
| 166 | debug!("New Colors:"); | ||
| 167 | for i in 0..NUM_LEDS { | ||
| 168 | data[i] = wheel((((i * 256) as u16 / NUM_LEDS as u16 + j as u16) & 255) as u8); | ||
| 169 | debug!("R: {} G: {} B: {}", data[i].r, data[i].g, data[i].b); | ||
| 170 | } | ||
| 171 | ws2812.write(&data).await; | ||
| 172 | |||
| 173 | ticker.next().await; | ||
| 174 | } | ||
| 175 | } | ||
| 176 | } | ||
diff --git a/examples/rp23/src/bin/pwm.rs b/examples/rp23/src/bin/pwm.rs deleted file mode 100644 index 4cd3b3eb4..000000000 --- a/examples/rp23/src/bin/pwm.rs +++ /dev/null | |||
| @@ -1,44 +0,0 @@ | |||
| 1 | //! This example shows how to use PWM (Pulse Width Modulation) in the RP2040 chip. | ||
| 2 | //! | ||
| 3 | //! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_rp::block::ImageDef; | ||
| 11 | use embassy_rp::pwm::{Config, Pwm}; | ||
| 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 | // 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_cargo_bin_name!(), | ||
| 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] | ||
| 30 | async fn main(_spawner: Spawner) { | ||
| 31 | let p = embassy_rp::init(Default::default()); | ||
| 32 | |||
| 33 | let mut c: Config = Default::default(); | ||
| 34 | c.top = 0x8000; | ||
| 35 | c.compare_b = 8; | ||
| 36 | let mut pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, c.clone()); | ||
| 37 | |||
| 38 | loop { | ||
| 39 | info!("current LED duty cycle: {}/32768", c.compare_b); | ||
| 40 | Timer::after_secs(1).await; | ||
| 41 | c.compare_b = c.compare_b.rotate_left(4); | ||
| 42 | pwm.set_config(&c); | ||
| 43 | } | ||
| 44 | } | ||
diff --git a/examples/rp23/src/bin/spi_display.rs b/examples/rp23/src/bin/spi_display.rs deleted file mode 100644 index 2441b1186..000000000 --- a/examples/rp23/src/bin/spi_display.rs +++ /dev/null | |||
| @@ -1,327 +0,0 @@ | |||
| 1 | //! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip. | ||
| 2 | //! | ||
| 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) | ||
| 5 | |||
| 6 | #![no_std] | ||
| 7 | #![no_main] | ||
| 8 | |||
| 9 | use core::cell::RefCell; | ||
| 10 | |||
| 11 | use defmt::*; | ||
| 12 | use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig; | ||
| 13 | use embassy_executor::Spawner; | ||
| 14 | use embassy_rp::block::ImageDef; | ||
| 15 | use embassy_rp::gpio::{Level, Output}; | ||
| 16 | use embassy_rp::spi; | ||
| 17 | use embassy_rp::spi::{Blocking, Spi}; | ||
| 18 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
| 19 | use embassy_sync::blocking_mutex::Mutex; | ||
| 20 | use embassy_time::Delay; | ||
| 21 | use embedded_graphics::image::{Image, ImageRawLE}; | ||
| 22 | use embedded_graphics::mono_font::ascii::FONT_10X20; | ||
| 23 | use embedded_graphics::mono_font::MonoTextStyle; | ||
| 24 | use embedded_graphics::pixelcolor::Rgb565; | ||
| 25 | use embedded_graphics::prelude::*; | ||
| 26 | use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle}; | ||
| 27 | use embedded_graphics::text::Text; | ||
| 28 | use st7789::{Orientation, ST7789}; | ||
| 29 | use {defmt_rtt as _, panic_probe as _}; | ||
| 30 | |||
| 31 | #[link_section = ".start_block"] | ||
| 32 | #[used] | ||
| 33 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 34 | |||
| 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_cargo_bin_name!(), | ||
| 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; | ||
| 47 | |||
| 48 | const DISPLAY_FREQ: u32 = 64_000_000; | ||
| 49 | const TOUCH_FREQ: u32 = 200_000; | ||
| 50 | |||
| 51 | #[embassy_executor::main] | ||
| 52 | async fn main(_spawner: Spawner) { | ||
| 53 | let p = embassy_rp::init(Default::default()); | ||
| 54 | info!("Hello World!"); | ||
| 55 | |||
| 56 | let bl = p.PIN_13; | ||
| 57 | let rst = p.PIN_15; | ||
| 58 | let display_cs = p.PIN_9; | ||
| 59 | let dcx = p.PIN_8; | ||
| 60 | let miso = p.PIN_12; | ||
| 61 | let mosi = p.PIN_11; | ||
| 62 | let clk = p.PIN_10; | ||
| 63 | let touch_cs = p.PIN_16; | ||
| 64 | //let touch_irq = p.PIN_17; | ||
| 65 | |||
| 66 | // create SPI | ||
| 67 | let mut display_config = spi::Config::default(); | ||
| 68 | display_config.frequency = DISPLAY_FREQ; | ||
| 69 | display_config.phase = spi::Phase::CaptureOnSecondTransition; | ||
| 70 | display_config.polarity = spi::Polarity::IdleHigh; | ||
| 71 | let mut touch_config = spi::Config::default(); | ||
| 72 | touch_config.frequency = TOUCH_FREQ; | ||
| 73 | touch_config.phase = spi::Phase::CaptureOnSecondTransition; | ||
| 74 | touch_config.polarity = spi::Polarity::IdleHigh; | ||
| 75 | |||
| 76 | let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone()); | ||
| 77 | let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi)); | ||
| 78 | |||
| 79 | let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config); | ||
| 80 | let touch_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(touch_cs, Level::High), touch_config); | ||
| 81 | |||
| 82 | let mut touch = Touch::new(touch_spi); | ||
| 83 | |||
| 84 | let dcx = Output::new(dcx, Level::Low); | ||
| 85 | let rst = Output::new(rst, Level::Low); | ||
| 86 | // dcx: 0 = command, 1 = data | ||
| 87 | |||
| 88 | // Enable LCD backlight | ||
| 89 | let _bl = Output::new(bl, Level::High); | ||
| 90 | |||
| 91 | // display interface abstraction from SPI and DC | ||
| 92 | let di = SPIDeviceInterface::new(display_spi, dcx); | ||
| 93 | |||
| 94 | // create driver | ||
| 95 | let mut display = ST7789::new(di, rst, 240, 320); | ||
| 96 | |||
| 97 | // initialize | ||
| 98 | display.init(&mut Delay).unwrap(); | ||
| 99 | |||
| 100 | // set default orientation | ||
| 101 | display.set_orientation(Orientation::Landscape).unwrap(); | ||
| 102 | |||
| 103 | display.clear(Rgb565::BLACK).unwrap(); | ||
| 104 | |||
| 105 | let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), 86); | ||
| 106 | let ferris = Image::new(&raw_image_data, Point::new(34, 68)); | ||
| 107 | |||
| 108 | // Display the image | ||
| 109 | ferris.draw(&mut display).unwrap(); | ||
| 110 | |||
| 111 | let style = MonoTextStyle::new(&FONT_10X20, Rgb565::GREEN); | ||
| 112 | Text::new( | ||
| 113 | "Hello embedded_graphics \n + embassy + RP2040!", | ||
| 114 | Point::new(20, 200), | ||
| 115 | style, | ||
| 116 | ) | ||
| 117 | .draw(&mut display) | ||
| 118 | .unwrap(); | ||
| 119 | |||
| 120 | loop { | ||
| 121 | if let Some((x, y)) = touch.read() { | ||
| 122 | let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::BLUE).build(); | ||
| 123 | |||
| 124 | Rectangle::new(Point::new(x - 1, y - 1), Size::new(3, 3)) | ||
| 125 | .into_styled(style) | ||
| 126 | .draw(&mut display) | ||
| 127 | .unwrap(); | ||
| 128 | } | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | /// Driver for the XPT2046 resistive touchscreen sensor | ||
| 133 | mod touch { | ||
| 134 | use embedded_hal_1::spi::{Operation, SpiDevice}; | ||
| 135 | |||
| 136 | struct Calibration { | ||
| 137 | x1: i32, | ||
| 138 | x2: i32, | ||
| 139 | y1: i32, | ||
| 140 | y2: i32, | ||
| 141 | sx: i32, | ||
| 142 | sy: i32, | ||
| 143 | } | ||
| 144 | |||
| 145 | const CALIBRATION: Calibration = Calibration { | ||
| 146 | x1: 3880, | ||
| 147 | x2: 340, | ||
| 148 | y1: 262, | ||
| 149 | y2: 3850, | ||
| 150 | sx: 320, | ||
| 151 | sy: 240, | ||
| 152 | }; | ||
| 153 | |||
| 154 | pub struct Touch<SPI: SpiDevice> { | ||
| 155 | spi: SPI, | ||
| 156 | } | ||
| 157 | |||
| 158 | impl<SPI> Touch<SPI> | ||
| 159 | where | ||
| 160 | SPI: SpiDevice, | ||
| 161 | { | ||
| 162 | pub fn new(spi: SPI) -> Self { | ||
| 163 | Self { spi } | ||
| 164 | } | ||
| 165 | |||
| 166 | pub fn read(&mut self) -> Option<(i32, i32)> { | ||
| 167 | let mut x = [0; 2]; | ||
| 168 | let mut y = [0; 2]; | ||
| 169 | self.spi | ||
| 170 | .transaction(&mut [ | ||
| 171 | Operation::Write(&[0x90]), | ||
| 172 | Operation::Read(&mut x), | ||
| 173 | Operation::Write(&[0xd0]), | ||
| 174 | Operation::Read(&mut y), | ||
| 175 | ]) | ||
| 176 | .unwrap(); | ||
| 177 | |||
| 178 | let x = (u16::from_be_bytes(x) >> 3) as i32; | ||
| 179 | let y = (u16::from_be_bytes(y) >> 3) as i32; | ||
| 180 | |||
| 181 | let cal = &CALIBRATION; | ||
| 182 | |||
| 183 | let x = ((x - cal.x1) * cal.sx / (cal.x2 - cal.x1)).clamp(0, cal.sx); | ||
| 184 | let y = ((y - cal.y1) * cal.sy / (cal.y2 - cal.y1)).clamp(0, cal.sy); | ||
| 185 | if x == 0 && y == 0 { | ||
| 186 | None | ||
| 187 | } else { | ||
| 188 | Some((x, y)) | ||
| 189 | } | ||
| 190 | } | ||
| 191 | } | ||
| 192 | } | ||
| 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/rp235x/.cargo/config.toml b/examples/rp235x/.cargo/config.toml new file mode 100644 index 000000000..40f245785 --- /dev/null +++ b/examples/rp235x/.cargo/config.toml | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | ||
| 2 | runner = "probe-rs run --chip RP235x" | ||
| 3 | #runner = "elf2uf2-rs -d" | ||
| 4 | #runner = "picotool load -u -v -x -t elf" | ||
| 5 | |||
| 6 | [build] | ||
| 7 | target = "thumbv8m.main-none-eabihf" | ||
| 8 | |||
| 9 | [env] | ||
| 10 | DEFMT_LOG = "debug" | ||
diff --git a/examples/rp235x/Cargo.toml b/examples/rp235x/Cargo.toml new file mode 100644 index 000000000..c81b79ae1 --- /dev/null +++ b/examples/rp235x/Cargo.toml | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-rp2350-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | |||
| 8 | [dependencies] | ||
| 9 | embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal", features = ["defmt"] } | ||
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | ||
| 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | ||
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | ||
| 13 | embassy-rp = { version = "0.4.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] } | ||
| 14 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } | ||
| 15 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] } | ||
| 16 | embassy-net-wiznet = { version = "0.2.0", path = "../../embassy-net-wiznet", features = ["defmt"] } | ||
| 17 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | ||
| 18 | embassy-usb-logger = { version = "0.4.0", path = "../../embassy-usb-logger" } | ||
| 19 | cyw43 = { version = "0.3.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } | ||
| 20 | cyw43-pio = { version = "0.4.0", path = "../../cyw43-pio", features = ["defmt"] } | ||
| 21 | |||
| 22 | defmt = "1.0.1" | ||
| 23 | defmt-rtt = "1.0.0" | ||
| 24 | fixed = "1.23.1" | ||
| 25 | fixed-macro = "1.2" | ||
| 26 | |||
| 27 | serde = { version = "1.0.203", default-features = false, features = ["derive"] } | ||
| 28 | serde-json-core = "0.5.1" | ||
| 29 | |||
| 30 | # for assign resources example | ||
| 31 | assign-resources = { git = "https://github.com/adamgreig/assign-resources", rev = "bd22cb7a92031fb16f74a5da42469d466c33383e" } | ||
| 32 | |||
| 33 | # for TB6612FNG example | ||
| 34 | tb6612fng = "1.0.0" | ||
| 35 | |||
| 36 | #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | ||
| 37 | cortex-m = { version = "0.7.6", features = ["inline-asm"] } | ||
| 38 | cortex-m-rt = "0.7.0" | ||
| 39 | critical-section = "1.1" | ||
| 40 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } | ||
| 41 | display-interface-spi = "0.5.0" | ||
| 42 | embedded-graphics = "0.8.1" | ||
| 43 | mipidsi = "0.8.0" | ||
| 44 | display-interface = "0.5.0" | ||
| 45 | byte-slice-cast = { version = "1.2.0", default-features = false } | ||
| 46 | smart-leds = "0.3.0" | ||
| 47 | heapless = "0.8" | ||
| 48 | usbd-hid = "0.8.1" | ||
| 49 | |||
| 50 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | ||
| 51 | embedded-hal-async = "1.0" | ||
| 52 | embedded-hal-bus = { version = "0.1", features = ["async"] } | ||
| 53 | embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } | ||
| 54 | embedded-storage = { version = "0.3" } | ||
| 55 | static_cell = "2.1" | ||
| 56 | portable-atomic = { version = "1.5", features = ["critical-section"] } | ||
| 57 | log = "0.4" | ||
| 58 | embedded-sdmmc = "0.7.0" | ||
| 59 | |||
| 60 | [profile.release] | ||
| 61 | debug = 2 | ||
| 62 | |||
| 63 | [profile.dev] | ||
| 64 | lto = true | ||
| 65 | opt-level = "z" | ||
diff --git a/examples/rp23/assets/ferris.raw b/examples/rp235x/assets/ferris.raw index 9733889c5..9733889c5 100644 --- a/examples/rp23/assets/ferris.raw +++ b/examples/rp235x/assets/ferris.raw | |||
| Binary files differ | |||
diff --git a/examples/rp235x/build.rs b/examples/rp235x/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/examples/rp235x/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/rp23/memory.x b/examples/rp235x/memory.x index 777492062..c803896f6 100644 --- a/examples/rp23/memory.x +++ b/examples/rp235x/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/rp235x/src/bin/adc.rs index 19872607e..5c4135268 100644 --- a/examples/rp23/src/bin/adc.rs +++ b/examples/rp235x/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] |
| @@ -8,25 +8,10 @@ use defmt::*; | |||
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler}; | 9 | use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler}; |
| 10 | use embassy_rp::bind_interrupts; | 10 | use embassy_rp::bind_interrupts; |
| 11 | use embassy_rp::block::ImageDef; | ||
| 12 | use embassy_rp::gpio::Pull; | 11 | use embassy_rp::gpio::Pull; |
| 13 | use embassy_time::Timer; | 12 | use embassy_time::Timer; |
| 14 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 15 | 14 | ||
| 16 | #[link_section = ".start_block"] | ||
| 17 | #[used] | ||
| 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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 { | 15 | bind_interrupts!(struct Irqs { |
| 31 | ADC_IRQ_FIFO => InterruptHandler; | 16 | ADC_IRQ_FIFO => InterruptHandler; |
| 32 | }); | 17 | }); |
| @@ -55,7 +40,7 @@ async fn main(_spawner: Spawner) { | |||
| 55 | } | 40 | } |
| 56 | 41 | ||
| 57 | fn convert_to_celsius(raw_temp: u16) -> f32 { | 42 | fn convert_to_celsius(raw_temp: u16) -> f32 { |
| 58 | // According to chapter 4.9.5. Temperature Sensor in RP2040 datasheet | 43 | // According to chapter 12.4.6 Temperature Sensor in RP235x datasheet |
| 59 | let temp = 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721; | 44 | let temp = 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721; |
| 60 | let sign = if temp < 0.0 { -1.0 } else { 1.0 }; | 45 | let sign = if temp < 0.0 { -1.0 } else { 1.0 }; |
| 61 | let rounded_temp_x10: i16 = ((temp * 10.0) + 0.5 * sign) as i16; | 46 | let rounded_temp_x10: i16 = ((temp * 10.0) + 0.5 * sign) as i16; |
diff --git a/examples/rp23/src/bin/adc_dma.rs b/examples/rp235x/src/bin/adc_dma.rs index d538ddaa2..4003cc078 100644 --- a/examples/rp23/src/bin/adc_dma.rs +++ b/examples/rp235x/src/bin/adc_dma.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | //! This example shows how to use the RP2040 ADC with DMA, both single- and multichannel reads. | 1 | //! This example shows how to use the RP235x ADC with DMA, both single- and multichannel reads. |
| 2 | //! For multichannel, the samples are interleaved in the buffer: | 2 | //! For multichannel, the samples are interleaved in the buffer: |
| 3 | //! `[ch1, ch2, ch3, ch4, ch1, ch2, ch3, ch4, ...]` | 3 | //! `[ch1, ch2, ch3, ch4, ch1, ch2, ch3, ch4, ...]` |
| 4 | #![no_std] | 4 | #![no_std] |
| @@ -8,25 +8,10 @@ use defmt::*; | |||
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler}; | 9 | use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler}; |
| 10 | use embassy_rp::bind_interrupts; | 10 | use embassy_rp::bind_interrupts; |
| 11 | use embassy_rp::block::ImageDef; | ||
| 12 | use embassy_rp::gpio::Pull; | 11 | use embassy_rp::gpio::Pull; |
| 13 | use embassy_time::{Duration, Ticker}; | 12 | use embassy_time::{Duration, Ticker}; |
| 14 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 15 | 14 | ||
| 16 | #[link_section = ".start_block"] | ||
| 17 | #[used] | ||
| 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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 { | 15 | bind_interrupts!(struct Irqs { |
| 31 | ADC_IRQ_FIFO => InterruptHandler; | 16 | ADC_IRQ_FIFO => InterruptHandler; |
| 32 | }); | 17 | }); |
| @@ -53,13 +38,13 @@ async fn main(_spawner: Spawner) { | |||
| 53 | // Read 100 samples from a single channel | 38 | // Read 100 samples from a single channel |
| 54 | let mut buf = [0_u16; BLOCK_SIZE]; | 39 | let mut buf = [0_u16; BLOCK_SIZE]; |
| 55 | let div = 479; // 100kHz sample rate (48Mhz / 100kHz - 1) | 40 | let div = 479; // 100kHz sample rate (48Mhz / 100kHz - 1) |
| 56 | adc.read_many(&mut pin, &mut buf, div, &mut dma).await.unwrap(); | 41 | adc.read_many(&mut pin, &mut buf, div, dma.reborrow()).await.unwrap(); |
| 57 | info!("single: {:?} ...etc", buf[..8]); | 42 | info!("single: {:?} ...etc", buf[..8]); |
| 58 | 43 | ||
| 59 | // Read 100 samples from 4 channels interleaved | 44 | // Read 100 samples from 4 channels interleaved |
| 60 | let mut buf = [0_u16; { BLOCK_SIZE * NUM_CHANNELS }]; | 45 | let mut buf = [0_u16; { BLOCK_SIZE * NUM_CHANNELS }]; |
| 61 | let div = 119; // 100kHz sample rate (48Mhz / 100kHz * 4ch - 1) | 46 | let div = 119; // 100kHz sample rate (48Mhz / 100kHz * 4ch - 1) |
| 62 | adc.read_many_multichannel(&mut pins, &mut buf, div, &mut dma) | 47 | adc.read_many_multichannel(&mut pins, &mut buf, div, dma.reborrow()) |
| 63 | .await | 48 | .await |
| 64 | .unwrap(); | 49 | .unwrap(); |
| 65 | info!("multi: {:?} ...etc", buf[..NUM_CHANNELS * 2]); | 50 | info!("multi: {:?} ...etc", buf[..NUM_CHANNELS * 2]); |
diff --git a/examples/rp23/src/bin/assign_resources.rs b/examples/rp235x/src/bin/assign_resources.rs index 923c13514..341f54d22 100644 --- a/examples/rp23/src/bin/assign_resources.rs +++ b/examples/rp235x/src/bin/assign_resources.rs | |||
| @@ -14,26 +14,12 @@ | |||
| 14 | use assign_resources::assign_resources; | 14 | use assign_resources::assign_resources; |
| 15 | use defmt::*; | 15 | use defmt::*; |
| 16 | use embassy_executor::Spawner; | 16 | use embassy_executor::Spawner; |
| 17 | use embassy_rp::block::ImageDef; | ||
| 18 | use embassy_rp::gpio::{Level, Output}; | 17 | use embassy_rp::gpio::{Level, Output}; |
| 19 | use embassy_rp::peripherals::{self, PIN_20, PIN_21}; | 18 | use embassy_rp::peripherals::{self, PIN_20, PIN_21}; |
| 19 | use embassy_rp::Peri; | ||
| 20 | use embassy_time::Timer; | 20 | use embassy_time::Timer; |
| 21 | use {defmt_rtt as _, panic_probe as _}; | 21 | use {defmt_rtt as _, panic_probe as _}; |
| 22 | 22 | ||
| 23 | #[link_section = ".start_block"] | ||
| 24 | #[used] | ||
| 25 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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] | 23 | #[embassy_executor::main] |
| 38 | async fn main(spawner: Spawner) { | 24 | async fn main(spawner: Spawner) { |
| 39 | // initialize the peripherals | 25 | // initialize the peripherals |
| @@ -53,7 +39,11 @@ async fn main(spawner: Spawner) { | |||
| 53 | 39 | ||
| 54 | // 1) Assigning a resource to a task by passing parts of the peripherals. | 40 | // 1) Assigning a resource to a task by passing parts of the peripherals. |
| 55 | #[embassy_executor::task] | 41 | #[embassy_executor::task] |
| 56 | async fn double_blinky_manually_assigned(_spawner: Spawner, pin_20: PIN_20, pin_21: PIN_21) { | 42 | async fn double_blinky_manually_assigned( |
| 43 | _spawner: Spawner, | ||
| 44 | pin_20: Peri<'static, PIN_20>, | ||
| 45 | pin_21: Peri<'static, PIN_21>, | ||
| 46 | ) { | ||
| 57 | let mut led_20 = Output::new(pin_20, Level::Low); | 47 | let mut led_20 = Output::new(pin_20, Level::Low); |
| 58 | let mut led_21 = Output::new(pin_21, Level::High); | 48 | let mut led_21 = Output::new(pin_21, Level::High); |
| 59 | 49 | ||
diff --git a/examples/rp23/src/bin/blinky.rs b/examples/rp235x/src/bin/blinky.rs index 02bdf9b3d..8a2464fbb 100644 --- a/examples/rp23/src/bin/blinky.rs +++ b/examples/rp235x/src/bin/blinky.rs | |||
| @@ -1,36 +1,34 @@ | |||
| 1 | //! This example test the RP Pico on board LED. | 1 | //! This example test the RP Pico on board LED. |
| 2 | //! | 2 | //! |
| 3 | //! It does not work with the RP Pico W board. See wifi_blinky.rs. | 3 | //! It does not work with the RP Pico W board. See `blinky_wifi.rs`. |
| 4 | 4 | ||
| 5 | #![no_std] | 5 | #![no_std] |
| 6 | #![no_main] | 6 | #![no_main] |
| 7 | 7 | ||
| 8 | use defmt::*; | 8 | use defmt::*; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_rp::block::ImageDef; | ||
| 11 | use embassy_rp::gpio; | 10 | use embassy_rp::gpio; |
| 12 | use embassy_time::Timer; | 11 | use embassy_time::Timer; |
| 13 | use gpio::{Level, Output}; | 12 | use gpio::{Level, Output}; |
| 14 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 15 | 14 | ||
| 16 | #[link_section = ".start_block"] | 15 | // Program metadata for `picotool info`. |
| 17 | #[used] | 16 | // This isn't needed, but it's recomended to have these minimal entries. |
| 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | 17 | #[unsafe(link_section = ".bi_entries")] |
| 19 | |||
| 20 | // Program metadata for `picotool info` | ||
| 21 | #[link_section = ".bi_entries"] | ||
| 22 | #[used] | 18 | #[used] |
| 23 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | 19 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ |
| 24 | embassy_rp::binary_info_rp_cargo_bin_name!(), | 20 | embassy_rp::binary_info::rp_program_name!(c"Blinky Example"), |
| 25 | embassy_rp::binary_info_rp_cargo_version!(), | 21 | embassy_rp::binary_info::rp_program_description!( |
| 26 | embassy_rp::binary_info_rp_program_description!(c"Blinky"), | 22 | c"This example tests the RP Pico on board LED, connected to gpio 25" |
| 27 | embassy_rp::binary_info_rp_program_build_attribute!(), | 23 | ), |
| 24 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 25 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 28 | ]; | 26 | ]; |
| 29 | 27 | ||
| 30 | #[embassy_executor::main] | 28 | #[embassy_executor::main] |
| 31 | async fn main(_spawner: Spawner) { | 29 | async fn main(_spawner: Spawner) { |
| 32 | let p = embassy_rp::init(Default::default()); | 30 | let p = embassy_rp::init(Default::default()); |
| 33 | let mut led = Output::new(p.PIN_2, Level::Low); | 31 | let mut led = Output::new(p.PIN_25, Level::Low); |
| 34 | 32 | ||
| 35 | loop { | 33 | loop { |
| 36 | info!("led on!"); | 34 | info!("led on!"); |
diff --git a/examples/rp23/src/bin/blinky_two_channels.rs b/examples/rp235x/src/bin/blinky_two_channels.rs index 4d7dc89fa..51e139e94 100644 --- a/examples/rp23/src/bin/blinky_two_channels.rs +++ b/examples/rp235x/src/bin/blinky_two_channels.rs | |||
| @@ -7,28 +7,13 @@ | |||
| 7 | /// [Link explaining it](https://www.physicsclassroom.com/class/sound/Lesson-3/Interference-and-Beats) | 7 | /// [Link explaining it](https://www.physicsclassroom.com/class/sound/Lesson-3/Interference-and-Beats) |
| 8 | use defmt::*; | 8 | use defmt::*; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_rp::block::ImageDef; | ||
| 11 | use embassy_rp::gpio; | 10 | use embassy_rp::gpio; |
| 12 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | 11 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; |
| 13 | use embassy_sync::channel::{Channel, Sender}; | 12 | use embassy_sync::channel::{Channel, Sender}; |
| 14 | use embassy_time::{Duration, Ticker}; | 13 | use embassy_time::{Duration, Ticker}; |
| 15 | use gpio::{AnyPin, Level, Output}; | 14 | use gpio::{Level, Output}; |
| 16 | use {defmt_rtt as _, panic_probe as _}; | 15 | use {defmt_rtt as _, panic_probe as _}; |
| 17 | 16 | ||
| 18 | #[link_section = ".start_block"] | ||
| 19 | #[used] | ||
| 20 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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 { | 17 | enum LedState { |
| 33 | Toggle, | 18 | Toggle, |
| 34 | } | 19 | } |
| @@ -37,7 +22,7 @@ static CHANNEL: Channel<ThreadModeRawMutex, LedState, 64> = Channel::new(); | |||
| 37 | #[embassy_executor::main] | 22 | #[embassy_executor::main] |
| 38 | async fn main(spawner: Spawner) { | 23 | async fn main(spawner: Spawner) { |
| 39 | let p = embassy_rp::init(Default::default()); | 24 | let p = embassy_rp::init(Default::default()); |
| 40 | let mut led = Output::new(AnyPin::from(p.PIN_25), Level::High); | 25 | let mut led = Output::new(p.PIN_25, Level::High); |
| 41 | 26 | ||
| 42 | let dt = 100 * 1_000_000; | 27 | let dt = 100 * 1_000_000; |
| 43 | let k = 1.003; | 28 | let k = 1.003; |
diff --git a/examples/rp23/src/bin/blinky_two_tasks.rs b/examples/rp235x/src/bin/blinky_two_tasks.rs index 24b960242..67a9108c0 100644 --- a/examples/rp23/src/bin/blinky_two_tasks.rs +++ b/examples/rp235x/src/bin/blinky_two_tasks.rs | |||
| @@ -7,28 +7,13 @@ | |||
| 7 | /// [Link explaining it](https://www.physicsclassroom.com/class/sound/Lesson-3/Interference-and-Beats) | 7 | /// [Link explaining it](https://www.physicsclassroom.com/class/sound/Lesson-3/Interference-and-Beats) |
| 8 | use defmt::*; | 8 | use defmt::*; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_rp::block::ImageDef; | ||
| 11 | use embassy_rp::gpio; | 10 | use embassy_rp::gpio; |
| 12 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | 11 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; |
| 13 | use embassy_sync::mutex::Mutex; | 12 | use embassy_sync::mutex::Mutex; |
| 14 | use embassy_time::{Duration, Ticker}; | 13 | use embassy_time::{Duration, Ticker}; |
| 15 | use gpio::{AnyPin, Level, Output}; | 14 | use gpio::{Level, Output}; |
| 16 | use {defmt_rtt as _, panic_probe as _}; | 15 | use {defmt_rtt as _, panic_probe as _}; |
| 17 | 16 | ||
| 18 | #[link_section = ".start_block"] | ||
| 19 | #[used] | ||
| 20 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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>>>; | 17 | type LedType = Mutex<ThreadModeRawMutex, Option<Output<'static>>>; |
| 33 | static LED: LedType = Mutex::new(None); | 18 | static LED: LedType = Mutex::new(None); |
| 34 | 19 | ||
| @@ -36,7 +21,7 @@ static LED: LedType = Mutex::new(None); | |||
| 36 | async fn main(spawner: Spawner) { | 21 | async fn main(spawner: Spawner) { |
| 37 | let p = embassy_rp::init(Default::default()); | 22 | let p = embassy_rp::init(Default::default()); |
| 38 | // set the content of the global LED reference to the real LED pin | 23 | // set the content of the global LED reference to the real LED pin |
| 39 | let led = Output::new(AnyPin::from(p.PIN_25), Level::High); | 24 | let led = Output::new(p.PIN_25, Level::High); |
| 40 | // inner scope is so that once the mutex is written to, the MutexGuard is dropped, thus the | 25 | // inner scope is so that once the mutex is written to, the MutexGuard is dropped, thus the |
| 41 | // Mutex is released | 26 | // Mutex is released |
| 42 | { | 27 | { |
diff --git a/examples/rp235x/src/bin/blinky_wifi.rs b/examples/rp235x/src/bin/blinky_wifi.rs new file mode 100644 index 000000000..8c352ebc4 --- /dev/null +++ b/examples/rp235x/src/bin/blinky_wifi.rs | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | //! This example tests the RP Pico 2 W onboard LED. | ||
| 2 | //! | ||
| 3 | //! It does not work with the RP Pico 2 board. See `blinky.rs`. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | |||
| 8 | use cyw43_pio::{PioSpi, DEFAULT_CLOCK_DIVIDER}; | ||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_rp::bind_interrupts; | ||
| 12 | use embassy_rp::gpio::{Level, Output}; | ||
| 13 | use embassy_rp::peripherals::{DMA_CH0, PIO0}; | ||
| 14 | use embassy_rp::pio::{InterruptHandler, Pio}; | ||
| 15 | use embassy_time::{Duration, Timer}; | ||
| 16 | use static_cell::StaticCell; | ||
| 17 | use {defmt_rtt as _, panic_probe as _}; | ||
| 18 | |||
| 19 | // Program metadata for `picotool info`. | ||
| 20 | // This isn't needed, but it's recommended to have these minimal entries. | ||
| 21 | #[unsafe(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"Blinky Example"), | ||
| 25 | embassy_rp::binary_info::rp_program_description!( | ||
| 26 | c"This example tests the RP Pico 2 W's onboard LED, connected to GPIO 0 of the cyw43 \ | ||
| 27 | (WiFi chip) via PIO 0 over the SPI bus." | ||
| 28 | ), | ||
| 29 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 30 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 31 | ]; | ||
| 32 | |||
| 33 | bind_interrupts!(struct Irqs { | ||
| 34 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 35 | }); | ||
| 36 | |||
| 37 | #[embassy_executor::task] | ||
| 38 | async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { | ||
| 39 | runner.run().await | ||
| 40 | } | ||
| 41 | |||
| 42 | #[embassy_executor::main] | ||
| 43 | async fn main(spawner: Spawner) { | ||
| 44 | let p = embassy_rp::init(Default::default()); | ||
| 45 | let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin"); | ||
| 46 | let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin"); | ||
| 47 | |||
| 48 | // To make flashing faster for development, you may want to flash the firmwares independently | ||
| 49 | // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: | ||
| 50 | // probe-rs download ../../cyw43-firmware/43439A0.bin --binary-format bin --chip RP235x --base-address 0x10100000 | ||
| 51 | // probe-rs download ../../cyw43-firmware/43439A0_clm.bin --binary-format bin --chip RP235x --base-address 0x10140000 | ||
| 52 | //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) }; | ||
| 53 | //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; | ||
| 54 | |||
| 55 | let pwr = Output::new(p.PIN_23, Level::Low); | ||
| 56 | let cs = Output::new(p.PIN_25, Level::High); | ||
| 57 | let mut pio = Pio::new(p.PIO0, Irqs); | ||
| 58 | let spi = PioSpi::new( | ||
| 59 | &mut pio.common, | ||
| 60 | pio.sm0, | ||
| 61 | DEFAULT_CLOCK_DIVIDER, | ||
| 62 | pio.irq0, | ||
| 63 | cs, | ||
| 64 | p.PIN_24, | ||
| 65 | p.PIN_29, | ||
| 66 | p.DMA_CH0, | ||
| 67 | ); | ||
| 68 | |||
| 69 | static STATE: StaticCell<cyw43::State> = StaticCell::new(); | ||
| 70 | let state = STATE.init(cyw43::State::new()); | ||
| 71 | let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; | ||
| 72 | unwrap!(spawner.spawn(cyw43_task(runner))); | ||
| 73 | |||
| 74 | control.init(clm).await; | ||
| 75 | control | ||
| 76 | .set_power_management(cyw43::PowerManagementMode::PowerSave) | ||
| 77 | .await; | ||
| 78 | |||
| 79 | let delay = Duration::from_millis(250); | ||
| 80 | loop { | ||
| 81 | info!("led on!"); | ||
| 82 | control.gpio_set(0, true).await; | ||
| 83 | Timer::after(delay).await; | ||
| 84 | |||
| 85 | info!("led off!"); | ||
| 86 | control.gpio_set(0, false).await; | ||
| 87 | Timer::after(delay).await; | ||
| 88 | } | ||
| 89 | } | ||
diff --git a/examples/rp235x/src/bin/blinky_wifi_pico_plus_2.rs b/examples/rp235x/src/bin/blinky_wifi_pico_plus_2.rs new file mode 100644 index 000000000..0a5bccfb3 --- /dev/null +++ b/examples/rp235x/src/bin/blinky_wifi_pico_plus_2.rs | |||
| @@ -0,0 +1,88 @@ | |||
| 1 | //! This example test the Pimoroni Pico Plus 2 on board LED. | ||
| 2 | //! | ||
| 3 | //! It does not work with the RP Pico 2 board. See `blinky.rs`. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | |||
| 8 | use cyw43_pio::{PioSpi, RM2_CLOCK_DIVIDER}; | ||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_rp::peripherals::{DMA_CH0, PIO0}; | ||
| 12 | use embassy_rp::pio::{InterruptHandler, Pio}; | ||
| 13 | use embassy_rp::{bind_interrupts, gpio}; | ||
| 14 | use embassy_time::{Duration, Timer}; | ||
| 15 | use gpio::{Level, Output}; | ||
| 16 | use static_cell::StaticCell; | ||
| 17 | use {defmt_rtt as _, panic_probe as _}; | ||
| 18 | |||
| 19 | // Program metadata for `picotool info`. | ||
| 20 | // This isn't needed, but it's recomended to have these minimal entries. | ||
| 21 | #[unsafe(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"Blinky Example"), | ||
| 25 | embassy_rp::binary_info::rp_program_description!( | ||
| 26 | c"This example tests the RP Pico on board LED, connected to gpio 25" | ||
| 27 | ), | ||
| 28 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 29 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 30 | ]; | ||
| 31 | |||
| 32 | bind_interrupts!(struct Irqs { | ||
| 33 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 34 | }); | ||
| 35 | |||
| 36 | #[embassy_executor::task] | ||
| 37 | async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { | ||
| 38 | runner.run().await | ||
| 39 | } | ||
| 40 | |||
| 41 | #[embassy_executor::main] | ||
| 42 | async fn main(spawner: Spawner) { | ||
| 43 | let p = embassy_rp::init(Default::default()); | ||
| 44 | let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin"); | ||
| 45 | let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin"); | ||
| 46 | |||
| 47 | // To make flashing faster for development, you may want to flash the firmwares independently | ||
| 48 | // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: | ||
| 49 | // probe-rs download ../../cyw43-firmware/43439A0.bin --binary-format bin --chip RP235x --base-address 0x10100000 | ||
| 50 | // probe-rs download ../../cyw43-firmware/43439A0_clm.bin --binary-format bin --chip RP235x --base-address 0x10140000 | ||
| 51 | //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) }; | ||
| 52 | //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; | ||
| 53 | |||
| 54 | let pwr = Output::new(p.PIN_23, Level::Low); | ||
| 55 | let cs = Output::new(p.PIN_25, Level::High); | ||
| 56 | let mut pio = Pio::new(p.PIO0, Irqs); | ||
| 57 | let spi = PioSpi::new( | ||
| 58 | &mut pio.common, | ||
| 59 | pio.sm0, | ||
| 60 | RM2_CLOCK_DIVIDER, | ||
| 61 | pio.irq0, | ||
| 62 | cs, | ||
| 63 | p.PIN_24, | ||
| 64 | p.PIN_29, | ||
| 65 | p.DMA_CH0, | ||
| 66 | ); | ||
| 67 | |||
| 68 | static STATE: StaticCell<cyw43::State> = StaticCell::new(); | ||
| 69 | let state = STATE.init(cyw43::State::new()); | ||
| 70 | let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; | ||
| 71 | unwrap!(spawner.spawn(cyw43_task(runner))); | ||
| 72 | |||
| 73 | control.init(clm).await; | ||
| 74 | control | ||
| 75 | .set_power_management(cyw43::PowerManagementMode::PowerSave) | ||
| 76 | .await; | ||
| 77 | |||
| 78 | let delay = Duration::from_secs(1); | ||
| 79 | loop { | ||
| 80 | info!("led on!"); | ||
| 81 | control.gpio_set(0, true).await; | ||
| 82 | Timer::after(delay).await; | ||
| 83 | |||
| 84 | info!("led off!"); | ||
| 85 | control.gpio_set(0, false).await; | ||
| 86 | Timer::after(delay).await; | ||
| 87 | } | ||
| 88 | } | ||
diff --git a/examples/rp23/src/bin/button.rs b/examples/rp235x/src/bin/button.rs index 0a0559397..4ad2ca3b7 100644 --- a/examples/rp23/src/bin/button.rs +++ b/examples/rp235x/src/bin/button.rs | |||
| @@ -6,24 +6,9 @@ | |||
| 6 | #![no_main] | 6 | #![no_main] |
| 7 | 7 | ||
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_rp::block::ImageDef; | ||
| 10 | use embassy_rp::gpio::{Input, Level, Output, Pull}; | 9 | use embassy_rp::gpio::{Input, Level, Output, Pull}; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 11 | ||
| 13 | #[link_section = ".start_block"] | ||
| 14 | #[used] | ||
| 15 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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] | 12 | #[embassy_executor::main] |
| 28 | async fn main(_spawner: Spawner) { | 13 | async fn main(_spawner: Spawner) { |
| 29 | let p = embassy_rp::init(Default::default()); | 14 | let p = embassy_rp::init(Default::default()); |
diff --git a/examples/rp23/src/bin/debounce.rs b/examples/rp235x/src/bin/debounce.rs index e82e71f61..0077f19fc 100644 --- a/examples/rp23/src/bin/debounce.rs +++ b/examples/rp235x/src/bin/debounce.rs | |||
| @@ -6,25 +6,10 @@ | |||
| 6 | 6 | ||
| 7 | use defmt::info; | 7 | use defmt::info; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_rp::block::ImageDef; | ||
| 10 | use embassy_rp::gpio::{Input, Level, Pull}; | 9 | use embassy_rp::gpio::{Input, Level, Pull}; |
| 11 | use embassy_time::{with_deadline, Duration, Instant, Timer}; | 10 | use embassy_time::{with_deadline, Duration, Instant, Timer}; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 12 | ||
| 14 | #[link_section = ".start_block"] | ||
| 15 | #[used] | ||
| 16 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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> { | 13 | pub struct Debouncer<'a> { |
| 29 | input: Input<'a>, | 14 | input: Input<'a>, |
| 30 | debounce: Duration, | 15 | debounce: Duration, |
diff --git a/examples/rp23/src/bin/flash.rs b/examples/rp235x/src/bin/flash.rs index 2917dda0b..31ad4aafc 100644 --- a/examples/rp23/src/bin/flash.rs +++ b/examples/rp235x/src/bin/flash.rs | |||
| @@ -1,30 +1,15 @@ | |||
| 1 | //! This example test the flash connected to the RP2040 chip. | 1 | //! This example test the flash connected to the RP2350 chip. |
| 2 | 2 | ||
| 3 | #![no_std] | 3 | #![no_std] |
| 4 | #![no_main] | 4 | #![no_main] |
| 5 | 5 | ||
| 6 | use defmt::*; | 6 | use defmt::*; |
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 8 | use embassy_rp::block::ImageDef; | ||
| 9 | use embassy_rp::flash::{Async, ERASE_SIZE, FLASH_BASE}; | 8 | use embassy_rp::flash::{Async, ERASE_SIZE, FLASH_BASE}; |
| 10 | use embassy_rp::peripherals::FLASH; | 9 | use embassy_rp::peripherals::FLASH; |
| 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 _}; |
| 13 | 12 | ||
| 14 | #[link_section = ".start_block"] | ||
| 15 | #[used] | ||
| 16 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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 | const ADDR_OFFSET: u32 = 0x100000; | 13 | const ADDR_OFFSET: u32 = 0x100000; |
| 29 | const FLASH_SIZE: usize = 2 * 1024 * 1024; | 14 | const FLASH_SIZE: usize = 2 * 1024 * 1024; |
| 30 | 15 | ||
| @@ -41,22 +26,13 @@ async fn main(_spawner: Spawner) { | |||
| 41 | 26 | ||
| 42 | let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH0); | 27 | let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH0); |
| 43 | 28 | ||
| 44 | // Get JEDEC id | ||
| 45 | let jedec = flash.blocking_jedec_id().unwrap(); | ||
| 46 | info!("jedec id: 0x{:x}", jedec); | ||
| 47 | |||
| 48 | // Get unique id | ||
| 49 | let mut uid = [0; 8]; | ||
| 50 | flash.blocking_unique_id(&mut uid).unwrap(); | ||
| 51 | info!("unique id: {:?}", uid); | ||
| 52 | |||
| 53 | erase_write_sector(&mut flash, 0x00); | 29 | erase_write_sector(&mut flash, 0x00); |
| 54 | 30 | ||
| 55 | multiwrite_bytes(&mut flash, ERASE_SIZE as u32); | 31 | multiwrite_bytes(&mut flash, ERASE_SIZE as u32); |
| 56 | 32 | ||
| 57 | background_read(&mut flash, (ERASE_SIZE * 2) as u32).await; | 33 | background_read(&mut flash, (ERASE_SIZE * 2) as u32).await; |
| 58 | 34 | ||
| 59 | loop {} | 35 | info!("Flash Works!"); |
| 60 | } | 36 | } |
| 61 | 37 | ||
| 62 | fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) { | 38 | fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) { |
| @@ -82,7 +58,7 @@ fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH | |||
| 82 | 58 | ||
| 83 | defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf)); | 59 | defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf)); |
| 84 | info!("Contents after write starts with {=[u8]}", read_buf[0..4]); | 60 | info!("Contents after write starts with {=[u8]}", read_buf[0..4]); |
| 85 | if &read_buf[0..4] != &[0x01, 0x02, 0x03, 0x04] { | 61 | if read_buf[0..4] != [0x01, 0x02, 0x03, 0x04] { |
| 86 | defmt::panic!("unexpected"); | 62 | defmt::panic!("unexpected"); |
| 87 | } | 63 | } |
| 88 | } | 64 | } |
diff --git a/examples/rp23/src/bin/gpio_async.rs b/examples/rp235x/src/bin/gpio_async.rs index 1618f7c8b..3be8569bc 100644 --- a/examples/rp23/src/bin/gpio_async.rs +++ b/examples/rp235x/src/bin/gpio_async.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | //! This example shows how async gpio can be used with a RP2040. | 1 | //! This example shows how async gpio can be used with a RP235x. |
| 2 | //! | 2 | //! |
| 3 | //! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs. | 3 | //! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs. |
| 4 | 4 | ||
| @@ -7,26 +7,11 @@ | |||
| 7 | 7 | ||
| 8 | use defmt::*; | 8 | use defmt::*; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_rp::block::ImageDef; | ||
| 11 | use embassy_rp::gpio; | 10 | use embassy_rp::gpio; |
| 12 | use embassy_time::Timer; | 11 | use embassy_time::Timer; |
| 13 | use gpio::{Input, Level, Output, Pull}; | 12 | use gpio::{Input, Level, Output, Pull}; |
| 14 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 15 | 14 | ||
| 16 | #[link_section = ".start_block"] | ||
| 17 | #[used] | ||
| 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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 | 15 | /// 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 | 16 | /// 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. | 17 | /// 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/rp235x/src/bin/gpout.rs index b15963f02..011359253 100644 --- a/examples/rp23/src/bin/gpout.rs +++ b/examples/rp235x/src/bin/gpout.rs | |||
| @@ -7,25 +7,10 @@ | |||
| 7 | 7 | ||
| 8 | use defmt::*; | 8 | use defmt::*; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_rp::block::ImageDef; | ||
| 11 | use embassy_rp::clocks; | 10 | use embassy_rp::clocks; |
| 12 | use embassy_time::Timer; | 11 | use embassy_time::Timer; |
| 13 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {defmt_rtt as _, panic_probe as _}; |
| 14 | 13 | ||
| 15 | #[link_section = ".start_block"] | ||
| 16 | #[used] | ||
| 17 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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] | 14 | #[embassy_executor::main] |
| 30 | async fn main(_spawner: Spawner) { | 15 | async fn main(_spawner: Spawner) { |
| 31 | let p = embassy_rp::init(Default::default()); | 16 | let p = embassy_rp::init(Default::default()); |
diff --git a/examples/rp23/src/bin/i2c_async.rs b/examples/rp235x/src/bin/i2c_async.rs index 2528fe1d2..e31cc894c 100644 --- a/examples/rp23/src/bin/i2c_async.rs +++ b/examples/rp235x/src/bin/i2c_async.rs | |||
| @@ -9,27 +9,12 @@ | |||
| 9 | use defmt::*; | 9 | use defmt::*; |
| 10 | use embassy_executor::Spawner; | 10 | use embassy_executor::Spawner; |
| 11 | use embassy_rp::bind_interrupts; | 11 | use embassy_rp::bind_interrupts; |
| 12 | use embassy_rp::block::ImageDef; | ||
| 13 | use embassy_rp::i2c::{self, Config, InterruptHandler}; | 12 | use embassy_rp::i2c::{self, Config, InterruptHandler}; |
| 14 | use embassy_rp::peripherals::I2C1; | 13 | use embassy_rp::peripherals::I2C1; |
| 15 | use embassy_time::Timer; | 14 | use embassy_time::Timer; |
| 16 | use embedded_hal_async::i2c::I2c; | 15 | use embedded_hal_async::i2c::I2c; |
| 17 | use {defmt_rtt as _, panic_probe as _}; | 16 | use {defmt_rtt as _, panic_probe as _}; |
| 18 | 17 | ||
| 19 | #[link_section = ".start_block"] | ||
| 20 | #[used] | ||
| 21 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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 { | 18 | bind_interrupts!(struct Irqs { |
| 34 | I2C1_IRQ => InterruptHandler<I2C1>; | 19 | I2C1_IRQ => InterruptHandler<I2C1>; |
| 35 | }); | 20 | }); |
diff --git a/examples/rp23/src/bin/i2c_async_embassy.rs b/examples/rp235x/src/bin/i2c_async_embassy.rs index 461b1d171..a65b71b9f 100644 --- a/examples/rp23/src/bin/i2c_async_embassy.rs +++ b/examples/rp235x/src/bin/i2c_async_embassy.rs | |||
| @@ -7,24 +7,9 @@ | |||
| 7 | #![no_main] | 7 | #![no_main] |
| 8 | 8 | ||
| 9 | use defmt::*; | 9 | use defmt::*; |
| 10 | use embassy_rp::block::ImageDef; | ||
| 11 | use embassy_rp::i2c::InterruptHandler; | 10 | use embassy_rp::i2c::InterruptHandler; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 12 | ||
| 14 | #[link_section = ".start_block"] | ||
| 15 | #[used] | ||
| 16 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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: | 13 | // 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 | 14 | // 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, | 15 | // It requires no configuration or calibration, works with all i2c bus speeds, |
diff --git a/examples/rp23/src/bin/i2c_blocking.rs b/examples/rp235x/src/bin/i2c_blocking.rs index 6d36d1890..c9c8a2760 100644 --- a/examples/rp23/src/bin/i2c_blocking.rs +++ b/examples/rp235x/src/bin/i2c_blocking.rs | |||
| @@ -8,26 +8,11 @@ | |||
| 8 | 8 | ||
| 9 | use defmt::*; | 9 | use defmt::*; |
| 10 | use embassy_executor::Spawner; | 10 | use embassy_executor::Spawner; |
| 11 | use embassy_rp::block::ImageDef; | ||
| 12 | use embassy_rp::i2c::{self, Config}; | 11 | use embassy_rp::i2c::{self, Config}; |
| 13 | use embassy_time::Timer; | 12 | use embassy_time::Timer; |
| 14 | use embedded_hal_1::i2c::I2c; | 13 | use embedded_hal_1::i2c::I2c; |
| 15 | use {defmt_rtt as _, panic_probe as _}; | 14 | use {defmt_rtt as _, panic_probe as _}; |
| 16 | 15 | ||
| 17 | #[link_section = ".start_block"] | ||
| 18 | #[used] | ||
| 19 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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)] | 16 | #[allow(dead_code)] |
| 32 | mod mcp23017 { | 17 | mod mcp23017 { |
| 33 | pub const ADDR: u8 = 0x20; // default addr | 18 | pub const ADDR: u8 = 0x20; // default addr |
diff --git a/examples/rp23/src/bin/i2c_slave.rs b/examples/rp235x/src/bin/i2c_slave.rs index 1f3408cf3..9fffb4646 100644 --- a/examples/rp23/src/bin/i2c_slave.rs +++ b/examples/rp235x/src/bin/i2c_slave.rs | |||
| @@ -4,27 +4,12 @@ | |||
| 4 | 4 | ||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_rp::block::ImageDef; | ||
| 8 | use embassy_rp::peripherals::{I2C0, I2C1}; | 7 | use embassy_rp::peripherals::{I2C0, I2C1}; |
| 9 | use embassy_rp::{bind_interrupts, i2c, i2c_slave}; | 8 | use embassy_rp::{bind_interrupts, i2c, i2c_slave}; |
| 10 | use embassy_time::Timer; | 9 | use embassy_time::Timer; |
| 11 | use embedded_hal_async::i2c::I2c; | 10 | use embedded_hal_async::i2c::I2c; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 12 | ||
| 14 | #[link_section = ".start_block"] | ||
| 15 | #[used] | ||
| 16 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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 { | 13 | bind_interrupts!(struct Irqs { |
| 29 | I2C0_IRQ => i2c::InterruptHandler<I2C0>; | 14 | I2C0_IRQ => i2c::InterruptHandler<I2C0>; |
| 30 | I2C1_IRQ => i2c::InterruptHandler<I2C1>; | 15 | I2C1_IRQ => i2c::InterruptHandler<I2C1>; |
diff --git a/examples/rp23/src/bin/interrupt.rs b/examples/rp235x/src/bin/interrupt.rs index 6184b1bd7..e9ac76486 100644 --- a/examples/rp23/src/bin/interrupt.rs +++ b/examples/rp235x/src/bin/interrupt.rs | |||
| @@ -13,7 +13,6 @@ use core::cell::{Cell, RefCell}; | |||
| 13 | use defmt::*; | 13 | use defmt::*; |
| 14 | use embassy_executor::Spawner; | 14 | use embassy_executor::Spawner; |
| 15 | use embassy_rp::adc::{self, Adc, Blocking}; | 15 | use embassy_rp::adc::{self, Adc, Blocking}; |
| 16 | use embassy_rp::block::ImageDef; | ||
| 17 | use embassy_rp::gpio::Pull; | 16 | use embassy_rp::gpio::Pull; |
| 18 | use embassy_rp::interrupt; | 17 | use embassy_rp::interrupt; |
| 19 | use embassy_rp::pwm::{Config, Pwm}; | 18 | use embassy_rp::pwm::{Config, Pwm}; |
| @@ -25,20 +24,6 @@ use portable_atomic::{AtomicU32, Ordering}; | |||
| 25 | use static_cell::StaticCell; | 24 | use static_cell::StaticCell; |
| 26 | use {defmt_rtt as _, panic_probe as _}; | 25 | use {defmt_rtt as _, panic_probe as _}; |
| 27 | 26 | ||
| 28 | #[link_section = ".start_block"] | ||
| 29 | #[used] | ||
| 30 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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); | 27 | static COUNTER: AtomicU32 = AtomicU32::new(0); |
| 43 | static PWM: Mutex<CriticalSectionRawMutex, RefCell<Option<Pwm>>> = Mutex::new(RefCell::new(None)); | 28 | static PWM: Mutex<CriticalSectionRawMutex, RefCell<Option<Pwm>>> = Mutex::new(RefCell::new(None)); |
| 44 | static ADC: Mutex<CriticalSectionRawMutex, RefCell<Option<(Adc<Blocking>, adc::Channel)>>> = | 29 | static ADC: Mutex<CriticalSectionRawMutex, RefCell<Option<(Adc<Blocking>, adc::Channel)>>> = |
| @@ -47,7 +32,6 @@ static ADC_VALUES: Channel<CriticalSectionRawMutex, u16, 2048> = Channel::new(); | |||
| 47 | 32 | ||
| 48 | #[embassy_executor::main] | 33 | #[embassy_executor::main] |
| 49 | async fn main(spawner: Spawner) { | 34 | async fn main(spawner: Spawner) { |
| 50 | embassy_rp::pac::SIO.spinlock(31).write_value(1); | ||
| 51 | let p = embassy_rp::init(Default::default()); | 35 | let p = embassy_rp::init(Default::default()); |
| 52 | 36 | ||
| 53 | let adc = Adc::new_blocking(p.ADC, Default::default()); | 37 | let adc = Adc::new_blocking(p.ADC, Default::default()); |
diff --git a/examples/rp23/src/bin/multicore.rs b/examples/rp235x/src/bin/multicore.rs index 8649143e1..f02dc3876 100644 --- a/examples/rp23/src/bin/multicore.rs +++ b/examples/rp235x/src/bin/multicore.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | //! This example shows how to send messages between the two cores in the RP2040 chip. | 1 | //! This example shows how to send messages between the two cores in the RP235x chip. |
| 2 | //! | 2 | //! |
| 3 | //! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs. | 3 | //! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs. |
| 4 | 4 | ||
| @@ -7,7 +7,6 @@ | |||
| 7 | 7 | ||
| 8 | use defmt::*; | 8 | use defmt::*; |
| 9 | use embassy_executor::Executor; | 9 | use embassy_executor::Executor; |
| 10 | use embassy_rp::block::ImageDef; | ||
| 11 | use embassy_rp::gpio::{Level, Output}; | 10 | use embassy_rp::gpio::{Level, Output}; |
| 12 | use embassy_rp::multicore::{spawn_core1, Stack}; | 11 | use embassy_rp::multicore::{spawn_core1, Stack}; |
| 13 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 12 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| @@ -16,20 +15,6 @@ use embassy_time::Timer; | |||
| 16 | use static_cell::StaticCell; | 15 | use static_cell::StaticCell; |
| 17 | use {defmt_rtt as _, panic_probe as _}; | 16 | use {defmt_rtt as _, panic_probe as _}; |
| 18 | 17 | ||
| 19 | #[link_section = ".start_block"] | ||
| 20 | #[used] | ||
| 21 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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(); | 18 | static mut CORE1_STACK: Stack<4096> = Stack::new(); |
| 34 | static EXECUTOR0: StaticCell<Executor> = StaticCell::new(); | 19 | static EXECUTOR0: StaticCell<Executor> = StaticCell::new(); |
| 35 | static EXECUTOR1: StaticCell<Executor> = StaticCell::new(); | 20 | static EXECUTOR1: StaticCell<Executor> = StaticCell::new(); |
diff --git a/examples/rp23/src/bin/multiprio.rs b/examples/rp235x/src/bin/multiprio.rs index 7590fb431..2b397f97d 100644 --- a/examples/rp23/src/bin/multiprio.rs +++ b/examples/rp235x/src/bin/multiprio.rs | |||
| @@ -59,27 +59,12 @@ | |||
| 59 | use cortex_m_rt::entry; | 59 | use cortex_m_rt::entry; |
| 60 | use defmt::{info, unwrap}; | 60 | use defmt::{info, unwrap}; |
| 61 | use embassy_executor::{Executor, InterruptExecutor}; | 61 | use embassy_executor::{Executor, InterruptExecutor}; |
| 62 | use embassy_rp::block::ImageDef; | ||
| 63 | use embassy_rp::interrupt; | 62 | use embassy_rp::interrupt; |
| 64 | use embassy_rp::interrupt::{InterruptExt, Priority}; | 63 | use embassy_rp::interrupt::{InterruptExt, Priority}; |
| 65 | use embassy_time::{Instant, Timer, TICK_HZ}; | 64 | use embassy_time::{Instant, Timer, TICK_HZ}; |
| 66 | use static_cell::StaticCell; | 65 | use static_cell::StaticCell; |
| 67 | use {defmt_rtt as _, panic_probe as _}; | 66 | use {defmt_rtt as _, panic_probe as _}; |
| 68 | 67 | ||
| 69 | #[link_section = ".start_block"] | ||
| 70 | #[used] | ||
| 71 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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] | 68 | #[embassy_executor::task] |
| 84 | async fn run_high() { | 69 | async fn run_high() { |
| 85 | loop { | 70 | loop { |
diff --git a/examples/rp235x/src/bin/otp.rs b/examples/rp235x/src/bin/otp.rs new file mode 100644 index 000000000..5ffbb7610 --- /dev/null +++ b/examples/rp235x/src/bin/otp.rs | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | //! This example shows reading the OTP constants on the RP235x. | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | |||
| 6 | use defmt::*; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_rp::otp; | ||
| 9 | use embassy_time::Timer; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_spawner: Spawner) { | ||
| 14 | let _ = embassy_rp::init(Default::default()); | ||
| 15 | // | ||
| 16 | // add some delay to give an attached debug probe time to parse the | ||
| 17 | // defmt RTT header. Reading that header might touch flash memory, which | ||
| 18 | // interferes with flash write operations. | ||
| 19 | // https://github.com/knurling-rs/defmt/pull/683 | ||
| 20 | Timer::after_millis(10).await; | ||
| 21 | |||
| 22 | let chip_id = unwrap!(otp::get_chipid()); | ||
| 23 | info!("Unique id:{:X}", chip_id); | ||
| 24 | |||
| 25 | let private_rand = unwrap!(otp::get_private_random_number()); | ||
| 26 | info!("Private Rand:{:X}", private_rand); | ||
| 27 | |||
| 28 | loop { | ||
| 29 | Timer::after_secs(1).await; | ||
| 30 | } | ||
| 31 | } | ||
diff --git a/examples/rp235x/src/bin/overclock.rs b/examples/rp235x/src/bin/overclock.rs new file mode 100644 index 000000000..5fd97ef97 --- /dev/null +++ b/examples/rp235x/src/bin/overclock.rs | |||
| @@ -0,0 +1,74 @@ | |||
| 1 | //! # Overclocking the RP2350 to 200 MHz | ||
| 2 | //! | ||
| 3 | //! This example demonstrates how to configure the RP2350 to run at 200 MHz instead of the default 150 MHz. | ||
| 4 | //! | ||
| 5 | //! ## Note | ||
| 6 | //! | ||
| 7 | //! As of yet there is no official support for running the RP235x at higher clock frequencies and/or other core voltages than the default. | ||
| 8 | //! Doing so may cause unexpected behavior and/or damage the chip. | ||
| 9 | |||
| 10 | #![no_std] | ||
| 11 | #![no_main] | ||
| 12 | |||
| 13 | use defmt::*; | ||
| 14 | use embassy_executor::Spawner; | ||
| 15 | use embassy_rp::clocks::{clk_sys_freq, core_voltage, ClockConfig, CoreVoltage}; | ||
| 16 | use embassy_rp::config::Config; | ||
| 17 | use embassy_rp::gpio::{Level, Output}; | ||
| 18 | use embassy_time::{Duration, Instant, Timer}; | ||
| 19 | use {defmt_rtt as _, panic_probe as _}; | ||
| 20 | |||
| 21 | const COUNT_TO: i64 = 10_000_000; | ||
| 22 | |||
| 23 | #[embassy_executor::main] | ||
| 24 | async fn main(_spawner: Spawner) -> ! { | ||
| 25 | // Set up for clock frequency of 200 MHz, setting all necessary defaults. | ||
| 26 | let mut config = Config::new(ClockConfig::system_freq(200_000_000).unwrap()); | ||
| 27 | |||
| 28 | // since for the rp235x there is no official support for higher clock frequencies, `system_freq()` will not set a voltage for us. | ||
| 29 | // We need to guess the core voltage, that is needed for the higher clock frequency. Going with a small increase from the default 1.1V here, based on | ||
| 30 | // what we know about the RP2040. This is not guaranteed to be correct. | ||
| 31 | config.clocks.core_voltage = CoreVoltage::V1_15; | ||
| 32 | |||
| 33 | // Initialize the peripherals | ||
| 34 | let p = embassy_rp::init(config); | ||
| 35 | |||
| 36 | // Show CPU frequency for verification | ||
| 37 | let sys_freq = clk_sys_freq(); | ||
| 38 | info!("System clock frequency: {} MHz", sys_freq / 1_000_000); | ||
| 39 | // Show core voltage for verification | ||
| 40 | let core_voltage = core_voltage().unwrap(); | ||
| 41 | info!("Core voltage: {}", core_voltage); | ||
| 42 | |||
| 43 | // LED to indicate the system is running | ||
| 44 | let mut led = Output::new(p.PIN_25, Level::Low); | ||
| 45 | |||
| 46 | loop { | ||
| 47 | // Reset the counter at the start of measurement period | ||
| 48 | let mut counter = 0; | ||
| 49 | |||
| 50 | // Turn LED on while counting | ||
| 51 | led.set_high(); | ||
| 52 | |||
| 53 | let start = Instant::now(); | ||
| 54 | |||
| 55 | // This is a busy loop that will take some time to complete | ||
| 56 | while counter < COUNT_TO { | ||
| 57 | counter += 1; | ||
| 58 | } | ||
| 59 | |||
| 60 | let elapsed = Instant::now() - start; | ||
| 61 | |||
| 62 | // Report the elapsed time | ||
| 63 | led.set_low(); | ||
| 64 | info!( | ||
| 65 | "At {}Mhz: Elapsed time to count to {}: {}ms", | ||
| 66 | sys_freq / 1_000_000, | ||
| 67 | counter, | ||
| 68 | elapsed.as_millis() | ||
| 69 | ); | ||
| 70 | |||
| 71 | // Wait 2 seconds before starting the next measurement | ||
| 72 | Timer::after(Duration::from_secs(2)).await; | ||
| 73 | } | ||
| 74 | } | ||
diff --git a/examples/rp23/src/bin/pio_async.rs b/examples/rp235x/src/bin/pio_async.rs index 005708bc2..a519b8a50 100644 --- a/examples/rp23/src/bin/pio_async.rs +++ b/examples/rp235x/src/bin/pio_async.rs | |||
| @@ -1,40 +1,26 @@ | |||
| 1 | //! This example shows powerful PIO module in the RP2040 chip. | 1 | //! This example shows powerful PIO module in the RP235x chip. |
| 2 | 2 | ||
| 3 | #![no_std] | 3 | #![no_std] |
| 4 | #![no_main] | 4 | #![no_main] |
| 5 | use defmt::info; | 5 | use defmt::info; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_rp::bind_interrupts; | ||
| 8 | use embassy_rp::block::ImageDef; | ||
| 9 | use embassy_rp::peripherals::PIO0; | 7 | use embassy_rp::peripherals::PIO0; |
| 8 | use embassy_rp::pio::program::pio_asm; | ||
| 10 | use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine}; | 9 | use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine}; |
| 10 | use embassy_rp::{bind_interrupts, Peri}; | ||
| 11 | use fixed::traits::ToFixed; | 11 | use fixed::traits::ToFixed; |
| 12 | use fixed_macro::types::U56F8; | 12 | use fixed_macro::types::U56F8; |
| 13 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 14 | 14 | ||
| 15 | #[link_section = ".start_block"] | ||
| 16 | #[used] | ||
| 17 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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 { | 15 | bind_interrupts!(struct Irqs { |
| 30 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | 16 | PIO0_IRQ_0 => InterruptHandler<PIO0>; |
| 31 | }); | 17 | }); |
| 32 | 18 | ||
| 33 | fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 0>, pin: impl PioPin) { | 19 | fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 0>, pin: Peri<'a, impl PioPin>) { |
| 34 | // Setup sm0 | 20 | // Setup sm0 |
| 35 | 21 | ||
| 36 | // Send data serially to pin | 22 | // Send data serially to pin |
| 37 | let prg = pio_proc::pio_asm!( | 23 | let prg = pio_asm!( |
| 38 | ".origin 16", | 24 | ".origin 16", |
| 39 | "set pindirs, 1", | 25 | "set pindirs, 1", |
| 40 | ".wrap_target", | 26 | ".wrap_target", |
| @@ -68,7 +54,7 @@ fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, | |||
| 68 | // Setupm sm1 | 54 | // Setupm sm1 |
| 69 | 55 | ||
| 70 | // Read 0b10101 repeatedly until ISR is full | 56 | // Read 0b10101 repeatedly until ISR is full |
| 71 | let prg = pio_proc::pio_asm!( | 57 | let prg = pio_asm!( |
| 72 | // | 58 | // |
| 73 | ".origin 8", | 59 | ".origin 8", |
| 74 | "set x, 0x15", | 60 | "set x, 0x15", |
| @@ -98,7 +84,7 @@ fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, | |||
| 98 | // Setup sm2 | 84 | // Setup sm2 |
| 99 | 85 | ||
| 100 | // Repeatedly trigger IRQ 3 | 86 | // Repeatedly trigger IRQ 3 |
| 101 | let prg = pio_proc::pio_asm!( | 87 | let prg = pio_asm!( |
| 102 | ".origin 0", | 88 | ".origin 0", |
| 103 | ".wrap_target", | 89 | ".wrap_target", |
| 104 | "set x,10", | 90 | "set x,10", |
diff --git a/examples/rp23/src/bin/pio_dma.rs b/examples/rp235x/src/bin/pio_dma.rs index 48fd9123f..17332a238 100644 --- a/examples/rp23/src/bin/pio_dma.rs +++ b/examples/rp235x/src/bin/pio_dma.rs | |||
| @@ -1,32 +1,18 @@ | |||
| 1 | //! This example shows powerful PIO module in the RP2040 chip. | 1 | //! This example shows powerful PIO module in the RP235x chip. |
| 2 | 2 | ||
| 3 | #![no_std] | 3 | #![no_std] |
| 4 | #![no_main] | 4 | #![no_main] |
| 5 | use defmt::info; | 5 | use defmt::info; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_futures::join::join; | 7 | use embassy_futures::join::join; |
| 8 | use embassy_rp::block::ImageDef; | 8 | use embassy_rp::bind_interrupts; |
| 9 | use embassy_rp::peripherals::PIO0; | 9 | use embassy_rp::peripherals::PIO0; |
| 10 | use embassy_rp::pio::program::pio_asm; | ||
| 10 | use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; | 11 | use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; |
| 11 | use embassy_rp::{bind_interrupts, Peripheral}; | ||
| 12 | use fixed::traits::ToFixed; | 12 | use fixed::traits::ToFixed; |
| 13 | use fixed_macro::types::U56F8; | 13 | use fixed_macro::types::U56F8; |
| 14 | use {defmt_rtt as _, panic_probe as _}; | 14 | use {defmt_rtt as _, panic_probe as _}; |
| 15 | 15 | ||
| 16 | #[link_section = ".start_block"] | ||
| 17 | #[used] | ||
| 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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 { | 16 | bind_interrupts!(struct Irqs { |
| 31 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | 17 | PIO0_IRQ_0 => InterruptHandler<PIO0>; |
| 32 | }); | 18 | }); |
| @@ -47,7 +33,7 @@ async fn main(_spawner: Spawner) { | |||
| 47 | .. | 33 | .. |
| 48 | } = Pio::new(pio, Irqs); | 34 | } = Pio::new(pio, Irqs); |
| 49 | 35 | ||
| 50 | let prg = pio_proc::pio_asm!( | 36 | let prg = pio_asm!( |
| 51 | ".origin 0", | 37 | ".origin 0", |
| 52 | "set pindirs,1", | 38 | "set pindirs,1", |
| 53 | ".wrap_target", | 39 | ".wrap_target", |
| @@ -76,8 +62,8 @@ async fn main(_spawner: Spawner) { | |||
| 76 | sm.set_config(&cfg); | 62 | sm.set_config(&cfg); |
| 77 | sm.set_enable(true); | 63 | sm.set_enable(true); |
| 78 | 64 | ||
| 79 | let mut dma_out_ref = p.DMA_CH0.into_ref(); | 65 | let mut dma_out_ref = p.DMA_CH0; |
| 80 | let mut dma_in_ref = p.DMA_CH1.into_ref(); | 66 | let mut dma_in_ref = p.DMA_CH1; |
| 81 | let mut dout = [0x12345678u32; 29]; | 67 | let mut dout = [0x12345678u32; 29]; |
| 82 | for i in 1..dout.len() { | 68 | for i in 1..dout.len() { |
| 83 | dout[i] = (dout[i - 1] & 0x0fff_ffff) * 13 + 7; | 69 | dout[i] = (dout[i - 1] & 0x0fff_ffff) * 13 + 7; |
| @@ -86,8 +72,8 @@ async fn main(_spawner: Spawner) { | |||
| 86 | loop { | 72 | loop { |
| 87 | let (rx, tx) = sm.rx_tx(); | 73 | let (rx, tx) = sm.rx_tx(); |
| 88 | join( | 74 | join( |
| 89 | tx.dma_push(dma_out_ref.reborrow(), &dout), | 75 | tx.dma_push(dma_out_ref.reborrow(), &dout, false), |
| 90 | rx.dma_pull(dma_in_ref.reborrow(), &mut din), | 76 | rx.dma_pull(dma_in_ref.reborrow(), &mut din, false), |
| 91 | ) | 77 | ) |
| 92 | .await; | 78 | .await; |
| 93 | for i in 0..din.len() { | 79 | for i in 0..din.len() { |
diff --git a/examples/rp235x/src/bin/pio_hd44780.rs b/examples/rp235x/src/bin/pio_hd44780.rs new file mode 100644 index 000000000..06d989505 --- /dev/null +++ b/examples/rp235x/src/bin/pio_hd44780.rs | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | //! This example shows powerful PIO module in the RP235x chip to communicate with a HD44780 display. | ||
| 2 | //! See (https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | |||
| 7 | use core::fmt::Write; | ||
| 8 | |||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_rp::bind_interrupts; | ||
| 11 | use embassy_rp::peripherals::PIO0; | ||
| 12 | use embassy_rp::pio::{InterruptHandler, Pio}; | ||
| 13 | use embassy_rp::pio_programs::hd44780::{PioHD44780, PioHD44780CommandSequenceProgram, PioHD44780CommandWordProgram}; | ||
| 14 | use embassy_rp::pwm::{self, Pwm}; | ||
| 15 | use embassy_time::{Instant, Timer}; | ||
| 16 | use {defmt_rtt as _, panic_probe as _}; | ||
| 17 | |||
| 18 | bind_interrupts!(pub struct Irqs { | ||
| 19 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 20 | }); | ||
| 21 | |||
| 22 | #[embassy_executor::main] | ||
| 23 | async fn main(_spawner: Spawner) { | ||
| 24 | // this test assumes a 2x16 HD44780 display attached as follow: | ||
| 25 | // rs = PIN0 | ||
| 26 | // rw = PIN1 | ||
| 27 | // e = PIN2 | ||
| 28 | // db4 = PIN3 | ||
| 29 | // db5 = PIN4 | ||
| 30 | // db6 = PIN5 | ||
| 31 | // db7 = PIN6 | ||
| 32 | // additionally a pwm signal for a bias voltage charge pump is provided on pin 15, | ||
| 33 | // allowing direct connection of the display to the RP235x without level shifters. | ||
| 34 | let p = embassy_rp::init(Default::default()); | ||
| 35 | |||
| 36 | let _pwm = Pwm::new_output_b(p.PWM_SLICE7, p.PIN_15, { | ||
| 37 | let mut c = pwm::Config::default(); | ||
| 38 | c.divider = 125.into(); | ||
| 39 | c.top = 100; | ||
| 40 | c.compare_b = 50; | ||
| 41 | c | ||
| 42 | }); | ||
| 43 | |||
| 44 | let Pio { | ||
| 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, | ||
| 65 | ) | ||
| 66 | .await; | ||
| 67 | |||
| 68 | loop { | ||
| 69 | struct Buf<const N: usize>([u8; N], usize); | ||
| 70 | impl<const N: usize> Write for Buf<N> { | ||
| 71 | fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { | ||
| 72 | for b in s.as_bytes() { | ||
| 73 | if self.1 >= N { | ||
| 74 | return Err(core::fmt::Error); | ||
| 75 | } | ||
| 76 | self.0[self.1] = *b; | ||
| 77 | self.1 += 1; | ||
| 78 | } | ||
| 79 | Ok(()) | ||
| 80 | } | ||
| 81 | } | ||
| 82 | let mut buf = Buf([0; 16], 0); | ||
| 83 | write!(buf, "up {}s", Instant::now().as_micros() as f32 / 1e6).unwrap(); | ||
| 84 | hd.add_line(&buf.0[0..buf.1]).await; | ||
| 85 | Timer::after_secs(1).await; | ||
| 86 | } | ||
| 87 | } | ||
diff --git a/examples/rp235x/src/bin/pio_i2s.rs b/examples/rp235x/src/bin/pio_i2s.rs new file mode 100644 index 000000000..cfcb0221d --- /dev/null +++ b/examples/rp235x/src/bin/pio_i2s.rs | |||
| @@ -0,0 +1,93 @@ | |||
| 1 | //! This example shows generating audio and sending it to a connected i2s DAC using the PIO | ||
| 2 | //! module of the RP235x. | ||
| 3 | //! | ||
| 4 | //! Connect the i2s DAC as follows: | ||
| 5 | //! bclk : GPIO 18 | ||
| 6 | //! lrc : GPIO 19 | ||
| 7 | //! din : GPIO 20 | ||
| 8 | //! Then hold down the boot select button to trigger a rising triangle waveform. | ||
| 9 | |||
| 10 | #![no_std] | ||
| 11 | #![no_main] | ||
| 12 | |||
| 13 | use core::mem; | ||
| 14 | |||
| 15 | use embassy_executor::Spawner; | ||
| 16 | use embassy_rp::bind_interrupts; | ||
| 17 | use embassy_rp::gpio::{Input, Pull}; | ||
| 18 | use embassy_rp::peripherals::PIO0; | ||
| 19 | use embassy_rp::pio::{InterruptHandler, Pio}; | ||
| 20 | use embassy_rp::pio_programs::i2s::{PioI2sOut, PioI2sOutProgram}; | ||
| 21 | use static_cell::StaticCell; | ||
| 22 | use {defmt_rtt as _, panic_probe as _}; | ||
| 23 | |||
| 24 | bind_interrupts!(struct Irqs { | ||
| 25 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 26 | }); | ||
| 27 | |||
| 28 | const SAMPLE_RATE: u32 = 48_000; | ||
| 29 | const BIT_DEPTH: u32 = 16; | ||
| 30 | |||
| 31 | #[embassy_executor::main] | ||
| 32 | async fn main(_spawner: Spawner) { | ||
| 33 | let p = embassy_rp::init(Default::default()); | ||
| 34 | |||
| 35 | // Setup pio state machine for i2s output | ||
| 36 | let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); | ||
| 37 | |||
| 38 | let bit_clock_pin = p.PIN_18; | ||
| 39 | let left_right_clock_pin = p.PIN_19; | ||
| 40 | let data_pin = p.PIN_20; | ||
| 41 | |||
| 42 | let program = PioI2sOutProgram::new(&mut common); | ||
| 43 | let mut i2s = PioI2sOut::new( | ||
| 44 | &mut common, | ||
| 45 | sm0, | ||
| 46 | p.DMA_CH0, | ||
| 47 | data_pin, | ||
| 48 | bit_clock_pin, | ||
| 49 | left_right_clock_pin, | ||
| 50 | SAMPLE_RATE, | ||
| 51 | BIT_DEPTH, | ||
| 52 | &program, | ||
| 53 | ); | ||
| 54 | |||
| 55 | let fade_input = Input::new(p.PIN_0, Pull::Up); | ||
| 56 | |||
| 57 | // create two audio buffers (back and front) which will take turns being | ||
| 58 | // filled with new audio data and being sent to the pio fifo using dma | ||
| 59 | const BUFFER_SIZE: usize = 960; | ||
| 60 | static DMA_BUFFER: StaticCell<[u32; BUFFER_SIZE * 2]> = StaticCell::new(); | ||
| 61 | let dma_buffer = DMA_BUFFER.init_with(|| [0u32; BUFFER_SIZE * 2]); | ||
| 62 | let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE); | ||
| 63 | |||
| 64 | // start pio state machine | ||
| 65 | let mut fade_value: i32 = 0; | ||
| 66 | let mut phase: i32 = 0; | ||
| 67 | |||
| 68 | loop { | ||
| 69 | // trigger transfer of front buffer data to the pio fifo | ||
| 70 | // but don't await the returned future, yet | ||
| 71 | let dma_future = i2s.write(front_buffer); | ||
| 72 | |||
| 73 | // fade in audio when bootsel is pressed | ||
| 74 | let fade_target = if fade_input.is_low() { i32::MAX } else { 0 }; | ||
| 75 | |||
| 76 | // fill back buffer with fresh audio samples before awaiting the dma future | ||
| 77 | for s in back_buffer.iter_mut() { | ||
| 78 | // exponential approach of fade_value => fade_target | ||
| 79 | fade_value += (fade_target - fade_value) >> 14; | ||
| 80 | // generate triangle wave with amplitude and frequency based on fade value | ||
| 81 | phase = (phase + (fade_value >> 22)) & 0xffff; | ||
| 82 | let triangle_sample = (phase as i16 as i32).abs() - 16384; | ||
| 83 | let sample = (triangle_sample * (fade_value >> 15)) >> 16; | ||
| 84 | // duplicate mono sample into lower and upper half of dma word | ||
| 85 | *s = (sample as u16 as u32) * 0x10001; | ||
| 86 | } | ||
| 87 | |||
| 88 | // now await the dma future. once the dma finishes, the next buffer needs to be queued | ||
| 89 | // within DMA_DEPTH / SAMPLE_RATE = 8 / 48000 seconds = 166us | ||
| 90 | dma_future.await; | ||
| 91 | mem::swap(&mut back_buffer, &mut front_buffer); | ||
| 92 | } | ||
| 93 | } | ||
diff --git a/examples/rp235x/src/bin/pio_onewire.rs b/examples/rp235x/src/bin/pio_onewire.rs new file mode 100644 index 000000000..991510851 --- /dev/null +++ b/examples/rp235x/src/bin/pio_onewire.rs | |||
| @@ -0,0 +1,83 @@ | |||
| 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::peripherals::PIO0; | ||
| 9 | use embassy_rp::pio::{self, InterruptHandler, Pio}; | ||
| 10 | use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram}; | ||
| 11 | use embassy_time::Timer; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | bind_interrupts!(struct Irqs { | ||
| 15 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 16 | }); | ||
| 17 | |||
| 18 | #[embassy_executor::main] | ||
| 19 | async fn main(_spawner: Spawner) { | ||
| 20 | let p = embassy_rp::init(Default::default()); | ||
| 21 | let mut pio = Pio::new(p.PIO0, Irqs); | ||
| 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); | ||
| 27 | |||
| 28 | loop { | ||
| 29 | sensor.start().await; // Start a new measurement | ||
| 30 | Timer::after_secs(1).await; // Allow 1s for the measurement to finish | ||
| 31 | match sensor.temperature().await { | ||
| 32 | Ok(temp) => info!("temp = {:?} deg C", temp), | ||
| 33 | _ => error!("sensor error"), | ||
| 34 | } | ||
| 35 | Timer::after_secs(1).await; | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | /// DS18B20 temperature sensor driver | ||
| 40 | pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> { | ||
| 41 | wire: PioOneWire<'d, PIO, SM>, | ||
| 42 | } | ||
| 43 | |||
| 44 | impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> { | ||
| 45 | pub fn new(wire: PioOneWire<'d, PIO, SM>) -> Self { | ||
| 46 | Self { wire } | ||
| 47 | } | ||
| 48 | |||
| 49 | /// Calculate CRC8 of the data | ||
| 50 | fn crc8(data: &[u8]) -> u8 { | ||
| 51 | let mut temp; | ||
| 52 | let mut data_byte; | ||
| 53 | let mut crc = 0; | ||
| 54 | for b in data { | ||
| 55 | data_byte = *b; | ||
| 56 | for _ in 0..8 { | ||
| 57 | temp = (crc ^ data_byte) & 0x01; | ||
| 58 | crc >>= 1; | ||
| 59 | if temp != 0 { | ||
| 60 | crc ^= 0x8C; | ||
| 61 | } | ||
| 62 | data_byte >>= 1; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | crc | ||
| 66 | } | ||
| 67 | |||
| 68 | /// Start a new measurement. Allow at least 1000ms before getting `temperature`. | ||
| 69 | pub async fn start(&mut self) { | ||
| 70 | self.wire.write_bytes(&[0xCC, 0x44]).await; | ||
| 71 | } | ||
| 72 | |||
| 73 | /// Read the temperature. Ensure >1000ms has passed since `start` before calling this. | ||
| 74 | pub async fn temperature(&mut self) -> Result<f32, ()> { | ||
| 75 | self.wire.write_bytes(&[0xCC, 0xBE]).await; | ||
| 76 | let mut data = [0; 9]; | ||
| 77 | self.wire.read_bytes(&mut data).await; | ||
| 78 | match Self::crc8(&data) == 0 { | ||
| 79 | true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.), | ||
| 80 | false => Err(()), | ||
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
diff --git a/examples/rp235x/src/bin/pio_pwm.rs b/examples/rp235x/src/bin/pio_pwm.rs new file mode 100644 index 000000000..5712b5b91 --- /dev/null +++ b/examples/rp235x/src/bin/pio_pwm.rs | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | //! This example shows how to create a pwm using the PIO module in the RP235x chip. | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | use core::time::Duration; | ||
| 6 | |||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_rp::bind_interrupts; | ||
| 9 | use embassy_rp::peripherals::PIO0; | ||
| 10 | use embassy_rp::pio::{InterruptHandler, Pio}; | ||
| 11 | use embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram}; | ||
| 12 | use embassy_time::Timer; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | const REFRESH_INTERVAL: u64 = 20000; | ||
| 16 | |||
| 17 | bind_interrupts!(struct Irqs { | ||
| 18 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 19 | }); | ||
| 20 | |||
| 21 | #[embassy_executor::main] | ||
| 22 | async fn main(_spawner: Spawner) { | ||
| 23 | let p = embassy_rp::init(Default::default()); | ||
| 24 | let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); | ||
| 25 | |||
| 26 | // Note that PIN_25 is the led pin on the Pico | ||
| 27 | let prg = PioPwmProgram::new(&mut common); | ||
| 28 | let mut pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_25, &prg); | ||
| 29 | pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL)); | ||
| 30 | pwm_pio.start(); | ||
| 31 | |||
| 32 | let mut duration = 0; | ||
| 33 | loop { | ||
| 34 | duration = (duration + 1) % 1000; | ||
| 35 | pwm_pio.write(Duration::from_micros(duration)); | ||
| 36 | Timer::after_millis(1).await; | ||
| 37 | } | ||
| 38 | } | ||
diff --git a/examples/rp235x/src/bin/pio_rotary_encoder.rs b/examples/rp235x/src/bin/pio_rotary_encoder.rs new file mode 100644 index 000000000..e820d316d --- /dev/null +++ b/examples/rp235x/src/bin/pio_rotary_encoder.rs | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | //! This example shows how to use the PIO module in the RP235x to read a quadrature rotary encoder. | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | |||
| 6 | use defmt::info; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_rp::bind_interrupts; | ||
| 9 | use embassy_rp::peripherals::PIO0; | ||
| 10 | use embassy_rp::pio::{InterruptHandler, Pio}; | ||
| 11 | use embassy_rp::pio_programs::rotary_encoder::{Direction, PioEncoder, PioEncoderProgram}; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | bind_interrupts!(struct Irqs { | ||
| 15 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 16 | }); | ||
| 17 | |||
| 18 | #[embassy_executor::task] | ||
| 19 | async fn encoder_0(mut encoder: PioEncoder<'static, PIO0, 0>) { | ||
| 20 | let mut count = 0; | ||
| 21 | loop { | ||
| 22 | info!("Count: {}", count); | ||
| 23 | count += match encoder.read().await { | ||
| 24 | Direction::Clockwise => 1, | ||
| 25 | Direction::CounterClockwise => -1, | ||
| 26 | }; | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | #[embassy_executor::task] | ||
| 31 | async fn encoder_1(mut encoder: PioEncoder<'static, PIO0, 1>) { | ||
| 32 | let mut count = 0; | ||
| 33 | loop { | ||
| 34 | info!("Count: {}", count); | ||
| 35 | count += match encoder.read().await { | ||
| 36 | Direction::Clockwise => 1, | ||
| 37 | Direction::CounterClockwise => -1, | ||
| 38 | }; | ||
| 39 | } | ||
| 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/rp23/src/bin/pio_rotary_encoder.rs b/examples/rp235x/src/bin/pio_rotary_encoder_rxf.rs index 287992a83..61af94560 100644 --- a/examples/rp23/src/bin/pio_rotary_encoder.rs +++ b/examples/rp235x/src/bin/pio_rotary_encoder_rxf.rs | |||
| @@ -1,30 +1,28 @@ | |||
| 1 | //! This example shows how to use the PIO module in the RP2040 to read a quadrature rotary encoder. | 1 | //! This example shows how to use the PIO module in the RP235x to read a quadrature rotary encoder. |
| 2 | //! It differs from the other example in that it uses the RX FIFO as a status register | ||
| 2 | 3 | ||
| 3 | #![no_std] | 4 | #![no_std] |
| 4 | #![no_main] | 5 | #![no_main] |
| 5 | 6 | ||
| 6 | use defmt::info; | 7 | use defmt::info; |
| 7 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 8 | use embassy_rp::block::ImageDef; | ||
| 9 | use embassy_rp::gpio::Pull; | 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::program::pio_asm; |
| 12 | use embassy_rp::{bind_interrupts, pio, Peri}; | ||
| 13 | use embassy_time::Timer; | ||
| 12 | use fixed::traits::ToFixed; | 14 | use fixed::traits::ToFixed; |
| 13 | use pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftDirection, StateMachine}; | 15 | use pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftDirection, StateMachine}; |
| 14 | use {defmt_rtt as _, panic_probe as _}; | 16 | use {defmt_rtt as _, panic_probe as _}; |
| 15 | 17 | ||
| 16 | #[link_section = ".start_block"] | ||
| 17 | #[used] | ||
| 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 19 | |||
| 20 | // Program metadata for `picotool info` | 18 | // Program metadata for `picotool info` |
| 21 | #[link_section = ".bi_entries"] | 19 | #[unsafe(link_section = ".bi_entries")] |
| 22 | #[used] | 20 | #[used] |
| 23 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | 21 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ |
| 24 | embassy_rp::binary_info_rp_cargo_bin_name!(), | 22 | embassy_rp::binary_info::rp_program_name!(c"example_pio_rotary_encoder_rxf"), |
| 25 | embassy_rp::binary_info_rp_cargo_version!(), | 23 | embassy_rp::binary_info::rp_cargo_version!(), |
| 26 | embassy_rp::binary_info_rp_program_description!(c"Blinky"), | 24 | embassy_rp::binary_info::rp_program_description!(c"Rotary encoder (RXF)"), |
| 27 | embassy_rp::binary_info_rp_program_build_attribute!(), | 25 | embassy_rp::binary_info::rp_program_build_attribute!(), |
| 28 | ]; | 26 | ]; |
| 29 | 27 | ||
| 30 | bind_interrupts!(struct Irqs { | 28 | bind_interrupts!(struct Irqs { |
| @@ -39,36 +37,59 @@ impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> { | |||
| 39 | pub fn new( | 37 | pub fn new( |
| 40 | pio: &mut Common<'d, T>, | 38 | pio: &mut Common<'d, T>, |
| 41 | mut sm: StateMachine<'d, T, SM>, | 39 | mut sm: StateMachine<'d, T, SM>, |
| 42 | pin_a: impl PioPin, | 40 | pin_a: Peri<'d, impl PioPin>, |
| 43 | pin_b: impl PioPin, | 41 | pin_b: Peri<'d, impl PioPin>, |
| 44 | ) -> Self { | 42 | ) -> Self { |
| 45 | let mut pin_a = pio.make_pio_pin(pin_a); | 43 | let mut pin_a = pio.make_pio_pin(pin_a); |
| 46 | let mut pin_b = pio.make_pio_pin(pin_b); | 44 | let mut pin_b = pio.make_pio_pin(pin_b); |
| 47 | pin_a.set_pull(Pull::Up); | 45 | pin_a.set_pull(Pull::Up); |
| 48 | pin_b.set_pull(Pull::Up); | 46 | pin_b.set_pull(Pull::Up); |
| 47 | |||
| 49 | sm.set_pin_dirs(pio::Direction::In, &[&pin_a, &pin_b]); | 48 | sm.set_pin_dirs(pio::Direction::In, &[&pin_a, &pin_b]); |
| 50 | 49 | ||
| 51 | let prg = pio_proc::pio_asm!("wait 1 pin 1", "wait 0 pin 1", "in pins, 2", "push",); | 50 | let prg = pio_asm!( |
| 51 | "start:" | ||
| 52 | // encoder count is stored in X | ||
| 53 | "mov isr, x" | ||
| 54 | // and then moved to the RX FIFO register | ||
| 55 | "mov rxfifo[0], isr" | ||
| 56 | |||
| 57 | // wait for encoder transition | ||
| 58 | "wait 1 pin 1" | ||
| 59 | "wait 0 pin 1" | ||
| 60 | |||
| 61 | "set y, 0" | ||
| 62 | "mov y, pins[1]" | ||
| 63 | |||
| 64 | // update X depending on pin 1 | ||
| 65 | "jmp !y decr" | ||
| 66 | |||
| 67 | // this is just a clever way of doing x++ | ||
| 68 | "mov x, ~x" | ||
| 69 | "jmp x--, incr" | ||
| 70 | "incr:" | ||
| 71 | "mov x, ~x" | ||
| 72 | "jmp start" | ||
| 73 | |||
| 74 | // and this is x-- | ||
| 75 | "decr:" | ||
| 76 | "jmp x--, start" | ||
| 77 | ); | ||
| 52 | 78 | ||
| 53 | let mut cfg = Config::default(); | 79 | let mut cfg = Config::default(); |
| 54 | cfg.set_in_pins(&[&pin_a, &pin_b]); | 80 | cfg.set_in_pins(&[&pin_a, &pin_b]); |
| 55 | cfg.fifo_join = FifoJoin::RxOnly; | 81 | cfg.fifo_join = FifoJoin::RxAsStatus; |
| 56 | cfg.shift_in.direction = ShiftDirection::Left; | 82 | cfg.shift_in.direction = ShiftDirection::Left; |
| 57 | cfg.clock_divider = 10_000.to_fixed(); | 83 | cfg.clock_divider = 10_000.to_fixed(); |
| 58 | cfg.use_program(&pio.load_program(&prg.program), &[]); | 84 | cfg.use_program(&pio.load_program(&prg.program), &[]); |
| 59 | sm.set_config(&cfg); | 85 | sm.set_config(&cfg); |
| 86 | |||
| 60 | sm.set_enable(true); | 87 | sm.set_enable(true); |
| 61 | Self { sm } | 88 | Self { sm } |
| 62 | } | 89 | } |
| 63 | 90 | ||
| 64 | pub async fn read(&mut self) -> Direction { | 91 | pub async fn read(&mut self) -> i32 { |
| 65 | loop { | 92 | self.sm.get_rxf_entry(0) as i32 |
| 66 | match self.sm.rx().wait_pull().await { | ||
| 67 | 0 => return Direction::CounterClockwise, | ||
| 68 | 1 => return Direction::Clockwise, | ||
| 69 | _ => {} | ||
| 70 | } | ||
| 71 | } | ||
| 72 | } | 93 | } |
| 73 | } | 94 | } |
| 74 | 95 | ||
| @@ -84,12 +105,8 @@ async fn main(_spawner: Spawner) { | |||
| 84 | 105 | ||
| 85 | let mut encoder = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5); | 106 | let mut encoder = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5); |
| 86 | 107 | ||
| 87 | let mut count = 0; | ||
| 88 | loop { | 108 | loop { |
| 89 | info!("Count: {}", count); | 109 | info!("Count: {}", encoder.read().await); |
| 90 | count += match encoder.read().await { | 110 | Timer::after_millis(1000).await; |
| 91 | Direction::Clockwise => 1, | ||
| 92 | Direction::CounterClockwise => -1, | ||
| 93 | }; | ||
| 94 | } | 111 | } |
| 95 | } | 112 | } |
diff --git a/examples/rp23/src/bin/pio_servo.rs b/examples/rp235x/src/bin/pio_servo.rs index 1dec86927..086b02f03 100644 --- a/examples/rp23/src/bin/pio_servo.rs +++ b/examples/rp235x/src/bin/pio_servo.rs | |||
| @@ -1,33 +1,17 @@ | |||
| 1 | //! This example shows how to create a pwm using the PIO module in the RP2040 chip. | 1 | //! This example shows how to create a pwm using the PIO module in the RP235x chip. |
| 2 | 2 | ||
| 3 | #![no_std] | 3 | #![no_std] |
| 4 | #![no_main] | 4 | #![no_main] |
| 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::block::ImageDef; | 8 | use embassy_rp::bind_interrupts; |
| 9 | use embassy_rp::gpio::Level; | ||
| 10 | use embassy_rp::peripherals::PIO0; | 9 | use embassy_rp::peripherals::PIO0; |
| 11 | use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine}; | 10 | use embassy_rp::pio::{Instance, InterruptHandler, Pio}; |
| 12 | use embassy_rp::{bind_interrupts, clocks}; | 11 | use embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram}; |
| 13 | use embassy_time::Timer; | 12 | use embassy_time::Timer; |
| 14 | use pio::InstructionOperands; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 16 | 14 | ||
| 17 | #[link_section = ".start_block"] | ||
| 18 | #[used] | ||
| 19 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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 | 15 | 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 | 16 | 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 | 17 | const DEFAULT_MAX_DEGREE_ROTATION: u64 = 160; // 160 degrees is typical |
| @@ -37,88 +21,8 @@ bind_interrupts!(struct Irqs { | |||
| 37 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | 21 | PIO0_IRQ_0 => InterruptHandler<PIO0>; |
| 38 | }); | 22 | }); |
| 39 | 23 | ||
| 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> { | 24 | pub struct ServoBuilder<'d, T: Instance, const SM: usize> { |
| 121 | pwm: PwmPio<'d, T, SM>, | 25 | pwm: PioPwm<'d, T, SM>, |
| 122 | period: Duration, | 26 | period: Duration, |
| 123 | min_pulse_width: Duration, | 27 | min_pulse_width: Duration, |
| 124 | max_pulse_width: Duration, | 28 | max_pulse_width: Duration, |
| @@ -126,7 +30,7 @@ pub struct ServoBuilder<'d, T: Instance, const SM: usize> { | |||
| 126 | } | 30 | } |
| 127 | 31 | ||
| 128 | impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> { | 32 | impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> { |
| 129 | pub fn new(pwm: PwmPio<'d, T, SM>) -> Self { | 33 | pub fn new(pwm: PioPwm<'d, T, SM>) -> Self { |
| 130 | Self { | 34 | Self { |
| 131 | pwm, | 35 | pwm, |
| 132 | period: Duration::from_micros(REFRESH_INTERVAL), | 36 | period: Duration::from_micros(REFRESH_INTERVAL), |
| @@ -168,7 +72,7 @@ impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> { | |||
| 168 | } | 72 | } |
| 169 | 73 | ||
| 170 | pub struct Servo<'d, T: Instance, const SM: usize> { | 74 | pub struct Servo<'d, T: Instance, const SM: usize> { |
| 171 | pwm: PwmPio<'d, T, SM>, | 75 | pwm: PioPwm<'d, T, SM>, |
| 172 | min_pulse_width: Duration, | 76 | min_pulse_width: Duration, |
| 173 | max_pulse_width: Duration, | 77 | max_pulse_width: Duration, |
| 174 | max_degree_rotation: u64, | 78 | max_degree_rotation: u64, |
| @@ -205,7 +109,8 @@ async fn main(_spawner: Spawner) { | |||
| 205 | let p = embassy_rp::init(Default::default()); | 109 | let p = embassy_rp::init(Default::default()); |
| 206 | let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); | 110 | let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); |
| 207 | 111 | ||
| 208 | 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); | ||
| 209 | let mut servo = ServoBuilder::new(pwm_pio) | 114 | let mut servo = ServoBuilder::new(pwm_pio) |
| 210 | .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 |
| 211 | .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/rp235x/src/bin/pio_stepper.rs b/examples/rp235x/src/bin/pio_stepper.rs new file mode 100644 index 000000000..931adbeda --- /dev/null +++ b/examples/rp235x/src/bin/pio_stepper.rs | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | //! This example shows how to use the PIO module in the RP235x to implement a stepper motor driver | ||
| 2 | //! for a 5-wire stepper such as the 28BYJ-48. You can halt an ongoing rotation by dropping the future. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | |||
| 7 | use defmt::info; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_rp::bind_interrupts; | ||
| 10 | use embassy_rp::peripherals::PIO0; | ||
| 11 | use embassy_rp::pio::{InterruptHandler, Pio}; | ||
| 12 | use embassy_rp::pio_programs::stepper::{PioStepper, PioStepperProgram}; | ||
| 13 | use embassy_time::{with_timeout, Duration, Timer}; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | ||
| 15 | |||
| 16 | bind_interrupts!(struct Irqs { | ||
| 17 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 18 | }); | ||
| 19 | |||
| 20 | #[embassy_executor::main] | ||
| 21 | async fn main(_spawner: Spawner) { | ||
| 22 | let p = embassy_rp::init(Default::default()); | ||
| 23 | let Pio { | ||
| 24 | mut common, irq0, sm0, .. | ||
| 25 | } = Pio::new(p.PIO0, Irqs); | ||
| 26 | |||
| 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); | ||
| 29 | stepper.set_frequency(120); | ||
| 30 | loop { | ||
| 31 | info!("CW full steps"); | ||
| 32 | stepper.step(1000).await; | ||
| 33 | |||
| 34 | info!("CCW full steps, drop after 1 sec"); | ||
| 35 | if with_timeout(Duration::from_secs(1), stepper.step(-i32::MAX)) | ||
| 36 | .await | ||
| 37 | .is_err() | ||
| 38 | { | ||
| 39 | info!("Time's up!"); | ||
| 40 | Timer::after(Duration::from_secs(1)).await; | ||
| 41 | } | ||
| 42 | |||
| 43 | info!("CW half steps"); | ||
| 44 | stepper.step_half(1000).await; | ||
| 45 | |||
| 46 | info!("CCW half steps"); | ||
| 47 | stepper.step_half(-1000).await; | ||
| 48 | } | ||
| 49 | } | ||
diff --git a/examples/rp235x/src/bin/pio_uart.rs b/examples/rp235x/src/bin/pio_uart.rs new file mode 100644 index 000000000..d92e33feb --- /dev/null +++ b/examples/rp235x/src/bin/pio_uart.rs | |||
| @@ -0,0 +1,190 @@ | |||
| 1 | //! This example shows how to use the PIO module in the RP235x 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 RP235x 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::peripherals::{PIO0, USB}; | ||
| 17 | use embassy_rp::pio_programs::uart::{PioUartRx, PioUartRxProgram, PioUartTx, PioUartTxProgram}; | ||
| 18 | use embassy_rp::usb::{Driver, Instance, InterruptHandler}; | ||
| 19 | use embassy_rp::{bind_interrupts, pio}; | ||
| 20 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
| 21 | use embassy_sync::pipe::Pipe; | ||
| 22 | use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State}; | ||
| 23 | use embassy_usb::driver::EndpointError; | ||
| 24 | use embassy_usb::{Builder, Config}; | ||
| 25 | use embedded_io_async::{Read, Write}; | ||
| 26 | use {defmt_rtt as _, panic_probe as _}; | ||
| 27 | |||
| 28 | bind_interrupts!(struct Irqs { | ||
| 29 | USBCTRL_IRQ => InterruptHandler<USB>; | ||
| 30 | PIO0_IRQ_0 => pio::InterruptHandler<PIO0>; | ||
| 31 | }); | ||
| 32 | |||
| 33 | #[embassy_executor::main] | ||
| 34 | async fn main(_spawner: Spawner) { | ||
| 35 | info!("Hello there!"); | ||
| 36 | |||
| 37 | let p = embassy_rp::init(Default::default()); | ||
| 38 | |||
| 39 | // Create the driver, from the HAL. | ||
| 40 | let driver = Driver::new(p.USB, Irqs); | ||
| 41 | |||
| 42 | // Create embassy-usb Config | ||
| 43 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 44 | config.manufacturer = Some("Embassy"); | ||
| 45 | config.product = Some("PIO UART example"); | ||
| 46 | config.serial_number = Some("12345678"); | ||
| 47 | config.max_power = 100; | ||
| 48 | config.max_packet_size_0 = 64; | ||
| 49 | |||
| 50 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 51 | // It needs some buffers for building the descriptors. | ||
| 52 | let mut config_descriptor = [0; 256]; | ||
| 53 | let mut bos_descriptor = [0; 256]; | ||
| 54 | let mut control_buf = [0; 64]; | ||
| 55 | |||
| 56 | let mut state = State::new(); | ||
| 57 | |||
| 58 | let mut builder = Builder::new( | ||
| 59 | driver, | ||
| 60 | config, | ||
| 61 | &mut config_descriptor, | ||
| 62 | &mut bos_descriptor, | ||
| 63 | &mut [], // no msos descriptors | ||
| 64 | &mut control_buf, | ||
| 65 | ); | ||
| 66 | |||
| 67 | // Create classes on the builder. | ||
| 68 | let class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 69 | |||
| 70 | // Build the builder. | ||
| 71 | let mut usb = builder.build(); | ||
| 72 | |||
| 73 | // Run the USB device. | ||
| 74 | let usb_fut = usb.run(); | ||
| 75 | |||
| 76 | // PIO UART setup | ||
| 77 | let pio::Pio { | ||
| 78 | mut common, sm0, sm1, .. | ||
| 79 | } = pio::Pio::new(p.PIO0, Irqs); | ||
| 80 | |||
| 81 | let tx_program = PioUartTxProgram::new(&mut common); | ||
| 82 | let mut uart_tx = PioUartTx::new(9600, &mut common, sm0, p.PIN_4, &tx_program); | ||
| 83 | |||
| 84 | let rx_program = PioUartRxProgram::new(&mut common); | ||
| 85 | let mut uart_rx = PioUartRx::new(9600, &mut common, sm1, p.PIN_5, &rx_program); | ||
| 86 | |||
| 87 | // Pipe setup | ||
| 88 | let mut usb_pipe: Pipe<NoopRawMutex, 20> = Pipe::new(); | ||
| 89 | let (mut usb_pipe_reader, mut usb_pipe_writer) = usb_pipe.split(); | ||
| 90 | |||
| 91 | let mut uart_pipe: Pipe<NoopRawMutex, 20> = Pipe::new(); | ||
| 92 | let (mut uart_pipe_reader, mut uart_pipe_writer) = uart_pipe.split(); | ||
| 93 | |||
| 94 | let (mut usb_tx, mut usb_rx) = class.split(); | ||
| 95 | |||
| 96 | // Read + write from USB | ||
| 97 | let usb_future = async { | ||
| 98 | loop { | ||
| 99 | info!("Wait for USB connection"); | ||
| 100 | usb_rx.wait_connection().await; | ||
| 101 | info!("Connected"); | ||
| 102 | let _ = join( | ||
| 103 | usb_read(&mut usb_rx, &mut uart_pipe_writer), | ||
| 104 | usb_write(&mut usb_tx, &mut usb_pipe_reader), | ||
| 105 | ) | ||
| 106 | .await; | ||
| 107 | info!("Disconnected"); | ||
| 108 | } | ||
| 109 | }; | ||
| 110 | |||
| 111 | // Read + write from UART | ||
| 112 | let uart_future = join( | ||
| 113 | uart_read(&mut uart_rx, &mut usb_pipe_writer), | ||
| 114 | uart_write(&mut uart_tx, &mut uart_pipe_reader), | ||
| 115 | ); | ||
| 116 | |||
| 117 | // Run everything concurrently. | ||
| 118 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 119 | join3(usb_fut, usb_future, uart_future).await; | ||
| 120 | } | ||
| 121 | |||
| 122 | struct Disconnected {} | ||
| 123 | |||
| 124 | impl From<EndpointError> for Disconnected { | ||
| 125 | fn from(val: EndpointError) -> Self { | ||
| 126 | match val { | ||
| 127 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 128 | EndpointError::Disabled => Disconnected {}, | ||
| 129 | } | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | /// Read from the USB and write it to the UART TX pipe | ||
| 134 | async fn usb_read<'d, T: Instance + 'd>( | ||
| 135 | usb_rx: &mut Receiver<'d, Driver<'d, T>>, | ||
| 136 | uart_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>, | ||
| 137 | ) -> Result<(), Disconnected> { | ||
| 138 | let mut buf = [0; 64]; | ||
| 139 | loop { | ||
| 140 | let n = usb_rx.read_packet(&mut buf).await?; | ||
| 141 | let data = &buf[..n]; | ||
| 142 | trace!("USB IN: {:x}", data); | ||
| 143 | (*uart_pipe_writer).write(data).await; | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | /// Read from the USB TX pipe and write it to the USB | ||
| 148 | async fn usb_write<'d, T: Instance + 'd>( | ||
| 149 | usb_tx: &mut Sender<'d, Driver<'d, T>>, | ||
| 150 | usb_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>, | ||
| 151 | ) -> Result<(), Disconnected> { | ||
| 152 | let mut buf = [0; 64]; | ||
| 153 | loop { | ||
| 154 | let n = (*usb_pipe_reader).read(&mut buf).await; | ||
| 155 | let data = &buf[..n]; | ||
| 156 | trace!("USB OUT: {:x}", data); | ||
| 157 | usb_tx.write_packet(&data).await?; | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | /// Read from the UART and write it to the USB TX pipe | ||
| 162 | async fn uart_read<PIO: pio::Instance, const SM: usize>( | ||
| 163 | uart_rx: &mut PioUartRx<'_, PIO, SM>, | ||
| 164 | usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>, | ||
| 165 | ) -> ! { | ||
| 166 | let mut buf = [0; 64]; | ||
| 167 | loop { | ||
| 168 | let n = uart_rx.read(&mut buf).await.expect("UART read error"); | ||
| 169 | if n == 0 { | ||
| 170 | continue; | ||
| 171 | } | ||
| 172 | let data = &buf[..n]; | ||
| 173 | trace!("UART IN: {:x}", buf); | ||
| 174 | (*usb_pipe_writer).write(data).await; | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 178 | /// Read from the UART TX pipe and write it to the UART | ||
| 179 | async fn uart_write<PIO: pio::Instance, const SM: usize>( | ||
| 180 | uart_tx: &mut PioUartTx<'_, PIO, SM>, | ||
| 181 | uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>, | ||
| 182 | ) -> ! { | ||
| 183 | let mut buf = [0; 64]; | ||
| 184 | loop { | ||
| 185 | let n = (*uart_pipe_reader).read(&mut buf).await; | ||
| 186 | let data = &buf[..n]; | ||
| 187 | trace!("UART OUT: {:x}", data); | ||
| 188 | let _ = uart_tx.write(&data).await; | ||
| 189 | } | ||
| 190 | } | ||
diff --git a/examples/rp235x/src/bin/pio_ws2812.rs b/examples/rp235x/src/bin/pio_ws2812.rs new file mode 100644 index 000000000..42694c527 --- /dev/null +++ b/examples/rp235x/src/bin/pio_ws2812.rs | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | //! This example shows powerful PIO module in the RP235x chip to communicate with WS2812 LED modules. | ||
| 2 | //! See (https://www.sparkfun.com/categories/tags/ws2812) | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | |||
| 7 | use defmt::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_rp::bind_interrupts; | ||
| 10 | use embassy_rp::peripherals::PIO0; | ||
| 11 | use embassy_rp::pio::{InterruptHandler, Pio}; | ||
| 12 | use embassy_rp::pio_programs::ws2812::{PioWs2812, PioWs2812Program}; | ||
| 13 | use embassy_time::{Duration, Ticker}; | ||
| 14 | use smart_leds::RGB8; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | bind_interrupts!(struct Irqs { | ||
| 18 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 19 | }); | ||
| 20 | |||
| 21 | /// Input a value 0 to 255 to get a color value | ||
| 22 | /// The colours are a transition r - g - b - back to r. | ||
| 23 | fn wheel(mut wheel_pos: u8) -> RGB8 { | ||
| 24 | wheel_pos = 255 - wheel_pos; | ||
| 25 | if wheel_pos < 85 { | ||
| 26 | return (255 - wheel_pos * 3, 0, wheel_pos * 3).into(); | ||
| 27 | } | ||
| 28 | if wheel_pos < 170 { | ||
| 29 | wheel_pos -= 85; | ||
| 30 | return (0, wheel_pos * 3, 255 - wheel_pos * 3).into(); | ||
| 31 | } | ||
| 32 | wheel_pos -= 170; | ||
| 33 | (wheel_pos * 3, 255 - wheel_pos * 3, 0).into() | ||
| 34 | } | ||
| 35 | |||
| 36 | #[embassy_executor::main] | ||
| 37 | async fn main(_spawner: Spawner) { | ||
| 38 | info!("Start"); | ||
| 39 | let p = embassy_rp::init(Default::default()); | ||
| 40 | |||
| 41 | let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); | ||
| 42 | |||
| 43 | // This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit | ||
| 44 | // feather boards for the 2040 both have one built in. | ||
| 45 | const NUM_LEDS: usize = 1; | ||
| 46 | let mut data = [RGB8::default(); NUM_LEDS]; | ||
| 47 | |||
| 48 | // Common neopixel pins: | ||
| 49 | // Thing plus: 8 | ||
| 50 | // Adafruit Feather: 16; Adafruit Feather+RFM95: 4 | ||
| 51 | let program = PioWs2812Program::new(&mut common); | ||
| 52 | let mut ws2812 = PioWs2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16, &program); | ||
| 53 | |||
| 54 | // Loop forever making RGB values and pushing them out to the WS2812. | ||
| 55 | let mut ticker = Ticker::every(Duration::from_millis(10)); | ||
| 56 | loop { | ||
| 57 | for j in 0..(256 * 5) { | ||
| 58 | debug!("New Colors:"); | ||
| 59 | for i in 0..NUM_LEDS { | ||
| 60 | data[i] = wheel((((i * 256) as u16 / NUM_LEDS as u16 + j as u16) & 255) as u8); | ||
| 61 | debug!("R: {} G: {} B: {}", data[i].r, data[i].g, data[i].b); | ||
| 62 | } | ||
| 63 | ws2812.write(&data).await; | ||
| 64 | |||
| 65 | ticker.next().await; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | } | ||
diff --git a/examples/rp235x/src/bin/pwm.rs b/examples/rp235x/src/bin/pwm.rs new file mode 100644 index 000000000..da1acc18a --- /dev/null +++ b/examples/rp235x/src/bin/pwm.rs | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | //! This example shows how to use PWM (Pulse Width Modulation) in the RP235x chip. | ||
| 2 | //! | ||
| 3 | //! We demonstrate two ways of using PWM: | ||
| 4 | //! 1. Via config | ||
| 5 | //! 2. Via setting a duty cycle | ||
| 6 | |||
| 7 | #![no_std] | ||
| 8 | #![no_main] | ||
| 9 | |||
| 10 | use defmt::*; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_rp::peripherals::{PIN_25, PIN_4, PWM_SLICE2, PWM_SLICE4}; | ||
| 13 | use embassy_rp::pwm::{Config, Pwm, SetDutyCycle}; | ||
| 14 | use embassy_rp::Peri; | ||
| 15 | use embassy_time::Timer; | ||
| 16 | use {defmt_rtt as _, panic_probe as _}; | ||
| 17 | |||
| 18 | #[embassy_executor::main] | ||
| 19 | async fn main(spawner: Spawner) { | ||
| 20 | let p = embassy_rp::init(Default::default()); | ||
| 21 | spawner.spawn(pwm_set_config(p.PWM_SLICE4, p.PIN_25)).unwrap(); | ||
| 22 | spawner.spawn(pwm_set_dutycycle(p.PWM_SLICE2, p.PIN_4)).unwrap(); | ||
| 23 | } | ||
| 24 | |||
| 25 | /// Demonstrate PWM by modifying & applying the config | ||
| 26 | /// | ||
| 27 | /// Using the onboard led, if You are using a different Board than plain Pico2 (i.e. W variant) | ||
| 28 | /// you must use another slice & pin and an appropriate resistor. | ||
| 29 | #[embassy_executor::task] | ||
| 30 | async fn pwm_set_config(slice4: Peri<'static, PWM_SLICE4>, pin25: Peri<'static, PIN_25>) { | ||
| 31 | let mut c = Config::default(); | ||
| 32 | c.top = 32_768; | ||
| 33 | c.compare_b = 8; | ||
| 34 | let mut pwm = Pwm::new_output_b(slice4, pin25, c.clone()); | ||
| 35 | |||
| 36 | loop { | ||
| 37 | info!("current LED duty cycle: {}/32768", c.compare_b); | ||
| 38 | Timer::after_secs(1).await; | ||
| 39 | c.compare_b = c.compare_b.rotate_left(4); | ||
| 40 | pwm.set_config(&c); | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | /// Demonstrate PWM by setting duty cycle | ||
| 45 | /// | ||
| 46 | /// Using GP4 in Slice2, make sure to use an appropriate resistor. | ||
| 47 | #[embassy_executor::task] | ||
| 48 | async fn pwm_set_dutycycle(slice2: Peri<'static, PWM_SLICE2>, pin4: Peri<'static, PIN_4>) { | ||
| 49 | // If we aim for a specific frequency, here is how we can calculate the top value. | ||
| 50 | // The top value sets the period of the PWM cycle, so a counter goes from 0 to top and then wraps around to 0. | ||
| 51 | // Every such wraparound is one PWM cycle. So here is how we get 25KHz: | ||
| 52 | let desired_freq_hz = 25_000; | ||
| 53 | let clock_freq_hz = embassy_rp::clocks::clk_sys_freq(); | ||
| 54 | let divider = 16u8; | ||
| 55 | let period = (clock_freq_hz / (desired_freq_hz * divider as u32)) as u16 - 1; | ||
| 56 | |||
| 57 | let mut c = Config::default(); | ||
| 58 | c.top = period; | ||
| 59 | c.divider = divider.into(); | ||
| 60 | |||
| 61 | let mut pwm = Pwm::new_output_a(slice2, pin4, c.clone()); | ||
| 62 | |||
| 63 | loop { | ||
| 64 | // 100% duty cycle, fully on | ||
| 65 | pwm.set_duty_cycle_fully_on().unwrap(); | ||
| 66 | Timer::after_secs(1).await; | ||
| 67 | |||
| 68 | // 66% duty cycle. Expressed as simple percentage. | ||
| 69 | pwm.set_duty_cycle_percent(66).unwrap(); | ||
| 70 | Timer::after_secs(1).await; | ||
| 71 | |||
| 72 | // 25% duty cycle. Expressed as 32768/4 = 8192. | ||
| 73 | pwm.set_duty_cycle(c.top / 4).unwrap(); | ||
| 74 | Timer::after_secs(1).await; | ||
| 75 | |||
| 76 | // 0% duty cycle, fully off. | ||
| 77 | pwm.set_duty_cycle_fully_off().unwrap(); | ||
| 78 | Timer::after_secs(1).await; | ||
| 79 | } | ||
| 80 | } | ||
diff --git a/examples/rp23/src/bin/pwm_input.rs b/examples/rp235x/src/bin/pwm_input.rs index b75d04963..bf454a936 100644 --- a/examples/rp23/src/bin/pwm_input.rs +++ b/examples/rp235x/src/bin/pwm_input.rs | |||
| @@ -5,26 +5,11 @@ | |||
| 5 | 5 | ||
| 6 | use defmt::*; | 6 | use defmt::*; |
| 7 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 8 | use embassy_rp::block::ImageDef; | ||
| 9 | use embassy_rp::gpio::Pull; | 8 | use embassy_rp::gpio::Pull; |
| 10 | use embassy_rp::pwm::{Config, InputMode, Pwm}; | 9 | use embassy_rp::pwm::{Config, InputMode, Pwm}; |
| 11 | use embassy_time::{Duration, Ticker}; | 10 | use embassy_time::{Duration, Ticker}; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 12 | ||
| 14 | #[link_section = ".start_block"] | ||
| 15 | #[used] | ||
| 16 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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] | 13 | #[embassy_executor::main] |
| 29 | async fn main(_spawner: Spawner) { | 14 | async fn main(_spawner: Spawner) { |
| 30 | let p = embassy_rp::init(Default::default()); | 15 | let p = embassy_rp::init(Default::default()); |
diff --git a/examples/rp235x/src/bin/pwm_tb6612fng_motor_driver.rs b/examples/rp235x/src/bin/pwm_tb6612fng_motor_driver.rs new file mode 100644 index 000000000..2cfb2038d --- /dev/null +++ b/examples/rp235x/src/bin/pwm_tb6612fng_motor_driver.rs | |||
| @@ -0,0 +1,105 @@ | |||
| 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::config::Config; | ||
| 12 | use embassy_rp::gpio::Output; | ||
| 13 | use embassy_rp::{gpio, peripherals, pwm, Peri}; | ||
| 14 | use embassy_time::{Duration, Timer}; | ||
| 15 | use tb6612fng::{DriveCommand, Motor, Tb6612fng}; | ||
| 16 | use {defmt_rtt as _, panic_probe as _}; | ||
| 17 | |||
| 18 | assign_resources! { | ||
| 19 | motor: MotorResources { | ||
| 20 | standby_pin: PIN_22, | ||
| 21 | left_slice: PWM_SLICE6, | ||
| 22 | left_pwm_pin: PIN_28, | ||
| 23 | left_forward_pin: PIN_21, | ||
| 24 | left_backward_pin: PIN_20, | ||
| 25 | right_slice: PWM_SLICE5, | ||
| 26 | right_pwm_pin: PIN_27, | ||
| 27 | right_forward_pin: PIN_19, | ||
| 28 | right_backward_pin: PIN_18, | ||
| 29 | }, | ||
| 30 | } | ||
| 31 | |||
| 32 | #[embassy_executor::main] | ||
| 33 | async fn main(_spawner: Spawner) { | ||
| 34 | let p = embassy_rp::init(Config::default()); | ||
| 35 | let s = split_resources!(p); | ||
| 36 | let r = s.motor; | ||
| 37 | |||
| 38 | // we want a PWM frequency of 10KHz, especially cheaper motors do not respond well to higher frequencies | ||
| 39 | let desired_freq_hz = 10_000; | ||
| 40 | let clock_freq_hz = embassy_rp::clocks::clk_sys_freq(); | ||
| 41 | let divider = 16u8; | ||
| 42 | let period = (clock_freq_hz / (desired_freq_hz * divider as u32)) as u16 - 1; | ||
| 43 | |||
| 44 | // we need a standby output and two motors to construct a full TB6612FNG | ||
| 45 | |||
| 46 | // standby pin | ||
| 47 | let stby = Output::new(r.standby_pin, gpio::Level::Low); | ||
| 48 | |||
| 49 | // motor A, here defined to be the left motor | ||
| 50 | let left_fwd = gpio::Output::new(r.left_forward_pin, gpio::Level::Low); | ||
| 51 | let left_bckw = gpio::Output::new(r.left_backward_pin, gpio::Level::Low); | ||
| 52 | let mut left_speed = pwm::Config::default(); | ||
| 53 | left_speed.top = period; | ||
| 54 | left_speed.divider = divider.into(); | ||
| 55 | let left_pwm = pwm::Pwm::new_output_a(r.left_slice, r.left_pwm_pin, left_speed); | ||
| 56 | let left_motor = Motor::new(left_fwd, left_bckw, left_pwm).unwrap(); | ||
| 57 | |||
| 58 | // motor B, here defined to be the right motor | ||
| 59 | let right_fwd = gpio::Output::new(r.right_forward_pin, gpio::Level::Low); | ||
| 60 | let right_bckw = gpio::Output::new(r.right_backward_pin, gpio::Level::Low); | ||
| 61 | let mut right_speed = pwm::Config::default(); | ||
| 62 | right_speed.top = period; | ||
| 63 | right_speed.divider = divider.into(); | ||
| 64 | let right_pwm = pwm::Pwm::new_output_b(r.right_slice, r.right_pwm_pin, right_speed); | ||
| 65 | let right_motor = Motor::new(right_fwd, right_bckw, right_pwm).unwrap(); | ||
| 66 | |||
| 67 | // construct the motor driver | ||
| 68 | let mut control = Tb6612fng::new(left_motor, right_motor, stby).unwrap(); | ||
| 69 | |||
| 70 | loop { | ||
| 71 | // wake up the motor driver | ||
| 72 | info!("end standby"); | ||
| 73 | control.disable_standby().unwrap(); | ||
| 74 | Timer::after(Duration::from_millis(100)).await; | ||
| 75 | |||
| 76 | // drive a straight line forward at 20% speed for 5s | ||
| 77 | info!("drive straight"); | ||
| 78 | control.motor_a.drive(DriveCommand::Forward(80)).unwrap(); | ||
| 79 | control.motor_b.drive(DriveCommand::Forward(80)).unwrap(); | ||
| 80 | Timer::after(Duration::from_secs(5)).await; | ||
| 81 | |||
| 82 | // coast for 2s | ||
| 83 | info!("coast"); | ||
| 84 | control.motor_a.drive(DriveCommand::Stop).unwrap(); | ||
| 85 | control.motor_b.drive(DriveCommand::Stop).unwrap(); | ||
| 86 | Timer::after(Duration::from_secs(2)).await; | ||
| 87 | |||
| 88 | // actively brake | ||
| 89 | info!("brake"); | ||
| 90 | control.motor_a.drive(DriveCommand::Brake).unwrap(); | ||
| 91 | control.motor_b.drive(DriveCommand::Brake).unwrap(); | ||
| 92 | Timer::after(Duration::from_secs(1)).await; | ||
| 93 | |||
| 94 | // slowly turn for 3s | ||
| 95 | info!("turn"); | ||
| 96 | control.motor_a.drive(DriveCommand::Backward(50)).unwrap(); | ||
| 97 | control.motor_b.drive(DriveCommand::Forward(50)).unwrap(); | ||
| 98 | Timer::after(Duration::from_secs(3)).await; | ||
| 99 | |||
| 100 | // and put the driver in standby mode and wait for 5s | ||
| 101 | info!("standby"); | ||
| 102 | control.enable_standby().unwrap(); | ||
| 103 | Timer::after(Duration::from_secs(5)).await; | ||
| 104 | } | ||
| 105 | } | ||
diff --git a/examples/rp23/src/bin/rosc.rs b/examples/rp235x/src/bin/rosc.rs index 28c778f51..942b72319 100644 --- a/examples/rp23/src/bin/rosc.rs +++ b/examples/rp235x/src/bin/rosc.rs | |||
| @@ -7,26 +7,11 @@ | |||
| 7 | 7 | ||
| 8 | use defmt::*; | 8 | use defmt::*; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_rp::block::ImageDef; | ||
| 11 | use embassy_rp::{clocks, gpio}; | 10 | use embassy_rp::{clocks, gpio}; |
| 12 | use embassy_time::Timer; | 11 | use embassy_time::Timer; |
| 13 | use gpio::{Level, Output}; | 12 | use gpio::{Level, Output}; |
| 14 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 15 | 14 | ||
| 16 | #[link_section = ".start_block"] | ||
| 17 | #[used] | ||
| 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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] | 15 | #[embassy_executor::main] |
| 31 | async fn main(_spawner: Spawner) { | 16 | async fn main(_spawner: Spawner) { |
| 32 | let mut config = embassy_rp::config::Config::default(); | 17 | let mut config = embassy_rp::config::Config::default(); |
diff --git a/examples/rp23/src/bin/shared_bus.rs b/examples/rp235x/src/bin/shared_bus.rs index 00e65f80d..9267dfccb 100644 --- a/examples/rp23/src/bin/shared_bus.rs +++ b/examples/rp235x/src/bin/shared_bus.rs | |||
| @@ -8,8 +8,7 @@ use embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice; | |||
| 8 | use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice; | 8 | use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_rp::bind_interrupts; | 10 | use embassy_rp::bind_interrupts; |
| 11 | use embassy_rp::block::ImageDef; | 11 | use embassy_rp::gpio::{Level, Output}; |
| 12 | use embassy_rp::gpio::{AnyPin, Level, Output}; | ||
| 13 | use embassy_rp::i2c::{self, I2c, InterruptHandler}; | 12 | use embassy_rp::i2c::{self, I2c, InterruptHandler}; |
| 14 | use embassy_rp::peripherals::{I2C1, SPI1}; | 13 | use embassy_rp::peripherals::{I2C1, SPI1}; |
| 15 | use embassy_rp::spi::{self, Spi}; | 14 | use embassy_rp::spi::{self, Spi}; |
| @@ -19,20 +18,6 @@ use embassy_time::Timer; | |||
| 19 | use static_cell::StaticCell; | 18 | use static_cell::StaticCell; |
| 20 | use {defmt_rtt as _, panic_probe as _}; | 19 | use {defmt_rtt as _, panic_probe as _}; |
| 21 | 20 | ||
| 22 | #[link_section = ".start_block"] | ||
| 23 | #[used] | ||
| 24 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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>>; | 21 | type Spi1Bus = Mutex<NoopRawMutex, Spi<'static, SPI1, spi::Async>>; |
| 37 | type I2c1Bus = Mutex<NoopRawMutex, I2c<'static, I2C1, i2c::Async>>; | 22 | type I2c1Bus = Mutex<NoopRawMutex, I2c<'static, I2C1, i2c::Async>>; |
| 38 | 23 | ||
| @@ -60,8 +45,8 @@ async fn main(spawner: Spawner) { | |||
| 60 | let spi_bus = SPI_BUS.init(Mutex::new(spi)); | 45 | let spi_bus = SPI_BUS.init(Mutex::new(spi)); |
| 61 | 46 | ||
| 62 | // Chip select pins for the SPI devices | 47 | // Chip select pins for the SPI devices |
| 63 | let cs_a = Output::new(AnyPin::from(p.PIN_0), Level::High); | 48 | let cs_a = Output::new(p.PIN_0, Level::High); |
| 64 | let cs_b = Output::new(AnyPin::from(p.PIN_1), Level::High); | 49 | let cs_b = Output::new(p.PIN_1, Level::High); |
| 65 | 50 | ||
| 66 | spawner.must_spawn(spi_task_a(spi_bus, cs_a)); | 51 | spawner.must_spawn(spi_task_a(spi_bus, cs_a)); |
| 67 | spawner.must_spawn(spi_task_b(spi_bus, cs_b)); | 52 | spawner.must_spawn(spi_task_b(spi_bus, cs_b)); |
diff --git a/examples/rp23/src/bin/sharing.rs b/examples/rp235x/src/bin/sharing.rs index b5ef08147..856be6ace 100644 --- a/examples/rp23/src/bin/sharing.rs +++ b/examples/rp235x/src/bin/sharing.rs | |||
| @@ -19,7 +19,6 @@ use core::sync::atomic::{AtomicU32, Ordering}; | |||
| 19 | use cortex_m_rt::entry; | 19 | use cortex_m_rt::entry; |
| 20 | use defmt::info; | 20 | use defmt::info; |
| 21 | use embassy_executor::{Executor, InterruptExecutor}; | 21 | use embassy_executor::{Executor, InterruptExecutor}; |
| 22 | use embassy_rp::block::ImageDef; | ||
| 23 | use embassy_rp::clocks::RoscRng; | 22 | use embassy_rp::clocks::RoscRng; |
| 24 | use embassy_rp::interrupt::{InterruptExt, Priority}; | 23 | use embassy_rp::interrupt::{InterruptExt, Priority}; |
| 25 | use embassy_rp::peripherals::UART0; | 24 | use embassy_rp::peripherals::UART0; |
| @@ -28,25 +27,10 @@ use embassy_rp::{bind_interrupts, interrupt}; | |||
| 28 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 27 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 29 | use embassy_sync::{blocking_mutex, mutex}; | 28 | use embassy_sync::{blocking_mutex, mutex}; |
| 30 | use embassy_time::{Duration, Ticker}; | 29 | use embassy_time::{Duration, Ticker}; |
| 31 | use rand::RngCore; | ||
| 32 | use static_cell::{ConstStaticCell, StaticCell}; | 30 | use static_cell::{ConstStaticCell, StaticCell}; |
| 33 | use {defmt_rtt as _, panic_probe as _}; | 31 | use {defmt_rtt as _, panic_probe as _}; |
| 34 | 32 | ||
| 35 | #[link_section = ".start_block"] | 33 | type UartAsyncMutex = mutex::Mutex<CriticalSectionRawMutex, UartTx<'static, uart::Async>>; |
| 36 | #[used] | ||
| 37 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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>>; | ||
| 50 | 34 | ||
| 51 | struct MyType { | 35 | struct MyType { |
| 52 | inner: u32, | 36 | inner: u32, |
diff --git a/examples/rp23/src/bin/spi.rs b/examples/rp235x/src/bin/spi.rs index 98aa7622c..308f05c01 100644 --- a/examples/rp23/src/bin/spi.rs +++ b/examples/rp235x/src/bin/spi.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 RP235x chip. |
| 2 | //! | 2 | //! |
| 3 | //! Example for resistive touch sensor in Waveshare Pico-ResTouch | 3 | //! Example for resistive touch sensor in Waveshare Pico-ResTouch |
| 4 | 4 | ||
| @@ -7,26 +7,11 @@ | |||
| 7 | 7 | ||
| 8 | use defmt::*; | 8 | use defmt::*; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_rp::block::ImageDef; | ||
| 11 | use embassy_rp::spi::Spi; | 10 | use embassy_rp::spi::Spi; |
| 12 | use embassy_rp::{gpio, spi}; | 11 | use embassy_rp::{gpio, spi}; |
| 13 | use gpio::{Level, Output}; | 12 | use gpio::{Level, Output}; |
| 14 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 15 | 14 | ||
| 16 | #[link_section = ".start_block"] | ||
| 17 | #[used] | ||
| 18 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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] | 15 | #[embassy_executor::main] |
| 31 | async fn main(_spawner: Spawner) { | 16 | async fn main(_spawner: Spawner) { |
| 32 | let p = embassy_rp::init(Default::default()); | 17 | let p = embassy_rp::init(Default::default()); |
diff --git a/examples/rp23/src/bin/spi_async.rs b/examples/rp235x/src/bin/spi_async.rs index 71eaa5c05..62bedc68a 100644 --- a/examples/rp23/src/bin/spi_async.rs +++ b/examples/rp235x/src/bin/spi_async.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 RP235x chip. |
| 2 | //! No specific hardware is specified in this example. If you connect pin 11 and 12 you should get the same data back. | 2 | //! No specific hardware is specified in this example. If you connect pin 11 and 12 you should get the same data back. |
| 3 | 3 | ||
| 4 | #![no_std] | 4 | #![no_std] |
| @@ -6,25 +6,10 @@ | |||
| 6 | 6 | ||
| 7 | use defmt::*; | 7 | use defmt::*; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_rp::block::ImageDef; | ||
| 10 | use embassy_rp::spi::{Config, Spi}; | 9 | use embassy_rp::spi::{Config, Spi}; |
| 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 _}; |
| 13 | 12 | ||
| 14 | #[link_section = ".start_block"] | ||
| 15 | #[used] | ||
| 16 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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] | 13 | #[embassy_executor::main] |
| 29 | async fn main(_spawner: Spawner) { | 14 | async fn main(_spawner: Spawner) { |
| 30 | let p = embassy_rp::init(Default::default()); | 15 | let p = embassy_rp::init(Default::default()); |
diff --git a/examples/rp235x/src/bin/spi_display.rs b/examples/rp235x/src/bin/spi_display.rs new file mode 100644 index 000000000..9967abefd --- /dev/null +++ b/examples/rp235x/src/bin/spi_display.rs | |||
| @@ -0,0 +1,177 @@ | |||
| 1 | //! This example shows how to use SPI (Serial Peripheral Interface) in the RP2350 chip. | ||
| 2 | //! | ||
| 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) | ||
| 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::gpio::{Level, Output}; | ||
| 16 | use embassy_rp::spi; | ||
| 17 | use embassy_rp::spi::{Blocking, Spi}; | ||
| 18 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
| 19 | use embassy_sync::blocking_mutex::Mutex; | ||
| 20 | use embassy_time::Delay; | ||
| 21 | use embedded_graphics::image::{Image, ImageRawLE}; | ||
| 22 | use embedded_graphics::mono_font::ascii::FONT_10X20; | ||
| 23 | use embedded_graphics::mono_font::MonoTextStyle; | ||
| 24 | use embedded_graphics::pixelcolor::Rgb565; | ||
| 25 | use embedded_graphics::prelude::*; | ||
| 26 | use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle}; | ||
| 27 | use embedded_graphics::text::Text; | ||
| 28 | use mipidsi::models::ST7789; | ||
| 29 | use mipidsi::options::{Orientation, Rotation}; | ||
| 30 | use mipidsi::Builder; | ||
| 31 | use {defmt_rtt as _, panic_probe as _}; | ||
| 32 | |||
| 33 | use crate::touch::Touch; | ||
| 34 | |||
| 35 | const DISPLAY_FREQ: u32 = 64_000_000; | ||
| 36 | const TOUCH_FREQ: u32 = 200_000; | ||
| 37 | |||
| 38 | #[embassy_executor::main] | ||
| 39 | async fn main(_spawner: Spawner) { | ||
| 40 | let p = embassy_rp::init(Default::default()); | ||
| 41 | info!("Hello World!"); | ||
| 42 | |||
| 43 | let bl = p.PIN_13; | ||
| 44 | let rst = p.PIN_15; | ||
| 45 | let display_cs = p.PIN_9; | ||
| 46 | let dcx = p.PIN_8; | ||
| 47 | let miso = p.PIN_12; | ||
| 48 | let mosi = p.PIN_11; | ||
| 49 | let clk = p.PIN_10; | ||
| 50 | let touch_cs = p.PIN_16; | ||
| 51 | //let touch_irq = p.PIN_17; | ||
| 52 | |||
| 53 | // create SPI | ||
| 54 | let mut display_config = spi::Config::default(); | ||
| 55 | display_config.frequency = DISPLAY_FREQ; | ||
| 56 | display_config.phase = spi::Phase::CaptureOnSecondTransition; | ||
| 57 | display_config.polarity = spi::Polarity::IdleHigh; | ||
| 58 | let mut touch_config = spi::Config::default(); | ||
| 59 | touch_config.frequency = TOUCH_FREQ; | ||
| 60 | touch_config.phase = spi::Phase::CaptureOnSecondTransition; | ||
| 61 | touch_config.polarity = spi::Polarity::IdleHigh; | ||
| 62 | |||
| 63 | let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone()); | ||
| 64 | let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi)); | ||
| 65 | |||
| 66 | let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config); | ||
| 67 | let touch_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(touch_cs, Level::High), touch_config); | ||
| 68 | |||
| 69 | let mut touch = Touch::new(touch_spi); | ||
| 70 | |||
| 71 | let dcx = Output::new(dcx, Level::Low); | ||
| 72 | let rst = Output::new(rst, Level::Low); | ||
| 73 | // dcx: 0 = command, 1 = data | ||
| 74 | |||
| 75 | // Enable LCD backlight | ||
| 76 | let _bl = Output::new(bl, Level::High); | ||
| 77 | |||
| 78 | // display interface abstraction from SPI and DC | ||
| 79 | let di = SPIInterface::new(display_spi, dcx); | ||
| 80 | |||
| 81 | // Define the display from the display interface and initialize it | ||
| 82 | let mut display = Builder::new(ST7789, di) | ||
| 83 | .display_size(240, 320) | ||
| 84 | .reset_pin(rst) | ||
| 85 | .orientation(Orientation::new().rotate(Rotation::Deg90)) | ||
| 86 | .init(&mut Delay) | ||
| 87 | .unwrap(); | ||
| 88 | display.clear(Rgb565::BLACK).unwrap(); | ||
| 89 | |||
| 90 | let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), 86); | ||
| 91 | let ferris = Image::new(&raw_image_data, Point::new(34, 68)); | ||
| 92 | |||
| 93 | // Display the image | ||
| 94 | ferris.draw(&mut display).unwrap(); | ||
| 95 | |||
| 96 | let style = MonoTextStyle::new(&FONT_10X20, Rgb565::GREEN); | ||
| 97 | Text::new( | ||
| 98 | "Hello embedded_graphics \n + embassy + RP235x!", | ||
| 99 | Point::new(20, 200), | ||
| 100 | style, | ||
| 101 | ) | ||
| 102 | .draw(&mut display) | ||
| 103 | .unwrap(); | ||
| 104 | |||
| 105 | loop { | ||
| 106 | if let Some((x, y)) = touch.read() { | ||
| 107 | let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::BLUE).build(); | ||
| 108 | |||
| 109 | Rectangle::new(Point::new(x - 1, y - 1), Size::new(3, 3)) | ||
| 110 | .into_styled(style) | ||
| 111 | .draw(&mut display) | ||
| 112 | .unwrap(); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | /// Driver for the XPT2046 resistive touchscreen sensor | ||
| 118 | mod touch { | ||
| 119 | use embedded_hal_1::spi::{Operation, SpiDevice}; | ||
| 120 | |||
| 121 | struct Calibration { | ||
| 122 | x1: i32, | ||
| 123 | x2: i32, | ||
| 124 | y1: i32, | ||
| 125 | y2: i32, | ||
| 126 | sx: i32, | ||
| 127 | sy: i32, | ||
| 128 | } | ||
| 129 | |||
| 130 | const CALIBRATION: Calibration = Calibration { | ||
| 131 | x1: 3880, | ||
| 132 | x2: 340, | ||
| 133 | y1: 262, | ||
| 134 | y2: 3850, | ||
| 135 | sx: 320, | ||
| 136 | sy: 240, | ||
| 137 | }; | ||
| 138 | |||
| 139 | pub struct Touch<SPI: SpiDevice> { | ||
| 140 | spi: SPI, | ||
| 141 | } | ||
| 142 | |||
| 143 | impl<SPI> Touch<SPI> | ||
| 144 | where | ||
| 145 | SPI: SpiDevice, | ||
| 146 | { | ||
| 147 | pub fn new(spi: SPI) -> Self { | ||
| 148 | Self { spi } | ||
| 149 | } | ||
| 150 | |||
| 151 | pub fn read(&mut self) -> Option<(i32, i32)> { | ||
| 152 | let mut x = [0; 2]; | ||
| 153 | let mut y = [0; 2]; | ||
| 154 | self.spi | ||
| 155 | .transaction(&mut [ | ||
| 156 | Operation::Write(&[0x90]), | ||
| 157 | Operation::Read(&mut x), | ||
| 158 | Operation::Write(&[0xd0]), | ||
| 159 | Operation::Read(&mut y), | ||
| 160 | ]) | ||
| 161 | .unwrap(); | ||
| 162 | |||
| 163 | let x = (u16::from_be_bytes(x) >> 3) as i32; | ||
| 164 | let y = (u16::from_be_bytes(y) >> 3) as i32; | ||
| 165 | |||
| 166 | let cal = &CALIBRATION; | ||
| 167 | |||
| 168 | let x = ((x - cal.x1) * cal.sx / (cal.x2 - cal.x1)).clamp(0, cal.sx); | ||
| 169 | let y = ((y - cal.y1) * cal.sy / (cal.y2 - cal.y1)).clamp(0, cal.sy); | ||
| 170 | if x == 0 && y == 0 { | ||
| 171 | None | ||
| 172 | } else { | ||
| 173 | Some((x, y)) | ||
| 174 | } | ||
| 175 | } | ||
| 176 | } | ||
| 177 | } | ||
diff --git a/examples/rp23/src/bin/spi_sdmmc.rs b/examples/rp235x/src/bin/spi_sdmmc.rs index d7af77a30..e14a62c31 100644 --- a/examples/rp23/src/bin/spi_sdmmc.rs +++ b/examples/rp235x/src/bin/spi_sdmmc.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | //! This example shows how to use `embedded-sdmmc` with the RP2040 chip, over SPI. | 1 | //! This example shows how to use `embedded-sdmmc` with the RP235x chip, over SPI. |
| 2 | //! | 2 | //! |
| 3 | //! The example will attempt to read a file `MY_FILE.TXT` from the root directory | 3 | //! The example will attempt to read a file `MY_FILE.TXT` from the root directory |
| 4 | //! of the SD card and print its contents. | 4 | //! of the SD card and print its contents. |
| @@ -9,7 +9,6 @@ | |||
| 9 | use defmt::*; | 9 | use defmt::*; |
| 10 | use embassy_embedded_hal::SetConfig; | 10 | use embassy_embedded_hal::SetConfig; |
| 11 | use embassy_executor::Spawner; | 11 | use embassy_executor::Spawner; |
| 12 | use embassy_rp::block::ImageDef; | ||
| 13 | use embassy_rp::spi::Spi; | 12 | use embassy_rp::spi::Spi; |
| 14 | use embassy_rp::{gpio, spi}; | 13 | use embassy_rp::{gpio, spi}; |
| 15 | use embedded_hal_bus::spi::ExclusiveDevice; | 14 | use embedded_hal_bus::spi::ExclusiveDevice; |
| @@ -17,20 +16,6 @@ use embedded_sdmmc::sdcard::{DummyCsPin, SdCard}; | |||
| 17 | use gpio::{Level, Output}; | 16 | use gpio::{Level, Output}; |
| 18 | use {defmt_rtt as _, panic_probe as _}; | 17 | use {defmt_rtt as _, panic_probe as _}; |
| 19 | 18 | ||
| 20 | #[link_section = ".start_block"] | ||
| 21 | #[used] | ||
| 22 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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(); | 19 | struct DummyTimesource(); |
| 35 | 20 | ||
| 36 | impl embedded_sdmmc::TimeSource for DummyTimesource { | 21 | impl embedded_sdmmc::TimeSource for DummyTimesource { |
| @@ -48,7 +33,6 @@ impl embedded_sdmmc::TimeSource for DummyTimesource { | |||
| 48 | 33 | ||
| 49 | #[embassy_executor::main] | 34 | #[embassy_executor::main] |
| 50 | async fn main(_spawner: Spawner) { | 35 | async fn main(_spawner: Spawner) { |
| 51 | embassy_rp::pac::SIO.spinlock(31).write_value(1); | ||
| 52 | let p = embassy_rp::init(Default::default()); | 36 | let p = embassy_rp::init(Default::default()); |
| 53 | 37 | ||
| 54 | // SPI clock needs to be running at <= 400kHz during initialization | 38 | // SPI clock needs to be running at <= 400kHz during initialization |
| @@ -66,7 +50,7 @@ async fn main(_spawner: Spawner) { | |||
| 66 | // 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 |
| 67 | let mut config = spi::Config::default(); | 51 | let mut config = spi::Config::default(); |
| 68 | config.frequency = 16_000_000; | 52 | config.frequency = 16_000_000; |
| 69 | sdcard.spi(|dev| dev.bus_mut().set_config(&config)).ok(); | 53 | sdcard.spi(|dev| SetConfig::set_config(dev.bus_mut(), &config)).ok(); |
| 70 | 54 | ||
| 71 | // 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. |
| 72 | // 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/rp235x/src/bin/trng.rs b/examples/rp235x/src/bin/trng.rs new file mode 100644 index 000000000..100d6b104 --- /dev/null +++ b/examples/rp235x/src/bin/trng.rs | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | //! This example shows TRNG usage | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | |||
| 6 | use defmt::*; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_rp::bind_interrupts; | ||
| 9 | use embassy_rp::gpio::{Level, Output}; | ||
| 10 | use embassy_rp::peripherals::TRNG; | ||
| 11 | use embassy_rp::trng::Trng; | ||
| 12 | use embassy_time::Timer; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | bind_interrupts!(struct Irqs { | ||
| 16 | TRNG_IRQ => embassy_rp::trng::InterruptHandler<TRNG>; | ||
| 17 | }); | ||
| 18 | |||
| 19 | #[embassy_executor::main] | ||
| 20 | async fn main(_spawner: Spawner) { | ||
| 21 | let peripherals = embassy_rp::init(Default::default()); | ||
| 22 | |||
| 23 | // Initialize the TRNG with default configuration | ||
| 24 | let mut trng = Trng::new(peripherals.TRNG, Irqs, embassy_rp::trng::Config::default()); | ||
| 25 | // A buffer to collect random bytes in. | ||
| 26 | let mut randomness = [0u8; 58]; | ||
| 27 | |||
| 28 | let mut led = Output::new(peripherals.PIN_25, Level::Low); | ||
| 29 | |||
| 30 | loop { | ||
| 31 | trng.fill_bytes(&mut randomness).await; | ||
| 32 | info!("Random bytes async {}", &randomness); | ||
| 33 | trng.blocking_fill_bytes(&mut randomness); | ||
| 34 | info!("Random bytes blocking {}", &randomness); | ||
| 35 | let random_u32 = trng.blocking_next_u32(); | ||
| 36 | let random_u64 = trng.blocking_next_u64(); | ||
| 37 | info!("Random u32 {} u64 {}", random_u32, random_u64); | ||
| 38 | // Random number of blinks between 0 and 31 | ||
| 39 | let blinks = random_u32 % 32; | ||
| 40 | for _ in 0..blinks { | ||
| 41 | led.set_high(); | ||
| 42 | Timer::after_millis(20).await; | ||
| 43 | led.set_low(); | ||
| 44 | Timer::after_millis(20).await; | ||
| 45 | } | ||
| 46 | Timer::after_millis(1000).await; | ||
| 47 | } | ||
| 48 | } | ||
diff --git a/examples/rp23/src/bin/uart.rs b/examples/rp235x/src/bin/uart.rs index ae00f36dc..ed912b959 100644 --- a/examples/rp23/src/bin/uart.rs +++ b/examples/rp235x/src/bin/uart.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | //! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip. | 1 | //! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP235x chip. |
| 2 | //! | 2 | //! |
| 3 | //! No specific hardware is specified in this example. Only output on pin 0 is tested. | 3 | //! No specific hardware is specified in this example. Only output on pin 0 is tested. |
| 4 | //! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used | 4 | //! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used |
| @@ -8,24 +8,9 @@ | |||
| 8 | #![no_main] | 8 | #![no_main] |
| 9 | 9 | ||
| 10 | use embassy_executor::Spawner; | 10 | use embassy_executor::Spawner; |
| 11 | use embassy_rp::block::ImageDef; | ||
| 12 | use embassy_rp::uart; | 11 | use embassy_rp::uart; |
| 13 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {defmt_rtt as _, panic_probe as _}; |
| 14 | 13 | ||
| 15 | #[link_section = ".start_block"] | ||
| 16 | #[used] | ||
| 17 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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] | 14 | #[embassy_executor::main] |
| 30 | async fn main(_spawner: Spawner) { | 15 | async fn main(_spawner: Spawner) { |
| 31 | let p = embassy_rp::init(Default::default()); | 16 | let p = embassy_rp::init(Default::default()); |
diff --git a/examples/rp23/src/bin/uart_buffered_split.rs b/examples/rp235x/src/bin/uart_buffered_split.rs index 2b14520d5..7cad09f9b 100644 --- a/examples/rp23/src/bin/uart_buffered_split.rs +++ b/examples/rp235x/src/bin/uart_buffered_split.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | //! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip. | 1 | //! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP235x chip. |
| 2 | //! | 2 | //! |
| 3 | //! No specific hardware is specified in this example. If you connect pin 0 and 1 you should get the same data back. | 3 | //! No specific hardware is specified in this example. If you connect pin 0 and 1 you should get the same data back. |
| 4 | //! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used | 4 | //! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used |
| @@ -10,7 +10,6 @@ | |||
| 10 | use defmt::*; | 10 | use defmt::*; |
| 11 | use embassy_executor::Spawner; | 11 | use embassy_executor::Spawner; |
| 12 | use embassy_rp::bind_interrupts; | 12 | use embassy_rp::bind_interrupts; |
| 13 | use embassy_rp::block::ImageDef; | ||
| 14 | use embassy_rp::peripherals::UART0; | 13 | use embassy_rp::peripherals::UART0; |
| 15 | use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config}; | 14 | use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config}; |
| 16 | use embassy_time::Timer; | 15 | use embassy_time::Timer; |
| @@ -18,20 +17,6 @@ use embedded_io_async::{Read, Write}; | |||
| 18 | use static_cell::StaticCell; | 17 | use static_cell::StaticCell; |
| 19 | use {defmt_rtt as _, panic_probe as _}; | 18 | use {defmt_rtt as _, panic_probe as _}; |
| 20 | 19 | ||
| 21 | #[link_section = ".start_block"] | ||
| 22 | #[used] | ||
| 23 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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 { | 20 | bind_interrupts!(struct Irqs { |
| 36 | UART0_IRQ => BufferedInterruptHandler<UART0>; | 21 | UART0_IRQ => BufferedInterruptHandler<UART0>; |
| 37 | }); | 22 | }); |
| @@ -45,7 +30,7 @@ async fn main(spawner: Spawner) { | |||
| 45 | let tx_buf = &mut TX_BUF.init([0; 16])[..]; | 30 | let tx_buf = &mut TX_BUF.init([0; 16])[..]; |
| 46 | static RX_BUF: StaticCell<[u8; 16]> = StaticCell::new(); | 31 | static RX_BUF: StaticCell<[u8; 16]> = StaticCell::new(); |
| 47 | let rx_buf = &mut RX_BUF.init([0; 16])[..]; | 32 | let rx_buf = &mut RX_BUF.init([0; 16])[..]; |
| 48 | let uart = BufferedUart::new(uart, Irqs, tx_pin, rx_pin, tx_buf, rx_buf, Config::default()); | 33 | let uart = BufferedUart::new(uart, tx_pin, rx_pin, Irqs, tx_buf, rx_buf, Config::default()); |
| 49 | let (mut tx, rx) = uart.split(); | 34 | let (mut tx, rx) = uart.split(); |
| 50 | 35 | ||
| 51 | unwrap!(spawner.spawn(reader(rx))); | 36 | unwrap!(spawner.spawn(reader(rx))); |
| @@ -63,7 +48,7 @@ async fn main(spawner: Spawner) { | |||
| 63 | } | 48 | } |
| 64 | 49 | ||
| 65 | #[embassy_executor::task] | 50 | #[embassy_executor::task] |
| 66 | async fn reader(mut rx: BufferedUartRx<'static, UART0>) { | 51 | async fn reader(mut rx: BufferedUartRx) { |
| 67 | info!("Reading..."); | 52 | info!("Reading..."); |
| 68 | loop { | 53 | loop { |
| 69 | let mut buf = [0; 31]; | 54 | let mut buf = [0; 31]; |
diff --git a/examples/rp23/src/bin/uart_r503.rs b/examples/rp235x/src/bin/uart_r503.rs index 39a17d305..085be280b 100644 --- a/examples/rp23/src/bin/uart_r503.rs +++ b/examples/rp235x/src/bin/uart_r503.rs | |||
| @@ -4,27 +4,12 @@ | |||
| 4 | use defmt::{debug, error, info}; | 4 | use defmt::{debug, error, info}; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_rp::bind_interrupts; | 6 | use embassy_rp::bind_interrupts; |
| 7 | use embassy_rp::block::ImageDef; | ||
| 8 | use embassy_rp::peripherals::UART0; | 7 | use embassy_rp::peripherals::UART0; |
| 9 | use embassy_rp::uart::{Config, DataBits, InterruptHandler as UARTInterruptHandler, Parity, StopBits, Uart}; | 8 | use embassy_rp::uart::{Config, DataBits, InterruptHandler as UARTInterruptHandler, Parity, StopBits, Uart}; |
| 10 | use embassy_time::{with_timeout, Duration, Timer}; | 9 | use embassy_time::{with_timeout, Duration, Timer}; |
| 11 | use heapless::Vec; | 10 | use heapless::Vec; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 12 | ||
| 14 | #[link_section = ".start_block"] | ||
| 15 | #[used] | ||
| 16 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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 { | 13 | bind_interrupts!(pub struct Irqs { |
| 29 | UART0_IRQ => UARTInterruptHandler<UART0>; | 14 | UART0_IRQ => UARTInterruptHandler<UART0>; |
| 30 | }); | 15 | }); |
diff --git a/examples/rp23/src/bin/uart_unidir.rs b/examples/rp235x/src/bin/uart_unidir.rs index 38210a8d0..45c9c8407 100644 --- a/examples/rp23/src/bin/uart_unidir.rs +++ b/examples/rp235x/src/bin/uart_unidir.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | //! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip. | 1 | //! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP235x chip. |
| 2 | //! | 2 | //! |
| 3 | //! Test TX-only and RX-only on two different UARTs. You need to connect GPIO0 to GPIO5 for | 3 | //! Test TX-only and RX-only on two different UARTs. You need to connect GPIO0 to GPIO5 for |
| 4 | //! this to work | 4 | //! this to work |
| @@ -11,26 +11,11 @@ | |||
| 11 | use defmt::*; | 11 | use defmt::*; |
| 12 | use embassy_executor::Spawner; | 12 | use embassy_executor::Spawner; |
| 13 | use embassy_rp::bind_interrupts; | 13 | use embassy_rp::bind_interrupts; |
| 14 | use embassy_rp::block::ImageDef; | ||
| 15 | use embassy_rp::peripherals::UART1; | 14 | use embassy_rp::peripherals::UART1; |
| 16 | use embassy_rp::uart::{Async, Config, InterruptHandler, UartRx, UartTx}; | 15 | use embassy_rp::uart::{Async, Config, InterruptHandler, UartRx, UartTx}; |
| 17 | use embassy_time::Timer; | 16 | use embassy_time::Timer; |
| 18 | use {defmt_rtt as _, panic_probe as _}; | 17 | use {defmt_rtt as _, panic_probe as _}; |
| 19 | 18 | ||
| 20 | #[link_section = ".start_block"] | ||
| 21 | #[used] | ||
| 22 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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 { | 19 | bind_interrupts!(struct Irqs { |
| 35 | UART1_IRQ => InterruptHandler<UART1>; | 20 | UART1_IRQ => InterruptHandler<UART1>; |
| 36 | }); | 21 | }); |
| @@ -54,7 +39,7 @@ async fn main(spawner: Spawner) { | |||
| 54 | } | 39 | } |
| 55 | 40 | ||
| 56 | #[embassy_executor::task] | 41 | #[embassy_executor::task] |
| 57 | async fn reader(mut rx: UartRx<'static, UART1, Async>) { | 42 | async fn reader(mut rx: UartRx<'static, Async>) { |
| 58 | info!("Reading..."); | 43 | info!("Reading..."); |
| 59 | loop { | 44 | loop { |
| 60 | // read a total of 4 transmissions (32 / 8) and then print the result | 45 | // read a total of 4 transmissions (32 / 8) and then print the result |
diff --git a/examples/rp235x/src/bin/usb_hid_keyboard.rs b/examples/rp235x/src/bin/usb_hid_keyboard.rs new file mode 100644 index 000000000..6f496e23a --- /dev/null +++ b/examples/rp235x/src/bin/usb_hid_keyboard.rs | |||
| @@ -0,0 +1,188 @@ | |||
| 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::gpio::{Input, Pull}; | ||
| 11 | use embassy_rp::peripherals::USB; | ||
| 12 | use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler}; | ||
| 13 | use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State as HidState}; | ||
| 14 | use embassy_usb::control::OutResponse; | ||
| 15 | use embassy_usb::{Builder, Config, Handler}; | ||
| 16 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; | ||
| 17 | use {defmt_rtt as _, panic_probe as _}; | ||
| 18 | |||
| 19 | bind_interrupts!(struct Irqs { | ||
| 20 | USBCTRL_IRQ => InterruptHandler<USB>; | ||
| 21 | }); | ||
| 22 | |||
| 23 | #[embassy_executor::main] | ||
| 24 | async fn main(_spawner: Spawner) { | ||
| 25 | let p = embassy_rp::init(Default::default()); | ||
| 26 | // Create the driver, from the HAL. | ||
| 27 | let driver = UsbDriver::new(p.USB, Irqs); | ||
| 28 | |||
| 29 | // Create embassy-usb Config | ||
| 30 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 31 | config.manufacturer = Some("Embassy"); | ||
| 32 | config.product = Some("HID keyboard example"); | ||
| 33 | config.serial_number = Some("12345678"); | ||
| 34 | config.max_power = 100; | ||
| 35 | config.max_packet_size_0 = 64; | ||
| 36 | |||
| 37 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 38 | // It needs some buffers for building the descriptors. | ||
| 39 | let mut config_descriptor = [0; 256]; | ||
| 40 | let mut bos_descriptor = [0; 256]; | ||
| 41 | // You can also add a Microsoft OS descriptor. | ||
| 42 | let mut msos_descriptor = [0; 256]; | ||
| 43 | let mut control_buf = [0; 64]; | ||
| 44 | let mut request_handler = MyRequestHandler {}; | ||
| 45 | let mut device_handler = MyDeviceHandler::new(); | ||
| 46 | |||
| 47 | let mut state = HidState::new(); | ||
| 48 | |||
| 49 | let mut builder = Builder::new( | ||
| 50 | driver, | ||
| 51 | config, | ||
| 52 | &mut config_descriptor, | ||
| 53 | &mut bos_descriptor, | ||
| 54 | &mut msos_descriptor, | ||
| 55 | &mut control_buf, | ||
| 56 | ); | ||
| 57 | |||
| 58 | builder.handler(&mut device_handler); | ||
| 59 | |||
| 60 | // Create classes on the builder. | ||
| 61 | let config = embassy_usb::class::hid::Config { | ||
| 62 | report_descriptor: KeyboardReport::desc(), | ||
| 63 | request_handler: None, | ||
| 64 | poll_ms: 60, | ||
| 65 | max_packet_size: 64, | ||
| 66 | }; | ||
| 67 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); | ||
| 68 | |||
| 69 | // Build the builder. | ||
| 70 | let mut usb = builder.build(); | ||
| 71 | |||
| 72 | // Run the USB device. | ||
| 73 | let usb_fut = usb.run(); | ||
| 74 | |||
| 75 | // Set up the signal pin that will be used to trigger the keyboard. | ||
| 76 | let mut signal_pin = Input::new(p.PIN_16, Pull::None); | ||
| 77 | |||
| 78 | // Enable the schmitt trigger to slightly debounce. | ||
| 79 | signal_pin.set_schmitt(true); | ||
| 80 | |||
| 81 | let (reader, mut writer) = hid.split(); | ||
| 82 | |||
| 83 | // Do stuff with the class! | ||
| 84 | let in_fut = async { | ||
| 85 | loop { | ||
| 86 | info!("Waiting for HIGH on pin 16"); | ||
| 87 | signal_pin.wait_for_high().await; | ||
| 88 | info!("HIGH DETECTED"); | ||
| 89 | // Create a report with the A key pressed. (no shift modifier) | ||
| 90 | let report = KeyboardReport { | ||
| 91 | keycodes: [4, 0, 0, 0, 0, 0], | ||
| 92 | leds: 0, | ||
| 93 | modifier: 0, | ||
| 94 | reserved: 0, | ||
| 95 | }; | ||
| 96 | // Send the report. | ||
| 97 | match writer.write_serialize(&report).await { | ||
| 98 | Ok(()) => {} | ||
| 99 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 100 | }; | ||
| 101 | signal_pin.wait_for_low().await; | ||
| 102 | info!("LOW DETECTED"); | ||
| 103 | let report = KeyboardReport { | ||
| 104 | keycodes: [0, 0, 0, 0, 0, 0], | ||
| 105 | leds: 0, | ||
| 106 | modifier: 0, | ||
| 107 | reserved: 0, | ||
| 108 | }; | ||
| 109 | match writer.write_serialize(&report).await { | ||
| 110 | Ok(()) => {} | ||
| 111 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 112 | }; | ||
| 113 | } | ||
| 114 | }; | ||
| 115 | |||
| 116 | let out_fut = async { | ||
| 117 | reader.run(false, &mut request_handler).await; | ||
| 118 | }; | ||
| 119 | |||
| 120 | // Run everything concurrently. | ||
| 121 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 122 | join(usb_fut, join(in_fut, out_fut)).await; | ||
| 123 | } | ||
| 124 | |||
| 125 | struct MyRequestHandler {} | ||
| 126 | |||
| 127 | impl RequestHandler for MyRequestHandler { | ||
| 128 | fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option<usize> { | ||
| 129 | info!("Get report for {:?}", id); | ||
| 130 | None | ||
| 131 | } | ||
| 132 | |||
| 133 | fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse { | ||
| 134 | info!("Set report for {:?}: {=[u8]}", id, data); | ||
| 135 | OutResponse::Accepted | ||
| 136 | } | ||
| 137 | |||
| 138 | fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { | ||
| 139 | info!("Set idle rate for {:?} to {:?}", id, dur); | ||
| 140 | } | ||
| 141 | |||
| 142 | fn get_idle_ms(&mut self, id: Option<ReportId>) -> Option<u32> { | ||
| 143 | info!("Get idle rate for {:?}", id); | ||
| 144 | None | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | struct MyDeviceHandler { | ||
| 149 | configured: AtomicBool, | ||
| 150 | } | ||
| 151 | |||
| 152 | impl MyDeviceHandler { | ||
| 153 | fn new() -> Self { | ||
| 154 | MyDeviceHandler { | ||
| 155 | configured: AtomicBool::new(false), | ||
| 156 | } | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | impl Handler for MyDeviceHandler { | ||
| 161 | fn enabled(&mut self, enabled: bool) { | ||
| 162 | self.configured.store(false, Ordering::Relaxed); | ||
| 163 | if enabled { | ||
| 164 | info!("Device enabled"); | ||
| 165 | } else { | ||
| 166 | info!("Device disabled"); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | fn reset(&mut self) { | ||
| 171 | self.configured.store(false, Ordering::Relaxed); | ||
| 172 | info!("Bus reset, the Vbus current limit is 100mA"); | ||
| 173 | } | ||
| 174 | |||
| 175 | fn addressed(&mut self, addr: u8) { | ||
| 176 | self.configured.store(false, Ordering::Relaxed); | ||
| 177 | info!("USB address set to: {}", addr); | ||
| 178 | } | ||
| 179 | |||
| 180 | fn configured(&mut self, configured: bool) { | ||
| 181 | self.configured.store(configured, Ordering::Relaxed); | ||
| 182 | if configured { | ||
| 183 | info!("Device configured, it may now draw up to the configured current limit from Vbus.") | ||
| 184 | } else { | ||
| 185 | info!("Device is no longer configured, the Vbus current limit is 100mA."); | ||
| 186 | } | ||
| 187 | } | ||
| 188 | } | ||
diff --git a/examples/rp23/src/bin/usb_webusb.rs b/examples/rp235x/src/bin/usb_webusb.rs index f4ecde30e..75d28c853 100644 --- a/examples/rp23/src/bin/usb_webusb.rs +++ b/examples/rp235x/src/bin/usb_webusb.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | //! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip. | 1 | //! This example shows how to use USB (Universal Serial Bus) in the RP235x chip. |
| 2 | //! | 2 | //! |
| 3 | //! This creates a WebUSB capable device that echoes data back to the host. | 3 | //! This creates a WebUSB capable device that echoes data back to the host. |
| 4 | //! | 4 | //! |
| @@ -21,7 +21,6 @@ use defmt::info; | |||
| 21 | use embassy_executor::Spawner; | 21 | use embassy_executor::Spawner; |
| 22 | use embassy_futures::join::join; | 22 | use embassy_futures::join::join; |
| 23 | use embassy_rp::bind_interrupts; | 23 | use embassy_rp::bind_interrupts; |
| 24 | use embassy_rp::block::ImageDef; | ||
| 25 | use embassy_rp::peripherals::USB; | 24 | use embassy_rp::peripherals::USB; |
| 26 | use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler}; | 25 | use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler}; |
| 27 | use embassy_usb::class::web_usb::{Config as WebUsbConfig, State, Url, WebUsb}; | 26 | use embassy_usb::class::web_usb::{Config as WebUsbConfig, State, Url, WebUsb}; |
| @@ -30,20 +29,6 @@ use embassy_usb::msos::{self, windows_version}; | |||
| 30 | use embassy_usb::{Builder, Config}; | 29 | use embassy_usb::{Builder, Config}; |
| 31 | use {defmt_rtt as _, panic_probe as _}; | 30 | use {defmt_rtt as _, panic_probe as _}; |
| 32 | 31 | ||
| 33 | #[link_section = ".start_block"] | ||
| 34 | #[used] | ||
| 35 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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 { | 32 | bind_interrupts!(struct Irqs { |
| 48 | USBCTRL_IRQ => InterruptHandler<USB>; | 33 | USBCTRL_IRQ => InterruptHandler<USB>; |
| 49 | }); | 34 | }); |
| @@ -66,12 +51,6 @@ async fn main(_spawner: Spawner) { | |||
| 66 | config.max_power = 100; | 51 | config.max_power = 100; |
| 67 | config.max_packet_size_0 = 64; | 52 | config.max_packet_size_0 = 64; |
| 68 | 53 | ||
| 69 | // Required for windows compatibility. | ||
| 70 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 71 | config.device_class = 0xff; | ||
| 72 | config.device_sub_class = 0x00; | ||
| 73 | config.device_protocol = 0x00; | ||
| 74 | |||
| 75 | // Create embassy-usb DeviceBuilder using the driver and config. | 54 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 76 | // It needs some buffers for building the descriptors. | 55 | // It needs some buffers for building the descriptors. |
| 77 | let mut config_descriptor = [0; 256]; | 56 | let mut config_descriptor = [0; 256]; |
diff --git a/examples/rp23/src/bin/watchdog.rs b/examples/rp235x/src/bin/watchdog.rs index 3ac457219..a54ec493a 100644 --- a/examples/rp23/src/bin/watchdog.rs +++ b/examples/rp235x/src/bin/watchdog.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | //! This example shows how to use Watchdog in the RP2040 chip. | 1 | //! This example shows how to use Watchdog in the RP235x chip. |
| 2 | //! | 2 | //! |
| 3 | //! It does not work with the RP Pico W board. See wifi_blinky.rs or connect external LED and resistor. | 3 | //! It does not work with the RP Pico W board. See wifi_blinky.rs or connect external LED and resistor. |
| 4 | 4 | ||
| @@ -7,27 +7,12 @@ | |||
| 7 | 7 | ||
| 8 | use defmt::info; | 8 | use defmt::info; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_rp::block::ImageDef; | ||
| 11 | use embassy_rp::gpio; | 10 | use embassy_rp::gpio; |
| 12 | use embassy_rp::watchdog::*; | 11 | use embassy_rp::watchdog::*; |
| 13 | use embassy_time::{Duration, Timer}; | 12 | use embassy_time::{Duration, Timer}; |
| 14 | use gpio::{Level, Output}; | 13 | use gpio::{Level, Output}; |
| 15 | use {defmt_rtt as _, panic_probe as _}; | 14 | use {defmt_rtt as _, panic_probe as _}; |
| 16 | 15 | ||
| 17 | #[link_section = ".start_block"] | ||
| 18 | #[used] | ||
| 19 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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] | 16 | #[embassy_executor::main] |
| 32 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 33 | let p = embassy_rp::init(Default::default()); | 18 | let p = embassy_rp::init(Default::default()); |
diff --git a/examples/rp23/src/bin/zerocopy.rs b/examples/rp235x/src/bin/zerocopy.rs index d04e1bf2a..086c86cac 100644 --- a/examples/rp23/src/bin/zerocopy.rs +++ b/examples/rp235x/src/bin/zerocopy.rs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | //! This example shows how to use `zerocopy_channel` from `embassy_sync` for | 1 | //! This example shows how to use `zerocopy_channel` from `embassy_sync` for |
| 2 | //! sending large values between two tasks without copying. | 2 | //! sending large values between two tasks without copying. |
| 3 | //! The example also shows how to use the RP2040 ADC with DMA. | 3 | //! The example also shows how to use the RP235x ADC with DMA. |
| 4 | #![no_std] | 4 | #![no_std] |
| 5 | #![no_main] | 5 | #![no_main] |
| 6 | 6 | ||
| @@ -9,30 +9,15 @@ use core::sync::atomic::{AtomicU16, Ordering}; | |||
| 9 | use defmt::*; | 9 | use defmt::*; |
| 10 | use embassy_executor::Spawner; | 10 | use embassy_executor::Spawner; |
| 11 | use embassy_rp::adc::{self, Adc, Async, Config, InterruptHandler}; | 11 | use embassy_rp::adc::{self, Adc, Async, Config, InterruptHandler}; |
| 12 | use embassy_rp::bind_interrupts; | ||
| 13 | use embassy_rp::block::ImageDef; | ||
| 14 | use embassy_rp::gpio::Pull; | 12 | use embassy_rp::gpio::Pull; |
| 15 | use embassy_rp::peripherals::DMA_CH0; | 13 | use embassy_rp::peripherals::DMA_CH0; |
| 14 | use embassy_rp::{bind_interrupts, Peri}; | ||
| 16 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | 15 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; |
| 17 | use embassy_sync::zerocopy_channel::{Channel, Receiver, Sender}; | 16 | use embassy_sync::zerocopy_channel::{Channel, Receiver, Sender}; |
| 18 | use embassy_time::{Duration, Ticker, Timer}; | 17 | use embassy_time::{Duration, Ticker, Timer}; |
| 19 | use static_cell::StaticCell; | 18 | use static_cell::StaticCell; |
| 20 | use {defmt_rtt as _, panic_probe as _}; | 19 | use {defmt_rtt as _, panic_probe as _}; |
| 21 | 20 | ||
| 22 | #[link_section = ".start_block"] | ||
| 23 | #[used] | ||
| 24 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 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_cargo_bin_name!(), | ||
| 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]; | 21 | type SampleBuffer = [u16; 512]; |
| 37 | 22 | ||
| 38 | bind_interrupts!(struct Irqs { | 23 | bind_interrupts!(struct Irqs { |
| @@ -46,7 +31,7 @@ static MAX: AtomicU16 = AtomicU16::new(0); | |||
| 46 | struct AdcParts { | 31 | struct AdcParts { |
| 47 | adc: Adc<'static, Async>, | 32 | adc: Adc<'static, Async>, |
| 48 | pin: adc::Channel<'static>, | 33 | pin: adc::Channel<'static>, |
| 49 | dma: DMA_CH0, | 34 | dma: Peri<'static, DMA_CH0>, |
| 50 | } | 35 | } |
| 51 | 36 | ||
| 52 | #[embassy_executor::main] | 37 | #[embassy_executor::main] |
| @@ -85,7 +70,10 @@ async fn producer(mut sender: Sender<'static, NoopRawMutex, SampleBuffer>, mut a | |||
| 85 | let buf = sender.send().await; | 70 | let buf = sender.send().await; |
| 86 | 71 | ||
| 87 | // Fill it with data | 72 | // Fill it with data |
| 88 | adc.adc.read_many(&mut adc.pin, buf, 1, &mut adc.dma).await.unwrap(); | 73 | adc.adc |
| 74 | .read_many(&mut adc.pin, buf, 1, adc.dma.reborrow()) | ||
| 75 | .await | ||
| 76 | .unwrap(); | ||
| 89 | 77 | ||
| 90 | // Notify the channel that the buffer is now ready to be received | 78 | // Notify the channel that the buffer is now ready to be received |
| 91 | sender.send_done(); | 79 | sender.send_done(); |
diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 87491b1d2..63740963d 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml | |||
| @@ -5,12 +5,12 @@ 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.7.0", 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.7.0", path = "../../embassy-executor", features = ["arch-std", "executor-thread", "log"] } |
| 10 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["log", "std", ] } | 10 | embassy-time = { version = "0.4.0", 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.7.0", path = "../../embassy-net", features=[ "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.2.0", path = "../../embassy-net-ppp", features = ["log"]} |
| 14 | embedded-io-async = { version = "0.6.1" } | 14 | embedded-io-async = { version = "0.6.1" } |
| 15 | embedded-io-adapters = { version = "0.6.1", features = ["futures-03"] } | 15 | embedded-io-adapters = { version = "0.6.1", features = ["futures-03"] } |
| 16 | critical-section = { version = "1.1", features = ["std"] } | 16 | critical-section = { version = "1.1", features = ["std"] } |
| @@ -21,7 +21,7 @@ futures = { version = "0.3.17" } | |||
| 21 | log = "0.4.14" | 21 | log = "0.4.14" |
| 22 | nix = "0.26.2" | 22 | nix = "0.26.2" |
| 23 | clap = { version = "3.0.0-beta.5", features = ["derive"] } | 23 | clap = { version = "3.0.0-beta.5", features = ["derive"] } |
| 24 | rand_core = { version = "0.6.3", features = ["std"] } | 24 | rand_core = { version = "0.9.1", features = ["std", "os_rng"] } |
| 25 | heapless = { version = "0.8", default-features = false } | 25 | heapless = { version = "0.8", default-features = false } |
| 26 | static_cell = "2" | 26 | static_cell = "2" |
| 27 | 27 | ||
diff --git a/examples/std/README.md b/examples/std/README.md index e3a59d6ea..ac2c2a1a6 100644 --- a/examples/std/README.md +++ b/examples/std/README.md | |||
| @@ -1,23 +1,128 @@ | |||
| 1 | 1 | ||
| 2 | ## Running the `embassy-net` examples | 2 | ## Running the `embassy-net` examples |
| 3 | 3 | ||
| 4 | First, create the tap0 interface. You only need to do this once. | 4 | To run `net`, `tcp_accept`, `net_udp` and `net_dns` examples you will need a tap interface. Before running these examples, create the tap99 interface. (The number was chosen to |
| 5 | hopefully not collide with anything.) You only need to do this once every time you reboot your computer. | ||
| 5 | 6 | ||
| 6 | ```sh | 7 | ```sh |
| 7 | sudo ip tuntap add name tap0 mode tap user $USER | 8 | cd $EMBASSY_ROOT/examples/std/ |
| 8 | sudo ip link set tap0 up | 9 | sudo sh tap.sh |
| 9 | sudo ip addr add 192.168.69.100/24 dev tap0 | ||
| 10 | sudo ip -6 addr add fe80::100/64 dev tap0 | ||
| 11 | sudo ip -6 addr add fdaa::100/64 dev tap0 | ||
| 12 | sudo ip -6 route add fe80::/64 dev tap0 | ||
| 13 | sudo ip -6 route add fdaa::/64 dev tap0 | ||
| 14 | ``` | 10 | ``` |
| 15 | 11 | ||
| 16 | Second, have something listening there. For example `nc -lp 8000` | 12 | The example `net_ppp` requires different steps that are detailed in its section. |
| 13 | |||
| 14 | ### `net` example | ||
| 15 | |||
| 16 | For this example, you need to have something listening in the correct port. For example `nc -lp 8000`. | ||
| 17 | 17 | ||
| 18 | Then run the example located in the `examples` folder: | 18 | Then run the example located in the `examples` folder: |
| 19 | 19 | ||
| 20 | ```sh | 20 | ```sh |
| 21 | cd $EMBASSY_ROOT/examples/std/ | 21 | cd $EMBASSY_ROOT/examples/std/ |
| 22 | cargo run --bin net -- --static-ip | 22 | cargo run --bin net -- --tap tap99 --static-ip |
| 23 | ``` | ||
| 24 | ### `tcp_accept` example | ||
| 25 | |||
| 26 | This example listen for a tcp connection. | ||
| 27 | |||
| 28 | First run the example located in the `examples` folder: | ||
| 29 | |||
| 30 | ```sh | ||
| 31 | cd $EMBASSY_ROOT/examples/std/ | ||
| 32 | cargo run --bin tcp_accept -- --tap tap99 --static-ip | ||
| 33 | ``` | ||
| 34 | |||
| 35 | Then open a connection to the port. For example `nc 192.168.69.2 9999`. | ||
| 36 | |||
| 37 | ### `net_udp` example | ||
| 38 | |||
| 39 | This example listen for a udp connection. | ||
| 40 | |||
| 41 | First run the example located in the `examples` folder: | ||
| 42 | |||
| 43 | ```sh | ||
| 44 | cd $EMBASSY_ROOT/examples/std/ | ||
| 45 | cargo run --bin net_udp -- --tap tap99 --static-ip | ||
| 46 | ``` | ||
| 47 | |||
| 48 | Then open a connection to the port. For example `nc -u 192.168.69.2 9400`. | ||
| 49 | |||
| 50 | ### `net_dns` example | ||
| 51 | |||
| 52 | This example queries a `DNS` for the IP address of `www.example.com`. | ||
| 53 | |||
| 54 | In order to achieve this, the `tap99` interface requires configuring tap99 as a gateway device temporarily. | ||
| 55 | |||
| 56 | For example, in Ubuntu you can do this by: | ||
| 57 | |||
| 58 | 1. Identifying your default route device. In the next example `eth0` | ||
| 59 | |||
| 60 | ```sh | ||
| 61 | ip r | grep "default" | ||
| 62 | default via 192.168.2.1 dev eth0 proto kernel metric 35 | ||
| 63 | ``` | ||
| 64 | |||
| 65 | 2. Enabling temporarily IP Forwarding: | ||
| 66 | |||
| 67 | ```sh | ||
| 68 | sudo sysctl -w net.ipv4.ip_forward=1 | ||
| 69 | ``` | ||
| 70 | |||
| 71 | 3. Configuring NAT to mascarade traffic from `tap99` to `eth0` | ||
| 72 | |||
| 73 | ```sh | ||
| 74 | sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE | ||
| 75 | sudo iptables -A FORWARD -i tap99 -j ACCEPT | ||
| 76 | sudo iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT | ||
| 77 | ``` | ||
| 78 | |||
| 79 | 4. Then you can run the example located in the `examples` folder: | ||
| 80 | |||
| 81 | ```sh | ||
| 82 | cd $EMBASSY_ROOT/examples/std/ | ||
| 83 | cargo run --bin net_dns -- --tap tap99 --static-ip | ||
| 84 | ``` | ||
| 85 | |||
| 86 | ### `net_ppp` example | ||
| 87 | |||
| 88 | This example establish a Point-to-Point Protocol (PPP) connection that can be used, for example, for connecting to internet through a 4G modem via a serial channel. | ||
| 89 | |||
| 90 | The example creates a PPP bridge over a virtual serial channel between `pty1` and `pty2` for the example code and a PPP server running on the same computer. | ||
| 91 | |||
| 92 | To run this example you will need: | ||
| 93 | - ppp (pppd server) | ||
| 94 | - socat (socket CAT) | ||
| 95 | |||
| 96 | To run the examples you may follow the next steps: | ||
| 97 | |||
| 98 | 1. Save the PPP server configuration: | ||
| 99 | ```sh | ||
| 100 | sudo sh -c 'echo "myuser $(hostname) mypass 192.168.7.10" >> /etc/ppp/pap-secrets' | ||
| 101 | ``` | ||
| 102 | |||
| 103 | 2. Create a files `pty1` and `pty2` and link them | ||
| 104 | ```sh | ||
| 105 | cd $EMBASSY_ROOT/examples/std/ | ||
| 106 | socat -v -x PTY,link=pty1,rawer PTY,link=pty2,rawer | ||
| 107 | ``` | ||
| 108 | |||
| 109 | 3. open a second terminal and start the PPP server: | ||
| 110 | ```sh | ||
| 111 | cd $EMBASSY_ROOT/examples/std/ | ||
| 112 | sudo pppd $PWD/pty1 115200 192.168.7.1: ms-dns 8.8.4.4 ms-dns 8.8.8.8 nodetach debug local persist silent | ||
| 113 | ``` | ||
| 114 | |||
| 115 | 4. Open a third terminal and run the example | ||
| 116 | ```sh | ||
| 117 | cd $EMBASSY_ROOT/examples/std/ | ||
| 118 | RUST_LOG=trace cargo run --bin net_ppp -- --device pty2 | ||
| 119 | ``` | ||
| 120 | 5. Observe the output in the second and third terminal | ||
| 121 | 6. Open one last terminal to interact with `net_ppp` example through the PPP connection | ||
| 122 | ```sh | ||
| 123 | # ping the net_ppp client | ||
| 124 | ping 192.168.7.10 | ||
| 125 | # open an tcp connection | ||
| 126 | nc 192.168.7.10 1234 | ||
| 127 | # Type anything and observe the output in the different terminals | ||
| 23 | ``` | 128 | ``` |
diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index 310e7264d..232cf494b 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs | |||
| @@ -1,13 +1,15 @@ | |||
| 1 | use core::fmt::Write as _; | ||
| 2 | |||
| 1 | use clap::Parser; | 3 | use clap::Parser; |
| 2 | use embassy_executor::{Executor, Spawner}; | 4 | use embassy_executor::{Executor, Spawner}; |
| 3 | use embassy_net::tcp::TcpSocket; | 5 | use embassy_net::tcp::TcpSocket; |
| 4 | use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; | 6 | use embassy_net::{Config, Ipv4Address, Ipv4Cidr, StackResources}; |
| 5 | use embassy_net_tuntap::TunTapDevice; | 7 | use embassy_net_tuntap::TunTapDevice; |
| 6 | use embassy_time::Duration; | 8 | use embassy_time::Duration; |
| 7 | use embedded_io_async::Write; | 9 | use embedded_io_async::Write; |
| 8 | use heapless::Vec; | 10 | use heapless::Vec; |
| 9 | use log::*; | 11 | use log::*; |
| 10 | use rand_core::{OsRng, RngCore}; | 12 | use rand_core::{OsRng, TryRngCore}; |
| 11 | use static_cell::StaticCell; | 13 | use static_cell::StaticCell; |
| 12 | 14 | ||
| 13 | #[derive(Parser)] | 15 | #[derive(Parser)] |
| @@ -22,8 +24,8 @@ struct Opts { | |||
| 22 | } | 24 | } |
| 23 | 25 | ||
| 24 | #[embassy_executor::task] | 26 | #[embassy_executor::task] |
| 25 | async fn net_task(stack: &'static Stack<TunTapDevice>) -> ! { | 27 | async fn net_task(mut runner: embassy_net::Runner<'static, TunTapDevice>) -> ! { |
| 26 | stack.run().await | 28 | runner.run().await |
| 27 | } | 29 | } |
| 28 | 30 | ||
| 29 | #[embassy_executor::task] | 31 | #[embassy_executor::task] |
| @@ -46,16 +48,15 @@ async fn main_task(spawner: Spawner) { | |||
| 46 | 48 | ||
| 47 | // Generate random seed | 49 | // Generate random seed |
| 48 | let mut seed = [0; 8]; | 50 | let mut seed = [0; 8]; |
| 49 | OsRng.fill_bytes(&mut seed); | 51 | OsRng.try_fill_bytes(&mut seed).unwrap(); |
| 50 | let seed = u64::from_le_bytes(seed); | 52 | let seed = u64::from_le_bytes(seed); |
| 51 | 53 | ||
| 52 | // Init network stack | 54 | // Init network stack |
| 53 | static STACK: StaticCell<Stack<TunTapDevice>> = StaticCell::new(); | ||
| 54 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 55 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 55 | let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); | 56 | let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); |
| 56 | 57 | ||
| 57 | // Launch network task | 58 | // Launch network task |
| 58 | spawner.spawn(net_task(stack)).unwrap(); | 59 | spawner.spawn(net_task(runner)).unwrap(); |
| 59 | 60 | ||
| 60 | // Then we can use it! | 61 | // Then we can use it! |
| 61 | let mut rx_buffer = [0; 4096]; | 62 | let mut rx_buffer = [0; 4096]; |
| @@ -72,8 +73,10 @@ async fn main_task(spawner: Spawner) { | |||
| 72 | return; | 73 | return; |
| 73 | } | 74 | } |
| 74 | info!("connected!"); | 75 | info!("connected!"); |
| 75 | loop { | 76 | for i in 0.. { |
| 76 | let r = socket.write_all(b"Hello!\n").await; | 77 | let mut buf = heapless::String::<100>::new(); |
| 78 | write!(buf, "Hello! ({})\r\n", i).unwrap(); | ||
| 79 | let r = socket.write_all(buf.as_bytes()).await; | ||
| 77 | if let Err(e) = r { | 80 | if let Err(e) = r { |
| 78 | warn!("write error: {:?}", e); | 81 | warn!("write error: {:?}", e); |
| 79 | return; | 82 | return; |
diff --git a/examples/std/src/bin/net_dns.rs b/examples/std/src/bin/net_dns.rs index c9615ef35..cf90731dd 100644 --- a/examples/std/src/bin/net_dns.rs +++ b/examples/std/src/bin/net_dns.rs | |||
| @@ -1,11 +1,11 @@ | |||
| 1 | use clap::Parser; | 1 | use clap::Parser; |
| 2 | use embassy_executor::{Executor, Spawner}; | 2 | use embassy_executor::{Executor, Spawner}; |
| 3 | use embassy_net::dns::DnsQueryType; | 3 | use embassy_net::dns::DnsQueryType; |
| 4 | use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; | 4 | use embassy_net::{Config, Ipv4Address, Ipv4Cidr, StackResources}; |
| 5 | use embassy_net_tuntap::TunTapDevice; | 5 | use embassy_net_tuntap::TunTapDevice; |
| 6 | use heapless::Vec; | 6 | use heapless::Vec; |
| 7 | use log::*; | 7 | use log::*; |
| 8 | use rand_core::{OsRng, RngCore}; | 8 | use rand_core::{OsRng, TryRngCore}; |
| 9 | use static_cell::StaticCell; | 9 | use static_cell::StaticCell; |
| 10 | 10 | ||
| 11 | #[derive(Parser)] | 11 | #[derive(Parser)] |
| @@ -20,8 +20,8 @@ struct Opts { | |||
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | #[embassy_executor::task] | 22 | #[embassy_executor::task] |
| 23 | async fn net_task(stack: &'static Stack<TunTapDevice>) -> ! { | 23 | async fn net_task(mut runner: embassy_net::Runner<'static, TunTapDevice>) -> ! { |
| 24 | stack.run().await | 24 | runner.run().await |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | #[embassy_executor::task] | 27 | #[embassy_executor::task] |
| @@ -45,16 +45,15 @@ async fn main_task(spawner: Spawner) { | |||
| 45 | 45 | ||
| 46 | // Generate random seed | 46 | // Generate random seed |
| 47 | let mut seed = [0; 8]; | 47 | let mut seed = [0; 8]; |
| 48 | OsRng.fill_bytes(&mut seed); | 48 | OsRng.try_fill_bytes(&mut seed).unwrap(); |
| 49 | let seed = u64::from_le_bytes(seed); | 49 | let seed = u64::from_le_bytes(seed); |
| 50 | 50 | ||
| 51 | // Init network stack | 51 | // Init network stack |
| 52 | static STACK: StaticCell<Stack<TunTapDevice>> = StaticCell::new(); | ||
| 53 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 52 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 54 | let stack: &Stack<_> = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); | 53 | let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); |
| 55 | 54 | ||
| 56 | // Launch network task | 55 | // Launch network task |
| 57 | spawner.spawn(net_task(stack)).unwrap(); | 56 | spawner.spawn(net_task(runner)).unwrap(); |
| 58 | 57 | ||
| 59 | let host = "example.com"; | 58 | let host = "example.com"; |
| 60 | info!("querying host {:?}...", host); | 59 | info!("querying host {:?}...", host); |
diff --git a/examples/std/src/bin/net_ppp.rs b/examples/std/src/bin/net_ppp.rs index c5c27c4a3..ac3aea6ff 100644 --- a/examples/std/src/bin/net_ppp.rs +++ b/examples/std/src/bin/net_ppp.rs | |||
| @@ -16,14 +16,14 @@ 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; |
| 23 | use heapless::Vec; | 23 | use heapless::Vec; |
| 24 | use log::*; | 24 | use log::*; |
| 25 | use nix::sys::termios; | 25 | use nix::sys::termios; |
| 26 | use rand_core::{OsRng, RngCore}; | 26 | use rand_core::{OsRng, TryRngCore}; |
| 27 | use static_cell::StaticCell; | 27 | use static_cell::StaticCell; |
| 28 | 28 | ||
| 29 | use crate::serial_port::SerialPort; | 29 | use crate::serial_port::SerialPort; |
| @@ -37,19 +37,15 @@ struct Opts { | |||
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | #[embassy_executor::task] | 39 | #[embassy_executor::task] |
| 40 | async fn net_task(stack: &'static Stack<embassy_net_ppp::Device<'static>>) -> ! { | 40 | async fn net_task(mut runner: embassy_net::Runner<'static, embassy_net_ppp::Device<'static>>) -> ! { |
| 41 | stack.run().await | 41 | runner.run().await |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | #[embassy_executor::task] | 44 | #[embassy_executor::task] |
| 45 | async fn ppp_task( | 45 | async fn ppp_task(stack: Stack<'static>, mut runner: Runner<'static>, port: SerialPort) -> ! { |
| 46 | stack: &'static Stack<embassy_net_ppp::Device<'static>>, | ||
| 47 | mut runner: Runner<'static>, | ||
| 48 | port: SerialPort, | ||
| 49 | ) -> ! { | ||
| 50 | let port = Async::new(port).unwrap(); | 46 | let port = Async::new(port).unwrap(); |
| 51 | let port = BufReader::new(port); | 47 | let port = BufReader::new(port); |
| 52 | let port = adapter::FromFutures::new(port); | 48 | let port = embedded_io_adapters::futures_03::FromFutures::new(port); |
| 53 | 49 | ||
| 54 | let config = embassy_net_ppp::Config { | 50 | let config = embassy_net_ppp::Config { |
| 55 | username: b"myuser", | 51 | username: b"myuser", |
| @@ -64,10 +60,10 @@ async fn ppp_task( | |||
| 64 | }; | 60 | }; |
| 65 | let mut dns_servers = Vec::new(); | 61 | let mut dns_servers = Vec::new(); |
| 66 | for s in ipv4.dns_servers.iter().flatten() { | 62 | for s in ipv4.dns_servers.iter().flatten() { |
| 67 | let _ = dns_servers.push(Ipv4Address::from_bytes(&s.0)); | 63 | let _ = dns_servers.push(*s); |
| 68 | } | 64 | } |
| 69 | let config = ConfigV4::Static(embassy_net::StaticConfigV4 { | 65 | let config = ConfigV4::Static(embassy_net::StaticConfigV4 { |
| 70 | address: Ipv4Cidr::new(Ipv4Address::from_bytes(&addr.0), 0), | 66 | address: Ipv4Cidr::new(addr, 0), |
| 71 | gateway: None, | 67 | gateway: None, |
| 72 | dns_servers, | 68 | dns_servers, |
| 73 | }); | 69 | }); |
| @@ -93,21 +89,20 @@ async fn main_task(spawner: Spawner) { | |||
| 93 | 89 | ||
| 94 | // Generate random seed | 90 | // Generate random seed |
| 95 | let mut seed = [0; 8]; | 91 | let mut seed = [0; 8]; |
| 96 | OsRng.fill_bytes(&mut seed); | 92 | OsRng.try_fill_bytes(&mut seed).unwrap(); |
| 97 | let seed = u64::from_le_bytes(seed); | 93 | let seed = u64::from_le_bytes(seed); |
| 98 | 94 | ||
| 99 | // Init network stack | 95 | // Init network stack |
| 100 | static STACK: StaticCell<Stack<embassy_net_ppp::Device<'static>>> = StaticCell::new(); | ||
| 101 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 96 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 102 | let stack = &*STACK.init(Stack::new( | 97 | let (stack, net_runner) = embassy_net::new( |
| 103 | device, | 98 | device, |
| 104 | Config::default(), // don't configure IP yet | 99 | Config::default(), // don't configure IP yet |
| 105 | RESOURCES.init(StackResources::new()), | 100 | RESOURCES.init(StackResources::new()), |
| 106 | seed, | 101 | seed, |
| 107 | )); | 102 | ); |
| 108 | 103 | ||
| 109 | // Launch network task | 104 | // Launch network task |
| 110 | spawner.spawn(net_task(stack)).unwrap(); | 105 | spawner.spawn(net_task(net_runner)).unwrap(); |
| 111 | spawner.spawn(ppp_task(stack, runner, port)).unwrap(); | 106 | spawner.spawn(ppp_task(stack, runner, port)).unwrap(); |
| 112 | 107 | ||
| 113 | // Then we can use it! | 108 | // Then we can use it! |
| @@ -168,53 +163,3 @@ fn main() { | |||
| 168 | spawner.spawn(main_task(spawner)).unwrap(); | 163 | spawner.spawn(main_task(spawner)).unwrap(); |
| 169 | }); | 164 | }); |
| 170 | } | 165 | } |
| 171 | |||
| 172 | mod adapter { | ||
| 173 | use core::future::poll_fn; | ||
| 174 | use core::pin::Pin; | ||
| 175 | |||
| 176 | use futures::AsyncBufReadExt; | ||
| 177 | |||
| 178 | /// Adapter from `futures::io` traits. | ||
| 179 | #[derive(Clone)] | ||
| 180 | pub struct FromFutures<T: ?Sized> { | ||
| 181 | inner: T, | ||
| 182 | } | ||
| 183 | |||
| 184 | impl<T> FromFutures<T> { | ||
| 185 | /// Create a new adapter. | ||
| 186 | pub fn new(inner: T) -> Self { | ||
| 187 | Self { inner } | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | impl<T: ?Sized> embedded_io_async::ErrorType for FromFutures<T> { | ||
| 192 | type Error = std::io::Error; | ||
| 193 | } | ||
| 194 | |||
| 195 | impl<T: futures::io::AsyncRead + Unpin + ?Sized> embedded_io_async::Read for FromFutures<T> { | ||
| 196 | async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { | ||
| 197 | poll_fn(|cx| Pin::new(&mut self.inner).poll_read(cx, buf)).await | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 201 | impl<T: futures::io::AsyncBufRead + Unpin + ?Sized> embedded_io_async::BufRead for FromFutures<T> { | ||
| 202 | async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { | ||
| 203 | self.inner.fill_buf().await | ||
| 204 | } | ||
| 205 | |||
| 206 | fn consume(&mut self, amt: usize) { | ||
| 207 | Pin::new(&mut self.inner).consume(amt) | ||
| 208 | } | ||
| 209 | } | ||
| 210 | |||
| 211 | impl<T: futures::io::AsyncWrite + Unpin + ?Sized> embedded_io_async::Write for FromFutures<T> { | ||
| 212 | async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { | ||
| 213 | poll_fn(|cx| Pin::new(&mut self.inner).poll_write(cx, buf)).await | ||
| 214 | } | ||
| 215 | |||
| 216 | async fn flush(&mut self) -> Result<(), Self::Error> { | ||
| 217 | poll_fn(|cx| Pin::new(&mut self.inner).poll_flush(cx)).await | ||
| 218 | } | ||
| 219 | } | ||
| 220 | } | ||
diff --git a/examples/std/src/bin/net_udp.rs b/examples/std/src/bin/net_udp.rs index b2ba4915a..53632a5b4 100644 --- a/examples/std/src/bin/net_udp.rs +++ b/examples/std/src/bin/net_udp.rs | |||
| @@ -1,11 +1,11 @@ | |||
| 1 | use clap::Parser; | 1 | use clap::Parser; |
| 2 | use embassy_executor::{Executor, Spawner}; | 2 | use embassy_executor::{Executor, Spawner}; |
| 3 | use embassy_net::udp::{PacketMetadata, UdpSocket}; | 3 | use embassy_net::udp::{PacketMetadata, UdpSocket}; |
| 4 | use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; | 4 | use embassy_net::{Config, Ipv4Address, Ipv4Cidr, StackResources}; |
| 5 | use embassy_net_tuntap::TunTapDevice; | 5 | use embassy_net_tuntap::TunTapDevice; |
| 6 | use heapless::Vec; | 6 | use heapless::Vec; |
| 7 | use log::*; | 7 | use log::*; |
| 8 | use rand_core::{OsRng, RngCore}; | 8 | use rand_core::{OsRng, TryRngCore}; |
| 9 | use static_cell::StaticCell; | 9 | use static_cell::StaticCell; |
| 10 | 10 | ||
| 11 | #[derive(Parser)] | 11 | #[derive(Parser)] |
| @@ -20,8 +20,8 @@ struct Opts { | |||
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | #[embassy_executor::task] | 22 | #[embassy_executor::task] |
| 23 | async fn net_task(stack: &'static Stack<TunTapDevice>) -> ! { | 23 | async fn net_task(mut runner: embassy_net::Runner<'static, TunTapDevice>) -> ! { |
| 24 | stack.run().await | 24 | runner.run().await |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | #[embassy_executor::task] | 27 | #[embassy_executor::task] |
| @@ -44,16 +44,15 @@ async fn main_task(spawner: Spawner) { | |||
| 44 | 44 | ||
| 45 | // Generate random seed | 45 | // Generate random seed |
| 46 | let mut seed = [0; 8]; | 46 | let mut seed = [0; 8]; |
| 47 | OsRng.fill_bytes(&mut seed); | 47 | OsRng.try_fill_bytes(&mut seed).unwrap(); |
| 48 | let seed = u64::from_le_bytes(seed); | 48 | let seed = u64::from_le_bytes(seed); |
| 49 | 49 | ||
| 50 | // Init network stack | 50 | // Init network stack |
| 51 | static STACK: StaticCell<Stack<TunTapDevice>> = StaticCell::new(); | ||
| 52 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 51 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 53 | let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); | 52 | let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); |
| 54 | 53 | ||
| 55 | // Launch network task | 54 | // Launch network task |
| 56 | spawner.spawn(net_task(stack)).unwrap(); | 55 | spawner.spawn(net_task(runner)).unwrap(); |
| 57 | 56 | ||
| 58 | // Then we can use it! | 57 | // Then we can use it! |
| 59 | let mut rx_meta = [PacketMetadata::EMPTY; 16]; | 58 | let mut rx_meta = [PacketMetadata::EMPTY; 16]; |
diff --git a/examples/std/src/bin/tcp_accept.rs b/examples/std/src/bin/tcp_accept.rs index 39b29a449..961c20e2d 100644 --- a/examples/std/src/bin/tcp_accept.rs +++ b/examples/std/src/bin/tcp_accept.rs | |||
| @@ -1,15 +1,13 @@ | |||
| 1 | use core::fmt::Write as _; | ||
| 2 | |||
| 3 | use clap::Parser; | 1 | use clap::Parser; |
| 4 | use embassy_executor::{Executor, Spawner}; | 2 | use embassy_executor::{Executor, Spawner}; |
| 5 | use embassy_net::tcp::TcpSocket; | 3 | use embassy_net::tcp::TcpSocket; |
| 6 | use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; | 4 | use embassy_net::{Config, Ipv4Address, Ipv4Cidr, StackResources}; |
| 7 | use embassy_net_tuntap::TunTapDevice; | 5 | use embassy_net_tuntap::TunTapDevice; |
| 8 | use embassy_time::{Duration, Timer}; | 6 | use embassy_time::{Duration, Timer}; |
| 9 | use embedded_io_async::Write as _; | 7 | use embedded_io_async::Write as _; |
| 10 | use heapless::Vec; | 8 | use heapless::Vec; |
| 11 | use log::*; | 9 | use log::*; |
| 12 | use rand_core::{OsRng, RngCore}; | 10 | use rand_core::{OsRng, TryRngCore}; |
| 13 | use static_cell::StaticCell; | 11 | use static_cell::StaticCell; |
| 14 | 12 | ||
| 15 | #[derive(Parser)] | 13 | #[derive(Parser)] |
| @@ -24,18 +22,8 @@ struct Opts { | |||
| 24 | } | 22 | } |
| 25 | 23 | ||
| 26 | #[embassy_executor::task] | 24 | #[embassy_executor::task] |
| 27 | async fn net_task(stack: &'static Stack<TunTapDevice>) -> ! { | 25 | async fn net_task(mut runner: embassy_net::Runner<'static, TunTapDevice>) -> ! { |
| 28 | stack.run().await | 26 | runner.run().await |
| 29 | } | ||
| 30 | |||
| 31 | #[derive(Default)] | ||
| 32 | struct StrWrite(pub heapless::Vec<u8, 30>); | ||
| 33 | |||
| 34 | impl core::fmt::Write for StrWrite { | ||
| 35 | fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { | ||
| 36 | self.0.extend_from_slice(s.as_bytes()).unwrap(); | ||
| 37 | Ok(()) | ||
| 38 | } | ||
| 39 | } | 27 | } |
| 40 | 28 | ||
| 41 | #[embassy_executor::task] | 29 | #[embassy_executor::task] |
| @@ -58,16 +46,15 @@ async fn main_task(spawner: Spawner) { | |||
| 58 | 46 | ||
| 59 | // Generate random seed | 47 | // Generate random seed |
| 60 | let mut seed = [0; 8]; | 48 | let mut seed = [0; 8]; |
| 61 | OsRng.fill_bytes(&mut seed); | 49 | OsRng.try_fill_bytes(&mut seed).unwrap(); |
| 62 | let seed = u64::from_le_bytes(seed); | 50 | let seed = u64::from_le_bytes(seed); |
| 63 | 51 | ||
| 64 | // Init network stack | 52 | // Init network stack |
| 65 | static STACK: StaticCell<Stack<TunTapDevice>> = StaticCell::new(); | ||
| 66 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 53 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 67 | let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); | 54 | let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); |
| 68 | 55 | ||
| 69 | // Launch network task | 56 | // Launch network task |
| 70 | spawner.spawn(net_task(stack)).unwrap(); | 57 | spawner.spawn(net_task(runner)).unwrap(); |
| 71 | 58 | ||
| 72 | // Then we can use it! | 59 | // Then we can use it! |
| 73 | let mut rx_buffer = [0; 4096]; | 60 | let mut rx_buffer = [0; 4096]; |
| @@ -86,9 +73,8 @@ async fn main_task(spawner: Spawner) { | |||
| 86 | 73 | ||
| 87 | // Write some quick output | 74 | // Write some quick output |
| 88 | for i in 1..=5 { | 75 | for i in 1..=5 { |
| 89 | let mut w = StrWrite::default(); | 76 | let s = format!("{}! ", i); |
| 90 | write!(w, "{}! ", i).unwrap(); | 77 | let r = socket.write_all(s.as_bytes()).await; |
| 91 | let r = socket.write_all(&w.0).await; | ||
| 92 | if let Err(e) = r { | 78 | if let Err(e) = r { |
| 93 | warn!("write error: {:?}", e); | 79 | warn!("write error: {:?}", e); |
| 94 | return; | 80 | return; |
diff --git a/examples/std/tap.sh b/examples/std/tap.sh new file mode 100644 index 000000000..fb89d2381 --- /dev/null +++ b/examples/std/tap.sh | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | ip tuntap add name tap99 mode tap user $SUDO_USER | ||
| 2 | ip link set tap99 up | ||
| 3 | ip addr add 192.168.69.100/24 dev tap99 | ||
| 4 | ip -6 addr add fe80::100/64 dev tap99 | ||
| 5 | ip -6 addr add fdaa::100/64 dev tap99 | ||
| 6 | ip -6 route add fe80::/64 dev tap99 | ||
| 7 | ip -6 route add fdaa::/64 dev tap99 | ||
diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index 9102467eb..4cf07cef4 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml | |||
| @@ -6,18 +6,18 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 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.2.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.7.0", 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.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | 13 | ||
| 14 | defmt = "0.3" | 14 | defmt = "1.0.1" |
| 15 | defmt-rtt = "0.4" | 15 | defmt-rtt = "1.0.0" |
| 16 | 16 | ||
| 17 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 17 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 18 | cortex-m-rt = "0.7.0" | 18 | cortex-m-rt = "0.7.0" |
| 19 | embedded-hal = "0.2.6" | 19 | embedded-hal = "0.2.6" |
| 20 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 20 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 21 | heapless = { version = "0.8", default-features = false } | 21 | heapless = { version = "0.8", default-features = false } |
| 22 | 22 | ||
| 23 | [profile.release] | 23 | [profile.release] |
diff --git a/examples/stm32c0/src/bin/adc.rs b/examples/stm32c0/src/bin/adc.rs new file mode 100644 index 000000000..1f54b0b18 --- /dev/null +++ b/examples/stm32c0/src/bin/adc.rs | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::adc::vals::Scandir; | ||
| 7 | use embassy_stm32::adc::{Adc, AdcChannel, AnyAdcChannel, Resolution, SampleTime}; | ||
| 8 | use embassy_stm32::peripherals::ADC1; | ||
| 9 | use embassy_time::Timer; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_spawner: Spawner) { | ||
| 14 | let config = Default::default(); | ||
| 15 | let p = embassy_stm32::init(config); | ||
| 16 | |||
| 17 | info!("ADC STM32C0 example."); | ||
| 18 | |||
| 19 | // We need to set certain sample time to be able to read temp sensor. | ||
| 20 | let mut adc = Adc::new(p.ADC1, SampleTime::CYCLES12_5, Resolution::BITS12); | ||
| 21 | let mut temp = adc.enable_temperature().degrade_adc(); | ||
| 22 | let mut vref = adc.enable_vrefint().degrade_adc(); | ||
| 23 | let mut pin0 = p.PA0.degrade_adc(); | ||
| 24 | |||
| 25 | let mut dma = p.DMA1_CH1; | ||
| 26 | let mut read_buffer: [u16; 3] = [0; 3]; | ||
| 27 | |||
| 28 | loop { | ||
| 29 | info!("============================"); | ||
| 30 | let blocking_temp = adc.blocking_read(&mut temp); | ||
| 31 | let blocking_vref = adc.blocking_read(&mut vref); | ||
| 32 | let blocing_pin0 = adc.blocking_read(&mut pin0); | ||
| 33 | info!( | ||
| 34 | "Blocking ADC read: vref = {}, temp = {}, pin0 = {}.", | ||
| 35 | blocking_vref, blocking_temp, blocing_pin0 | ||
| 36 | ); | ||
| 37 | |||
| 38 | let channels_seqence: [&mut AnyAdcChannel<ADC1>; 3] = [&mut vref, &mut temp, &mut pin0]; | ||
| 39 | adc.read(dma.reborrow(), channels_seqence.into_iter(), &mut read_buffer) | ||
| 40 | .await; | ||
| 41 | // Values are ordered according to hardware ADC channel number! | ||
| 42 | info!( | ||
| 43 | "DMA ADC read in set: vref = {}, temp = {}, pin0 = {}.", | ||
| 44 | read_buffer[0], read_buffer[1], read_buffer[2] | ||
| 45 | ); | ||
| 46 | |||
| 47 | let hw_channel_selection: u32 = | ||
| 48 | (1 << temp.get_hw_channel()) + (1 << vref.get_hw_channel()) + (1 << pin0.get_hw_channel()); | ||
| 49 | adc.read_in_hw_order(dma.reborrow(), hw_channel_selection, Scandir::UP, &mut read_buffer) | ||
| 50 | .await; | ||
| 51 | info!( | ||
| 52 | "DMA ADC read in hardware order: vref = {}, temp = {}, pin0 = {}.", | ||
| 53 | read_buffer[2], read_buffer[1], read_buffer[0] | ||
| 54 | ); | ||
| 55 | |||
| 56 | Timer::after_millis(2000).await; | ||
| 57 | } | ||
| 58 | } | ||
diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index 724efdaff..400e6b94c 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml | |||
| @@ -6,15 +6,15 @@ 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.2.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 = "1.0.1" |
| 13 | defmt-rtt = "0.4" | 13 | defmt-rtt = "1.0.0" |
| 14 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 14 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 15 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 15 | embassy-sync = { version = "0.7.0", 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.7.0", 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.4.0", 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"] } |
| 20 | 20 | ||
diff --git a/examples/stm32f0/src/bin/button_controlled_blink.rs b/examples/stm32f0/src/bin/button_controlled_blink.rs index 4465483d9..744df3e3b 100644 --- a/examples/stm32f0/src/bin/button_controlled_blink.rs +++ b/examples/stm32f0/src/bin/button_controlled_blink.rs | |||
| @@ -8,14 +8,15 @@ use core::sync::atomic::{AtomicU32, Ordering}; | |||
| 8 | use defmt::info; | 8 | use defmt::info; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_stm32::exti::ExtiInput; | 10 | use embassy_stm32::exti::ExtiInput; |
| 11 | use embassy_stm32::gpio::{AnyPin, Level, Output, Pin, Pull, Speed}; | 11 | use embassy_stm32::gpio::{AnyPin, Level, Output, Pull, Speed}; |
| 12 | use embassy_stm32::Peri; | ||
| 12 | use embassy_time::Timer; | 13 | use embassy_time::Timer; |
| 13 | use {defmt_rtt as _, panic_probe as _}; | 14 | use {defmt_rtt as _, panic_probe as _}; |
| 14 | 15 | ||
| 15 | static BLINK_MS: AtomicU32 = AtomicU32::new(0); | 16 | static BLINK_MS: AtomicU32 = AtomicU32::new(0); |
| 16 | 17 | ||
| 17 | #[embassy_executor::task] | 18 | #[embassy_executor::task] |
| 18 | async fn led_task(led: AnyPin) { | 19 | async fn led_task(led: Peri<'static, AnyPin>) { |
| 19 | // Configure the LED pin as a push pull output and obtain handler. | 20 | // Configure the LED pin as a push pull output and obtain handler. |
| 20 | // On the Nucleo F091RC there's an on-board LED connected to pin PA5. | 21 | // On the Nucleo F091RC there's an on-board LED connected to pin PA5. |
| 21 | let mut led = Output::new(led, Level::Low, Speed::Low); | 22 | let mut led = Output::new(led, Level::Low, Speed::Low); |
| @@ -45,7 +46,7 @@ async fn main(spawner: Spawner) { | |||
| 45 | BLINK_MS.store(del_var, Ordering::Relaxed); | 46 | BLINK_MS.store(del_var, Ordering::Relaxed); |
| 46 | 47 | ||
| 47 | // Spawn LED blinking task | 48 | // Spawn LED blinking task |
| 48 | spawner.spawn(led_task(p.PA5.degrade())).unwrap(); | 49 | spawner.spawn(led_task(p.PA5.into())).unwrap(); |
| 49 | 50 | ||
| 50 | loop { | 51 | loop { |
| 51 | // Check if button got pressed | 52 | // Check if button got pressed |
diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 0084651a3..261733305 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml | |||
| @@ -6,20 +6,20 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 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.2.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.7.0", 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.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 13 | embassy-usb = { version = "0.4.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" } |
| 15 | 15 | ||
| 16 | defmt = "0.3" | 16 | defmt = "1.0.1" |
| 17 | defmt-rtt = "0.4" | 17 | defmt-rtt = "1.0.0" |
| 18 | 18 | ||
| 19 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 19 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 20 | cortex-m-rt = "0.7.0" | 20 | 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 = "1.0.0", features = ["print-defmt"] } |
| 23 | heapless = { version = "0.8", default-features = false } | 23 | heapless = { version = "0.8", default-features = false } |
| 24 | nb = "1.0.0" | 24 | nb = "1.0.0" |
| 25 | static_cell = "2.0.0" | 25 | static_cell = "2.0.0" |
diff --git a/examples/stm32f1/src/bin/input_capture.rs b/examples/stm32f1/src/bin/input_capture.rs index 5e2dab9e6..6fe8e0b50 100644 --- a/examples/stm32f1/src/bin/input_capture.rs +++ b/examples/stm32f1/src/bin/input_capture.rs | |||
| @@ -7,14 +7,14 @@ use embassy_stm32::gpio::{Level, Output, Pull, Speed}; | |||
| 7 | use embassy_stm32::time::khz; | 7 | use embassy_stm32::time::khz; |
| 8 | use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; | 8 | use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; |
| 9 | use embassy_stm32::timer::{self, Channel}; | 9 | use embassy_stm32::timer::{self, Channel}; |
| 10 | use embassy_stm32::{bind_interrupts, peripherals}; | 10 | use embassy_stm32::{bind_interrupts, peripherals, Peri}; |
| 11 | use embassy_time::Timer; | 11 | use embassy_time::Timer; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 13 | ||
| 14 | /// Connect PA2 and PC13 with a 1k Ohm resistor | 14 | /// Connect PA2 and PC13 with a 1k Ohm resistor |
| 15 | 15 | ||
| 16 | #[embassy_executor::task] | 16 | #[embassy_executor::task] |
| 17 | async fn blinky(led: peripherals::PC13) { | 17 | async fn blinky(led: Peri<'static, peripherals::PC13>) { |
| 18 | let mut led = Output::new(led, Level::High, Speed::Low); | 18 | let mut led = Output::new(led, Level::High, Speed::Low); |
| 19 | 19 | ||
| 20 | loop { | 20 | loop { |
diff --git a/examples/stm32f1/src/bin/pwm_input.rs b/examples/stm32f1/src/bin/pwm_input.rs index f74853d4e..afbef3edb 100644 --- a/examples/stm32f1/src/bin/pwm_input.rs +++ b/examples/stm32f1/src/bin/pwm_input.rs | |||
| @@ -6,14 +6,14 @@ use embassy_executor::Spawner; | |||
| 6 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; | 6 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; |
| 7 | use embassy_stm32::time::khz; | 7 | use embassy_stm32::time::khz; |
| 8 | use embassy_stm32::timer::pwm_input::PwmInput; | 8 | use embassy_stm32::timer::pwm_input::PwmInput; |
| 9 | use embassy_stm32::{bind_interrupts, peripherals, timer}; | 9 | use embassy_stm32::{bind_interrupts, peripherals, timer, Peri}; |
| 10 | use embassy_time::Timer; | 10 | use embassy_time::Timer; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 12 | ||
| 13 | /// Connect PA0 and PC13 with a 1k Ohm resistor | 13 | /// Connect PA0 and PC13 with a 1k Ohm resistor |
| 14 | 14 | ||
| 15 | #[embassy_executor::task] | 15 | #[embassy_executor::task] |
| 16 | async fn blinky(led: peripherals::PC13) { | 16 | async fn blinky(led: Peri<'static, peripherals::PC13>) { |
| 17 | let mut led = Output::new(led, Level::High, Speed::Low); | 17 | let mut led = Output::new(led, Level::High, Speed::Low); |
| 18 | 18 | ||
| 19 | loop { | 19 | loop { |
diff --git a/examples/stm32f1/src/bin/usb_serial.rs b/examples/stm32f1/src/bin/usb_serial.rs index ee99acf41..77ec307b9 100644 --- a/examples/stm32f1/src/bin/usb_serial.rs +++ b/examples/stm32f1/src/bin/usb_serial.rs | |||
| @@ -47,7 +47,7 @@ async fn main(_spawner: Spawner) { | |||
| 47 | // Pull the D+ pin down to send a RESET condition to the USB bus. | 47 | // Pull the D+ pin down to send a RESET condition to the USB bus. |
| 48 | // This forced reset is needed only for development, without it host | 48 | // This forced reset is needed only for development, without it host |
| 49 | // will not reset your device when you upload new firmware. | 49 | // will not reset your device when you upload new firmware. |
| 50 | let _dp = Output::new(&mut p.PA12, Level::Low, Speed::Low); | 50 | let _dp = Output::new(p.PA12.reborrow(), Level::Low, Speed::Low); |
| 51 | Timer::after_millis(10).await; | 51 | Timer::after_millis(10).await; |
| 52 | } | 52 | } |
| 53 | 53 | ||
diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index 60eb0eb93..905cffff0 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml | |||
| @@ -6,18 +6,18 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 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.2.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.7.0", 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.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | 13 | ||
| 14 | defmt = "0.3" | 14 | defmt = "1.0.1" |
| 15 | defmt-rtt = "0.4" | 15 | defmt-rtt = "1.0.0" |
| 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"] } |
| 18 | cortex-m-rt = "0.7.0" | 18 | cortex-m-rt = "0.7.0" |
| 19 | embedded-hal = "0.2.6" | 19 | embedded-hal = "0.2.6" |
| 20 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 20 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 21 | heapless = { version = "0.8", default-features = false } | 21 | heapless = { version = "0.8", default-features = false } |
| 22 | nb = "1.0.0" | 22 | nb = "1.0.0" |
| 23 | 23 | ||
diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 7fda410d9..f675b0be1 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml | |||
| @@ -6,20 +6,20 @@ 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.2.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.7.0", 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.7.0", 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.4.0", 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.4.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" } |
| 15 | 15 | ||
| 16 | defmt = "0.3" | 16 | defmt = "1.0.1" |
| 17 | defmt-rtt = "0.4" | 17 | defmt-rtt = "1.0.0" |
| 18 | 18 | ||
| 19 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 19 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 20 | cortex-m-rt = "0.7.0" | 20 | 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 = "1.0.0", features = ["print-defmt"] } |
| 23 | heapless = { version = "0.8", default-features = false } | 23 | heapless = { version = "0.8", default-features = false } |
| 24 | nb = "1.0.0" | 24 | nb = "1.0.0" |
| 25 | embedded-storage = "0.3.1" | 25 | embedded-storage = "0.3.1" |
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..b47a81e1b 100644 --- a/examples/stm32f334/Cargo.toml +++ b/examples/stm32f334/Cargo.toml | |||
| @@ -5,20 +5,20 @@ 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.7.0", 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.7.0", 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.4.0", 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.2.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.4.0", path = "../../embassy-usb", features = ["defmt"] } |
| 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 14 | 14 | ||
| 15 | defmt = "0.3" | 15 | defmt = "1.0.1" |
| 16 | defmt-rtt = "0.4" | 16 | defmt-rtt = "1.0.0" |
| 17 | 17 | ||
| 18 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 18 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 19 | cortex-m-rt = "0.7.0" | 19 | cortex-m-rt = "0.7.0" |
| 20 | embedded-hal = "0.2.6" | 20 | embedded-hal = "0.2.6" |
| 21 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 21 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 22 | heapless = { version = "0.8", default-features = false } | 22 | heapless = { version = "0.8", default-features = false } |
| 23 | nb = "1.0.0" | 23 | nb = "1.0.0" |
| 24 | embedded-storage = "0.3.1" | 24 | embedded-storage = "0.3.1" |
diff --git a/examples/stm32f334/src/bin/opamp.rs b/examples/stm32f334/src/bin/opamp.rs index 2dbf1bdab..c344935d7 100644 --- a/examples/stm32f334/src/bin/opamp.rs +++ b/examples/stm32f334/src/bin/opamp.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | use defmt::info; | 4 | use defmt::info; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::adc::{Adc, SampleTime}; | 6 | use embassy_stm32::adc::{Adc, SampleTime}; |
| 7 | use embassy_stm32::opamp::{OpAmp, OpAmpGain}; | 7 | use embassy_stm32::opamp::OpAmp; |
| 8 | use embassy_stm32::peripherals::ADC2; | 8 | use embassy_stm32::peripherals::ADC2; |
| 9 | use embassy_stm32::time::mhz; | 9 | use embassy_stm32::time::mhz; |
| 10 | use embassy_stm32::{adc, bind_interrupts, Config}; | 10 | use embassy_stm32::{adc, bind_interrupts, Config}; |
| @@ -48,7 +48,7 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 48 | 48 | ||
| 49 | let mut vrefint = adc.enable_vref(); | 49 | let mut vrefint = adc.enable_vref(); |
| 50 | let mut temperature = adc.enable_temperature(); | 50 | let mut temperature = adc.enable_temperature(); |
| 51 | let mut buffer = opamp.buffer_ext(&mut p.PA7, &mut p.PA6, OpAmpGain::Mul1); | 51 | let mut buffer = opamp.buffer_ext(p.PA7.reborrow(), p.PA6.reborrow()); |
| 52 | 52 | ||
| 53 | loop { | 53 | loop { |
| 54 | let vref = adc.read(&mut vrefint).await; | 54 | let vref = adc.read(&mut vrefint).await; |
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..edab9ea00 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml | |||
| @@ -6,17 +6,17 @@ 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.2.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.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.7.0", 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.4.0", 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.4.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.7.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.2.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 | ||
| 18 | defmt = "0.3" | 18 | defmt = "1.0.1" |
| 19 | defmt-rtt = "0.4" | 19 | defmt-rtt = "1.0.0" |
| 20 | 20 | ||
| 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 22 | cortex-m-rt = "0.7.0" | 22 | cortex-m-rt = "0.7.0" |
| @@ -24,9 +24,10 @@ embedded-hal = "0.2.6" | |||
| 24 | embedded-hal-bus = { version = "0.2", features = ["async"] } | 24 | embedded-hal-bus = { version = "0.2", features = ["async"] } |
| 25 | embedded-io = { version = "0.6.0" } | 25 | embedded-io = { version = "0.6.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 = "1.0.0", 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/can.rs b/examples/stm32f4/src/bin/can.rs index 8e3beee24..fd90e0d6d 100644 --- a/examples/stm32f4/src/bin/can.rs +++ b/examples/stm32f4/src/bin/can.rs | |||
| @@ -30,7 +30,7 @@ async fn main(_spawner: Spawner) { | |||
| 30 | // To synchronise to the bus the RX input needs to see a high level. | 30 | // To synchronise to the bus the RX input needs to see a high level. |
| 31 | // Use `mem::forget()` to release the borrow on the pin but keep the | 31 | // Use `mem::forget()` to release the borrow on the pin but keep the |
| 32 | // pull-up resistor enabled. | 32 | // pull-up resistor enabled. |
| 33 | let rx_pin = Input::new(&mut p.PA11, Pull::Up); | 33 | let rx_pin = Input::new(p.PA11.reborrow(), Pull::Up); |
| 34 | core::mem::forget(rx_pin); | 34 | core::mem::forget(rx_pin); |
| 35 | 35 | ||
| 36 | let mut can = Can::new(p.CAN1, p.PA11, p.PA12, Irqs); | 36 | let mut can = Can::new(p.CAN1, p.PA11, p.PA12, Irqs); |
diff --git a/examples/stm32f4/src/bin/dac.rs b/examples/stm32f4/src/bin/dac.rs index dd2a45718..68fe6cabd 100644 --- a/examples/stm32f4/src/bin/dac.rs +++ b/examples/stm32f4/src/bin/dac.rs | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::dac::{DacCh1, Value}; | 6 | use embassy_stm32::dac::{DacCh1, Value}; |
| 7 | use embassy_stm32::dma::NoDma; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 7 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 8 | ||
| 10 | #[embassy_executor::main] | 9 | #[embassy_executor::main] |
| @@ -12,7 +11,7 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 12 | let p = embassy_stm32::init(Default::default()); | 11 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World, dude!"); | 12 | info!("Hello World, dude!"); |
| 14 | 13 | ||
| 15 | let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); | 14 | let mut dac = DacCh1::new_blocking(p.DAC1, p.PA4); |
| 16 | 15 | ||
| 17 | loop { | 16 | loop { |
| 18 | for v in 0..=255 { | 17 | for v in 0..=255 { |
diff --git a/examples/stm32f4/src/bin/eth.rs b/examples/stm32f4/src/bin/eth.rs index 9388c64bf..634d8e2c6 100644 --- a/examples/stm32f4/src/bin/eth.rs +++ b/examples/stm32f4/src/bin/eth.rs | |||
| @@ -4,9 +4,8 @@ | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_net::tcp::TcpSocket; | 6 | use embassy_net::tcp::TcpSocket; |
| 7 | use embassy_net::{Ipv4Address, Stack, StackResources}; | 7 | use embassy_net::{Ipv4Address, StackResources}; |
| 8 | use embassy_stm32::eth::generic_smi::GenericSMI; | 8 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; |
| 9 | use embassy_stm32::eth::{Ethernet, PacketQueue}; | ||
| 10 | use embassy_stm32::peripherals::ETH; | 9 | use embassy_stm32::peripherals::ETH; |
| 11 | use embassy_stm32::rng::Rng; | 10 | use embassy_stm32::rng::Rng; |
| 12 | use embassy_stm32::time::Hertz; | 11 | use embassy_stm32::time::Hertz; |
| @@ -21,11 +20,11 @@ bind_interrupts!(struct Irqs { | |||
| 21 | HASH_RNG => rng::InterruptHandler<peripherals::RNG>; | 20 | HASH_RNG => rng::InterruptHandler<peripherals::RNG>; |
| 22 | }); | 21 | }); |
| 23 | 22 | ||
| 24 | type Device = Ethernet<'static, ETH, GenericSMI>; | 23 | type Device = Ethernet<'static, ETH, GenericPhy>; |
| 25 | 24 | ||
| 26 | #[embassy_executor::task] | 25 | #[embassy_executor::task] |
| 27 | async fn net_task(stack: &'static Stack<Device>) -> ! { | 26 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { |
| 28 | stack.run().await | 27 | runner.run().await |
| 29 | } | 28 | } |
| 30 | 29 | ||
| 31 | #[embassy_executor::main] | 30 | #[embassy_executor::main] |
| @@ -76,7 +75,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 76 | p.PG13, | 75 | p.PG13, |
| 77 | p.PB13, | 76 | p.PB13, |
| 78 | p.PG11, | 77 | p.PG11, |
| 79 | GenericSMI::new(0), | 78 | GenericPhy::new_auto(), |
| 80 | mac_addr, | 79 | mac_addr, |
| 81 | ); | 80 | ); |
| 82 | 81 | ||
| @@ -88,12 +87,11 @@ async fn main(spawner: Spawner) -> ! { | |||
| 88 | //}); | 87 | //}); |
| 89 | 88 | ||
| 90 | // Init network stack | 89 | // Init network stack |
| 91 | static STACK: StaticCell<Stack<Device>> = StaticCell::new(); | ||
| 92 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 90 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 93 | let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); | 91 | let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); |
| 94 | 92 | ||
| 95 | // Launch network task | 93 | // Launch network task |
| 96 | unwrap!(spawner.spawn(net_task(stack))); | 94 | unwrap!(spawner.spawn(net_task(runner))); |
| 97 | 95 | ||
| 98 | // Ensure DHCP configuration is up before trying connect | 96 | // Ensure DHCP configuration is up before trying connect |
| 99 | stack.wait_config_up().await; | 97 | stack.wait_config_up().await; |
| @@ -105,7 +103,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 105 | let mut tx_buffer = [0; 4096]; | 103 | let mut tx_buffer = [0; 4096]; |
| 106 | 104 | ||
| 107 | loop { | 105 | loop { |
| 108 | let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); | 106 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); |
| 109 | 107 | ||
| 110 | socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); | 108 | socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); |
| 111 | 109 | ||
diff --git a/examples/stm32f4/src/bin/eth_compliance_test.rs b/examples/stm32f4/src/bin/eth_compliance_test.rs new file mode 100644 index 000000000..52f9d57f6 --- /dev/null +++ b/examples/stm32f4/src/bin/eth_compliance_test.rs | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, StationManagement}; | ||
| 7 | use embassy_stm32::time::Hertz; | ||
| 8 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; | ||
| 9 | use embassy_time::Timer; | ||
| 10 | use static_cell::StaticCell; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | bind_interrupts!(struct Irqs { | ||
| 14 | ETH => eth::InterruptHandler; | ||
| 15 | HASH_RNG => rng::InterruptHandler<peripherals::RNG>; | ||
| 16 | }); | ||
| 17 | |||
| 18 | #[embassy_executor::main] | ||
| 19 | async fn main(_spawner: Spawner) -> ! { | ||
| 20 | let mut config = Config::default(); | ||
| 21 | { | ||
| 22 | use embassy_stm32::rcc::*; | ||
| 23 | config.rcc.hse = Some(Hse { | ||
| 24 | freq: Hertz(8_000_000), | ||
| 25 | mode: HseMode::Bypass, | ||
| 26 | }); | ||
| 27 | config.rcc.pll_src = PllSource::HSE; | ||
| 28 | config.rcc.pll = Some(Pll { | ||
| 29 | prediv: PllPreDiv::DIV4, | ||
| 30 | mul: PllMul::MUL180, | ||
| 31 | divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz. | ||
| 32 | divq: None, | ||
| 33 | divr: None, | ||
| 34 | }); | ||
| 35 | config.rcc.ahb_pre = AHBPrescaler::DIV1; | ||
| 36 | config.rcc.apb1_pre = APBPrescaler::DIV4; | ||
| 37 | config.rcc.apb2_pre = APBPrescaler::DIV2; | ||
| 38 | config.rcc.sys = Sysclk::PLL1_P; | ||
| 39 | } | ||
| 40 | let p = embassy_stm32::init(config); | ||
| 41 | |||
| 42 | info!("Hello Compliance World!"); | ||
| 43 | |||
| 44 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; | ||
| 45 | |||
| 46 | const PHY_ADDR: u8 = 0; | ||
| 47 | static PACKETS: StaticCell<PacketQueue<4, 4>> = StaticCell::new(); | ||
| 48 | let mut device = Ethernet::new( | ||
| 49 | PACKETS.init(PacketQueue::<4, 4>::new()), | ||
| 50 | p.ETH, | ||
| 51 | Irqs, | ||
| 52 | p.PA1, | ||
| 53 | p.PA2, | ||
| 54 | p.PC1, | ||
| 55 | p.PA7, | ||
| 56 | p.PC4, | ||
| 57 | p.PC5, | ||
| 58 | p.PG13, | ||
| 59 | p.PB13, | ||
| 60 | p.PG11, | ||
| 61 | GenericPhy::new(PHY_ADDR), | ||
| 62 | mac_addr, | ||
| 63 | ); | ||
| 64 | |||
| 65 | let sm = device.station_management(); | ||
| 66 | |||
| 67 | // Just an example. Exact register settings depend on the specific PHY and test. | ||
| 68 | sm.smi_write(PHY_ADDR, 0, 0x2100); | ||
| 69 | sm.smi_write(PHY_ADDR, 11, 0xA000); | ||
| 70 | |||
| 71 | // NB: Remember to reset the PHY after testing before starting the networking stack | ||
| 72 | |||
| 73 | loop { | ||
| 74 | Timer::after_secs(1).await; | ||
| 75 | } | ||
| 76 | } | ||
diff --git a/examples/stm32f4/src/bin/eth_w5500.rs b/examples/stm32f4/src/bin/eth_w5500.rs index 5c3c6c3ba..6e6bef08c 100644 --- a/examples/stm32f4/src/bin/eth_w5500.rs +++ b/examples/stm32f4/src/bin/eth_w5500.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_net::tcp::TcpSocket; | 6 | use embassy_net::tcp::TcpSocket; |
| 7 | use embassy_net::{Ipv4Address, Stack, StackResources}; | 7 | use embassy_net::{Ipv4Address, StackResources}; |
| 8 | use embassy_net_wiznet::chip::W5500; | 8 | use embassy_net_wiznet::chip::W5500; |
| 9 | use embassy_net_wiznet::{Device, Runner, State}; | 9 | use embassy_net_wiznet::{Device, Runner, State}; |
| 10 | use embassy_stm32::exti::ExtiInput; | 10 | use embassy_stm32::exti::ExtiInput; |
| @@ -31,8 +31,8 @@ async fn ethernet_task(runner: Runner<'static, W5500, EthernetSPI, ExtiInput<'st | |||
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | #[embassy_executor::task] | 33 | #[embassy_executor::task] |
| 34 | async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { | 34 | async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! { |
| 35 | stack.run().await | 35 | runner.run().await |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | #[embassy_executor::main] | 38 | #[embassy_executor::main] |
| @@ -92,12 +92,11 @@ async fn main(spawner: Spawner) -> ! { | |||
| 92 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | 92 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), |
| 93 | //}); | 93 | //}); |
| 94 | 94 | ||
| 95 | static STACK: StaticCell<Stack<Device>> = StaticCell::new(); | ||
| 96 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 95 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 97 | let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); | 96 | let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); |
| 98 | 97 | ||
| 99 | // Launch network task | 98 | // Launch network task |
| 100 | unwrap!(spawner.spawn(net_task(stack))); | 99 | unwrap!(spawner.spawn(net_task(runner))); |
| 101 | 100 | ||
| 102 | // Ensure DHCP configuration is up before trying connect | 101 | // Ensure DHCP configuration is up before trying connect |
| 103 | stack.wait_config_up().await; | 102 | stack.wait_config_up().await; |
diff --git a/examples/stm32f4/src/bin/flash_async.rs b/examples/stm32f4/src/bin/flash_async.rs index 493a536f3..755713542 100644 --- a/examples/stm32f4/src/bin/flash_async.rs +++ b/examples/stm32f4/src/bin/flash_async.rs | |||
| @@ -3,9 +3,9 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::{info, unwrap}; | 4 | use defmt::{info, unwrap}; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::bind_interrupts; | ||
| 7 | use embassy_stm32::flash::{Flash, InterruptHandler}; | 6 | use embassy_stm32::flash::{Flash, InterruptHandler}; |
| 8 | use embassy_stm32::gpio::{AnyPin, Level, Output, Pin, Speed}; | 7 | use embassy_stm32::gpio::{AnyPin, Level, Output, Speed}; |
| 8 | use embassy_stm32::{bind_interrupts, Peri}; | ||
| 9 | use embassy_time::Timer; | 9 | use embassy_time::Timer; |
| 10 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 11 | ||
| @@ -21,14 +21,14 @@ async fn main(spawner: Spawner) { | |||
| 21 | let mut f = Flash::new(p.FLASH, Irqs); | 21 | let mut f = Flash::new(p.FLASH, Irqs); |
| 22 | 22 | ||
| 23 | // Led should blink uninterrupted during ~2sec erase operation | 23 | // Led should blink uninterrupted during ~2sec erase operation |
| 24 | spawner.spawn(blinky(p.PB7.degrade())).unwrap(); | 24 | spawner.spawn(blinky(p.PB7.into())).unwrap(); |
| 25 | 25 | ||
| 26 | // Test on bank 2 in order not to stall CPU. | 26 | // Test on bank 2 in order not to stall CPU. |
| 27 | test_flash(&mut f, 1024 * 1024, 128 * 1024).await; | 27 | test_flash(&mut f, 1024 * 1024, 128 * 1024).await; |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | #[embassy_executor::task] | 30 | #[embassy_executor::task] |
| 31 | async fn blinky(p: AnyPin) { | 31 | async fn blinky(p: Peri<'static, AnyPin>) { |
| 32 | let mut led = Output::new(p, Level::High, Speed::Low); | 32 | let mut led = Output::new(p, Level::High, Speed::Low); |
| 33 | 33 | ||
| 34 | loop { | 34 | loop { |
diff --git a/examples/stm32f4/src/bin/i2s_dma.rs b/examples/stm32f4/src/bin/i2s_dma.rs index 27b165f1b..db5103d0f 100644 --- a/examples/stm32f4/src/bin/i2s_dma.rs +++ b/examples/stm32f4/src/bin/i2s_dma.rs | |||
| @@ -1,34 +1,83 @@ | |||
| 1 | // This example is written for an STM32F411 chip communicating with an external | ||
| 2 | // PCM5102a DAC. Remap pins, change clock speeds, etc. as necessary for your own | ||
| 3 | // hardware. | ||
| 4 | // | ||
| 5 | // NOTE: This example outputs potentially loud audio. Please run responsibly. | ||
| 6 | |||
| 1 | #![no_std] | 7 | #![no_std] |
| 2 | #![no_main] | 8 | #![no_main] |
| 3 | 9 | ||
| 4 | use core::fmt::Write; | ||
| 5 | |||
| 6 | use defmt::*; | ||
| 7 | use embassy_executor::Spawner; | 10 | use embassy_executor::Spawner; |
| 8 | use embassy_stm32::i2s::{Config, I2S}; | 11 | use embassy_stm32::i2s::{Config, Format, I2S}; |
| 9 | use embassy_stm32::time::Hertz; | 12 | use embassy_stm32::time::Hertz; |
| 10 | use heapless::String; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 14 | ||
| 13 | #[embassy_executor::main] | 15 | #[embassy_executor::main] |
| 14 | async fn main(_spawner: Spawner) { | 16 | async fn main(_spawner: Spawner) { |
| 15 | let p = embassy_stm32::init(Default::default()); | 17 | let config = { |
| 16 | info!("Hello World!"); | 18 | use embassy_stm32::rcc::*; |
| 17 | 19 | ||
| 18 | let mut i2s = I2S::new_txonly( | 20 | let mut config = embassy_stm32::Config::default(); |
| 19 | p.SPI2, | 21 | config.rcc.hse = Some(Hse { |
| 20 | p.PC3, // sd | 22 | freq: Hertz::mhz(25), |
| 21 | p.PB12, // ws | 23 | mode: HseMode::Oscillator, |
| 22 | p.PB10, // ck | 24 | }); |
| 23 | p.PC6, // mck | 25 | config.rcc.pll_src = PllSource::HSE; |
| 24 | p.DMA1_CH4, | 26 | config.rcc.pll = Some(Pll { |
| 25 | Hertz(1_000_000), | 27 | prediv: PllPreDiv::DIV25, |
| 26 | Config::default(), | 28 | mul: PllMul::MUL192, |
| 29 | divp: Some(PllPDiv::DIV2), | ||
| 30 | divq: Some(PllQDiv::DIV4), | ||
| 31 | divr: None, | ||
| 32 | }); | ||
| 33 | config.rcc.sys = Sysclk::PLL1_P; | ||
| 34 | |||
| 35 | config.rcc.ahb_pre = AHBPrescaler::DIV1; | ||
| 36 | config.rcc.apb1_pre = APBPrescaler::DIV2; | ||
| 37 | config.rcc.apb2_pre = APBPrescaler::DIV1; | ||
| 38 | |||
| 39 | // reference your chip's manual for proper clock settings; this config | ||
| 40 | // is recommended for a 32 bit frame at 48 kHz sample rate | ||
| 41 | config.rcc.plli2s = Some(Pll { | ||
| 42 | prediv: PllPreDiv::DIV25, | ||
| 43 | mul: PllMul::MUL384, | ||
| 44 | divp: None, | ||
| 45 | divq: None, | ||
| 46 | divr: Some(PllRDiv::DIV5), | ||
| 47 | }); | ||
| 48 | config.enable_debug_during_sleep = true; | ||
| 49 | |||
| 50 | config | ||
| 51 | }; | ||
| 52 | |||
| 53 | let p = embassy_stm32::init(config); | ||
| 54 | |||
| 55 | // stereo wavetable generation | ||
| 56 | let mut wavetable = [0u16; 1200]; | ||
| 57 | for (i, frame) in wavetable.chunks_mut(2).enumerate() { | ||
| 58 | frame[0] = ((((i / 150) % 2) * 2048) as i16 - 1024) as u16; // 160 Hz square wave in left channel | ||
| 59 | frame[1] = ((((i / 100) % 2) * 2048) as i16 - 1024) as u16; // 240 Hz square wave in right channel | ||
| 60 | } | ||
| 61 | |||
| 62 | // i2s configuration | ||
| 63 | let mut dma_buffer = [0u16; 2400]; | ||
| 64 | |||
| 65 | let mut i2s_config = Config::default(); | ||
| 66 | i2s_config.format = Format::Data16Channel32; | ||
| 67 | i2s_config.master_clock = false; | ||
| 68 | let mut i2s = I2S::new_txonly_nomck( | ||
| 69 | p.SPI3, | ||
| 70 | p.PB5, // sd | ||
| 71 | p.PA15, // ws | ||
| 72 | p.PB3, // ck | ||
| 73 | p.DMA1_CH7, | ||
| 74 | &mut dma_buffer, | ||
| 75 | Hertz(48_000), | ||
| 76 | i2s_config, | ||
| 27 | ); | 77 | ); |
| 78 | i2s.start(); | ||
| 28 | 79 | ||
| 29 | for n in 0u32.. { | 80 | loop { |
| 30 | let mut write: String<128> = String::new(); | 81 | i2s.write(&wavetable).await.ok(); |
| 31 | core::write!(&mut write, "Hello DMA World {}!\r\n", n).unwrap(); | ||
| 32 | i2s.write(&mut write.as_bytes()).await.ok(); | ||
| 33 | } | 82 | } |
| 34 | } | 83 | } |
diff --git a/examples/stm32f4/src/bin/input_capture.rs b/examples/stm32f4/src/bin/input_capture.rs index 49de33d2b..fe5e2bdfc 100644 --- a/examples/stm32f4/src/bin/input_capture.rs +++ b/examples/stm32f4/src/bin/input_capture.rs | |||
| @@ -7,14 +7,14 @@ use embassy_stm32::gpio::{Level, Output, Pull, Speed}; | |||
| 7 | use embassy_stm32::time::khz; | 7 | use embassy_stm32::time::khz; |
| 8 | use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; | 8 | use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; |
| 9 | use embassy_stm32::timer::{self, Channel}; | 9 | use embassy_stm32::timer::{self, Channel}; |
| 10 | use embassy_stm32::{bind_interrupts, peripherals}; | 10 | use embassy_stm32::{bind_interrupts, peripherals, Peri}; |
| 11 | use embassy_time::Timer; | 11 | use embassy_time::Timer; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 13 | ||
| 14 | /// Connect PB2 and PB10 with a 1k Ohm resistor | 14 | /// Connect PB2 and PB10 with a 1k Ohm resistor |
| 15 | 15 | ||
| 16 | #[embassy_executor::task] | 16 | #[embassy_executor::task] |
| 17 | async fn blinky(led: peripherals::PB2) { | 17 | async fn blinky(led: Peri<'static, peripherals::PB2>) { |
| 18 | let mut led = Output::new(led, Level::High, Speed::Low); | 18 | let mut led = Output::new(led, Level::High, Speed::Low); |
| 19 | 19 | ||
| 20 | loop { | 20 | loop { |
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/pwm_input.rs b/examples/stm32f4/src/bin/pwm_input.rs index ce200549d..465cbe4f5 100644 --- a/examples/stm32f4/src/bin/pwm_input.rs +++ b/examples/stm32f4/src/bin/pwm_input.rs | |||
| @@ -6,14 +6,14 @@ use embassy_executor::Spawner; | |||
| 6 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; | 6 | use embassy_stm32::gpio::{Level, Output, Pull, Speed}; |
| 7 | use embassy_stm32::time::khz; | 7 | use embassy_stm32::time::khz; |
| 8 | use embassy_stm32::timer::pwm_input::PwmInput; | 8 | use embassy_stm32::timer::pwm_input::PwmInput; |
| 9 | use embassy_stm32::{bind_interrupts, peripherals, timer}; | 9 | use embassy_stm32::{bind_interrupts, peripherals, timer, Peri}; |
| 10 | use embassy_time::Timer; | 10 | use embassy_time::Timer; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 12 | ||
| 13 | /// Connect PB2 and PA6 with a 1k Ohm resistor | 13 | /// Connect PB2 and PA6 with a 1k Ohm resistor |
| 14 | 14 | ||
| 15 | #[embassy_executor::task] | 15 | #[embassy_executor::task] |
| 16 | async fn blinky(led: peripherals::PB2) { | 16 | async fn blinky(led: Peri<'static, peripherals::PB2>) { |
| 17 | let mut led = Output::new(led, Level::High, Speed::Low); | 17 | let mut led = Output::new(led, Level::High, Speed::Low); |
| 18 | 18 | ||
| 19 | loop { | 19 | loop { |
diff --git a/examples/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs index 66e4e527c..e97b63925 100644 --- a/examples/stm32f4/src/bin/sdmmc.rs +++ b/examples/stm32f4/src/bin/sdmmc.rs | |||
| @@ -59,7 +59,7 @@ async fn main(_spawner: Spawner) { | |||
| 59 | 59 | ||
| 60 | let mut err = None; | 60 | let mut err = None; |
| 61 | loop { | 61 | loop { |
| 62 | match sdmmc.init_card(mhz(24)).await { | 62 | match sdmmc.init_sd_card(mhz(24)).await { |
| 63 | Ok(_) => break, | 63 | Ok(_) => break, |
| 64 | Err(e) => { | 64 | Err(e) => { |
| 65 | if err != Some(e) { | 65 | if err != Some(e) { |
diff --git a/examples/stm32f4/src/bin/usart_buffered.rs b/examples/stm32f4/src/bin/usart_buffered.rs index c99807f11..3b6cdad8d 100644 --- a/examples/stm32f4/src/bin/usart_buffered.rs +++ b/examples/stm32f4/src/bin/usart_buffered.rs | |||
| @@ -21,7 +21,7 @@ async fn main(_spawner: Spawner) { | |||
| 21 | 21 | ||
| 22 | let mut tx_buf = [0u8; 32]; | 22 | let mut tx_buf = [0u8; 32]; |
| 23 | let mut rx_buf = [0u8; 32]; | 23 | let mut rx_buf = [0u8; 32]; |
| 24 | let mut buf_usart = BufferedUart::new(p.USART3, Irqs, p.PD9, p.PD8, &mut tx_buf, &mut rx_buf, config).unwrap(); | 24 | let mut buf_usart = BufferedUart::new(p.USART3, p.PD9, p.PD8, &mut tx_buf, &mut rx_buf, Irqs, config).unwrap(); |
| 25 | 25 | ||
| 26 | loop { | 26 | loop { |
| 27 | let buf = buf_usart.fill_buf().await.unwrap(); | 27 | let buf = buf_usart.fill_buf().await.unwrap(); |
diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index 94e51c338..322cb90c7 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_net::tcp::TcpSocket; | 6 | use embassy_net::tcp::TcpSocket; |
| 7 | use embassy_net::{Stack, StackResources}; | 7 | use embassy_net::StackResources; |
| 8 | use embassy_stm32::rng::{self, Rng}; | 8 | use embassy_stm32::rng::{self, Rng}; |
| 9 | use embassy_stm32::time::Hertz; | 9 | use embassy_stm32::time::Hertz; |
| 10 | use embassy_stm32::usb::Driver; | 10 | use embassy_stm32::usb::Driver; |
| @@ -31,8 +31,8 @@ async fn usb_ncm_task(class: Runner<'static, UsbDriver, MTU>) -> ! { | |||
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | #[embassy_executor::task] | 33 | #[embassy_executor::task] |
| 34 | async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! { | 34 | async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static, MTU>>) -> ! { |
| 35 | stack.run().await | 35 | runner.run().await |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | bind_interrupts!(struct Irqs { | 38 | bind_interrupts!(struct Irqs { |
| @@ -93,12 +93,6 @@ async fn main(spawner: Spawner) { | |||
| 93 | config.max_power = 100; | 93 | config.max_power = 100; |
| 94 | config.max_packet_size_0 = 64; | 94 | config.max_packet_size_0 = 64; |
| 95 | 95 | ||
| 96 | // Required for Windows support. | ||
| 97 | config.composite_with_iads = true; | ||
| 98 | config.device_class = 0xEF; | ||
| 99 | config.device_sub_class = 0x02; | ||
| 100 | config.device_protocol = 0x01; | ||
| 101 | |||
| 102 | // Create embassy-usb DeviceBuilder using the driver and config. | 96 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 103 | static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new(); | 97 | static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new(); |
| 104 | static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new(); | 98 | static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new(); |
| @@ -144,11 +138,10 @@ async fn main(spawner: Spawner) { | |||
| 144 | let seed = u64::from_le_bytes(seed); | 138 | let seed = u64::from_le_bytes(seed); |
| 145 | 139 | ||
| 146 | // Init network stack | 140 | // Init network stack |
| 147 | static STACK: StaticCell<Stack<Device<'static, MTU>>> = StaticCell::new(); | ||
| 148 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 141 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 149 | let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); | 142 | let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); |
| 150 | 143 | ||
| 151 | unwrap!(spawner.spawn(net_task(stack))); | 144 | unwrap!(spawner.spawn(net_task(runner))); |
| 152 | 145 | ||
| 153 | // And now we can use it! | 146 | // And now we can use it! |
| 154 | 147 | ||
diff --git a/examples/stm32f4/src/bin/usb_hid_keyboard.rs b/examples/stm32f4/src/bin/usb_hid_keyboard.rs index 1270995c4..d6b4a9bc9 100644 --- a/examples/stm32f4/src/bin/usb_hid_keyboard.rs +++ b/examples/stm32f4/src/bin/usb_hid_keyboard.rs | |||
| @@ -71,13 +71,6 @@ async fn main(_spawner: Spawner) { | |||
| 71 | config.max_power = 100; | 71 | config.max_power = 100; |
| 72 | config.max_packet_size_0 = 64; | 72 | config.max_packet_size_0 = 64; |
| 73 | 73 | ||
| 74 | // Required for windows compatibility. | ||
| 75 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 76 | config.device_class = 0xEF; | ||
| 77 | config.device_sub_class = 0x02; | ||
| 78 | config.device_protocol = 0x01; | ||
| 79 | config.composite_with_iads = true; | ||
| 80 | |||
| 81 | // Create embassy-usb DeviceBuilder using the driver and config. | 74 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 82 | // It needs some buffers for building the descriptors. | 75 | // It needs some buffers for building the descriptors. |
| 83 | let mut config_descriptor = [0; 256]; | 76 | let mut config_descriptor = [0; 256]; |
diff --git a/examples/stm32f4/src/bin/usb_hid_mouse.rs b/examples/stm32f4/src/bin/usb_hid_mouse.rs index 45136f965..badb65e98 100644 --- a/examples/stm32f4/src/bin/usb_hid_mouse.rs +++ b/examples/stm32f4/src/bin/usb_hid_mouse.rs | |||
| @@ -66,13 +66,6 @@ async fn main(_spawner: Spawner) { | |||
| 66 | config.product = Some("HID mouse example"); | 66 | config.product = Some("HID mouse example"); |
| 67 | config.serial_number = Some("12345678"); | 67 | config.serial_number = Some("12345678"); |
| 68 | 68 | ||
| 69 | // Required for windows compatibility. | ||
| 70 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 71 | config.device_class = 0xEF; | ||
| 72 | config.device_sub_class = 0x02; | ||
| 73 | config.device_protocol = 0x01; | ||
| 74 | config.composite_with_iads = true; | ||
| 75 | |||
| 76 | // Create embassy-usb DeviceBuilder using the driver and config. | 69 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 77 | // It needs some buffers for building the descriptors. | 70 | // It needs some buffers for building the descriptors. |
| 78 | let mut config_descriptor = [0; 256]; | 71 | let mut config_descriptor = [0; 256]; |
diff --git a/examples/stm32f4/src/bin/usb_raw.rs b/examples/stm32f4/src/bin/usb_raw.rs index b2d706208..bbbcc082b 100644 --- a/examples/stm32f4/src/bin/usb_raw.rs +++ b/examples/stm32f4/src/bin/usb_raw.rs | |||
| @@ -119,13 +119,6 @@ async fn main(_spawner: Spawner) { | |||
| 119 | config.product = Some("USB-raw example"); | 119 | config.product = Some("USB-raw example"); |
| 120 | config.serial_number = Some("12345678"); | 120 | config.serial_number = Some("12345678"); |
| 121 | 121 | ||
| 122 | // Required for windows compatibility. | ||
| 123 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 124 | config.device_class = 0xEF; | ||
| 125 | config.device_sub_class = 0x02; | ||
| 126 | config.device_protocol = 0x01; | ||
| 127 | config.composite_with_iads = true; | ||
| 128 | |||
| 129 | // Create embassy-usb DeviceBuilder using the driver and config. | 122 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 130 | // It needs some buffers for building the descriptors. | 123 | // It needs some buffers for building the descriptors. |
| 131 | let mut config_descriptor = [0; 256]; | 124 | let mut config_descriptor = [0; 256]; |
diff --git a/examples/stm32f4/src/bin/usb_serial.rs b/examples/stm32f4/src/bin/usb_serial.rs index 328b5effe..e62b2d8d6 100644 --- a/examples/stm32f4/src/bin/usb_serial.rs +++ b/examples/stm32f4/src/bin/usb_serial.rs | |||
| @@ -66,13 +66,6 @@ async fn main(_spawner: Spawner) { | |||
| 66 | config.product = Some("USB-serial example"); | 66 | config.product = Some("USB-serial example"); |
| 67 | config.serial_number = Some("12345678"); | 67 | config.serial_number = Some("12345678"); |
| 68 | 68 | ||
| 69 | // Required for windows compatibility. | ||
| 70 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 71 | config.device_class = 0xEF; | ||
| 72 | config.device_sub_class = 0x02; | ||
| 73 | config.device_protocol = 0x01; | ||
| 74 | config.composite_with_iads = true; | ||
| 75 | |||
| 76 | // Create embassy-usb DeviceBuilder using the driver and config. | 69 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 77 | // It needs some buffers for building the descriptors. | 70 | // It needs some buffers for building the descriptors. |
| 78 | let mut config_descriptor = [0; 256]; | 71 | let mut config_descriptor = [0; 256]; |
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..654bec498 --- /dev/null +++ b/examples/stm32f4/src/bin/usb_uac_speaker.rs | |||
| @@ -0,0 +1,383 @@ | |||
| 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 | let mut builder = embassy_usb::Builder::new( | ||
| 319 | usb_driver, | ||
| 320 | config, | ||
| 321 | config_descriptor, | ||
| 322 | bos_descriptor, | ||
| 323 | &mut [], // no msos descriptors | ||
| 324 | control_buf, | ||
| 325 | ); | ||
| 326 | |||
| 327 | // Create the UAC1 Speaker class components | ||
| 328 | let (stream, feedback, control_monitor) = Speaker::new( | ||
| 329 | &mut builder, | ||
| 330 | state, | ||
| 331 | USB_MAX_PACKET_SIZE as u16, | ||
| 332 | uac1::SampleWidth::Width4Byte, | ||
| 333 | &[SAMPLE_RATE_HZ], | ||
| 334 | &AUDIO_CHANNELS, | ||
| 335 | FEEDBACK_REFRESH_PERIOD, | ||
| 336 | ); | ||
| 337 | |||
| 338 | // Create the USB device | ||
| 339 | let usb_device = builder.build(); | ||
| 340 | |||
| 341 | // Establish a zero-copy channel for transferring received audio samples between tasks | ||
| 342 | static SAMPLE_BLOCKS: StaticCell<[SampleBlock; 2]> = StaticCell::new(); | ||
| 343 | let sample_blocks = SAMPLE_BLOCKS.init([Vec::new(), Vec::new()]); | ||
| 344 | |||
| 345 | static CHANNEL: StaticCell<zerocopy_channel::Channel<'_, NoopRawMutex, SampleBlock>> = StaticCell::new(); | ||
| 346 | let channel = CHANNEL.init(zerocopy_channel::Channel::new(sample_blocks)); | ||
| 347 | let (sender, receiver) = channel.split(); | ||
| 348 | |||
| 349 | // Run a timer for counting between SOF interrupts. | ||
| 350 | let mut tim2 = timer::low_level::Timer::new(p.TIM2); | ||
| 351 | tim2.set_tick_freq(Hertz(FEEDBACK_COUNTER_TICK_RATE)); | ||
| 352 | tim2.set_trigger_source(timer::low_level::TriggerSource::ITR1); // The USB SOF signal. | ||
| 353 | |||
| 354 | const TIMER_CHANNEL: timer::Channel = timer::Channel::Ch1; | ||
| 355 | tim2.set_input_ti_selection(TIMER_CHANNEL, timer::low_level::InputTISelection::TRC); | ||
| 356 | tim2.set_input_capture_prescaler(TIMER_CHANNEL, 0); | ||
| 357 | tim2.set_input_capture_filter(TIMER_CHANNEL, timer::low_level::FilterValue::FCK_INT_N2); | ||
| 358 | |||
| 359 | // Reset all interrupt flags. | ||
| 360 | tim2.regs_gp32().sr().write(|r| r.0 = 0); | ||
| 361 | |||
| 362 | // Enable routing of SOF to the timer. | ||
| 363 | tim2.regs_gp32().or().write(|r| *r = 0b10 << 10); | ||
| 364 | |||
| 365 | tim2.enable_channel(TIMER_CHANNEL, true); | ||
| 366 | tim2.enable_input_interrupt(TIMER_CHANNEL, true); | ||
| 367 | |||
| 368 | tim2.start(); | ||
| 369 | |||
| 370 | TIMER.lock(|p| p.borrow_mut().replace(tim2)); | ||
| 371 | |||
| 372 | // Unmask the TIM2 interrupt. | ||
| 373 | unsafe { | ||
| 374 | cortex_m::peripheral::NVIC::unmask(interrupt::TIM2); | ||
| 375 | } | ||
| 376 | |||
| 377 | // Launch USB audio tasks. | ||
| 378 | unwrap!(spawner.spawn(usb_control_task(control_monitor))); | ||
| 379 | unwrap!(spawner.spawn(usb_streaming_task(stream, sender))); | ||
| 380 | unwrap!(spawner.spawn(usb_feedback_task(feedback))); | ||
| 381 | unwrap!(spawner.spawn(usb_task(usb_device))); | ||
| 382 | unwrap!(spawner.spawn(audio_receiver_task(receiver))); | ||
| 383 | } | ||
diff --git a/examples/stm32f4/src/bin/ws2812_pwm.rs b/examples/stm32f4/src/bin/ws2812_pwm.rs index cbaff75fc..ca924e181 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)); |
| @@ -92,7 +92,7 @@ async fn main(_spawner: Spawner) { | |||
| 92 | loop { | 92 | loop { |
| 93 | for &color in color_list { | 93 | for &color in color_list { |
| 94 | // with &mut, we can easily reuse same DMA channel multiple times | 94 | // with &mut, we can easily reuse same DMA channel multiple times |
| 95 | ws2812_pwm.waveform_up(&mut dp.DMA1_CH2, pwm_channel, color).await; | 95 | ws2812_pwm.waveform_up(dp.DMA1_CH2.reborrow(), pwm_channel, color).await; |
| 96 | // ws2812 need at least 50 us low level input to confirm the input data and change it's state | 96 | // ws2812 need at least 50 us low level input to confirm the input data and change it's state |
| 97 | Timer::after_micros(50).await; | 97 | Timer::after_micros(50).await; |
| 98 | // wait until ticker tick | 98 | // wait until ticker tick |
diff --git a/examples/stm32f469/Cargo.toml b/examples/stm32f469/Cargo.toml index 6a5bd0b29..87a3b8f75 100644 --- a/examples/stm32f469/Cargo.toml +++ b/examples/stm32f469/Cargo.toml | |||
| @@ -6,17 +6,17 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 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.2.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.7.0", path = "../../embassy-executor", features = ["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.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 12 | 12 | ||
| 13 | defmt = "0.3" | 13 | defmt = "1.0.1" |
| 14 | defmt-rtt = "0.4" | 14 | defmt-rtt = "1.0.0" |
| 15 | 15 | ||
| 16 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 16 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 17 | cortex-m-rt = "0.7.0" | 17 | cortex-m-rt = "0.7.0" |
| 18 | embedded-hal = "1.0.0" | 18 | embedded-hal = "1.0.0" |
| 19 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 19 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 20 | 20 | ||
| 21 | [profile.release] | 21 | [profile.release] |
| 22 | debug = 2 | 22 | debug = 2 |
diff --git a/examples/stm32f469/src/bin/dsi_bsp.rs b/examples/stm32f469/src/bin/dsi_bsp.rs index e4e9e9c01..3a24d5dcf 100644 --- a/examples/stm32f469/src/bin/dsi_bsp.rs +++ b/examples/stm32f469/src/bin/dsi_bsp.rs | |||
| @@ -363,20 +363,20 @@ async fn main(_spawner: Spawner) { | |||
| 363 | const _PCPOLARITY: bool = false; // LTDC_PCPOLARITY_IPC == 0 | 363 | const _PCPOLARITY: bool = false; // LTDC_PCPOLARITY_IPC == 0 |
| 364 | 364 | ||
| 365 | const LTDC_DE_POLARITY: Depol = if !DE_POLARITY { | 365 | const LTDC_DE_POLARITY: Depol = if !DE_POLARITY { |
| 366 | Depol::ACTIVELOW | 366 | Depol::ACTIVE_LOW |
| 367 | } else { | 367 | } else { |
| 368 | Depol::ACTIVEHIGH | 368 | Depol::ACTIVE_HIGH |
| 369 | }; | 369 | }; |
| 370 | const LTDC_VS_POLARITY: Vspol = if !VS_POLARITY { | 370 | const LTDC_VS_POLARITY: Vspol = if !VS_POLARITY { |
| 371 | Vspol::ACTIVEHIGH | 371 | Vspol::ACTIVE_HIGH |
| 372 | } else { | 372 | } else { |
| 373 | Vspol::ACTIVELOW | 373 | Vspol::ACTIVE_LOW |
| 374 | }; | 374 | }; |
| 375 | 375 | ||
| 376 | const LTDC_HS_POLARITY: Hspol = if !HS_POLARITY { | 376 | const LTDC_HS_POLARITY: Hspol = if !HS_POLARITY { |
| 377 | Hspol::ACTIVEHIGH | 377 | Hspol::ACTIVE_HIGH |
| 378 | } else { | 378 | } else { |
| 379 | Hspol::ACTIVELOW | 379 | Hspol::ACTIVE_LOW |
| 380 | }; | 380 | }; |
| 381 | 381 | ||
| 382 | /* Timing Configuration */ | 382 | /* Timing Configuration */ |
| @@ -397,7 +397,7 @@ async fn main(_spawner: Spawner) { | |||
| 397 | w.set_hspol(LTDC_HS_POLARITY); | 397 | w.set_hspol(LTDC_HS_POLARITY); |
| 398 | w.set_vspol(LTDC_VS_POLARITY); | 398 | w.set_vspol(LTDC_VS_POLARITY); |
| 399 | w.set_depol(LTDC_DE_POLARITY); | 399 | w.set_depol(LTDC_DE_POLARITY); |
| 400 | w.set_pcpol(Pcpol::RISINGEDGE); | 400 | w.set_pcpol(Pcpol::RISING_EDGE); |
| 401 | }); | 401 | }); |
| 402 | 402 | ||
| 403 | // Set Synchronization size | 403 | // Set Synchronization size |
diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 8c591ebd2..c5801ea90 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml | |||
| @@ -6,31 +6,30 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 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.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32f777zi", "memory-x", "unstable-pac", "time-driver-any", "exti", "single-bank"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } | 13 | embassy-net = { version = "0.7.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.4.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 | ||
| 18 | defmt = "0.3" | 18 | defmt = "1.0.1" |
| 19 | defmt-rtt = "0.4" | 19 | defmt-rtt = "1.0.0" |
| 20 | 20 | ||
| 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 22 | cortex-m-rt = "0.7.0" | 22 | cortex-m-rt = "0.7.0" |
| 23 | embedded-hal = "0.2.6" | 23 | embedded-hal = "0.2.6" |
| 24 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 24 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 25 | heapless = { version = "0.8", default-features = false } | 25 | heapless = { version = "0.8", default-features = false } |
| 26 | nb = "1.0.0" | 26 | nb = "1.0.0" |
| 27 | rand_core = "0.6.3" | ||
| 28 | critical-section = "1.1" | 27 | critical-section = "1.1" |
| 29 | embedded-storage = "0.3.1" | 28 | embedded-storage = "0.3.1" |
| 30 | static_cell = "2" | 29 | static_cell = "2" |
| 31 | sha2 = { version = "0.10.8", default-features = false } | 30 | sha2 = { version = "0.10.8", default-features = false } |
| 32 | hmac = "0.12.1" | 31 | hmac = "0.12.1" |
| 33 | aes-gcm = {version = "0.10.3", default-features = false, features = ["aes", "heapless"] } | 32 | aes-gcm = { version = "0.10.3", default-features = false, features = ["aes", "heapless"] } |
| 34 | 33 | ||
| 35 | [profile.release] | 34 | [profile.release] |
| 36 | debug = 2 | 35 | debug = 2 |
diff --git a/examples/stm32f7/src/bin/can.rs b/examples/stm32f7/src/bin/can.rs index a82e335a9..58ba940a8 100644 --- a/examples/stm32f7/src/bin/can.rs +++ b/examples/stm32f7/src/bin/can.rs | |||
| @@ -42,7 +42,7 @@ async fn main(spawner: Spawner) { | |||
| 42 | // To synchronise to the bus the RX input needs to see a high level. | 42 | // To synchronise to the bus the RX input needs to see a high level. |
| 43 | // Use `mem::forget()` to release the borrow on the pin but keep the | 43 | // Use `mem::forget()` to release the borrow on the pin but keep the |
| 44 | // pull-up resistor enabled. | 44 | // pull-up resistor enabled. |
| 45 | let rx_pin = Input::new(&mut p.PA15, Pull::Up); | 45 | let rx_pin = Input::new(p.PA15.reborrow(), Pull::Up); |
| 46 | core::mem::forget(rx_pin); | 46 | core::mem::forget(rx_pin); |
| 47 | 47 | ||
| 48 | static CAN: StaticCell<Can<'static>> = StaticCell::new(); | 48 | static CAN: StaticCell<Can<'static>> = StaticCell::new(); |
diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index 2fd10c8fb..67a2b34bb 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs | |||
| @@ -4,16 +4,14 @@ | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_net::tcp::TcpSocket; | 6 | use embassy_net::tcp::TcpSocket; |
| 7 | use embassy_net::{Ipv4Address, Stack, StackResources}; | 7 | use embassy_net::{Ipv4Address, StackResources}; |
| 8 | use embassy_stm32::eth::generic_smi::GenericSMI; | 8 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; |
| 9 | use embassy_stm32::eth::{Ethernet, PacketQueue}; | ||
| 10 | use embassy_stm32::peripherals::ETH; | 9 | use embassy_stm32::peripherals::ETH; |
| 11 | use embassy_stm32::rng::Rng; | 10 | use embassy_stm32::rng::Rng; |
| 12 | use embassy_stm32::time::Hertz; | 11 | use embassy_stm32::time::Hertz; |
| 13 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; | 12 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; |
| 14 | use embassy_time::Timer; | 13 | use embassy_time::Timer; |
| 15 | use embedded_io_async::Write; | 14 | use embedded_io_async::Write; |
| 16 | use rand_core::RngCore; | ||
| 17 | use static_cell::StaticCell; | 15 | use static_cell::StaticCell; |
| 18 | use {defmt_rtt as _, panic_probe as _}; | 16 | use {defmt_rtt as _, panic_probe as _}; |
| 19 | 17 | ||
| @@ -22,11 +20,11 @@ bind_interrupts!(struct Irqs { | |||
| 22 | HASH_RNG => rng::InterruptHandler<peripherals::RNG>; | 20 | HASH_RNG => rng::InterruptHandler<peripherals::RNG>; |
| 23 | }); | 21 | }); |
| 24 | 22 | ||
| 25 | type Device = Ethernet<'static, ETH, GenericSMI>; | 23 | type Device = Ethernet<'static, ETH, GenericPhy>; |
| 26 | 24 | ||
| 27 | #[embassy_executor::task] | 25 | #[embassy_executor::task] |
| 28 | async fn net_task(stack: &'static Stack<Device>) -> ! { | 26 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { |
| 29 | stack.run().await | 27 | runner.run().await |
| 30 | } | 28 | } |
| 31 | 29 | ||
| 32 | #[embassy_executor::main] | 30 | #[embassy_executor::main] |
| @@ -77,7 +75,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 77 | p.PG13, | 75 | p.PG13, |
| 78 | p.PB13, | 76 | p.PB13, |
| 79 | p.PG11, | 77 | p.PG11, |
| 80 | GenericSMI::new(0), | 78 | GenericPhy::new_auto(), |
| 81 | mac_addr, | 79 | mac_addr, |
| 82 | ); | 80 | ); |
| 83 | 81 | ||
| @@ -89,12 +87,11 @@ async fn main(spawner: Spawner) -> ! { | |||
| 89 | //}); | 87 | //}); |
| 90 | 88 | ||
| 91 | // Init network stack | 89 | // Init network stack |
| 92 | static STACK: StaticCell<Stack<Device>> = StaticCell::new(); | ||
| 93 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 90 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 94 | let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); | 91 | let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); |
| 95 | 92 | ||
| 96 | // Launch network task | 93 | // Launch network task |
| 97 | unwrap!(spawner.spawn(net_task(stack))); | 94 | unwrap!(spawner.spawn(net_task(runner))); |
| 98 | 95 | ||
| 99 | // Ensure DHCP configuration is up before trying connect | 96 | // Ensure DHCP configuration is up before trying connect |
| 100 | stack.wait_config_up().await; | 97 | stack.wait_config_up().await; |
| @@ -106,7 +103,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 106 | let mut tx_buffer = [0; 4096]; | 103 | let mut tx_buffer = [0; 4096]; |
| 107 | 104 | ||
| 108 | loop { | 105 | loop { |
| 109 | let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); | 106 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); |
| 110 | 107 | ||
| 111 | socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); | 108 | socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); |
| 112 | 109 | ||
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/stm32f7/src/bin/sdmmc.rs b/examples/stm32f7/src/bin/sdmmc.rs index 6d36ef518..787bef25e 100644 --- a/examples/stm32f7/src/bin/sdmmc.rs +++ b/examples/stm32f7/src/bin/sdmmc.rs | |||
| @@ -54,7 +54,7 @@ async fn main(_spawner: Spawner) { | |||
| 54 | // Should print 400kHz for initialization | 54 | // Should print 400kHz for initialization |
| 55 | info!("Configured clock: {}", sdmmc.clock().0); | 55 | info!("Configured clock: {}", sdmmc.clock().0); |
| 56 | 56 | ||
| 57 | unwrap!(sdmmc.init_card(mhz(25)).await); | 57 | unwrap!(sdmmc.init_sd_card(mhz(25)).await); |
| 58 | 58 | ||
| 59 | let card = unwrap!(sdmmc.card()); | 59 | let card = unwrap!(sdmmc.card()); |
| 60 | 60 | ||
diff --git a/examples/stm32f7/src/bin/usb_serial.rs b/examples/stm32f7/src/bin/usb_serial.rs index 1906b28ed..349012888 100644 --- a/examples/stm32f7/src/bin/usb_serial.rs +++ b/examples/stm32f7/src/bin/usb_serial.rs | |||
| @@ -66,13 +66,6 @@ async fn main(_spawner: Spawner) { | |||
| 66 | config.product = Some("USB-serial example"); | 66 | config.product = Some("USB-serial example"); |
| 67 | config.serial_number = Some("12345678"); | 67 | config.serial_number = Some("12345678"); |
| 68 | 68 | ||
| 69 | // Required for windows compatibility. | ||
| 70 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 71 | config.device_class = 0xEF; | ||
| 72 | config.device_sub_class = 0x02; | ||
| 73 | config.device_protocol = 0x01; | ||
| 74 | config.composite_with_iads = true; | ||
| 75 | |||
| 76 | // Create embassy-usb DeviceBuilder using the driver and config. | 69 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 77 | // It needs some buffers for building the descriptors. | 70 | // It needs some buffers for building the descriptors. |
| 78 | let mut config_descriptor = [0; 256]; | 71 | let mut config_descriptor = [0; 256]; |
diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index a50074ce0..bf1e7250e 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml | |||
| @@ -6,20 +6,20 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 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.2.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.7.0", 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.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } | 13 | embassy-usb = { version = "0.4.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" } |
| 15 | 15 | ||
| 16 | defmt = "0.3" | 16 | defmt = "1.0.1" |
| 17 | defmt-rtt = "0.4" | 17 | defmt-rtt = "1.0.0" |
| 18 | 18 | ||
| 19 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 19 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 20 | cortex-m-rt = "0.7.0" | 20 | 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 = "1.0.0", features = ["print-defmt"] } |
| 23 | heapless = { version = "0.8", default-features = false } | 23 | heapless = { version = "0.8", default-features = false } |
| 24 | portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] } | 24 | portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] } |
| 25 | 25 | ||
diff --git a/examples/stm32g0/src/bin/adc_dma.rs b/examples/stm32g0/src/bin/adc_dma.rs index 3713e5a21..d7515933c 100644 --- a/examples/stm32g0/src/bin/adc_dma.rs +++ b/examples/stm32g0/src/bin/adc_dma.rs | |||
| @@ -25,7 +25,7 @@ async fn main(_spawner: Spawner) { | |||
| 25 | 25 | ||
| 26 | loop { | 26 | loop { |
| 27 | adc.read( | 27 | adc.read( |
| 28 | &mut dma, | 28 | dma.reborrow(), |
| 29 | [ | 29 | [ |
| 30 | (&mut vrefint_channel, SampleTime::CYCLES160_5), | 30 | (&mut vrefint_channel, SampleTime::CYCLES160_5), |
| 31 | (&mut pa0, SampleTime::CYCLES160_5), | 31 | (&mut pa0, SampleTime::CYCLES160_5), |
diff --git a/examples/stm32g0/src/bin/hf_timer.rs b/examples/stm32g0/src/bin/hf_timer.rs index 3ea06cdee..dfb6e0edc 100644 --- a/examples/stm32g0/src/bin/hf_timer.rs +++ b/examples/stm32g0/src/bin/hf_timer.rs | |||
| @@ -16,7 +16,9 @@ async fn main(_spawner: Spawner) { | |||
| 16 | let mut config = PeripheralConfig::default(); | 16 | let mut config = PeripheralConfig::default(); |
| 17 | { | 17 | { |
| 18 | use embassy_stm32::rcc::*; | 18 | use embassy_stm32::rcc::*; |
| 19 | config.rcc.hsi = true; | 19 | config.rcc.hsi = Some(Hsi { |
| 20 | sys_div: HsiSysDiv::DIV1, | ||
| 21 | }); | ||
| 20 | config.rcc.pll = Some(Pll { | 22 | config.rcc.pll = Some(Pll { |
| 21 | source: PllSource::HSI, | 23 | source: PllSource::HSI, |
| 22 | prediv: PllPreDiv::DIV1, | 24 | prediv: PllPreDiv::DIV1, |
diff --git a/examples/stm32g0/src/bin/input_capture.rs b/examples/stm32g0/src/bin/input_capture.rs index 69fdae96d..08df4e043 100644 --- a/examples/stm32g0/src/bin/input_capture.rs +++ b/examples/stm32g0/src/bin/input_capture.rs | |||
| @@ -16,14 +16,14 @@ use embassy_stm32::time::khz; | |||
| 16 | use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; | 16 | use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; |
| 17 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | 17 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; |
| 18 | use embassy_stm32::timer::Channel; | 18 | use embassy_stm32::timer::Channel; |
| 19 | use embassy_stm32::{bind_interrupts, peripherals, timer}; | 19 | use embassy_stm32::{bind_interrupts, peripherals, timer, Peri}; |
| 20 | use embassy_time::Timer; | 20 | use embassy_time::Timer; |
| 21 | use {defmt_rtt as _, panic_probe as _}; | 21 | use {defmt_rtt as _, panic_probe as _}; |
| 22 | 22 | ||
| 23 | // Connect PB1 and PA6 with a 1k Ohm resistor | 23 | // Connect PB1 and PA6 with a 1k Ohm resistor |
| 24 | 24 | ||
| 25 | #[embassy_executor::task] | 25 | #[embassy_executor::task] |
| 26 | async fn blinky(led: peripherals::PB1) { | 26 | async fn blinky(led: Peri<'static, peripherals::PB1>) { |
| 27 | let mut led = Output::new(led, Level::High, Speed::Low); | 27 | let mut led = Output::new(led, Level::High, Speed::Low); |
| 28 | 28 | ||
| 29 | loop { | 29 | loop { |
| @@ -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/onewire_ds18b20.rs b/examples/stm32g0/src/bin/onewire_ds18b20.rs new file mode 100644 index 000000000..62f8711a6 --- /dev/null +++ b/examples/stm32g0/src/bin/onewire_ds18b20.rs | |||
| @@ -0,0 +1,274 @@ | |||
| 1 | //! This examples shows how you can use buffered or DMA UART to read a DS18B20 temperature sensor on 1-Wire bus. | ||
| 2 | //! Connect 5k pull-up resistor between PA9 and 3.3V. | ||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | |||
| 6 | use cortex_m::singleton; | ||
| 7 | use defmt::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_stm32::mode::Async; | ||
| 10 | use embassy_stm32::usart::{ | ||
| 11 | BufferedUartRx, BufferedUartTx, Config, ConfigError, OutputConfig, RingBufferedUartRx, UartTx, | ||
| 12 | }; | ||
| 13 | use embassy_stm32::{bind_interrupts, peripherals, usart}; | ||
| 14 | use embassy_time::{Duration, Timer}; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | /// Create onewire bus using DMA USART | ||
| 18 | fn create_onewire(p: embassy_stm32::Peripherals) -> OneWire<UartTx<'static, Async>, RingBufferedUartRx<'static>> { | ||
| 19 | use embassy_stm32::usart::Uart; | ||
| 20 | bind_interrupts!(struct Irqs { | ||
| 21 | USART1 => usart::InterruptHandler<peripherals::USART1>; | ||
| 22 | }); | ||
| 23 | |||
| 24 | let mut config = Config::default(); | ||
| 25 | config.tx_config = OutputConfig::OpenDrain; | ||
| 26 | |||
| 27 | let usart = Uart::new_half_duplex( | ||
| 28 | p.USART1, | ||
| 29 | p.PA9, | ||
| 30 | Irqs, | ||
| 31 | p.DMA1_CH1, | ||
| 32 | p.DMA1_CH2, | ||
| 33 | config, | ||
| 34 | // Enable readback so we can read sensor pulling data low while transmission is in progress | ||
| 35 | usart::HalfDuplexReadback::Readback, | ||
| 36 | ) | ||
| 37 | .unwrap(); | ||
| 38 | |||
| 39 | const BUFFER_SIZE: usize = 16; | ||
| 40 | let (tx, rx) = usart.split(); | ||
| 41 | let rx_buf: &mut [u8; BUFFER_SIZE] = singleton!(TX_BUF: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]).unwrap(); | ||
| 42 | let rx = rx.into_ring_buffered(rx_buf); | ||
| 43 | OneWire::new(tx, rx) | ||
| 44 | } | ||
| 45 | |||
| 46 | /* | ||
| 47 | /// Create onewire bus using buffered USART | ||
| 48 | fn create_onewire(p: embassy_stm32::Peripherals) -> OneWire<BufferedUartTx<'static>, BufferedUartRx<'static>> { | ||
| 49 | use embassy_stm32::usart::BufferedUart; | ||
| 50 | bind_interrupts!(struct Irqs { | ||
| 51 | USART1 => usart::BufferedInterruptHandler<peripherals::USART1>; | ||
| 52 | }); | ||
| 53 | |||
| 54 | const BUFFER_SIZE: usize = 16; | ||
| 55 | let mut config = Confi::default(); | ||
| 56 | config.tx_config = OutputConfig::OpenDrain; | ||
| 57 | let tx_buf: &mut [u8; BUFFER_SIZE] = singleton!(TX_BUF: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]).unwrap(); | ||
| 58 | let rx_buf: &mut [u8; BUFFER_SIZE] = singleton!(RX_BUF: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]).unwrap(); | ||
| 59 | let usart = BufferedUart::new_half_duplex( | ||
| 60 | p.USART1, | ||
| 61 | p.PA9, | ||
| 62 | Irqs, | ||
| 63 | tx_buf, | ||
| 64 | rx_buf, | ||
| 65 | config, | ||
| 66 | // Enable readback so we can read sensor pulling data low while transmission is in progress | ||
| 67 | usart::HalfDuplexReadback::Readback, | ||
| 68 | ) | ||
| 69 | .unwrap(); | ||
| 70 | let (tx, rx) = usart.split(); | ||
| 71 | OneWire::new(tx, rx) | ||
| 72 | } | ||
| 73 | */ | ||
| 74 | |||
| 75 | #[embassy_executor::main] | ||
| 76 | async fn main(_spawner: Spawner) { | ||
| 77 | let p = embassy_stm32::init(Default::default()); | ||
| 78 | |||
| 79 | let onewire = create_onewire(p); | ||
| 80 | let mut sensor = Ds18b20::new(onewire); | ||
| 81 | |||
| 82 | loop { | ||
| 83 | // Start a new temperature measurement | ||
| 84 | sensor.start().await; | ||
| 85 | // Wait for the measurement to finish | ||
| 86 | Timer::after(Duration::from_secs(1)).await; | ||
| 87 | match sensor.temperature().await { | ||
| 88 | Ok(temp) => info!("temp = {:?} deg C", temp), | ||
| 89 | _ => error!("sensor error"), | ||
| 90 | } | ||
| 91 | Timer::after(Duration::from_secs(1)).await; | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | pub trait SetBaudrate { | ||
| 96 | fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError>; | ||
| 97 | } | ||
| 98 | impl SetBaudrate for BufferedUartTx<'_> { | ||
| 99 | fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 100 | BufferedUartTx::set_baudrate(self, baudrate) | ||
| 101 | } | ||
| 102 | } | ||
| 103 | impl SetBaudrate for BufferedUartRx<'_> { | ||
| 104 | fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 105 | BufferedUartRx::set_baudrate(self, baudrate) | ||
| 106 | } | ||
| 107 | } | ||
| 108 | impl SetBaudrate for RingBufferedUartRx<'_> { | ||
| 109 | fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 110 | RingBufferedUartRx::set_baudrate(self, baudrate) | ||
| 111 | } | ||
| 112 | } | ||
| 113 | impl SetBaudrate for UartTx<'_, Async> { | ||
| 114 | fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 115 | UartTx::set_baudrate(self, baudrate) | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | /// Simplified OneWire bus driver | ||
| 120 | pub struct OneWire<TX, RX> | ||
| 121 | where | ||
| 122 | TX: embedded_io_async::Write + SetBaudrate, | ||
| 123 | RX: embedded_io_async::Read + SetBaudrate, | ||
| 124 | { | ||
| 125 | tx: TX, | ||
| 126 | rx: RX, | ||
| 127 | } | ||
| 128 | |||
| 129 | impl<TX, RX> OneWire<TX, RX> | ||
| 130 | where | ||
| 131 | TX: embedded_io_async::Write + SetBaudrate, | ||
| 132 | RX: embedded_io_async::Read + SetBaudrate, | ||
| 133 | { | ||
| 134 | // bitrate with one bit taking ~104 us | ||
| 135 | const RESET_BUADRATE: u32 = 9600; | ||
| 136 | // bitrate with one bit taking ~8.7 us | ||
| 137 | const BAUDRATE: u32 = 115200; | ||
| 138 | |||
| 139 | // startbit + 8 low bits = 9 * 1/115200 = 78 us low pulse | ||
| 140 | const LOGIC_1_CHAR: u8 = 0xFF; | ||
| 141 | // startbit only = 1/115200 = 8.7 us low pulse | ||
| 142 | const LOGIC_0_CHAR: u8 = 0x00; | ||
| 143 | |||
| 144 | // Address all devices on the bus | ||
| 145 | const COMMAND_SKIP_ROM: u8 = 0xCC; | ||
| 146 | |||
| 147 | pub fn new(tx: TX, rx: RX) -> Self { | ||
| 148 | Self { tx, rx } | ||
| 149 | } | ||
| 150 | |||
| 151 | fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 152 | self.tx.set_baudrate(baudrate)?; | ||
| 153 | self.rx.set_baudrate(baudrate) | ||
| 154 | } | ||
| 155 | |||
| 156 | /// Reset the bus by at least 480 us low pulse. | ||
| 157 | pub async fn reset(&mut self) { | ||
| 158 | // Switch to 9600 baudrate, so one bit takes ~104 us | ||
| 159 | self.set_baudrate(Self::RESET_BUADRATE).expect("set_baudrate failed"); | ||
| 160 | // Low USART start bit + 4x low bits = 5 * 104 us = 520 us low pulse | ||
| 161 | self.tx.write(&[0xF0]).await.expect("write failed"); | ||
| 162 | |||
| 163 | // Read the value on the bus | ||
| 164 | let mut buffer = [0; 1]; | ||
| 165 | self.rx.read_exact(&mut buffer).await.expect("read failed"); | ||
| 166 | |||
| 167 | // Switch back to 115200 baudrate, so one bit takes ~8.7 us | ||
| 168 | self.set_baudrate(Self::BAUDRATE).expect("set_baudrate failed"); | ||
| 169 | |||
| 170 | // read and expect sensor pulled some high bits to low (device present) | ||
| 171 | if buffer[0] & 0xF != 0 || buffer[0] & 0xF0 == 0xF0 { | ||
| 172 | warn!("No device present"); | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | /// Send byte and read response on the bus. | ||
| 177 | pub async fn write_read_byte(&mut self, byte: u8) -> u8 { | ||
| 178 | // One byte is sent as 8 UART characters | ||
| 179 | let mut tx = [0; 8]; | ||
| 180 | for (pos, char) in tx.iter_mut().enumerate() { | ||
| 181 | *char = if (byte >> pos) & 0x1 == 0x1 { | ||
| 182 | Self::LOGIC_1_CHAR | ||
| 183 | } else { | ||
| 184 | Self::LOGIC_0_CHAR | ||
| 185 | }; | ||
| 186 | } | ||
| 187 | self.tx.write_all(&tx).await.expect("write failed"); | ||
| 188 | |||
| 189 | // Readback the value on the bus, sensors can pull logic 1 to 0 | ||
| 190 | let mut rx = [0; 8]; | ||
| 191 | self.rx.read_exact(&mut rx).await.expect("read failed"); | ||
| 192 | let mut bus_byte = 0; | ||
| 193 | for (pos, char) in rx.iter().enumerate() { | ||
| 194 | // if its 0xFF, sensor didnt pull the bus to low level | ||
| 195 | if *char == 0xFF { | ||
| 196 | bus_byte |= 1 << pos; | ||
| 197 | } | ||
| 198 | } | ||
| 199 | |||
| 200 | bus_byte | ||
| 201 | } | ||
| 202 | |||
| 203 | /// Read a byte from the bus. | ||
| 204 | pub async fn read_byte(&mut self) -> u8 { | ||
| 205 | self.write_read_byte(0xFF).await | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | /// DS18B20 temperature sensor driver | ||
| 210 | pub struct Ds18b20<TX, RX> | ||
| 211 | where | ||
| 212 | TX: embedded_io_async::Write + SetBaudrate, | ||
| 213 | RX: embedded_io_async::Read + SetBaudrate, | ||
| 214 | { | ||
| 215 | bus: OneWire<TX, RX>, | ||
| 216 | } | ||
| 217 | |||
| 218 | impl<TX, RX> Ds18b20<TX, RX> | ||
| 219 | where | ||
| 220 | TX: embedded_io_async::Write + SetBaudrate, | ||
| 221 | RX: embedded_io_async::Read + SetBaudrate, | ||
| 222 | { | ||
| 223 | /// Start a temperature conversion. | ||
| 224 | const FN_CONVERT_T: u8 = 0x44; | ||
| 225 | /// Read contents of the scratchpad containing the temperature. | ||
| 226 | const FN_READ_SCRATCHPAD: u8 = 0xBE; | ||
| 227 | |||
| 228 | pub fn new(bus: OneWire<TX, RX>) -> Self { | ||
| 229 | Self { bus } | ||
| 230 | } | ||
| 231 | |||
| 232 | /// Start a new measurement. Allow at least 1000ms before getting `temperature`. | ||
| 233 | pub async fn start(&mut self) { | ||
| 234 | self.bus.reset().await; | ||
| 235 | self.bus.write_read_byte(OneWire::<TX, RX>::COMMAND_SKIP_ROM).await; | ||
| 236 | self.bus.write_read_byte(Self::FN_CONVERT_T).await; | ||
| 237 | } | ||
| 238 | |||
| 239 | /// Calculate CRC8 of the data | ||
| 240 | fn crc8(data: &[u8]) -> u8 { | ||
| 241 | let mut temp; | ||
| 242 | let mut data_byte; | ||
| 243 | let mut crc = 0; | ||
| 244 | for b in data { | ||
| 245 | data_byte = *b; | ||
| 246 | for _ in 0..8 { | ||
| 247 | temp = (crc ^ data_byte) & 0x01; | ||
| 248 | crc >>= 1; | ||
| 249 | if temp != 0 { | ||
| 250 | crc ^= 0x8C; | ||
| 251 | } | ||
| 252 | data_byte >>= 1; | ||
| 253 | } | ||
| 254 | } | ||
| 255 | crc | ||
| 256 | } | ||
| 257 | |||
| 258 | /// Read the temperature. Ensure >1000ms has passed since `start` before calling this. | ||
| 259 | pub async fn temperature(&mut self) -> Result<f32, ()> { | ||
| 260 | self.bus.reset().await; | ||
| 261 | self.bus.write_read_byte(OneWire::<TX, RX>::COMMAND_SKIP_ROM).await; | ||
| 262 | self.bus.write_read_byte(Self::FN_READ_SCRATCHPAD).await; | ||
| 263 | |||
| 264 | let mut data = [0; 9]; | ||
| 265 | for byte in data.iter_mut() { | ||
| 266 | *byte = self.bus.read_byte().await; | ||
| 267 | } | ||
| 268 | |||
| 269 | match Self::crc8(&data) == 0 { | ||
| 270 | true => Ok(((data[1] as u16) << 8 | data[0] as u16) as f32 / 16.), | ||
| 271 | false => Err(()), | ||
| 272 | } | ||
| 273 | } | ||
| 274 | } | ||
diff --git a/examples/stm32g0/src/bin/pwm_input.rs b/examples/stm32g0/src/bin/pwm_input.rs index 152ecda86..9d6b5fe97 100644 --- a/examples/stm32g0/src/bin/pwm_input.rs +++ b/examples/stm32g0/src/bin/pwm_input.rs | |||
| @@ -14,14 +14,13 @@ 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; | 17 | use embassy_stm32::{bind_interrupts, peripherals, timer, Peri}; |
| 18 | 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 _}; |
| 21 | 20 | ||
| 22 | // Connect PB1 and PA6 with a 1k Ohm resistor | 21 | // Connect PB1 and PA6 with a 1k Ohm resistor |
| 23 | #[embassy_executor::task] | 22 | #[embassy_executor::task] |
| 24 | async fn blinky(led: peripherals::PB1) { | 23 | async fn blinky(led: Peri<'static, peripherals::PB1>) { |
| 25 | let mut led = Output::new(led, Level::High, Speed::Low); | 24 | let mut led = Output::new(led, Level::High, Speed::Low); |
| 26 | 25 | ||
| 27 | loop { | 26 | loop { |
| @@ -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/stm32g0/src/bin/rtc.rs b/examples/stm32g0/src/bin/rtc.rs index c02c1ecd7..50fb6398e 100644 --- a/examples/stm32g0/src/bin/rtc.rs +++ b/examples/stm32g0/src/bin/rtc.rs | |||
| @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { | |||
| 15 | 15 | ||
| 16 | info!("Hello World!"); | 16 | info!("Hello World!"); |
| 17 | 17 | ||
| 18 | let now = DateTime::from(2023, 6, 14, DayOfWeek::Friday, 15, 59, 10); | 18 | let now = DateTime::from(2023, 6, 14, DayOfWeek::Friday, 15, 59, 10, 0); |
| 19 | 19 | ||
| 20 | let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); | 20 | let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); |
| 21 | 21 | ||
diff --git a/examples/stm32g0/src/bin/usart_buffered.rs b/examples/stm32g0/src/bin/usart_buffered.rs index c097a0c5a..6d9ec8cb4 100644 --- a/examples/stm32g0/src/bin/usart_buffered.rs +++ b/examples/stm32g0/src/bin/usart_buffered.rs | |||
| @@ -21,7 +21,7 @@ async fn main(_spawner: Spawner) { | |||
| 21 | config.baudrate = 115200; | 21 | config.baudrate = 115200; |
| 22 | let mut tx_buf = [0u8; 256]; | 22 | let mut tx_buf = [0u8; 256]; |
| 23 | let mut rx_buf = [0u8; 256]; | 23 | let mut rx_buf = [0u8; 256]; |
| 24 | let mut usart = BufferedUart::new(p.USART1, Irqs, p.PB7, p.PB6, &mut tx_buf, &mut rx_buf, config).unwrap(); | 24 | let mut usart = BufferedUart::new(p.USART1, p.PB7, p.PB6, &mut tx_buf, &mut rx_buf, Irqs, config).unwrap(); |
| 25 | 25 | ||
| 26 | usart.write_all(b"Hello Embassy World!\r\n").await.unwrap(); | 26 | usart.write_all(b"Hello Embassy World!\r\n").await.unwrap(); |
| 27 | info!("wrote Hello, starting echo"); | 27 | info!("wrote Hello, starting echo"); |
diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 2768147a1..3d2c2aa7d 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml | |||
| @@ -6,22 +6,22 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 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.2.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.7.0", 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.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 13 | embassy-usb = { version = "0.4.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" } |
| 15 | usbd-hid = "0.8.1" | 15 | usbd-hid = "0.8.1" |
| 16 | 16 | ||
| 17 | defmt = "0.3" | 17 | defmt = "1.0.1" |
| 18 | defmt-rtt = "0.4" | 18 | defmt-rtt = "1.0.0" |
| 19 | 19 | ||
| 20 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 20 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 21 | cortex-m-rt = "0.7.0" | 21 | cortex-m-rt = "0.7.0" |
| 22 | embedded-hal = "0.2.6" | 22 | embedded-hal = "0.2.6" |
| 23 | embedded-can = { version = "0.4" } | 23 | embedded-can = { version = "0.4" } |
| 24 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 24 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 25 | heapless = { version = "0.8", default-features = false } | 25 | heapless = { version = "0.8", default-features = false } |
| 26 | static_cell = "2.0.0" | 26 | static_cell = "2.0.0" |
| 27 | 27 | ||
diff --git a/examples/stm32g4/src/bin/adc_differential.rs b/examples/stm32g4/src/bin/adc_differential.rs new file mode 100644 index 000000000..78d071d45 --- /dev/null +++ b/examples/stm32g4/src/bin/adc_differential.rs | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | //! adc differential mode example | ||
| 2 | //! | ||
| 3 | //! This example uses adc1 in differential mode | ||
| 4 | //! p:pa0 n:pa1 | ||
| 5 | |||
| 6 | #![no_std] | ||
| 7 | #![no_main] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_stm32::adc::{Adc, SampleTime}; | ||
| 12 | use embassy_stm32::Config; | ||
| 13 | use embassy_time::Timer; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | ||
| 15 | |||
| 16 | #[embassy_executor::main] | ||
| 17 | async fn main(_spawner: Spawner) { | ||
| 18 | let mut config = Config::default(); | ||
| 19 | { | ||
| 20 | use embassy_stm32::rcc::*; | ||
| 21 | config.rcc.pll = Some(Pll { | ||
| 22 | source: PllSource::HSI, | ||
| 23 | prediv: PllPreDiv::DIV4, | ||
| 24 | mul: PllMul::MUL85, | ||
| 25 | divp: None, | ||
| 26 | divq: None, | ||
| 27 | // Main system clock at 170 MHz | ||
| 28 | divr: Some(PllRDiv::DIV2), | ||
| 29 | }); | ||
| 30 | config.rcc.mux.adc12sel = mux::Adcsel::SYS; | ||
| 31 | config.rcc.sys = Sysclk::PLL1_R; | ||
| 32 | } | ||
| 33 | let mut p = embassy_stm32::init(config); | ||
| 34 | |||
| 35 | let mut adc = Adc::new(p.ADC1); | ||
| 36 | adc.set_sample_time(SampleTime::CYCLES247_5); | ||
| 37 | adc.set_differential(&mut p.PA0, true); //p:pa0,n:pa1 | ||
| 38 | |||
| 39 | // can also use | ||
| 40 | // adc.set_differential_channel(1, true); | ||
| 41 | info!("adc initialized"); | ||
| 42 | loop { | ||
| 43 | let measured = adc.blocking_read(&mut p.PA0); | ||
| 44 | info!("data: {}", measured); | ||
| 45 | Timer::after_millis(500).await; | ||
| 46 | } | ||
| 47 | } | ||
diff --git a/examples/stm32g4/src/bin/adc_dma.rs b/examples/stm32g4/src/bin/adc_dma.rs new file mode 100644 index 000000000..202704085 --- /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 | dma.reborrow(), | ||
| 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/adc_oversampling.rs b/examples/stm32g4/src/bin/adc_oversampling.rs new file mode 100644 index 000000000..d31eb20f8 --- /dev/null +++ b/examples/stm32g4/src/bin/adc_oversampling.rs | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | //! adc oversampling example | ||
| 2 | //! | ||
| 3 | //! This example uses adc oversampling to achieve 16bit data | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_stm32::adc::vals::{Rovsm, Trovs}; | ||
| 11 | use embassy_stm32::adc::{Adc, SampleTime}; | ||
| 12 | use embassy_stm32::Config; | ||
| 13 | use embassy_time::Timer; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | ||
| 15 | |||
| 16 | #[embassy_executor::main] | ||
| 17 | async fn main(_spawner: Spawner) { | ||
| 18 | let mut config = Config::default(); | ||
| 19 | { | ||
| 20 | use embassy_stm32::rcc::*; | ||
| 21 | config.rcc.pll = Some(Pll { | ||
| 22 | source: PllSource::HSI, | ||
| 23 | prediv: PllPreDiv::DIV4, | ||
| 24 | mul: PllMul::MUL85, | ||
| 25 | divp: None, | ||
| 26 | divq: None, | ||
| 27 | // Main system clock at 170 MHz | ||
| 28 | divr: Some(PllRDiv::DIV2), | ||
| 29 | }); | ||
| 30 | config.rcc.mux.adc12sel = mux::Adcsel::SYS; | ||
| 31 | config.rcc.sys = Sysclk::PLL1_R; | ||
| 32 | } | ||
| 33 | let mut p = embassy_stm32::init(config); | ||
| 34 | |||
| 35 | let mut adc = Adc::new(p.ADC1); | ||
| 36 | adc.set_sample_time(SampleTime::CYCLES6_5); | ||
| 37 | // From https://www.st.com/resource/en/reference_manual/rm0440-stm32g4-series-advanced-armbased-32bit-mcus-stmicroelectronics.pdf | ||
| 38 | // page652 Oversampler | ||
| 39 | // Table 172. Maximum output results vs N and M. Grayed values indicates truncation | ||
| 40 | // 0x00 oversampling ratio X2 | ||
| 41 | // 0x01 oversampling ratio X4 | ||
| 42 | // 0x02 oversampling ratio X8 | ||
| 43 | // 0x03 oversampling ratio X16 | ||
| 44 | // 0x04 oversampling ratio X32 | ||
| 45 | // 0x05 oversampling ratio X64 | ||
| 46 | // 0x06 oversampling ratio X128 | ||
| 47 | // 0x07 oversampling ratio X256 | ||
| 48 | adc.set_oversampling_ratio(0x03); // ratio X3 | ||
| 49 | adc.set_oversampling_shift(0b0000); // no shift | ||
| 50 | adc.enable_regular_oversampling_mode(Rovsm::RESUMED, Trovs::AUTOMATIC, true); | ||
| 51 | |||
| 52 | loop { | ||
| 53 | let measured = adc.blocking_read(&mut p.PA0); | ||
| 54 | info!("data: 0x{:X}", measured); //max 0xFFF0 -> 65520 | ||
| 55 | Timer::after_millis(500).await; | ||
| 56 | } | ||
| 57 | } | ||
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/stm32g4/src/bin/usb_c_pd.rs b/examples/stm32g4/src/bin/usb_c_pd.rs index 7caea634f..2e87d3931 100644 --- a/examples/stm32g4/src/bin/usb_c_pd.rs +++ b/examples/stm32g4/src/bin/usb_c_pd.rs | |||
| @@ -55,7 +55,7 @@ async fn main(_spawner: Spawner) { | |||
| 55 | 55 | ||
| 56 | info!("Hello World!"); | 56 | info!("Hello World!"); |
| 57 | 57 | ||
| 58 | let mut ucpd = Ucpd::new(p.UCPD1, Irqs {}, p.PB6, p.PB4); | 58 | let mut ucpd = Ucpd::new(p.UCPD1, Irqs {}, p.PB6, p.PB4, Default::default()); |
| 59 | ucpd.cc_phy().set_pull(CcPull::Sink); | 59 | ucpd.cc_phy().set_pull(CcPull::Sink); |
| 60 | 60 | ||
| 61 | info!("Waiting for USB connection..."); | 61 | info!("Waiting for USB connection..."); |
diff --git a/examples/stm32g4/src/bin/usb_serial.rs b/examples/stm32g4/src/bin/usb_serial.rs index ed2ac7fac..9f66f0c53 100644 --- a/examples/stm32g4/src/bin/usb_serial.rs +++ b/examples/stm32g4/src/bin/usb_serial.rs | |||
| @@ -51,11 +51,6 @@ async fn main(_spawner: Spawner) { | |||
| 51 | config.product = Some("USB-Serial Example"); | 51 | config.product = Some("USB-Serial Example"); |
| 52 | config.serial_number = Some("123456"); | 52 | config.serial_number = Some("123456"); |
| 53 | 53 | ||
| 54 | config.device_class = 0xEF; | ||
| 55 | config.device_sub_class = 0x02; | ||
| 56 | config.device_protocol = 0x01; | ||
| 57 | config.composite_with_iads = true; | ||
| 58 | |||
| 59 | let mut config_descriptor = [0; 256]; | 54 | let mut config_descriptor = [0; 256]; |
| 60 | let mut bos_descriptor = [0; 256]; | 55 | let mut bos_descriptor = [0; 256]; |
| 61 | let mut control_buf = [0; 64]; | 56 | let mut control_buf = [0; 64]; |
diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index 30b1d2be9..f3fda7ff3 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml | |||
| @@ -6,16 +6,16 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 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.2.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.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] } | 13 | embassy-net = { version = "0.7.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.4.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 | ||
| 17 | defmt = "0.3" | 17 | defmt = "1.0.1" |
| 18 | defmt-rtt = "0.4" | 18 | defmt-rtt = "1.0.0" |
| 19 | 19 | ||
| 20 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 20 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 21 | cortex-m-rt = "0.7.0" | 21 | cortex-m-rt = "0.7.0" |
| @@ -23,10 +23,9 @@ 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 = "1.0.0", 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" | ||
| 30 | critical-section = "1.1" | 29 | critical-section = "1.1" |
| 31 | micromath = "2.0.0" | 30 | micromath = "2.0.0" |
| 32 | stm32-fmc = "0.3.0" | 31 | stm32-fmc = "0.3.0" |
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/cordic.rs b/examples/stm32h5/src/bin/cordic.rs index 73e873574..cbf854704 100644 --- a/examples/stm32h5/src/bin/cordic.rs +++ b/examples/stm32h5/src/bin/cordic.rs | |||
| @@ -11,7 +11,7 @@ async fn main(_spawner: Spawner) { | |||
| 11 | let mut dp = embassy_stm32::init(Default::default()); | 11 | let mut dp = embassy_stm32::init(Default::default()); |
| 12 | 12 | ||
| 13 | let mut cordic = cordic::Cordic::new( | 13 | let mut cordic = cordic::Cordic::new( |
| 14 | &mut dp.CORDIC, | 14 | dp.CORDIC.reborrow(), |
| 15 | unwrap!(cordic::Config::new( | 15 | unwrap!(cordic::Config::new( |
| 16 | cordic::Function::Sin, | 16 | cordic::Function::Sin, |
| 17 | Default::default(), | 17 | Default::default(), |
| @@ -59,8 +59,8 @@ async fn main(_spawner: Spawner) { | |||
| 59 | let cnt1 = unwrap!( | 59 | let cnt1 = unwrap!( |
| 60 | cordic | 60 | cordic |
| 61 | .async_calc_32bit( | 61 | .async_calc_32bit( |
| 62 | &mut dp.GPDMA1_CH0, | 62 | dp.GPDMA1_CH0.reborrow(), |
| 63 | &mut dp.GPDMA1_CH1, | 63 | dp.GPDMA1_CH1.reborrow(), |
| 64 | &input_buf[..arg1.len() - 1], // limit input buf to its actual length | 64 | &input_buf[..arg1.len() - 1], // limit input buf to its actual length |
| 65 | &mut output_u32, | 65 | &mut output_u32, |
| 66 | true, | 66 | true, |
diff --git a/examples/stm32h5/src/bin/dts.rs b/examples/stm32h5/src/bin/dts.rs new file mode 100644 index 000000000..8c18fafea --- /dev/null +++ b/examples/stm32h5/src/bin/dts.rs | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::dts::{Dts, InterruptHandler, SampleTime}; | ||
| 7 | use embassy_stm32::peripherals::DTS; | ||
| 8 | use embassy_stm32::rcc::frequency; | ||
| 9 | use embassy_stm32::{bind_interrupts, dts, Config}; | ||
| 10 | use embassy_time::Timer; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | bind_interrupts!(struct Irqs { | ||
| 14 | DTS => InterruptHandler; | ||
| 15 | }); | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | let mut config = Config::default(); | ||
| 20 | { | ||
| 21 | use embassy_stm32::rcc::*; | ||
| 22 | config.rcc.hsi = Some(HSIPrescaler::DIV1); | ||
| 23 | config.rcc.csi = true; | ||
| 24 | config.rcc.pll1 = Some(Pll { | ||
| 25 | source: PllSource::HSI, | ||
| 26 | prediv: PllPreDiv::DIV4, | ||
| 27 | mul: PllMul::MUL25, | ||
| 28 | divp: Some(PllDiv::DIV2), | ||
| 29 | divq: Some(PllDiv::DIV4), // SPI1 cksel defaults to pll1_q | ||
| 30 | divr: None, | ||
| 31 | }); | ||
| 32 | config.rcc.pll2 = Some(Pll { | ||
| 33 | source: PllSource::HSI, | ||
| 34 | prediv: PllPreDiv::DIV4, | ||
| 35 | mul: PllMul::MUL25, | ||
| 36 | divp: None, | ||
| 37 | divq: None, | ||
| 38 | divr: Some(PllDiv::DIV4), // 100mhz | ||
| 39 | }); | ||
| 40 | config.rcc.sys = Sysclk::PLL1_P; // 200 Mhz | ||
| 41 | config.rcc.ahb_pre = AHBPrescaler::DIV1; // 200 Mhz | ||
| 42 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 43 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 44 | config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 45 | config.rcc.voltage_scale = VoltageScale::Scale1; | ||
| 46 | config.rcc.mux.adcdacsel = mux::Adcdacsel::PLL2_R; | ||
| 47 | } | ||
| 48 | let p = embassy_stm32::init(config); | ||
| 49 | |||
| 50 | info!("Hello World!"); | ||
| 51 | |||
| 52 | let mut config = dts::Config::default(); | ||
| 53 | config.sample_time = SampleTime::ClockCycles15; | ||
| 54 | let mut dts = Dts::new(p.DTS, Irqs, config); | ||
| 55 | |||
| 56 | let cal = Dts::factory_calibration(); | ||
| 57 | let convert_to_celsius = |raw_temp: u16| { | ||
| 58 | let raw_temp = raw_temp as f32; | ||
| 59 | let sample_time = (config.sample_time as u8) as f32; | ||
| 60 | |||
| 61 | let f = frequency::<DTS>().0 as f32; | ||
| 62 | |||
| 63 | let t0 = cal.t0 as f32; | ||
| 64 | let fmt0 = cal.fmt0.0 as f32; | ||
| 65 | let ramp_coeff = cal.ramp_coeff as f32; | ||
| 66 | |||
| 67 | ((f * sample_time / raw_temp) - fmt0) / ramp_coeff + t0 | ||
| 68 | }; | ||
| 69 | |||
| 70 | loop { | ||
| 71 | let temp = dts.read().await; | ||
| 72 | info!("Temp: {} degrees", convert_to_celsius(temp)); | ||
| 73 | Timer::after_millis(500).await; | ||
| 74 | } | ||
| 75 | } | ||
diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index 65cfad8c9..1d85cc1e7 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs | |||
| @@ -4,9 +4,8 @@ | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_net::tcp::TcpSocket; | 6 | use embassy_net::tcp::TcpSocket; |
| 7 | use embassy_net::{Ipv4Address, Stack, StackResources}; | 7 | use embassy_net::{Ipv4Address, StackResources}; |
| 8 | use embassy_stm32::eth::generic_smi::GenericSMI; | 8 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; |
| 9 | use embassy_stm32::eth::{Ethernet, PacketQueue}; | ||
| 10 | use embassy_stm32::peripherals::ETH; | 9 | use embassy_stm32::peripherals::ETH; |
| 11 | use embassy_stm32::rcc::{ | 10 | use embassy_stm32::rcc::{ |
| 12 | AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, | 11 | AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, |
| @@ -16,7 +15,6 @@ use embassy_stm32::time::Hertz; | |||
| 16 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; | 15 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; |
| 17 | use embassy_time::Timer; | 16 | use embassy_time::Timer; |
| 18 | use embedded_io_async::Write; | 17 | use embedded_io_async::Write; |
| 19 | use rand_core::RngCore; | ||
| 20 | use static_cell::StaticCell; | 18 | use static_cell::StaticCell; |
| 21 | use {defmt_rtt as _, panic_probe as _}; | 19 | use {defmt_rtt as _, panic_probe as _}; |
| 22 | 20 | ||
| @@ -25,11 +23,11 @@ bind_interrupts!(struct Irqs { | |||
| 25 | RNG => rng::InterruptHandler<peripherals::RNG>; | 23 | RNG => rng::InterruptHandler<peripherals::RNG>; |
| 26 | }); | 24 | }); |
| 27 | 25 | ||
| 28 | type Device = Ethernet<'static, ETH, GenericSMI>; | 26 | type Device = Ethernet<'static, ETH, GenericPhy>; |
| 29 | 27 | ||
| 30 | #[embassy_executor::task] | 28 | #[embassy_executor::task] |
| 31 | async fn net_task(stack: &'static Stack<Device>) -> ! { | 29 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { |
| 32 | stack.run().await | 30 | runner.run().await |
| 33 | } | 31 | } |
| 34 | 32 | ||
| 35 | #[embassy_executor::main] | 33 | #[embassy_executor::main] |
| @@ -80,7 +78,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 80 | p.PG13, | 78 | p.PG13, |
| 81 | p.PB15, | 79 | p.PB15, |
| 82 | p.PG11, | 80 | p.PG11, |
| 83 | GenericSMI::new(0), | 81 | GenericPhy::new_auto(), |
| 84 | mac_addr, | 82 | mac_addr, |
| 85 | ); | 83 | ); |
| 86 | 84 | ||
| @@ -92,12 +90,11 @@ async fn main(spawner: Spawner) -> ! { | |||
| 92 | //}); | 90 | //}); |
| 93 | 91 | ||
| 94 | // Init network stack | 92 | // Init network stack |
| 95 | static STACK: StaticCell<Stack<Device>> = StaticCell::new(); | ||
| 96 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 93 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 97 | let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); | 94 | let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); |
| 98 | 95 | ||
| 99 | // Launch network task | 96 | // Launch network task |
| 100 | unwrap!(spawner.spawn(net_task(&stack))); | 97 | unwrap!(spawner.spawn(net_task(runner))); |
| 101 | 98 | ||
| 102 | // Ensure DHCP configuration is up before trying connect | 99 | // Ensure DHCP configuration is up before trying connect |
| 103 | stack.wait_config_up().await; | 100 | stack.wait_config_up().await; |
| @@ -109,7 +106,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 109 | let mut tx_buffer = [0; 1024]; | 106 | let mut tx_buffer = [0; 1024]; |
| 110 | 107 | ||
| 111 | loop { | 108 | loop { |
| 112 | let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); | 109 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); |
| 113 | 110 | ||
| 114 | socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); | 111 | socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); |
| 115 | 112 | ||
diff --git a/examples/stm32h5/src/bin/stop.rs b/examples/stm32h5/src/bin/stop.rs index 0d14c0668..e650791c5 100644 --- a/examples/stm32h5/src/bin/stop.rs +++ b/examples/stm32h5/src/bin/stop.rs | |||
| @@ -10,7 +10,7 @@ use embassy_stm32::gpio::{AnyPin, Level, Output, Speed}; | |||
| 10 | use embassy_stm32::low_power::Executor; | 10 | use embassy_stm32::low_power::Executor; |
| 11 | use embassy_stm32::rcc::{HSIPrescaler, LsConfig}; | 11 | use embassy_stm32::rcc::{HSIPrescaler, LsConfig}; |
| 12 | use embassy_stm32::rtc::{Rtc, RtcConfig}; | 12 | use embassy_stm32::rtc::{Rtc, RtcConfig}; |
| 13 | use embassy_stm32::Config; | 13 | use embassy_stm32::{Config, Peri}; |
| 14 | use embassy_time::Timer; | 14 | use embassy_time::Timer; |
| 15 | use static_cell::StaticCell; | 15 | use static_cell::StaticCell; |
| 16 | use {defmt_rtt as _, panic_probe as _}; | 16 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -48,7 +48,7 @@ async fn async_main(spawner: Spawner) { | |||
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | #[embassy_executor::task] | 50 | #[embassy_executor::task] |
| 51 | async fn blinky(led: AnyPin) { | 51 | async fn blinky(led: Peri<'static, AnyPin>) { |
| 52 | let mut led = Output::new(led, Level::Low, Speed::Low); | 52 | let mut led = Output::new(led, Level::Low, Speed::Low); |
| 53 | loop { | 53 | loop { |
| 54 | info!("high"); | 54 | info!("high"); |
diff --git a/examples/stm32h5/src/bin/usb_c_pd.rs b/examples/stm32h5/src/bin/usb_c_pd.rs new file mode 100644 index 000000000..acb03e498 --- /dev/null +++ b/examples/stm32h5/src/bin/usb_c_pd.rs | |||
| @@ -0,0 +1,93 @@ | |||
| 1 | //! This example targets the NUCLEO-H563ZI platform. | ||
| 2 | //! USB-C CC lines are protected by a TCPP01-M12 chipset. | ||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | |||
| 6 | use defmt::{error, info, Format}; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_stm32::gpio::Output; | ||
| 9 | use embassy_stm32::ucpd::{self, CcPhy, CcPull, CcSel, CcVState, Ucpd}; | ||
| 10 | use embassy_stm32::{bind_interrupts, peripherals, Config}; | ||
| 11 | use embassy_time::{with_timeout, Duration}; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | bind_interrupts!(struct Irqs { | ||
| 15 | UCPD1 => ucpd::InterruptHandler<peripherals::UCPD1>; | ||
| 16 | }); | ||
| 17 | |||
| 18 | #[derive(Debug, Format)] | ||
| 19 | enum CableOrientation { | ||
| 20 | Normal, | ||
| 21 | Flipped, | ||
| 22 | DebugAccessoryMode, | ||
| 23 | } | ||
| 24 | |||
| 25 | // Returns true when the cable | ||
| 26 | async fn wait_attached<T: ucpd::Instance>(cc_phy: &mut CcPhy<'_, T>) -> CableOrientation { | ||
| 27 | loop { | ||
| 28 | let (cc1, cc2) = cc_phy.vstate(); | ||
| 29 | if cc1 == CcVState::LOWEST && cc2 == CcVState::LOWEST { | ||
| 30 | // Detached, wait until attached by monitoring the CC lines. | ||
| 31 | cc_phy.wait_for_vstate_change().await; | ||
| 32 | continue; | ||
| 33 | } | ||
| 34 | |||
| 35 | // Attached, wait for CC lines to be stable for tCCDebounce (100..200ms). | ||
| 36 | if with_timeout(Duration::from_millis(100), cc_phy.wait_for_vstate_change()) | ||
| 37 | .await | ||
| 38 | .is_ok() | ||
| 39 | { | ||
| 40 | // State has changed, restart detection procedure. | ||
| 41 | continue; | ||
| 42 | }; | ||
| 43 | |||
| 44 | // State was stable for the complete debounce period, check orientation. | ||
| 45 | return match (cc1, cc2) { | ||
| 46 | (_, CcVState::LOWEST) => CableOrientation::Normal, // CC1 connected | ||
| 47 | (CcVState::LOWEST, _) => CableOrientation::Flipped, // CC2 connected | ||
| 48 | _ => CableOrientation::DebugAccessoryMode, // Both connected (special cable) | ||
| 49 | }; | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | #[embassy_executor::main] | ||
| 54 | async fn main(_spawner: Spawner) { | ||
| 55 | let config = Config::default(); | ||
| 56 | let p = embassy_stm32::init(config); | ||
| 57 | |||
| 58 | info!("Hello World!"); | ||
| 59 | |||
| 60 | let mut ucpd = Ucpd::new(p.UCPD1, Irqs {}, p.PB13, p.PB14, Default::default()); | ||
| 61 | ucpd.cc_phy().set_pull(CcPull::Sink); | ||
| 62 | |||
| 63 | // This pin controls the dead-battery mode on the attached TCPP01-M12. | ||
| 64 | // If low, TCPP01-M12 disconnects CC lines and presents dead-battery resistance on CC lines, thus set high. | ||
| 65 | // Must only be set after the CC pull is established. | ||
| 66 | let _tcpp01_m12_ndb = Output::new(p.PA9, embassy_stm32::gpio::Level::High, embassy_stm32::gpio::Speed::Low); | ||
| 67 | |||
| 68 | info!("Waiting for USB connection..."); | ||
| 69 | let cable_orientation = wait_attached(ucpd.cc_phy()).await; | ||
| 70 | info!("USB cable connected, orientation: {}", cable_orientation); | ||
| 71 | |||
| 72 | let cc_sel = match cable_orientation { | ||
| 73 | CableOrientation::Normal => { | ||
| 74 | info!("Starting PD communication on CC1 pin"); | ||
| 75 | CcSel::CC1 | ||
| 76 | } | ||
| 77 | CableOrientation::Flipped => { | ||
| 78 | info!("Starting PD communication on CC2 pin"); | ||
| 79 | CcSel::CC2 | ||
| 80 | } | ||
| 81 | CableOrientation::DebugAccessoryMode => panic!("No PD communication in DAM"), | ||
| 82 | }; | ||
| 83 | let (_cc_phy, mut pd_phy) = ucpd.split_pd_phy(p.GPDMA1_CH0, p.GPDMA1_CH1, cc_sel); | ||
| 84 | |||
| 85 | loop { | ||
| 86 | // Enough space for the longest non-extended data message. | ||
| 87 | let mut buf = [0_u8; 30]; | ||
| 88 | match pd_phy.receive(buf.as_mut()).await { | ||
| 89 | Ok(n) => info!("USB PD RX: {=[u8]:?}", &buf[..n]), | ||
| 90 | Err(e) => error!("USB PD RX: {}", e), | ||
| 91 | } | ||
| 92 | } | ||
| 93 | } | ||
diff --git a/examples/stm32h5/src/bin/usb_serial.rs b/examples/stm32h5/src/bin/usb_serial.rs index fbcbdb5f9..e8f536133 100644 --- a/examples/stm32h5/src/bin/usb_serial.rs +++ b/examples/stm32h5/src/bin/usb_serial.rs | |||
| @@ -56,13 +56,6 @@ async fn main(_spawner: Spawner) { | |||
| 56 | config.product = Some("USB-serial example"); | 56 | config.product = Some("USB-serial example"); |
| 57 | config.serial_number = Some("12345678"); | 57 | config.serial_number = Some("12345678"); |
| 58 | 58 | ||
| 59 | // Required for windows compatibility. | ||
| 60 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 61 | config.device_class = 0xEF; | ||
| 62 | config.device_sub_class = 0x02; | ||
| 63 | config.device_protocol = 0x01; | ||
| 64 | config.composite_with_iads = true; | ||
| 65 | |||
| 66 | // Create embassy-usb DeviceBuilder using the driver and config. | 59 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 67 | // It needs some buffers for building the descriptors. | 60 | // It needs some buffers for building the descriptors. |
| 68 | let mut config_descriptor = [0; 256]; | 61 | let mut config_descriptor = [0; 256]; |
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..5d007261c --- /dev/null +++ b/examples/stm32h5/src/bin/usb_uac_speaker.rs | |||
| @@ -0,0 +1,374 @@ | |||
| 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 | let mut builder = embassy_usb::Builder::new( | ||
| 313 | usb_driver, | ||
| 314 | config, | ||
| 315 | config_descriptor, | ||
| 316 | bos_descriptor, | ||
| 317 | &mut [], // no msos descriptors | ||
| 318 | control_buf, | ||
| 319 | ); | ||
| 320 | |||
| 321 | // Create the UAC1 Speaker class components | ||
| 322 | let (stream, feedback, control_monitor) = Speaker::new( | ||
| 323 | &mut builder, | ||
| 324 | state, | ||
| 325 | USB_MAX_PACKET_SIZE as u16, | ||
| 326 | uac1::SampleWidth::Width4Byte, | ||
| 327 | &[SAMPLE_RATE_HZ], | ||
| 328 | &AUDIO_CHANNELS, | ||
| 329 | FEEDBACK_REFRESH_PERIOD, | ||
| 330 | ); | ||
| 331 | |||
| 332 | // Create the USB device | ||
| 333 | let usb_device = builder.build(); | ||
| 334 | |||
| 335 | // Establish a zero-copy channel for transferring received audio samples between tasks | ||
| 336 | static SAMPLE_BLOCKS: StaticCell<[SampleBlock; 2]> = StaticCell::new(); | ||
| 337 | let sample_blocks = SAMPLE_BLOCKS.init([Vec::new(), Vec::new()]); | ||
| 338 | |||
| 339 | static CHANNEL: StaticCell<zerocopy_channel::Channel<'_, NoopRawMutex, SampleBlock>> = StaticCell::new(); | ||
| 340 | let channel = CHANNEL.init(zerocopy_channel::Channel::new(sample_blocks)); | ||
| 341 | let (sender, receiver) = channel.split(); | ||
| 342 | |||
| 343 | // Run a timer for counting between SOF interrupts. | ||
| 344 | let mut tim5 = timer::low_level::Timer::new(p.TIM5); | ||
| 345 | tim5.set_tick_freq(Hertz(FEEDBACK_COUNTER_TICK_RATE)); | ||
| 346 | tim5.set_trigger_source(timer::low_level::TriggerSource::ITR12); // The USB SOF signal. | ||
| 347 | |||
| 348 | const TIMER_CHANNEL: timer::Channel = timer::Channel::Ch1; | ||
| 349 | tim5.set_input_ti_selection(TIMER_CHANNEL, timer::low_level::InputTISelection::TRC); | ||
| 350 | tim5.set_input_capture_prescaler(TIMER_CHANNEL, 0); | ||
| 351 | tim5.set_input_capture_filter(TIMER_CHANNEL, timer::low_level::FilterValue::FCK_INT_N2); | ||
| 352 | |||
| 353 | // Reset all interrupt flags. | ||
| 354 | tim5.regs_gp32().sr().write(|r| r.0 = 0); | ||
| 355 | |||
| 356 | tim5.enable_channel(TIMER_CHANNEL, true); | ||
| 357 | tim5.enable_input_interrupt(TIMER_CHANNEL, true); | ||
| 358 | |||
| 359 | tim5.start(); | ||
| 360 | |||
| 361 | TIMER.lock(|p| p.borrow_mut().replace(tim5)); | ||
| 362 | |||
| 363 | // Unmask the TIM5 interrupt. | ||
| 364 | unsafe { | ||
| 365 | cortex_m::peripheral::NVIC::unmask(interrupt::TIM5); | ||
| 366 | } | ||
| 367 | |||
| 368 | // Launch USB audio tasks. | ||
| 369 | unwrap!(spawner.spawn(usb_control_task(control_monitor))); | ||
| 370 | unwrap!(spawner.spawn(usb_streaming_task(stream, sender))); | ||
| 371 | unwrap!(spawner.spawn(usb_feedback_task(feedback))); | ||
| 372 | unwrap!(spawner.spawn(usb_task(usb_device))); | ||
| 373 | unwrap!(spawner.spawn(audio_receiver_task(receiver))); | ||
| 374 | } | ||
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 13fce7dc7..27c59d980 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml | |||
| @@ -6,28 +6,27 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 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.2.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.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } | 11 | embassy-embedded-hal = { version = "0.3.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.7.0", path = "../../embassy-executor", features = ["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.4.0", 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.7.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.4.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 | ||
| 18 | defmt = "0.3" | 18 | defmt = "1.0.1" |
| 19 | defmt-rtt = "0.4" | 19 | defmt-rtt = "1.0.0" |
| 20 | 20 | ||
| 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 22 | cortex-m-rt = "0.7.0" | 22 | 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 = "1.0.0", features = ["print-defmt"] } |
| 29 | heapless = { version = "0.8", default-features = false } | 29 | heapless = { version = "0.8", default-features = false } |
| 30 | rand_core = "0.6.3" | ||
| 31 | critical-section = "1.1" | 30 | critical-section = "1.1" |
| 32 | micromath = "2.0.0" | 31 | micromath = "2.0.0" |
| 33 | stm32-fmc = "0.3.0" | 32 | stm32-fmc = "0.3.0" |
diff --git a/examples/stm32h7/src/bin/adc_dma.rs b/examples/stm32h7/src/bin/adc_dma.rs index 0b905d227..f06b5d06e 100644 --- a/examples/stm32h7/src/bin/adc_dma.rs +++ b/examples/stm32h7/src/bin/adc_dma.rs | |||
| @@ -8,7 +8,7 @@ use embassy_stm32::Config; | |||
| 8 | use embassy_time::Timer; | 8 | use embassy_time::Timer; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 10 | ||
| 11 | #[link_section = ".ram_d3"] | 11 | #[unsafe(link_section = ".ram_d3")] |
| 12 | static mut DMA_BUF: [u16; 2] = [0; 2]; | 12 | static mut DMA_BUF: [u16; 2] = [0; 2]; |
| 13 | 13 | ||
| 14 | #[embassy_executor::main] | 14 | #[embassy_executor::main] |
| @@ -57,7 +57,7 @@ async fn main(_spawner: Spawner) { | |||
| 57 | 57 | ||
| 58 | loop { | 58 | loop { |
| 59 | adc.read( | 59 | adc.read( |
| 60 | &mut dma, | 60 | dma.reborrow(), |
| 61 | [ | 61 | [ |
| 62 | (&mut vrefint_channel, SampleTime::CYCLES387_5), | 62 | (&mut vrefint_channel, SampleTime::CYCLES387_5), |
| 63 | (&mut pc0, SampleTime::CYCLES810_5), | 63 | (&mut pc0, SampleTime::CYCLES810_5), |
diff --git a/examples/stm32h7/src/bin/dac.rs b/examples/stm32h7/src/bin/dac.rs index a6f969aba..27df80336 100644 --- a/examples/stm32h7/src/bin/dac.rs +++ b/examples/stm32h7/src/bin/dac.rs | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | use cortex_m_rt::entry; | 4 | use cortex_m_rt::entry; |
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_stm32::dac::{DacCh1, Value}; | 6 | use embassy_stm32::dac::{DacCh1, Value}; |
| 7 | use embassy_stm32::dma::NoDma; | ||
| 8 | use embassy_stm32::Config; | 7 | use embassy_stm32::Config; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 10 | 9 | ||
| @@ -44,7 +43,7 @@ fn main() -> ! { | |||
| 44 | } | 43 | } |
| 45 | let p = embassy_stm32::init(config); | 44 | let p = embassy_stm32::init(config); |
| 46 | 45 | ||
| 47 | let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); | 46 | let mut dac = DacCh1::new_blocking(p.DAC1, p.PA4); |
| 48 | 47 | ||
| 49 | loop { | 48 | loop { |
| 50 | for v in 0..=255 { | 49 | for v in 0..=255 { |
diff --git a/examples/stm32h7/src/bin/dac_dma.rs b/examples/stm32h7/src/bin/dac_dma.rs index 3a9887e3c..8314754bc 100644 --- a/examples/stm32h7/src/bin/dac_dma.rs +++ b/examples/stm32h7/src/bin/dac_dma.rs | |||
| @@ -4,11 +4,13 @@ | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::dac::{DacCh1, DacCh2, ValueArray}; | 6 | use embassy_stm32::dac::{DacCh1, DacCh2, ValueArray}; |
| 7 | use embassy_stm32::mode::Async; | ||
| 7 | use embassy_stm32::pac::timer::vals::Mms; | 8 | use embassy_stm32::pac::timer::vals::Mms; |
| 8 | use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7}; | 9 | use embassy_stm32::peripherals::{DAC1, TIM6, TIM7}; |
| 9 | use embassy_stm32::rcc::frequency; | 10 | use embassy_stm32::rcc::frequency; |
| 10 | use embassy_stm32::time::Hertz; | 11 | use embassy_stm32::time::Hertz; |
| 11 | use embassy_stm32::timer::low_level::Timer; | 12 | use embassy_stm32::timer::low_level::Timer; |
| 13 | use embassy_stm32::Peri; | ||
| 12 | use micromath::F32Ext; | 14 | use micromath::F32Ext; |
| 13 | use {defmt_rtt as _, panic_probe as _}; | 15 | use {defmt_rtt as _, panic_probe as _}; |
| 14 | 16 | ||
| @@ -56,7 +58,7 @@ async fn main(spawner: Spawner) { | |||
| 56 | } | 58 | } |
| 57 | 59 | ||
| 58 | #[embassy_executor::task] | 60 | #[embassy_executor::task] |
| 59 | async fn dac_task1(tim: TIM6, mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { | 61 | async fn dac_task1(tim: Peri<'static, TIM6>, mut dac: DacCh1<'static, DAC1, Async>) { |
| 60 | let data: &[u8; 256] = &calculate_array::<256>(); | 62 | let data: &[u8; 256] = &calculate_array::<256>(); |
| 61 | 63 | ||
| 62 | info!("TIM6 frequency is {}", frequency::<TIM6>()); | 64 | info!("TIM6 frequency is {}", frequency::<TIM6>()); |
| @@ -99,7 +101,7 @@ async fn dac_task1(tim: TIM6, mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { | |||
| 99 | } | 101 | } |
| 100 | 102 | ||
| 101 | #[embassy_executor::task] | 103 | #[embassy_executor::task] |
| 102 | async fn dac_task2(tim: TIM7, mut dac: DacCh2<'static, DAC1, DMA1_CH4>) { | 104 | async fn dac_task2(tim: Peri<'static, TIM7>, mut dac: DacCh2<'static, DAC1, Async>) { |
| 103 | let data: &[u8; 256] = &calculate_array::<256>(); | 105 | let data: &[u8; 256] = &calculate_array::<256>(); |
| 104 | 106 | ||
| 105 | info!("TIM7 frequency is {}", frequency::<TIM6>()); | 107 | info!("TIM7 frequency is {}", frequency::<TIM6>()); |
diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index b2f8ed91e..fc14c1a70 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs | |||
| @@ -4,15 +4,13 @@ | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_net::tcp::TcpSocket; | 6 | use embassy_net::tcp::TcpSocket; |
| 7 | use embassy_net::{Ipv4Address, Stack, StackResources}; | 7 | use embassy_net::{Ipv4Address, StackResources}; |
| 8 | use embassy_stm32::eth::generic_smi::GenericSMI; | 8 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; |
| 9 | use embassy_stm32::eth::{Ethernet, PacketQueue}; | ||
| 10 | use embassy_stm32::peripherals::ETH; | 9 | use embassy_stm32::peripherals::ETH; |
| 11 | use embassy_stm32::rng::Rng; | 10 | use embassy_stm32::rng::Rng; |
| 12 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; | 11 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; |
| 13 | use embassy_time::Timer; | 12 | use embassy_time::Timer; |
| 14 | use embedded_io_async::Write; | 13 | use embedded_io_async::Write; |
| 15 | use rand_core::RngCore; | ||
| 16 | use static_cell::StaticCell; | 14 | use static_cell::StaticCell; |
| 17 | use {defmt_rtt as _, panic_probe as _}; | 15 | use {defmt_rtt as _, panic_probe as _}; |
| 18 | 16 | ||
| @@ -21,11 +19,11 @@ bind_interrupts!(struct Irqs { | |||
| 21 | RNG => rng::InterruptHandler<peripherals::RNG>; | 19 | RNG => rng::InterruptHandler<peripherals::RNG>; |
| 22 | }); | 20 | }); |
| 23 | 21 | ||
| 24 | type Device = Ethernet<'static, ETH, GenericSMI>; | 22 | type Device = Ethernet<'static, ETH, GenericPhy>; |
| 25 | 23 | ||
| 26 | #[embassy_executor::task] | 24 | #[embassy_executor::task] |
| 27 | async fn net_task(stack: &'static Stack<Device>) -> ! { | 25 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { |
| 28 | stack.run().await | 26 | runner.run().await |
| 29 | } | 27 | } |
| 30 | 28 | ||
| 31 | #[embassy_executor::main] | 29 | #[embassy_executor::main] |
| @@ -79,7 +77,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 79 | p.PG13, // TX_D0: Transmit Bit 0 | 77 | p.PG13, // TX_D0: Transmit Bit 0 |
| 80 | p.PB13, // TX_D1: Transmit Bit 1 | 78 | p.PB13, // TX_D1: Transmit Bit 1 |
| 81 | p.PG11, // TX_EN: Transmit Enable | 79 | p.PG11, // TX_EN: Transmit Enable |
| 82 | GenericSMI::new(0), | 80 | GenericPhy::new_auto(), |
| 83 | mac_addr, | 81 | mac_addr, |
| 84 | ); | 82 | ); |
| 85 | 83 | ||
| @@ -91,12 +89,11 @@ async fn main(spawner: Spawner) -> ! { | |||
| 91 | //}); | 89 | //}); |
| 92 | 90 | ||
| 93 | // Init network stack | 91 | // Init network stack |
| 94 | static STACK: StaticCell<Stack<Device>> = StaticCell::new(); | ||
| 95 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 92 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 96 | let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); | 93 | let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); |
| 97 | 94 | ||
| 98 | // Launch network task | 95 | // Launch network task |
| 99 | unwrap!(spawner.spawn(net_task(&stack))); | 96 | unwrap!(spawner.spawn(net_task(runner))); |
| 100 | 97 | ||
| 101 | // Ensure DHCP configuration is up before trying connect | 98 | // Ensure DHCP configuration is up before trying connect |
| 102 | stack.wait_config_up().await; | 99 | stack.wait_config_up().await; |
| @@ -108,7 +105,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 108 | let mut tx_buffer = [0; 1024]; | 105 | let mut tx_buffer = [0; 1024]; |
| 109 | 106 | ||
| 110 | loop { | 107 | loop { |
| 111 | let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); | 108 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); |
| 112 | 109 | ||
| 113 | socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); | 110 | socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); |
| 114 | 111 | ||
diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 274c24ab1..46301a478 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs | |||
| @@ -1,19 +1,19 @@ | |||
| 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}; |
| 7 | use embassy_net::{Stack, StackResources}; | 9 | use embassy_net::StackResources; |
| 8 | use embassy_stm32::eth::generic_smi::GenericSMI; | 10 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; |
| 9 | use embassy_stm32::eth::{Ethernet, PacketQueue}; | ||
| 10 | use embassy_stm32::peripherals::ETH; | 11 | use embassy_stm32::peripherals::ETH; |
| 11 | use embassy_stm32::rng::Rng; | 12 | use embassy_stm32::rng::Rng; |
| 12 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; | 13 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; |
| 13 | use embassy_time::Timer; | 14 | use embassy_time::Timer; |
| 14 | use embedded_io_async::Write; | 15 | use embedded_io_async::Write; |
| 15 | use embedded_nal_async::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpConnect}; | 16 | use embedded_nal_async::TcpConnect; |
| 16 | use rand_core::RngCore; | ||
| 17 | use static_cell::StaticCell; | 17 | use static_cell::StaticCell; |
| 18 | use {defmt_rtt as _, panic_probe as _}; | 18 | use {defmt_rtt as _, panic_probe as _}; |
| 19 | 19 | ||
| @@ -22,11 +22,11 @@ bind_interrupts!(struct Irqs { | |||
| 22 | RNG => rng::InterruptHandler<peripherals::RNG>; | 22 | RNG => rng::InterruptHandler<peripherals::RNG>; |
| 23 | }); | 23 | }); |
| 24 | 24 | ||
| 25 | type Device = Ethernet<'static, ETH, GenericSMI>; | 25 | type Device = Ethernet<'static, ETH, GenericPhy>; |
| 26 | 26 | ||
| 27 | #[embassy_executor::task] | 27 | #[embassy_executor::task] |
| 28 | async fn net_task(stack: &'static Stack<Device>) -> ! { | 28 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { |
| 29 | stack.run().await | 29 | runner.run().await |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | #[embassy_executor::main] | 32 | #[embassy_executor::main] |
| @@ -79,7 +79,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 79 | p.PG13, | 79 | p.PG13, |
| 80 | p.PB13, | 80 | p.PB13, |
| 81 | p.PG11, | 81 | p.PG11, |
| 82 | GenericSMI::new(0), | 82 | GenericPhy::new_auto(), |
| 83 | mac_addr, | 83 | mac_addr, |
| 84 | ); | 84 | ); |
| 85 | 85 | ||
| @@ -91,12 +91,11 @@ async fn main(spawner: Spawner) -> ! { | |||
| 91 | //}); | 91 | //}); |
| 92 | 92 | ||
| 93 | // Init network stack | 93 | // Init network stack |
| 94 | static STACK: StaticCell<Stack<Device>> = StaticCell::new(); | ||
| 95 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 94 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 96 | let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); | 95 | let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); |
| 97 | 96 | ||
| 98 | // Launch network task | 97 | // Launch network task |
| 99 | unwrap!(spawner.spawn(net_task(stack))); | 98 | unwrap!(spawner.spawn(net_task(runner))); |
| 100 | 99 | ||
| 101 | // Ensure DHCP configuration is up before trying connect | 100 | // Ensure DHCP configuration is up before trying connect |
| 102 | stack.wait_config_up().await; | 101 | stack.wait_config_up().await; |
| @@ -104,7 +103,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 104 | info!("Network task initialized"); | 103 | info!("Network task initialized"); |
| 105 | 104 | ||
| 106 | let state: TcpClientState<1, 1024, 1024> = TcpClientState::new(); | 105 | let state: TcpClientState<1, 1024, 1024> = TcpClientState::new(); |
| 107 | let client = TcpClient::new(&stack, &state); | 106 | let client = TcpClient::new(stack, &state); |
| 108 | 107 | ||
| 109 | loop { | 108 | loop { |
| 110 | // You need to start a server on the host machine, for example: `nc -l 8000` | 109 | // You need to start a server on the host machine, for example: `nc -l 8000` |
diff --git a/examples/stm32h7/src/bin/eth_client_mii.rs b/examples/stm32h7/src/bin/eth_client_mii.rs index aa6544f41..99cd1a158 100644 --- a/examples/stm32h7/src/bin/eth_client_mii.rs +++ b/examples/stm32h7/src/bin/eth_client_mii.rs | |||
| @@ -1,19 +1,19 @@ | |||
| 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}; |
| 7 | use embassy_net::{Stack, StackResources}; | 9 | use embassy_net::StackResources; |
| 8 | use embassy_stm32::eth::generic_smi::GenericSMI; | 10 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; |
| 9 | use embassy_stm32::eth::{Ethernet, PacketQueue}; | ||
| 10 | use embassy_stm32::peripherals::ETH; | 11 | use embassy_stm32::peripherals::ETH; |
| 11 | use embassy_stm32::rng::Rng; | 12 | use embassy_stm32::rng::Rng; |
| 12 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; | 13 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; |
| 13 | use embassy_time::Timer; | 14 | use embassy_time::Timer; |
| 14 | use embedded_io_async::Write; | 15 | use embedded_io_async::Write; |
| 15 | use embedded_nal_async::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpConnect}; | 16 | use embedded_nal_async::TcpConnect; |
| 16 | use rand_core::RngCore; | ||
| 17 | use static_cell::StaticCell; | 17 | use static_cell::StaticCell; |
| 18 | use {defmt_rtt as _, panic_probe as _}; | 18 | use {defmt_rtt as _, panic_probe as _}; |
| 19 | 19 | ||
| @@ -22,11 +22,11 @@ bind_interrupts!(struct Irqs { | |||
| 22 | RNG => rng::InterruptHandler<peripherals::RNG>; | 22 | RNG => rng::InterruptHandler<peripherals::RNG>; |
| 23 | }); | 23 | }); |
| 24 | 24 | ||
| 25 | type Device = Ethernet<'static, ETH, GenericSMI>; | 25 | type Device = Ethernet<'static, ETH, GenericPhy>; |
| 26 | 26 | ||
| 27 | #[embassy_executor::task] | 27 | #[embassy_executor::task] |
| 28 | async fn net_task(stack: &'static Stack<Device>) -> ! { | 28 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { |
| 29 | stack.run().await | 29 | runner.run().await |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | #[embassy_executor::main] | 32 | #[embassy_executor::main] |
| @@ -84,7 +84,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 84 | p.PC2, | 84 | p.PC2, |
| 85 | p.PE2, | 85 | p.PE2, |
| 86 | p.PG11, | 86 | p.PG11, |
| 87 | GenericSMI::new(1), | 87 | GenericPhy::new_auto(), |
| 88 | mac_addr, | 88 | mac_addr, |
| 89 | ); | 89 | ); |
| 90 | info!("Device created"); | 90 | info!("Device created"); |
| @@ -97,12 +97,11 @@ async fn main(spawner: Spawner) -> ! { | |||
| 97 | //}); | 97 | //}); |
| 98 | 98 | ||
| 99 | // Init network stack | 99 | // Init network stack |
| 100 | static STACK: StaticCell<Stack<Device>> = StaticCell::new(); | ||
| 101 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 100 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 102 | let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); | 101 | let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); |
| 103 | 102 | ||
| 104 | // Launch network task | 103 | // Launch network task |
| 105 | unwrap!(spawner.spawn(net_task(stack))); | 104 | unwrap!(spawner.spawn(net_task(runner))); |
| 106 | 105 | ||
| 107 | // Ensure DHCP configuration is up before trying connect | 106 | // Ensure DHCP configuration is up before trying connect |
| 108 | stack.wait_config_up().await; | 107 | stack.wait_config_up().await; |
| @@ -110,7 +109,7 @@ async fn main(spawner: Spawner) -> ! { | |||
| 110 | info!("Network task initialized"); | 109 | info!("Network task initialized"); |
| 111 | 110 | ||
| 112 | let state: TcpClientState<1, 1024, 1024> = TcpClientState::new(); | 111 | let state: TcpClientState<1, 1024, 1024> = TcpClientState::new(); |
| 113 | let client = TcpClient::new(&stack, &state); | 112 | let client = TcpClient::new(stack, &state); |
| 114 | 113 | ||
| 115 | loop { | 114 | loop { |
| 116 | // You need to start a server on the host machine, for example: `nc -l 8000` | 115 | // You need to start a server on the host machine, for example: `nc -l 8000` |
diff --git a/examples/stm32h7/src/bin/i2c_shared.rs b/examples/stm32h7/src/bin/i2c_shared.rs index 321b35a3e..ec5757284 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/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs index b796996ea..8de31ea5b 100644 --- a/examples/stm32h7/src/bin/low_level_timer_api.rs +++ b/examples/stm32h7/src/bin/low_level_timer_api.rs | |||
| @@ -7,7 +7,7 @@ use embassy_stm32::gpio::{AfType, Flex, OutputType, Speed}; | |||
| 7 | use embassy_stm32::time::{khz, Hertz}; | 7 | use embassy_stm32::time::{khz, Hertz}; |
| 8 | use embassy_stm32::timer::low_level::{OutputCompareMode, Timer as LLTimer}; | 8 | use embassy_stm32::timer::low_level::{OutputCompareMode, Timer as LLTimer}; |
| 9 | use embassy_stm32::timer::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance32bit4Channel}; | 9 | use embassy_stm32::timer::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance32bit4Channel}; |
| 10 | use embassy_stm32::{into_ref, Config, Peripheral}; | 10 | use embassy_stm32::{Config, Peri}; |
| 11 | use embassy_time::Timer; | 11 | use embassy_time::Timer; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | 13 | ||
| @@ -66,15 +66,13 @@ pub struct SimplePwm32<'d, T: GeneralInstance32bit4Channel> { | |||
| 66 | 66 | ||
| 67 | impl<'d, T: GeneralInstance32bit4Channel> SimplePwm32<'d, T> { | 67 | impl<'d, T: GeneralInstance32bit4Channel> SimplePwm32<'d, T> { |
| 68 | pub fn new( | 68 | pub fn new( |
| 69 | tim: impl Peripheral<P = T> + 'd, | 69 | tim: Peri<'d, T>, |
| 70 | ch1: impl Peripheral<P = impl Channel1Pin<T>> + 'd, | 70 | ch1: Peri<'d, impl Channel1Pin<T>>, |
| 71 | ch2: impl Peripheral<P = impl Channel2Pin<T>> + 'd, | 71 | ch2: Peri<'d, impl Channel2Pin<T>>, |
| 72 | ch3: impl Peripheral<P = impl Channel3Pin<T>> + 'd, | 72 | ch3: Peri<'d, impl Channel3Pin<T>>, |
| 73 | ch4: impl Peripheral<P = impl Channel4Pin<T>> + 'd, | 73 | ch4: Peri<'d, impl Channel4Pin<T>>, |
| 74 | freq: Hertz, | 74 | freq: Hertz, |
| 75 | ) -> Self { | 75 | ) -> Self { |
| 76 | into_ref!(ch1, ch2, ch3, ch4); | ||
| 77 | |||
| 78 | let af1 = ch1.af_num(); | 76 | let af1 = ch1.af_num(); |
| 79 | let af2 = ch2.af_num(); | 77 | let af2 = ch2.af_num(); |
| 80 | let af3 = ch3.af_num(); | 78 | let af3 = ch3.af_num(); |
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..01937593a 100644 --- a/examples/stm32h7/src/bin/sai.rs +++ b/examples/stm32h7/src/bin/sai.rs | |||
| @@ -16,9 +16,9 @@ const DMA_BUFFER_LENGTH: usize = HALF_DMA_BUFFER_LENGTH * 2; // 2 half-blocks | |||
| 16 | const SAMPLE_RATE: u32 = 48000; | 16 | const SAMPLE_RATE: u32 = 48000; |
| 17 | 17 | ||
| 18 | //DMA buffer must be in special region. Refer https://embassy.dev/book/#_stm32_bdma_only_working_out_of_some_ram_regions | 18 | //DMA buffer must be in special region. Refer https://embassy.dev/book/#_stm32_bdma_only_working_out_of_some_ram_regions |
| 19 | #[link_section = ".sram1_bss"] | 19 | #[unsafe(link_section = ".sram1_bss")] |
| 20 | static mut TX_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit(); | 20 | static mut TX_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit(); |
| 21 | #[link_section = ".sram1_bss"] | 21 | #[unsafe(link_section = ".sram1_bss")] |
| 22 | static mut RX_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit(); | 22 | static mut RX_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit(); |
| 23 | 23 | ||
| 24 | #[embassy_executor::main] | 24 | #[embassy_executor::main] |
| @@ -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,21 +99,23 @@ 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 | ||
| 113 | loop { | 114 | loop { |
| 114 | sai_receiver.read(&mut buf).await.unwrap(); | 115 | // write() must be called before read() to start the master (transmitter) |
| 116 | // clock used by the receiver | ||
| 115 | sai_transmitter.write(&buf).await.unwrap(); | 117 | sai_transmitter.write(&buf).await.unwrap(); |
| 118 | sai_receiver.read(&mut buf).await.unwrap(); | ||
| 116 | } | 119 | } |
| 117 | } | 120 | } |
| 118 | 121 | ||
diff --git a/examples/stm32h7/src/bin/sdmmc.rs b/examples/stm32h7/src/bin/sdmmc.rs index abe2d4ba7..96840d8ff 100644 --- a/examples/stm32h7/src/bin/sdmmc.rs +++ b/examples/stm32h7/src/bin/sdmmc.rs | |||
| @@ -53,7 +53,7 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 53 | // Should print 400kHz for initialization | 53 | // Should print 400kHz for initialization |
| 54 | info!("Configured clock: {}", sdmmc.clock().0); | 54 | info!("Configured clock: {}", sdmmc.clock().0); |
| 55 | 55 | ||
| 56 | unwrap!(sdmmc.init_card(mhz(25)).await); | 56 | unwrap!(sdmmc.init_sd_card(mhz(25)).await); |
| 57 | 57 | ||
| 58 | let card = unwrap!(sdmmc.card()); | 58 | let card = unwrap!(sdmmc.card()); |
| 59 | 59 | ||
diff --git a/examples/stm32h7/src/bin/spi_bdma.rs b/examples/stm32h7/src/bin/spi_bdma.rs index 43fb6b41c..5a7dff572 100644 --- a/examples/stm32h7/src/bin/spi_bdma.rs +++ b/examples/stm32h7/src/bin/spi_bdma.rs | |||
| @@ -16,16 +16,17 @@ use static_cell::StaticCell; | |||
| 16 | use {defmt_rtt as _, panic_probe as _}; | 16 | use {defmt_rtt as _, panic_probe as _}; |
| 17 | 17 | ||
| 18 | // Defined in memory.x | 18 | // Defined in memory.x |
| 19 | #[link_section = ".ram_d3"] | 19 | #[unsafe(link_section = ".ram_d3")] |
| 20 | static mut RAM_D3: GroundedArrayCell<u8, 256> = GroundedArrayCell::uninit(); | 20 | static mut RAM_D3: GroundedArrayCell<u8, 256> = GroundedArrayCell::uninit(); |
| 21 | 21 | ||
| 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/stm32h7/src/bin/usb_serial.rs b/examples/stm32h7/src/bin/usb_serial.rs index 65ae597d4..50bb964da 100644 --- a/examples/stm32h7/src/bin/usb_serial.rs +++ b/examples/stm32h7/src/bin/usb_serial.rs | |||
| @@ -67,13 +67,6 @@ async fn main(_spawner: Spawner) { | |||
| 67 | config.product = Some("USB-serial example"); | 67 | config.product = Some("USB-serial example"); |
| 68 | config.serial_number = Some("12345678"); | 68 | config.serial_number = Some("12345678"); |
| 69 | 69 | ||
| 70 | // Required for windows compatibility. | ||
| 71 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 72 | config.device_class = 0xEF; | ||
| 73 | config.device_sub_class = 0x02; | ||
| 74 | config.device_protocol = 0x01; | ||
| 75 | config.composite_with_iads = true; | ||
| 76 | |||
| 77 | // Create embassy-usb DeviceBuilder using the driver and config. | 70 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 78 | // It needs some buffers for building the descriptors. | 71 | // It needs some buffers for building the descriptors. |
| 79 | let mut config_descriptor = [0; 256]; | 72 | let mut config_descriptor = [0; 256]; |
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..fb219733f --- /dev/null +++ b/examples/stm32h723/Cargo.toml | |||
| @@ -0,0 +1,68 @@ | |||
| 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.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h723zg", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } | ||
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | ||
| 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | ||
| 12 | embassy-time = { version = "0.4.0", 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 = "1.0.1" | ||
| 16 | defmt-rtt = "1.0.0" | ||
| 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 = "1.0.0", features = ["print-defmt"] } | ||
| 26 | heapless = { version = "0.8", default-features = false } | ||
| 27 | critical-section = "1.1" | ||
| 28 | static_cell = "2" | ||
| 29 | chrono = { version = "^0.4", default-features = false } | ||
| 30 | grounded = "0.2.0" | ||
| 31 | |||
| 32 | # cargo build/run | ||
| 33 | [profile.dev] | ||
| 34 | codegen-units = 1 | ||
| 35 | debug = 2 | ||
| 36 | debug-assertions = true # <- | ||
| 37 | incremental = false | ||
| 38 | opt-level = 3 # <- | ||
| 39 | overflow-checks = true # <- | ||
| 40 | |||
| 41 | # cargo test | ||
| 42 | [profile.test] | ||
| 43 | codegen-units = 1 | ||
| 44 | debug = 2 | ||
| 45 | debug-assertions = true # <- | ||
| 46 | incremental = false | ||
| 47 | opt-level = 3 # <- | ||
| 48 | overflow-checks = true # <- | ||
| 49 | |||
| 50 | # cargo build/run --release | ||
| 51 | [profile.release] | ||
| 52 | codegen-units = 1 | ||
| 53 | debug = 2 | ||
| 54 | debug-assertions = false # <- | ||
| 55 | incremental = false | ||
| 56 | lto = 'fat' | ||
| 57 | opt-level = 3 # <- | ||
| 58 | overflow-checks = false # <- | ||
| 59 | |||
| 60 | # cargo test --release | ||
| 61 | [profile.bench] | ||
| 62 | codegen-units = 1 | ||
| 63 | debug = 2 | ||
| 64 | debug-assertions = false # <- | ||
| 65 | incremental = false | ||
| 66 | lto = 'fat' | ||
| 67 | opt-level = 3 # <- | ||
| 68 | 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..a04d7cb34 --- /dev/null +++ b/examples/stm32h723/src/bin/spdifrx.rs | |||
| @@ -0,0 +1,175 @@ | |||
| 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 | #[unsafe(link_section = ".sram1")] | ||
| 28 | static mut SPDIFRX_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit(); | ||
| 29 | |||
| 30 | #[unsafe(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 | p.SAI4.reborrow(), | ||
| 81 | p.PD13.reborrow(), | ||
| 82 | p.PC1.reborrow(), | ||
| 83 | p.PD12.reborrow(), | ||
| 84 | p.BDMA_CH0.reborrow(), | ||
| 85 | sai_buffer, | ||
| 86 | ); | ||
| 87 | let mut spdif_receiver = new_spdif_receiver( | ||
| 88 | p.SPDIFRX1.reborrow(), | ||
| 89 | p.PD7.reborrow(), | ||
| 90 | p.DMA2_CH7.reborrow(), | ||
| 91 | spdifrx_buffer, | ||
| 92 | ); | ||
| 93 | spdif_receiver.start(); | ||
| 94 | |||
| 95 | let mut renew_sai = false; | ||
| 96 | loop { | ||
| 97 | let mut buf = [0u32; HALF_DMA_BUFFER_LENGTH]; | ||
| 98 | |||
| 99 | if renew_sai { | ||
| 100 | renew_sai = false; | ||
| 101 | trace!("Renew SAI."); | ||
| 102 | drop(sai_transmitter); | ||
| 103 | sai_transmitter = new_sai_transmitter( | ||
| 104 | p.SAI4.reborrow(), | ||
| 105 | p.PD13.reborrow(), | ||
| 106 | p.PC1.reborrow(), | ||
| 107 | p.PD12.reborrow(), | ||
| 108 | p.BDMA_CH0.reborrow(), | ||
| 109 | sai_buffer, | ||
| 110 | ); | ||
| 111 | } | ||
| 112 | |||
| 113 | match select(spdif_receiver.read(&mut buf), sai_transmitter.wait_write_error()).await { | ||
| 114 | Either::First(spdif_read_result) => match spdif_read_result { | ||
| 115 | Ok(_) => (), | ||
| 116 | Err(spdifrx::Error::RingbufferError(_)) => { | ||
| 117 | trace!("SPDIFRX ringbuffer error. Renew."); | ||
| 118 | drop(spdif_receiver); | ||
| 119 | spdif_receiver = new_spdif_receiver( | ||
| 120 | p.SPDIFRX1.reborrow(), | ||
| 121 | p.PD7.reborrow(), | ||
| 122 | p.DMA2_CH7.reborrow(), | ||
| 123 | spdifrx_buffer, | ||
| 124 | ); | ||
| 125 | spdif_receiver.start(); | ||
| 126 | continue; | ||
| 127 | } | ||
| 128 | Err(spdifrx::Error::ChannelSyncError) => { | ||
| 129 | trace!("SPDIFRX channel sync (left/right assignment) error."); | ||
| 130 | continue; | ||
| 131 | } | ||
| 132 | }, | ||
| 133 | Either::Second(_) => { | ||
| 134 | renew_sai = true; | ||
| 135 | continue; | ||
| 136 | } | ||
| 137 | }; | ||
| 138 | |||
| 139 | renew_sai = sai_transmitter.write(&buf).await.is_err(); | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | /// Creates a new SPDIFRX instance for receiving sample data. | ||
| 144 | /// | ||
| 145 | /// Used (again) after dropping the SPDIFRX instance, in case of errors (e.g. source disconnect). | ||
| 146 | fn new_spdif_receiver<'d>( | ||
| 147 | spdifrx: &'d mut peripherals::SPDIFRX1, | ||
| 148 | input_pin: &'d mut peripherals::PD7, | ||
| 149 | dma: &'d mut peripherals::DMA2_CH7, | ||
| 150 | buf: &'d mut [u32], | ||
| 151 | ) -> Spdifrx<'d, peripherals::SPDIFRX1> { | ||
| 152 | Spdifrx::new(spdifrx, Irqs, spdifrx::Config::default(), input_pin, dma, buf) | ||
| 153 | } | ||
| 154 | |||
| 155 | /// Creates a new SAI4 instance for transmitting sample data. | ||
| 156 | /// | ||
| 157 | /// Used (again) after dropping the SAI4 instance, in case of errors (e.g. buffer overrun). | ||
| 158 | fn new_sai_transmitter<'d>( | ||
| 159 | sai: &'d mut peripherals::SAI4, | ||
| 160 | sck: &'d mut peripherals::PD13, | ||
| 161 | sd: &'d mut peripherals::PC1, | ||
| 162 | fs: &'d mut peripherals::PD12, | ||
| 163 | dma: &'d mut peripherals::BDMA_CH0, | ||
| 164 | buf: &'d mut [u32], | ||
| 165 | ) -> Sai<'d, peripherals::SAI4, u32> { | ||
| 166 | let mut sai_config = hal::sai::Config::default(); | ||
| 167 | sai_config.slot_count = hal::sai::word::U4(CHANNEL_COUNT as u8); | ||
| 168 | sai_config.slot_enable = 0xFFFF; // All slots | ||
| 169 | sai_config.data_size = sai::DataSize::Data32; | ||
| 170 | sai_config.frame_length = (CHANNEL_COUNT * 32) as u8; | ||
| 171 | sai_config.master_clock_divider = hal::sai::MasterClockDivider::MasterClockDisabled; | ||
| 172 | |||
| 173 | let (sub_block_tx, _) = hal::sai::split_subblocks(sai); | ||
| 174 | Sai::new_asynchronous(sub_block_tx, sck, sd, fs, dma, buf, sai_config) | ||
| 175 | } | ||
diff --git a/examples/stm32h735/Cargo.toml b/examples/stm32h735/Cargo.toml index 93e9575b6..8d23c346a 100644 --- a/examples/stm32h735/Cargo.toml +++ b/examples/stm32h735/Cargo.toml | |||
| @@ -5,19 +5,19 @@ 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-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.2.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.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 10 | embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } | 10 | embassy-embedded-hal = { version = "0.3.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.7.0", 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.4.0", 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 | ||
| 15 | defmt = "0.3" | 15 | defmt = "1.0.1" |
| 16 | defmt-rtt = "0.4" | 16 | defmt-rtt = "1.0.0" |
| 17 | 17 | ||
| 18 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 18 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 19 | cortex-m-rt = "0.7.0" | 19 | cortex-m-rt = "0.7.0" |
| 20 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 20 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 21 | heapless = { version = "0.8", default-features = false } | 21 | heapless = { version = "0.8", default-features = false } |
| 22 | embedded-graphics = { version = "0.8.1" } | 22 | embedded-graphics = { version = "0.8.1" } |
| 23 | tinybmp = { version = "0.5" } | 23 | tinybmp = { version = "0.5" } |
diff --git a/examples/stm32h742/.cargo/config.toml b/examples/stm32h742/.cargo/config.toml new file mode 100644 index 000000000..f30a52a79 --- /dev/null +++ b/examples/stm32h742/.cargo/config.toml | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | ||
| 2 | # replace STM32H742VITx with your chip as listed in `probe-rs chip list` | ||
| 3 | runner = "probe-rs run --chip STM32H742VITx" | ||
| 4 | |||
| 5 | [build] | ||
| 6 | target = "thumbv7em-none-eabihf" | ||
| 7 | |||
| 8 | [env] | ||
| 9 | DEFMT_LOG = "trace" | ||
diff --git a/examples/stm32h742/Cargo.toml b/examples/stm32h742/Cargo.toml new file mode 100644 index 000000000..31eff4379 --- /dev/null +++ b/examples/stm32h742/Cargo.toml | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-stm32f7-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | # Change stm32f777zi to your chip name, if necessary. | ||
| 9 | embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ | ||
| 10 | "defmt", | ||
| 11 | "stm32h742vi", | ||
| 12 | "memory-x", | ||
| 13 | "unstable-pac", | ||
| 14 | "time-driver-any", | ||
| 15 | "exti", | ||
| 16 | ] } | ||
| 17 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = [ | ||
| 18 | "defmt", | ||
| 19 | ] } | ||
| 20 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = [ | ||
| 21 | "arch-cortex-m", | ||
| 22 | "executor-thread", | ||
| 23 | "defmt", | ||
| 24 | ] } | ||
| 25 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = [ | ||
| 26 | "defmt", | ||
| 27 | "defmt-timestamp-uptime", | ||
| 28 | "tick-hz-32_768", | ||
| 29 | ] } | ||
| 30 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = [ | ||
| 31 | "defmt", | ||
| 32 | "tcp", | ||
| 33 | "dhcpv4", | ||
| 34 | "medium-ethernet", | ||
| 35 | ] } | ||
| 36 | embedded-io-async = { version = "0.6.1" } | ||
| 37 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = [ | ||
| 38 | "defmt", | ||
| 39 | ] } | ||
| 40 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | ||
| 41 | |||
| 42 | defmt = "1.0.1" | ||
| 43 | defmt-rtt = "1.0.0" | ||
| 44 | |||
| 45 | cortex-m = { version = "0.7.6", features = [ | ||
| 46 | "inline-asm", | ||
| 47 | "critical-section-single-core", | ||
| 48 | ] } | ||
| 49 | cortex-m-rt = "0.7.0" | ||
| 50 | embedded-hal = "0.2.6" | ||
| 51 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } | ||
| 52 | heapless = { version = "0.8", default-features = false } | ||
| 53 | nb = "1.0.0" | ||
| 54 | critical-section = "1.1" | ||
| 55 | embedded-storage = "0.3.1" | ||
| 56 | static_cell = "2" | ||
| 57 | sha2 = { version = "0.10.8", default-features = false } | ||
| 58 | hmac = "0.12.1" | ||
| 59 | aes-gcm = { version = "0.10.3", default-features = false, features = [ | ||
| 60 | "aes", | ||
| 61 | "heapless", | ||
| 62 | ] } | ||
| 63 | |||
| 64 | [profile.release] | ||
| 65 | debug = 2 | ||
diff --git a/examples/stm32h742/build.rs b/examples/stm32h742/build.rs new file mode 100644 index 000000000..8cd32d7ed --- /dev/null +++ b/examples/stm32h742/build.rs | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | fn main() { | ||
| 2 | println!("cargo:rustc-link-arg-bins=--nmagic"); | ||
| 3 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); | ||
| 4 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | ||
| 5 | } | ||
diff --git a/examples/stm32h742/src/bin/qspi.rs b/examples/stm32h742/src/bin/qspi.rs new file mode 100644 index 000000000..aee07f3f2 --- /dev/null +++ b/examples/stm32h742/src/bin/qspi.rs | |||
| @@ -0,0 +1,292 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![allow(dead_code)] // Allow dead code as not all commands are used in the example | ||
| 4 | |||
| 5 | use defmt::info; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::mode::Blocking; | ||
| 8 | use embassy_stm32::qspi::enums::{AddressSize, ChipSelectHighTime, FIFOThresholdLevel, MemorySize, *}; | ||
| 9 | use embassy_stm32::qspi::{Config as QspiCfg, Instance, Qspi, TransferConfig}; | ||
| 10 | use embassy_stm32::Config as StmCfg; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | const MEMORY_PAGE_SIZE: usize = 256; | ||
| 14 | |||
| 15 | const CMD_READ: u8 = 0x03; | ||
| 16 | const CMD_HS_READ: u8 = 0x0B; | ||
| 17 | const CMD_QUAD_READ: u8 = 0x6B; | ||
| 18 | |||
| 19 | const CMD_WRITE_PG: u8 = 0xF2; | ||
| 20 | const CMD_QUAD_WRITE_PG: u8 = 0x32; | ||
| 21 | |||
| 22 | const CMD_READ_ID: u8 = 0x9F; | ||
| 23 | const CMD_READ_UUID: u8 = 0x4B; | ||
| 24 | |||
| 25 | const CMD_ENABLE_RESET: u8 = 0x66; | ||
| 26 | const CMD_RESET: u8 = 0x99; | ||
| 27 | |||
| 28 | const CMD_WRITE_ENABLE: u8 = 0x06; | ||
| 29 | const CMD_WRITE_DISABLE: u8 = 0x04; | ||
| 30 | |||
| 31 | const CMD_CHIP_ERASE: u8 = 0xC7; | ||
| 32 | const CMD_SECTOR_ERASE: u8 = 0x20; | ||
| 33 | const CMD_BLOCK_ERASE_32K: u8 = 0x52; | ||
| 34 | const CMD_BLOCK_ERASE_64K: u8 = 0xD8; | ||
| 35 | |||
| 36 | const CMD_READ_SR: u8 = 0x05; | ||
| 37 | const CMD_READ_CR: u8 = 0x35; | ||
| 38 | |||
| 39 | const CMD_WRITE_SR: u8 = 0x01; | ||
| 40 | const CMD_WRITE_CR: u8 = 0x31; | ||
| 41 | const MEMORY_ADDR: u32 = 0x00000001u32; | ||
| 42 | |||
| 43 | /// Implementation of access to flash chip. | ||
| 44 | /// Chip commands are hardcoded as it depends on used chip. | ||
| 45 | /// This implementation is using chip GD25Q64C from Giga Device | ||
| 46 | pub struct FlashMemory<I: Instance> { | ||
| 47 | qspi: Qspi<'static, I, Blocking>, | ||
| 48 | } | ||
| 49 | |||
| 50 | impl<I: Instance> FlashMemory<I> { | ||
| 51 | pub fn new(qspi: Qspi<'static, I, Blocking>) -> Self { | ||
| 52 | let mut memory = Self { qspi }; | ||
| 53 | |||
| 54 | memory.reset_memory(); | ||
| 55 | memory.enable_quad(); | ||
| 56 | |||
| 57 | memory | ||
| 58 | } | ||
| 59 | |||
| 60 | fn enable_quad(&mut self) { | ||
| 61 | let cr = self.read_cr(); | ||
| 62 | self.write_cr(cr | 0x02); | ||
| 63 | } | ||
| 64 | |||
| 65 | fn exec_command(&mut self, cmd: u8) { | ||
| 66 | let transaction = TransferConfig { | ||
| 67 | iwidth: QspiWidth::SING, | ||
| 68 | awidth: QspiWidth::NONE, | ||
| 69 | dwidth: QspiWidth::NONE, | ||
| 70 | instruction: cmd, | ||
| 71 | address: None, | ||
| 72 | dummy: DummyCycles::_0, | ||
| 73 | }; | ||
| 74 | self.qspi.blocking_command(transaction); | ||
| 75 | } | ||
| 76 | |||
| 77 | pub fn reset_memory(&mut self) { | ||
| 78 | self.exec_command(CMD_ENABLE_RESET); | ||
| 79 | self.exec_command(CMD_RESET); | ||
| 80 | self.wait_write_finish(); | ||
| 81 | } | ||
| 82 | |||
| 83 | pub fn enable_write(&mut self) { | ||
| 84 | self.exec_command(CMD_WRITE_ENABLE); | ||
| 85 | } | ||
| 86 | |||
| 87 | pub fn read_id(&mut self) -> [u8; 3] { | ||
| 88 | let mut buffer = [0; 3]; | ||
| 89 | let transaction: TransferConfig = TransferConfig { | ||
| 90 | iwidth: QspiWidth::SING, | ||
| 91 | awidth: QspiWidth::NONE, | ||
| 92 | dwidth: QspiWidth::SING, | ||
| 93 | instruction: CMD_READ_ID, | ||
| 94 | address: None, | ||
| 95 | dummy: DummyCycles::_0, | ||
| 96 | }; | ||
| 97 | self.qspi.blocking_read(&mut buffer, transaction); | ||
| 98 | buffer | ||
| 99 | } | ||
| 100 | |||
| 101 | pub fn read_uuid(&mut self) -> [u8; 16] { | ||
| 102 | let mut buffer = [0; 16]; | ||
| 103 | let transaction: TransferConfig = TransferConfig { | ||
| 104 | iwidth: QspiWidth::SING, | ||
| 105 | awidth: QspiWidth::SING, | ||
| 106 | dwidth: QspiWidth::SING, | ||
| 107 | instruction: CMD_READ_UUID, | ||
| 108 | address: Some(0), | ||
| 109 | dummy: DummyCycles::_8, | ||
| 110 | }; | ||
| 111 | self.qspi.blocking_read(&mut buffer, transaction); | ||
| 112 | buffer | ||
| 113 | } | ||
| 114 | |||
| 115 | pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8]) { | ||
| 116 | let transaction = TransferConfig { | ||
| 117 | iwidth: QspiWidth::SING, | ||
| 118 | awidth: QspiWidth::SING, | ||
| 119 | dwidth: QspiWidth::QUAD, | ||
| 120 | instruction: CMD_QUAD_READ, | ||
| 121 | address: Some(addr), | ||
| 122 | dummy: DummyCycles::_8, | ||
| 123 | }; | ||
| 124 | self.qspi.blocking_read(buffer, transaction); | ||
| 125 | } | ||
| 126 | |||
| 127 | fn wait_write_finish(&mut self) { | ||
| 128 | while (self.read_sr() & 0x01) != 0 {} | ||
| 129 | } | ||
| 130 | |||
| 131 | fn perform_erase(&mut self, addr: u32, cmd: u8) { | ||
| 132 | let transaction = TransferConfig { | ||
| 133 | iwidth: QspiWidth::SING, | ||
| 134 | awidth: QspiWidth::SING, | ||
| 135 | dwidth: QspiWidth::NONE, | ||
| 136 | instruction: cmd, | ||
| 137 | address: Some(addr), | ||
| 138 | dummy: DummyCycles::_0, | ||
| 139 | }; | ||
| 140 | self.enable_write(); | ||
| 141 | self.qspi.blocking_command(transaction); | ||
| 142 | self.wait_write_finish(); | ||
| 143 | } | ||
| 144 | |||
| 145 | pub fn erase_sector(&mut self, addr: u32) { | ||
| 146 | self.perform_erase(addr, CMD_SECTOR_ERASE); | ||
| 147 | } | ||
| 148 | |||
| 149 | pub fn erase_block_32k(&mut self, addr: u32) { | ||
| 150 | self.perform_erase(addr, CMD_BLOCK_ERASE_32K); | ||
| 151 | } | ||
| 152 | |||
| 153 | pub fn erase_block_64k(&mut self, addr: u32) { | ||
| 154 | self.perform_erase(addr, CMD_BLOCK_ERASE_64K); | ||
| 155 | } | ||
| 156 | |||
| 157 | pub fn erase_chip(&mut self) { | ||
| 158 | self.exec_command(CMD_CHIP_ERASE); | ||
| 159 | } | ||
| 160 | |||
| 161 | fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize) { | ||
| 162 | assert!( | ||
| 163 | (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32, | ||
| 164 | "write_page(): page write length exceeds page boundary (len = {}, addr = {:X}", | ||
| 165 | len, | ||
| 166 | addr | ||
| 167 | ); | ||
| 168 | |||
| 169 | let transaction = TransferConfig { | ||
| 170 | iwidth: QspiWidth::SING, | ||
| 171 | awidth: QspiWidth::SING, | ||
| 172 | dwidth: QspiWidth::QUAD, | ||
| 173 | instruction: CMD_QUAD_WRITE_PG, | ||
| 174 | address: Some(addr), | ||
| 175 | dummy: DummyCycles::_0, | ||
| 176 | }; | ||
| 177 | self.enable_write(); | ||
| 178 | self.qspi.blocking_write(buffer, transaction); | ||
| 179 | self.wait_write_finish(); | ||
| 180 | } | ||
| 181 | |||
| 182 | pub fn write_memory(&mut self, addr: u32, buffer: &[u8]) { | ||
| 183 | let mut left = buffer.len(); | ||
| 184 | let mut place = addr; | ||
| 185 | let mut chunk_start = 0; | ||
| 186 | |||
| 187 | while left > 0 { | ||
| 188 | let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize; | ||
| 189 | let chunk_size = if left >= max_chunk_size { max_chunk_size } else { left }; | ||
| 190 | let chunk = &buffer[chunk_start..(chunk_start + chunk_size)]; | ||
| 191 | self.write_page(place, chunk, chunk_size); | ||
| 192 | place += chunk_size as u32; | ||
| 193 | left -= chunk_size; | ||
| 194 | chunk_start += chunk_size; | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | fn read_register(&mut self, cmd: u8) -> u8 { | ||
| 199 | let mut buffer = [0; 1]; | ||
| 200 | let transaction: TransferConfig = TransferConfig { | ||
| 201 | iwidth: QspiWidth::SING, | ||
| 202 | awidth: QspiWidth::NONE, | ||
| 203 | dwidth: QspiWidth::SING, | ||
| 204 | instruction: cmd, | ||
| 205 | address: None, | ||
| 206 | dummy: DummyCycles::_0, | ||
| 207 | }; | ||
| 208 | self.qspi.blocking_read(&mut buffer, transaction); | ||
| 209 | buffer[0] | ||
| 210 | } | ||
| 211 | |||
| 212 | fn write_register(&mut self, cmd: u8, value: u8) { | ||
| 213 | let buffer = [value; 1]; | ||
| 214 | let transaction: TransferConfig = TransferConfig { | ||
| 215 | iwidth: QspiWidth::SING, | ||
| 216 | awidth: QspiWidth::NONE, | ||
| 217 | dwidth: QspiWidth::SING, | ||
| 218 | instruction: cmd, | ||
| 219 | address: None, | ||
| 220 | dummy: DummyCycles::_0, | ||
| 221 | }; | ||
| 222 | self.qspi.blocking_write(&buffer, transaction); | ||
| 223 | } | ||
| 224 | |||
| 225 | pub fn read_sr(&mut self) -> u8 { | ||
| 226 | self.read_register(CMD_READ_SR) | ||
| 227 | } | ||
| 228 | |||
| 229 | pub fn read_cr(&mut self) -> u8 { | ||
| 230 | self.read_register(CMD_READ_CR) | ||
| 231 | } | ||
| 232 | |||
| 233 | pub fn write_sr(&mut self, value: u8) { | ||
| 234 | self.write_register(CMD_WRITE_SR, value); | ||
| 235 | } | ||
| 236 | |||
| 237 | pub fn write_cr(&mut self, value: u8) { | ||
| 238 | self.write_register(CMD_WRITE_CR, value); | ||
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 242 | #[embassy_executor::main] | ||
| 243 | async fn main(_spawner: Spawner) -> ! { | ||
| 244 | let mut config = StmCfg::default(); | ||
| 245 | { | ||
| 246 | use embassy_stm32::rcc::*; | ||
| 247 | config.rcc.hsi = Some(HSIPrescaler::DIV1); | ||
| 248 | config.rcc.csi = true; | ||
| 249 | config.rcc.hsi48 = Some(Default::default()); // needed for RNG | ||
| 250 | config.rcc.pll1 = Some(Pll { | ||
| 251 | source: PllSource::HSI, | ||
| 252 | prediv: PllPreDiv::DIV4, | ||
| 253 | mul: PllMul::MUL50, | ||
| 254 | divp: Some(PllDiv::DIV2), | ||
| 255 | divq: None, | ||
| 256 | divr: None, | ||
| 257 | }); | ||
| 258 | config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz | ||
| 259 | config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz | ||
| 260 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 261 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 262 | config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 263 | config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 264 | config.rcc.voltage_scale = VoltageScale::Scale1; | ||
| 265 | } | ||
| 266 | let p = embassy_stm32::init(config); | ||
| 267 | info!("Embassy initialized"); | ||
| 268 | |||
| 269 | let config = QspiCfg { | ||
| 270 | memory_size: MemorySize::_8MiB, | ||
| 271 | address_size: AddressSize::_24bit, | ||
| 272 | prescaler: 16, | ||
| 273 | cs_high_time: ChipSelectHighTime::_1Cycle, | ||
| 274 | fifo_threshold: FIFOThresholdLevel::_16Bytes, | ||
| 275 | }; | ||
| 276 | let driver = Qspi::new_blocking_bank1(p.QUADSPI, p.PD11, p.PD12, p.PE2, p.PD13, p.PB2, p.PB10, config); | ||
| 277 | let mut flash = FlashMemory::new(driver); | ||
| 278 | let flash_id = flash.read_id(); | ||
| 279 | info!("FLASH ID: {:?}", flash_id); | ||
| 280 | let mut wr_buf = [0u8; 256]; | ||
| 281 | for i in 0..256 { | ||
| 282 | wr_buf[i] = i as u8; | ||
| 283 | } | ||
| 284 | let mut rd_buf = [0u8; 256]; | ||
| 285 | flash.erase_sector(MEMORY_ADDR); | ||
| 286 | flash.write_memory(MEMORY_ADDR, &wr_buf); | ||
| 287 | flash.read_memory(MEMORY_ADDR, &mut rd_buf); | ||
| 288 | info!("WRITE BUF: {:?}", wr_buf); | ||
| 289 | info!("READ BUF: {:?}", rd_buf); | ||
| 290 | info!("End of Program, proceed to empty endless loop"); | ||
| 291 | loop {} | ||
| 292 | } | ||
diff --git a/examples/stm32h755cm4/Cargo.toml b/examples/stm32h755cm4/Cargo.toml index 7a42fbdaa..71bd50d60 100644 --- a/examples/stm32h755cm4/Cargo.toml +++ b/examples/stm32h755cm4/Cargo.toml | |||
| @@ -6,28 +6,27 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 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.2.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.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } | 11 | embassy-embedded-hal = { version = "0.3.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.7.0", path = "../../embassy-executor", features = ["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.4.0", 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.7.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.4.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 | ||
| 18 | defmt = "0.3" | 18 | defmt = "1.0.1" |
| 19 | defmt-rtt = "0.4" | 19 | defmt-rtt = "1.0.0" |
| 20 | 20 | ||
| 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 22 | cortex-m-rt = "0.7.0" | 22 | 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 = "1.0.0", features = ["print-defmt"] } |
| 29 | heapless = { version = "0.8", default-features = false } | 29 | heapless = { version = "0.8", default-features = false } |
| 30 | rand_core = "0.6.3" | ||
| 31 | critical-section = "1.1" | 30 | critical-section = "1.1" |
| 32 | micromath = "2.0.0" | 31 | micromath = "2.0.0" |
| 33 | stm32-fmc = "0.3.0" | 32 | stm32-fmc = "0.3.0" |
diff --git a/examples/stm32h755cm4/src/bin/blinky.rs b/examples/stm32h755cm4/src/bin/blinky.rs index b5c547839..39112c1f5 100644 --- a/examples/stm32h755cm4/src/bin/blinky.rs +++ b/examples/stm32h755cm4/src/bin/blinky.rs | |||
| @@ -10,7 +10,7 @@ use embassy_stm32::SharedData; | |||
| 10 | use embassy_time::Timer; | 10 | use embassy_time::Timer; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 12 | ||
| 13 | #[link_section = ".ram_d3.shared_data"] | 13 | #[unsafe(link_section = ".ram_d3.shared_data")] |
| 14 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); | 14 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); |
| 15 | 15 | ||
| 16 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
diff --git a/examples/stm32h755cm7/Cargo.toml b/examples/stm32h755cm7/Cargo.toml index 4f0f69c3f..8e960932a 100644 --- a/examples/stm32h755cm7/Cargo.toml +++ b/examples/stm32h755cm7/Cargo.toml | |||
| @@ -6,28 +6,27 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 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.2.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.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } | 11 | embassy-embedded-hal = { version = "0.3.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.7.0", path = "../../embassy-executor", features = ["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.4.0", 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.7.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.4.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 | ||
| 18 | defmt = "0.3" | 18 | defmt = "1.0.1" |
| 19 | defmt-rtt = "0.4" | 19 | defmt-rtt = "1.0.0" |
| 20 | 20 | ||
| 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 22 | cortex-m-rt = "0.7.0" | 22 | 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 = "1.0.0", features = ["print-defmt"] } |
| 29 | heapless = { version = "0.8", default-features = false } | 29 | heapless = { version = "0.8", default-features = false } |
| 30 | rand_core = "0.6.3" | ||
| 31 | critical-section = "1.1" | 30 | critical-section = "1.1" |
| 32 | micromath = "2.0.0" | 31 | micromath = "2.0.0" |
| 33 | stm32-fmc = "0.3.0" | 32 | stm32-fmc = "0.3.0" |
diff --git a/examples/stm32h755cm7/src/bin/blinky.rs b/examples/stm32h755cm7/src/bin/blinky.rs index 94d2226c0..b30bf4de8 100644 --- a/examples/stm32h755cm7/src/bin/blinky.rs +++ b/examples/stm32h755cm7/src/bin/blinky.rs | |||
| @@ -10,7 +10,7 @@ use embassy_stm32::SharedData; | |||
| 10 | use embassy_time::Timer; | 10 | use embassy_time::Timer; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 12 | ||
| 13 | #[link_section = ".ram_d3.shared_data"] | 13 | #[unsafe(link_section = ".ram_d3.shared_data")] |
| 14 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); | 14 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); |
| 15 | 15 | ||
| 16 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
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..72f86e0cf --- /dev/null +++ b/examples/stm32h7b0/Cargo.toml | |||
| @@ -0,0 +1,73 @@ | |||
| 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.2.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7b0vb", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } | ||
| 9 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | ||
| 10 | embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } | ||
| 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } | ||
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | ||
| 13 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } | ||
| 14 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } | ||
| 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | ||
| 16 | |||
| 17 | defmt = "1.0.1" | ||
| 18 | defmt-rtt = "1.0.0" | ||
| 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 = "1.0.0", features = ["print-defmt"] } | ||
| 28 | heapless = { version = "0.8", default-features = false } | ||
| 29 | critical-section = "1.1" | ||
| 30 | micromath = "2.0.0" | ||
| 31 | stm32-fmc = "0.3.0" | ||
| 32 | embedded-storage = "0.3.1" | ||
| 33 | static_cell = "2" | ||
| 34 | chrono = { version = "^0.4", default-features = false } | ||
| 35 | grounded = "0.2.0" | ||
| 36 | |||
| 37 | # cargo build/run | ||
| 38 | [profile.dev] | ||
| 39 | codegen-units = 1 | ||
| 40 | debug = 2 | ||
| 41 | debug-assertions = true # <- | ||
| 42 | incremental = false | ||
| 43 | opt-level = 3 # <- | ||
| 44 | overflow-checks = true # <- | ||
| 45 | |||
| 46 | # cargo test | ||
| 47 | [profile.test] | ||
| 48 | codegen-units = 1 | ||
| 49 | debug = 2 | ||
| 50 | debug-assertions = true # <- | ||
| 51 | incremental = false | ||
| 52 | opt-level = 3 # <- | ||
| 53 | overflow-checks = true # <- | ||
| 54 | |||
| 55 | # cargo build/run --release | ||
| 56 | [profile.release] | ||
| 57 | codegen-units = 1 | ||
| 58 | debug = 2 | ||
| 59 | debug-assertions = false # <- | ||
| 60 | incremental = false | ||
| 61 | lto = 'fat' | ||
| 62 | opt-level = 3 # <- | ||
| 63 | overflow-checks = false # <- | ||
| 64 | |||
| 65 | # cargo test --release | ||
| 66 | [profile.bench] | ||
| 67 | codegen-units = 1 | ||
| 68 | debug = 2 | ||
| 69 | debug-assertions = false # <- | ||
| 70 | incremental = false | ||
| 71 | lto = 'fat' | ||
| 72 | opt-level = 3 # <- | ||
| 73 | 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..5f1ce8dfc 100644 --- a/examples/stm32h7rs/Cargo.toml +++ b/examples/stm32h7rs/Cargo.toml | |||
| @@ -6,27 +6,26 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 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.2.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.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.7.0", 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.4.0", 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.7.0", path = "../../embassy-net", features = ["defmt", "udp", "medium-ethernet", "medium-ip", "proto-ipv4"] } |
| 14 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } |
| 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 16 | 16 | ||
| 17 | defmt = "0.3" | 17 | defmt = "1.0.1" |
| 18 | defmt-rtt = "0.4" | 18 | defmt-rtt = "1.0.0" |
| 19 | 19 | ||
| 20 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 20 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 21 | cortex-m-rt = "0.7.0" | 21 | 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 = "1.0.0", 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" | ||
| 30 | critical-section = "1.1" | 29 | critical-section = "1.1" |
| 31 | micromath = "2.0.0" | 30 | micromath = "2.0.0" |
| 32 | stm32-fmc = "0.3.0" | 31 | stm32-fmc = "0.3.0" |
diff --git a/examples/stm32h7rs/src/bin/eth.rs b/examples/stm32h7rs/src/bin/eth.rs new file mode 100644 index 000000000..6d246bb09 --- /dev/null +++ b/examples/stm32h7rs/src/bin/eth.rs | |||
| @@ -0,0 +1,118 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_net::udp::{PacketMetadata, UdpSocket}; | ||
| 7 | use embassy_net::{Ipv4Address, Ipv4Cidr, StackResources}; | ||
| 8 | use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; | ||
| 9 | use embassy_stm32::peripherals::ETH; | ||
| 10 | use embassy_stm32::rng::Rng; | ||
| 11 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; | ||
| 12 | use embassy_time::Timer; | ||
| 13 | use heapless::Vec; | ||
| 14 | use static_cell::StaticCell; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | bind_interrupts!(struct Irqs { | ||
| 18 | ETH => eth::InterruptHandler; | ||
| 19 | RNG => rng::InterruptHandler<peripherals::RNG>; | ||
| 20 | }); | ||
| 21 | |||
| 22 | type Device = Ethernet<'static, ETH, GenericPhy>; | ||
| 23 | |||
| 24 | #[embassy_executor::task] | ||
| 25 | async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { | ||
| 26 | runner.run().await | ||
| 27 | } | ||
| 28 | |||
| 29 | #[embassy_executor::main] | ||
| 30 | async fn main(spawner: Spawner) -> ! { | ||
| 31 | let mut config = Config::default(); | ||
| 32 | { | ||
| 33 | use embassy_stm32::rcc::*; | ||
| 34 | config.rcc.hsi = Some(HSIPrescaler::DIV1); | ||
| 35 | config.rcc.csi = true; | ||
| 36 | config.rcc.hsi48 = Some(Default::default()); // needed for RNG | ||
| 37 | config.rcc.pll1 = Some(Pll { | ||
| 38 | source: PllSource::HSI, | ||
| 39 | prediv: PllPreDiv::DIV4, | ||
| 40 | mul: PllMul::MUL50, | ||
| 41 | divp: Some(PllDiv::DIV2), | ||
| 42 | divq: None, | ||
| 43 | divr: None, | ||
| 44 | }); | ||
| 45 | config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz | ||
| 46 | config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz | ||
| 47 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 48 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 49 | config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 50 | config.rcc.apb5_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 51 | config.rcc.voltage_scale = VoltageScale::HIGH; | ||
| 52 | } | ||
| 53 | let p = embassy_stm32::init(config); | ||
| 54 | info!("Hello World!"); | ||
| 55 | |||
| 56 | // Generate random seed. | ||
| 57 | let mut rng = Rng::new(p.RNG, Irqs); | ||
| 58 | let mut seed = [0; 8]; | ||
| 59 | rng.fill_bytes(&mut seed); | ||
| 60 | let seed = u64::from_le_bytes(seed); | ||
| 61 | |||
| 62 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; | ||
| 63 | |||
| 64 | static PACKETS: StaticCell<PacketQueue<4, 4>> = StaticCell::new(); | ||
| 65 | let device = Ethernet::new( | ||
| 66 | PACKETS.init(PacketQueue::<4, 4>::new()), | ||
| 67 | p.ETH, | ||
| 68 | Irqs, | ||
| 69 | p.PB6, | ||
| 70 | p.PA2, | ||
| 71 | p.PG6, | ||
| 72 | p.PA7, | ||
| 73 | p.PG4, | ||
| 74 | p.PG5, | ||
| 75 | p.PG13, | ||
| 76 | p.PG12, | ||
| 77 | p.PG11, | ||
| 78 | GenericPhy::new(0), | ||
| 79 | mac_addr, | ||
| 80 | ); | ||
| 81 | |||
| 82 | // Have to use UDP w/ static config to fit in internal flash | ||
| 83 | // let config = embassy_net::Config::dhcpv4(Default::default()); | ||
| 84 | let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { | ||
| 85 | address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), | ||
| 86 | dns_servers: Vec::new(), | ||
| 87 | gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | ||
| 88 | }); | ||
| 89 | |||
| 90 | // Init network stack | ||
| 91 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | ||
| 92 | let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); | ||
| 93 | |||
| 94 | // Launch network task | ||
| 95 | unwrap!(spawner.spawn(net_task(runner))); | ||
| 96 | |||
| 97 | // Ensure DHCP configuration is up before trying connect | ||
| 98 | //stack.wait_config_up().await; | ||
| 99 | |||
| 100 | info!("Network task initialized"); | ||
| 101 | |||
| 102 | // Then we can use it! | ||
| 103 | let mut rx_meta = [PacketMetadata::EMPTY; 16]; | ||
| 104 | let mut rx_buffer = [0; 1024]; | ||
| 105 | let mut tx_meta = [PacketMetadata::EMPTY; 16]; | ||
| 106 | let mut tx_buffer = [0; 1024]; | ||
| 107 | |||
| 108 | let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000); | ||
| 109 | let socket = UdpSocket::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer); | ||
| 110 | loop { | ||
| 111 | // You need to start a server on the host machine, for example: `nc -lu 8000` | ||
| 112 | socket | ||
| 113 | .send_to(b"Hello, world", remote_endpoint) | ||
| 114 | .await | ||
| 115 | .expect("Buffer sent"); | ||
| 116 | Timer::after_secs(1).await; | ||
| 117 | } | ||
| 118 | } | ||
diff --git a/examples/stm32h7rs/src/bin/usb_serial.rs b/examples/stm32h7rs/src/bin/usb_serial.rs new file mode 100644 index 000000000..56a9884af --- /dev/null +++ b/examples/stm32h7rs/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::{panic, *}; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_futures::join::join; | ||
| 7 | use embassy_stm32::time::Hertz; | ||
| 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 {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | bind_interrupts!(struct Irqs { | ||
| 16 | OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>; | ||
| 17 | }); | ||
| 18 | |||
| 19 | // If you are trying this and your USB device doesn't connect, the most | ||
| 20 | // common issues are the RCC config and vbus_detection | ||
| 21 | // | ||
| 22 | // See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure | ||
| 23 | // for more information. | ||
| 24 | #[embassy_executor::main] | ||
| 25 | async fn main(_spawner: Spawner) { | ||
| 26 | info!("Hello World!"); | ||
| 27 | |||
| 28 | let mut config = Config::default(); | ||
| 29 | |||
| 30 | { | ||
| 31 | use embassy_stm32::rcc::*; | ||
| 32 | config.rcc.hse = Some(Hse { | ||
| 33 | freq: Hertz(24_000_000), | ||
| 34 | mode: HseMode::Oscillator, | ||
| 35 | }); | ||
| 36 | config.rcc.pll1 = Some(Pll { | ||
| 37 | source: PllSource::HSE, | ||
| 38 | prediv: PllPreDiv::DIV12, | ||
| 39 | mul: PllMul::MUL300, | ||
| 40 | divp: Some(PllDiv::DIV1), //600 MHz | ||
| 41 | divq: Some(PllDiv::DIV2), // 300 MHz | ||
| 42 | divr: Some(PllDiv::DIV2), // 300 MHz | ||
| 43 | }); | ||
| 44 | config.rcc.sys = Sysclk::PLL1_P; // 600 MHz | ||
| 45 | config.rcc.ahb_pre = AHBPrescaler::DIV2; // 300 MHz | ||
| 46 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 150 MHz | ||
| 47 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 150 MHz | ||
| 48 | config.rcc.apb4_pre = APBPrescaler::DIV2; // 150 MHz | ||
| 49 | config.rcc.apb5_pre = APBPrescaler::DIV2; // 150 MHz | ||
| 50 | config.rcc.voltage_scale = VoltageScale::HIGH; | ||
| 51 | config.rcc.mux.usbphycsel = mux::Usbphycsel::HSE; | ||
| 52 | } | ||
| 53 | |||
| 54 | let p = embassy_stm32::init(config); | ||
| 55 | |||
| 56 | // Create the driver, from the HAL. | ||
| 57 | let mut ep_out_buffer = [0u8; 256]; | ||
| 58 | let mut config = embassy_stm32::usb::Config::default(); | ||
| 59 | |||
| 60 | // Do not enable vbus_detection. This is a safe default that works in all boards. | ||
| 61 | // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need | ||
| 62 | // to enable vbus_detection to comply with the USB spec. If you enable it, the board | ||
| 63 | // has to support it or USB won't work at all. See docs on `vbus_detection` for details. | ||
| 64 | config.vbus_detection = false; | ||
| 65 | |||
| 66 | let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PM6, p.PM5, &mut ep_out_buffer, config); | ||
| 67 | |||
| 68 | // Create embassy-usb Config | ||
| 69 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 70 | config.manufacturer = Some("Embassy"); | ||
| 71 | config.product = Some("USB-serial example"); | ||
| 72 | config.serial_number = Some("12345678"); | ||
| 73 | |||
| 74 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 75 | // It needs some buffers for building the descriptors. | ||
| 76 | let mut config_descriptor = [0; 256]; | ||
| 77 | let mut bos_descriptor = [0; 256]; | ||
| 78 | let mut control_buf = [0; 64]; | ||
| 79 | |||
| 80 | let mut state = State::new(); | ||
| 81 | |||
| 82 | let mut builder = Builder::new( | ||
| 83 | driver, | ||
| 84 | config, | ||
| 85 | &mut config_descriptor, | ||
| 86 | &mut bos_descriptor, | ||
| 87 | &mut [], // no msos descriptors | ||
| 88 | &mut control_buf, | ||
| 89 | ); | ||
| 90 | |||
| 91 | // Create classes on the builder. | ||
| 92 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 93 | |||
| 94 | // Build the builder. | ||
| 95 | let mut usb = builder.build(); | ||
| 96 | |||
| 97 | // Run the USB device. | ||
| 98 | let usb_fut = usb.run(); | ||
| 99 | |||
| 100 | // Do stuff with the class! | ||
| 101 | let echo_fut = async { | ||
| 102 | loop { | ||
| 103 | class.wait_connection().await; | ||
| 104 | info!("Connected"); | ||
| 105 | let _ = echo(&mut class).await; | ||
| 106 | info!("Disconnected"); | ||
| 107 | } | ||
| 108 | }; | ||
| 109 | |||
| 110 | // Run everything concurrently. | ||
| 111 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 112 | join(usb_fut, echo_fut).await; | ||
| 113 | } | ||
| 114 | |||
| 115 | struct Disconnected {} | ||
| 116 | |||
| 117 | impl From<EndpointError> for Disconnected { | ||
| 118 | fn from(val: EndpointError) -> Self { | ||
| 119 | match val { | ||
| 120 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 121 | EndpointError::Disabled => Disconnected {}, | ||
| 122 | } | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
| 127 | let mut buf = [0; 64]; | ||
| 128 | loop { | ||
| 129 | let n = class.read_packet(&mut buf).await?; | ||
| 130 | let data = &buf[..n]; | ||
| 131 | info!("data: {:x}", data); | ||
| 132 | class.write_packet(data).await?; | ||
| 133 | } | ||
| 134 | } | ||
diff --git a/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs b/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs new file mode 100644 index 000000000..88d914180 --- /dev/null +++ b/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs | |||
| @@ -0,0 +1,448 @@ | |||
| 1 | #![no_main] | ||
| 2 | #![no_std] | ||
| 3 | |||
| 4 | //! For Nucleo STM32H7S3L8 MB1737, has MX25UW25645GXDI00 | ||
| 5 | //! | ||
| 6 | //! TODO: Currently this only uses single SPI, pending flash chip documentation for octo SPI. | ||
| 7 | |||
| 8 | use defmt::info; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 11 | use embassy_stm32::mode::Blocking; | ||
| 12 | use embassy_stm32::time::Hertz; | ||
| 13 | use embassy_stm32::xspi::{ | ||
| 14 | AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, Instance, MemorySize, MemoryType, TransferConfig, | ||
| 15 | WrapSize, Xspi, XspiWidth, | ||
| 16 | }; | ||
| 17 | use embassy_stm32::Config; | ||
| 18 | use embassy_time::Timer; | ||
| 19 | use {defmt_rtt as _, panic_probe as _}; | ||
| 20 | |||
| 21 | #[embassy_executor::main] | ||
| 22 | async fn main(_spawner: Spawner) { | ||
| 23 | // RCC config | ||
| 24 | let mut config = Config::default(); | ||
| 25 | { | ||
| 26 | use embassy_stm32::rcc::*; | ||
| 27 | config.rcc.hse = Some(Hse { | ||
| 28 | freq: Hertz(24_000_000), | ||
| 29 | mode: HseMode::Oscillator, | ||
| 30 | }); | ||
| 31 | config.rcc.pll1 = Some(Pll { | ||
| 32 | source: PllSource::HSE, | ||
| 33 | prediv: PllPreDiv::DIV3, | ||
| 34 | mul: PllMul::MUL150, | ||
| 35 | divp: Some(PllDiv::DIV2), | ||
| 36 | divq: None, | ||
| 37 | divr: None, | ||
| 38 | }); | ||
| 39 | config.rcc.sys = Sysclk::PLL1_P; // 600 Mhz | ||
| 40 | config.rcc.ahb_pre = AHBPrescaler::DIV2; // 300 Mhz | ||
| 41 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 150 Mhz | ||
| 42 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 150 Mhz | ||
| 43 | config.rcc.apb4_pre = APBPrescaler::DIV2; // 150 Mhz | ||
| 44 | config.rcc.apb5_pre = APBPrescaler::DIV2; // 150 Mhz | ||
| 45 | config.rcc.voltage_scale = VoltageScale::HIGH; | ||
| 46 | } | ||
| 47 | |||
| 48 | // Initialize peripherals | ||
| 49 | let p = embassy_stm32::init(config); | ||
| 50 | |||
| 51 | let spi_config = embassy_stm32::xspi::Config { | ||
| 52 | fifo_threshold: FIFOThresholdLevel::_4Bytes, | ||
| 53 | memory_type: MemoryType::Macronix, | ||
| 54 | delay_hold_quarter_cycle: true, | ||
| 55 | // memory_type: MemoryType::Micron, | ||
| 56 | // delay_hold_quarter_cycle: false, | ||
| 57 | device_size: MemorySize::_32MiB, | ||
| 58 | chip_select_high_time: ChipSelectHighTime::_2Cycle, | ||
| 59 | free_running_clock: false, | ||
| 60 | clock_mode: false, | ||
| 61 | wrap_size: WrapSize::None, | ||
| 62 | // 300mhz / (4+1) = 60mhz. Unsure the limit, need to find a MX25UW25645GXDI00 datasheet. | ||
| 63 | clock_prescaler: 3, | ||
| 64 | sample_shifting: false, | ||
| 65 | chip_select_boundary: 0, | ||
| 66 | max_transfer: 0, | ||
| 67 | refresh: 0, | ||
| 68 | }; | ||
| 69 | |||
| 70 | let mut cor = cortex_m::Peripherals::take().unwrap(); | ||
| 71 | |||
| 72 | // Not necessary, but recommended if using XIP | ||
| 73 | cor.SCB.enable_icache(); | ||
| 74 | cor.SCB.enable_dcache(&mut cor.CPUID); | ||
| 75 | |||
| 76 | let xspi = embassy_stm32::xspi::Xspi::new_blocking_xspi( | ||
| 77 | p.XSPI2, p.PN6, p.PN2, p.PN3, p.PN4, p.PN5, p.PN8, p.PN9, p.PN10, p.PN11, p.PN1, spi_config, | ||
| 78 | ); | ||
| 79 | |||
| 80 | let mut flash = FlashMemory::new(xspi).await; | ||
| 81 | |||
| 82 | let flash_id = flash.read_id(); | ||
| 83 | info!("FLASH ID: {=[u8]:x}", flash_id); | ||
| 84 | |||
| 85 | let mut wr_buf = [0u8; 8]; | ||
| 86 | for i in 0..8 { | ||
| 87 | wr_buf[i] = 0x90 + i as u8; | ||
| 88 | } | ||
| 89 | let mut rd_buf = [0u8; 8]; | ||
| 90 | flash.erase_sector(0).await; | ||
| 91 | flash.write_memory(0, &wr_buf, true).await; | ||
| 92 | flash.read_memory(0, &mut rd_buf, true); | ||
| 93 | info!("WRITE BUF: {=[u8]:#X}", wr_buf); | ||
| 94 | info!("READ BUF: {=[u8]:#X}", rd_buf); | ||
| 95 | flash.enable_mm().await; | ||
| 96 | info!("Enabled memory mapped mode"); | ||
| 97 | |||
| 98 | let first_u32 = unsafe { *(0x70000000 as *const u32) }; | ||
| 99 | assert_eq!(first_u32, 0x93929190); | ||
| 100 | info!("first_u32 {:08x}", first_u32); | ||
| 101 | |||
| 102 | let second_u32 = unsafe { *(0x70000004 as *const u32) }; | ||
| 103 | assert_eq!(second_u32, 0x97969594); | ||
| 104 | info!("second_u32 {:08x}", first_u32); | ||
| 105 | |||
| 106 | flash.disable_mm().await; | ||
| 107 | info!("Disabled memory mapped mode"); | ||
| 108 | |||
| 109 | info!("DONE"); | ||
| 110 | // Output pin PE3 | ||
| 111 | let mut led = Output::new(p.PE3, Level::Low, Speed::Low); | ||
| 112 | |||
| 113 | loop { | ||
| 114 | led.toggle(); | ||
| 115 | Timer::after_millis(1000).await; | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | const MEMORY_PAGE_SIZE: usize = 8; | ||
| 120 | |||
| 121 | const CMD_READ: u8 = 0x0B; | ||
| 122 | const _CMD_QUAD_READ: u8 = 0x6B; | ||
| 123 | |||
| 124 | const CMD_WRITE_PG: u8 = 0x02; | ||
| 125 | const _CMD_QUAD_WRITE_PG: u8 = 0x32; | ||
| 126 | |||
| 127 | const CMD_READ_ID: u8 = 0x9F; | ||
| 128 | const CMD_READ_ID_OCTO: u16 = 0x9F60; | ||
| 129 | |||
| 130 | const CMD_ENABLE_RESET: u8 = 0x66; | ||
| 131 | const CMD_RESET: u8 = 0x99; | ||
| 132 | |||
| 133 | const CMD_WRITE_ENABLE: u8 = 0x06; | ||
| 134 | |||
| 135 | const CMD_CHIP_ERASE: u8 = 0xC7; | ||
| 136 | const CMD_SECTOR_ERASE: u8 = 0x20; | ||
| 137 | const CMD_BLOCK_ERASE_32K: u8 = 0x52; | ||
| 138 | const CMD_BLOCK_ERASE_64K: u8 = 0xD8; | ||
| 139 | |||
| 140 | const CMD_READ_SR: u8 = 0x05; | ||
| 141 | const CMD_READ_CR: u8 = 0x35; | ||
| 142 | |||
| 143 | const CMD_WRITE_SR: u8 = 0x01; | ||
| 144 | const CMD_WRITE_CR: u8 = 0x31; | ||
| 145 | |||
| 146 | /// Implementation of access to flash chip. | ||
| 147 | /// | ||
| 148 | /// Chip commands are hardcoded as it depends on used chip. | ||
| 149 | /// This targets a MX25UW25645GXDI00. | ||
| 150 | pub struct FlashMemory<I: Instance> { | ||
| 151 | xspi: Xspi<'static, I, Blocking>, | ||
| 152 | } | ||
| 153 | |||
| 154 | impl<I: Instance> FlashMemory<I> { | ||
| 155 | pub async fn new(xspi: Xspi<'static, I, Blocking>) -> Self { | ||
| 156 | let mut memory = Self { xspi }; | ||
| 157 | |||
| 158 | memory.reset_memory().await; | ||
| 159 | memory.enable_octo(); | ||
| 160 | memory | ||
| 161 | } | ||
| 162 | |||
| 163 | async fn qpi_mode(&mut self) { | ||
| 164 | // Enter qpi mode | ||
| 165 | self.exec_command(0x38).await; | ||
| 166 | |||
| 167 | // Set read param | ||
| 168 | let transaction = TransferConfig { | ||
| 169 | iwidth: XspiWidth::QUAD, | ||
| 170 | dwidth: XspiWidth::QUAD, | ||
| 171 | instruction: Some(0xC0), | ||
| 172 | ..Default::default() | ||
| 173 | }; | ||
| 174 | self.enable_write().await; | ||
| 175 | self.xspi.blocking_write(&[0x30_u8], transaction).unwrap(); | ||
| 176 | self.wait_write_finish(); | ||
| 177 | } | ||
| 178 | |||
| 179 | pub async fn disable_mm(&mut self) { | ||
| 180 | self.xspi.disable_memory_mapped_mode(); | ||
| 181 | } | ||
| 182 | |||
| 183 | pub async fn enable_mm(&mut self) { | ||
| 184 | self.qpi_mode().await; | ||
| 185 | |||
| 186 | let read_config = TransferConfig { | ||
| 187 | iwidth: XspiWidth::SING, | ||
| 188 | isize: AddressSize::_8bit, | ||
| 189 | adwidth: XspiWidth::SING, | ||
| 190 | adsize: AddressSize::_24bit, | ||
| 191 | dwidth: XspiWidth::SING, | ||
| 192 | instruction: Some(CMD_READ as u32), | ||
| 193 | dummy: DummyCycles::_8, | ||
| 194 | ..Default::default() | ||
| 195 | }; | ||
| 196 | |||
| 197 | let write_config = TransferConfig { | ||
| 198 | iwidth: XspiWidth::SING, | ||
| 199 | isize: AddressSize::_8bit, | ||
| 200 | adwidth: XspiWidth::SING, | ||
| 201 | adsize: AddressSize::_24bit, | ||
| 202 | dwidth: XspiWidth::SING, | ||
| 203 | instruction: Some(CMD_WRITE_PG as u32), | ||
| 204 | dummy: DummyCycles::_0, | ||
| 205 | ..Default::default() | ||
| 206 | }; | ||
| 207 | self.xspi.enable_memory_mapped_mode(read_config, write_config).unwrap(); | ||
| 208 | } | ||
| 209 | |||
| 210 | fn enable_octo(&mut self) { | ||
| 211 | let cr = self.read_cr(); | ||
| 212 | // info!("Read cr: {:x}", cr); | ||
| 213 | self.write_cr(cr | 0x02); | ||
| 214 | // info!("Read cr after writing: {:x}", cr); | ||
| 215 | } | ||
| 216 | |||
| 217 | pub fn disable_octo(&mut self) { | ||
| 218 | let cr = self.read_cr(); | ||
| 219 | self.write_cr(cr & (!(0x02))); | ||
| 220 | } | ||
| 221 | |||
| 222 | async fn exec_command_4(&mut self, cmd: u8) { | ||
| 223 | let transaction = TransferConfig { | ||
| 224 | iwidth: XspiWidth::QUAD, | ||
| 225 | adwidth: XspiWidth::NONE, | ||
| 226 | // adsize: AddressSize::_24bit, | ||
| 227 | dwidth: XspiWidth::NONE, | ||
| 228 | instruction: Some(cmd as u32), | ||
| 229 | address: None, | ||
| 230 | dummy: DummyCycles::_0, | ||
| 231 | ..Default::default() | ||
| 232 | }; | ||
| 233 | self.xspi.blocking_command(&transaction).unwrap(); | ||
| 234 | } | ||
| 235 | |||
| 236 | async fn exec_command(&mut self, cmd: u8) { | ||
| 237 | let transaction = TransferConfig { | ||
| 238 | iwidth: XspiWidth::SING, | ||
| 239 | adwidth: XspiWidth::NONE, | ||
| 240 | // adsize: AddressSize::_24bit, | ||
| 241 | dwidth: XspiWidth::NONE, | ||
| 242 | instruction: Some(cmd as u32), | ||
| 243 | address: None, | ||
| 244 | dummy: DummyCycles::_0, | ||
| 245 | ..Default::default() | ||
| 246 | }; | ||
| 247 | // info!("Excuting command: {:x}", transaction.instruction); | ||
| 248 | self.xspi.blocking_command(&transaction).unwrap(); | ||
| 249 | } | ||
| 250 | |||
| 251 | pub async fn reset_memory(&mut self) { | ||
| 252 | self.exec_command_4(CMD_ENABLE_RESET).await; | ||
| 253 | self.exec_command_4(CMD_RESET).await; | ||
| 254 | self.exec_command(CMD_ENABLE_RESET).await; | ||
| 255 | self.exec_command(CMD_RESET).await; | ||
| 256 | self.wait_write_finish(); | ||
| 257 | } | ||
| 258 | |||
| 259 | pub async fn enable_write(&mut self) { | ||
| 260 | self.exec_command(CMD_WRITE_ENABLE).await; | ||
| 261 | } | ||
| 262 | |||
| 263 | pub fn read_id(&mut self) -> [u8; 3] { | ||
| 264 | let mut buffer = [0; 3]; | ||
| 265 | let transaction: TransferConfig = TransferConfig { | ||
| 266 | iwidth: XspiWidth::SING, | ||
| 267 | isize: AddressSize::_8bit, | ||
| 268 | adwidth: XspiWidth::NONE, | ||
| 269 | // adsize: AddressSize::_24bit, | ||
| 270 | dwidth: XspiWidth::SING, | ||
| 271 | instruction: Some(CMD_READ_ID as u32), | ||
| 272 | ..Default::default() | ||
| 273 | }; | ||
| 274 | // info!("Reading id: 0x{:X}", transaction.instruction); | ||
| 275 | self.xspi.blocking_read(&mut buffer, transaction).unwrap(); | ||
| 276 | buffer | ||
| 277 | } | ||
| 278 | |||
| 279 | pub fn read_id_8(&mut self) -> [u8; 3] { | ||
| 280 | let mut buffer = [0; 3]; | ||
| 281 | let transaction: TransferConfig = TransferConfig { | ||
| 282 | iwidth: XspiWidth::OCTO, | ||
| 283 | isize: AddressSize::_16bit, | ||
| 284 | adwidth: XspiWidth::OCTO, | ||
| 285 | address: Some(0), | ||
| 286 | adsize: AddressSize::_32bit, | ||
| 287 | dwidth: XspiWidth::OCTO, | ||
| 288 | instruction: Some(CMD_READ_ID_OCTO as u32), | ||
| 289 | dummy: DummyCycles::_4, | ||
| 290 | ..Default::default() | ||
| 291 | }; | ||
| 292 | info!("Reading id: {:#X}", transaction.instruction); | ||
| 293 | self.xspi.blocking_read(&mut buffer, transaction).unwrap(); | ||
| 294 | buffer | ||
| 295 | } | ||
| 296 | |||
| 297 | pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8], use_dma: bool) { | ||
| 298 | let transaction = TransferConfig { | ||
| 299 | iwidth: XspiWidth::SING, | ||
| 300 | adwidth: XspiWidth::SING, | ||
| 301 | adsize: AddressSize::_24bit, | ||
| 302 | dwidth: XspiWidth::SING, | ||
| 303 | instruction: Some(CMD_READ as u32), | ||
| 304 | dummy: DummyCycles::_8, | ||
| 305 | // dwidth: XspiWidth::QUAD, | ||
| 306 | // instruction: Some(CMD_QUAD_READ as u32), | ||
| 307 | // dummy: DummyCycles::_8, | ||
| 308 | address: Some(addr), | ||
| 309 | ..Default::default() | ||
| 310 | }; | ||
| 311 | if use_dma { | ||
| 312 | self.xspi.blocking_read(buffer, transaction).unwrap(); | ||
| 313 | } else { | ||
| 314 | self.xspi.blocking_read(buffer, transaction).unwrap(); | ||
| 315 | } | ||
| 316 | } | ||
| 317 | |||
| 318 | fn wait_write_finish(&mut self) { | ||
| 319 | while (self.read_sr() & 0x01) != 0 {} | ||
| 320 | } | ||
| 321 | |||
| 322 | async fn perform_erase(&mut self, addr: u32, cmd: u8) { | ||
| 323 | let transaction = TransferConfig { | ||
| 324 | iwidth: XspiWidth::SING, | ||
| 325 | adwidth: XspiWidth::SING, | ||
| 326 | adsize: AddressSize::_24bit, | ||
| 327 | dwidth: XspiWidth::NONE, | ||
| 328 | instruction: Some(cmd as u32), | ||
| 329 | address: Some(addr), | ||
| 330 | dummy: DummyCycles::_0, | ||
| 331 | ..Default::default() | ||
| 332 | }; | ||
| 333 | self.enable_write().await; | ||
| 334 | self.xspi.blocking_command(&transaction).unwrap(); | ||
| 335 | self.wait_write_finish(); | ||
| 336 | } | ||
| 337 | |||
| 338 | pub async fn erase_sector(&mut self, addr: u32) { | ||
| 339 | self.perform_erase(addr, CMD_SECTOR_ERASE).await; | ||
| 340 | } | ||
| 341 | |||
| 342 | pub async fn erase_block_32k(&mut self, addr: u32) { | ||
| 343 | self.perform_erase(addr, CMD_BLOCK_ERASE_32K).await; | ||
| 344 | } | ||
| 345 | |||
| 346 | pub async fn erase_block_64k(&mut self, addr: u32) { | ||
| 347 | self.perform_erase(addr, CMD_BLOCK_ERASE_64K).await; | ||
| 348 | } | ||
| 349 | |||
| 350 | pub async fn erase_chip(&mut self) { | ||
| 351 | self.exec_command(CMD_CHIP_ERASE).await; | ||
| 352 | } | ||
| 353 | |||
| 354 | async fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize, use_dma: bool) { | ||
| 355 | assert!( | ||
| 356 | (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32, | ||
| 357 | "write_page(): page write length exceeds page boundary (len = {}, addr = {:X}", | ||
| 358 | len, | ||
| 359 | addr | ||
| 360 | ); | ||
| 361 | |||
| 362 | let transaction = TransferConfig { | ||
| 363 | iwidth: XspiWidth::SING, | ||
| 364 | adsize: AddressSize::_24bit, | ||
| 365 | adwidth: XspiWidth::SING, | ||
| 366 | dwidth: XspiWidth::SING, | ||
| 367 | instruction: Some(CMD_WRITE_PG as u32), | ||
| 368 | // dwidth: XspiWidth::QUAD, | ||
| 369 | // instruction: Some(CMD_QUAD_WRITE_PG as u32), | ||
| 370 | address: Some(addr), | ||
| 371 | dummy: DummyCycles::_0, | ||
| 372 | ..Default::default() | ||
| 373 | }; | ||
| 374 | self.enable_write().await; | ||
| 375 | if use_dma { | ||
| 376 | self.xspi.blocking_write(buffer, transaction).unwrap(); | ||
| 377 | } else { | ||
| 378 | self.xspi.blocking_write(buffer, transaction).unwrap(); | ||
| 379 | } | ||
| 380 | self.wait_write_finish(); | ||
| 381 | } | ||
| 382 | |||
| 383 | pub async fn write_memory(&mut self, addr: u32, buffer: &[u8], use_dma: bool) { | ||
| 384 | let mut left = buffer.len(); | ||
| 385 | let mut place = addr; | ||
| 386 | let mut chunk_start = 0; | ||
| 387 | |||
| 388 | while left > 0 { | ||
| 389 | let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize; | ||
| 390 | let chunk_size = if left >= max_chunk_size { max_chunk_size } else { left }; | ||
| 391 | let chunk = &buffer[chunk_start..(chunk_start + chunk_size)]; | ||
| 392 | self.write_page(place, chunk, chunk_size, use_dma).await; | ||
| 393 | place += chunk_size as u32; | ||
| 394 | left -= chunk_size; | ||
| 395 | chunk_start += chunk_size; | ||
| 396 | } | ||
| 397 | } | ||
| 398 | |||
| 399 | fn read_register(&mut self, cmd: u8) -> u8 { | ||
| 400 | let mut buffer = [0; 1]; | ||
| 401 | let transaction: TransferConfig = TransferConfig { | ||
| 402 | iwidth: XspiWidth::SING, | ||
| 403 | isize: AddressSize::_8bit, | ||
| 404 | adwidth: XspiWidth::NONE, | ||
| 405 | adsize: AddressSize::_24bit, | ||
| 406 | dwidth: XspiWidth::SING, | ||
| 407 | instruction: Some(cmd as u32), | ||
| 408 | address: None, | ||
| 409 | dummy: DummyCycles::_0, | ||
| 410 | ..Default::default() | ||
| 411 | }; | ||
| 412 | self.xspi.blocking_read(&mut buffer, transaction).unwrap(); | ||
| 413 | // info!("Read w25q64 register: 0x{:x}", buffer[0]); | ||
| 414 | buffer[0] | ||
| 415 | } | ||
| 416 | |||
| 417 | fn write_register(&mut self, cmd: u8, value: u8) { | ||
| 418 | let buffer = [value; 1]; | ||
| 419 | let transaction: TransferConfig = TransferConfig { | ||
| 420 | iwidth: XspiWidth::SING, | ||
| 421 | isize: AddressSize::_8bit, | ||
| 422 | instruction: Some(cmd as u32), | ||
| 423 | adsize: AddressSize::_24bit, | ||
| 424 | adwidth: XspiWidth::NONE, | ||
| 425 | dwidth: XspiWidth::SING, | ||
| 426 | address: None, | ||
| 427 | dummy: DummyCycles::_0, | ||
| 428 | ..Default::default() | ||
| 429 | }; | ||
| 430 | self.xspi.blocking_write(&buffer, transaction).unwrap(); | ||
| 431 | } | ||
| 432 | |||
| 433 | pub fn read_sr(&mut self) -> u8 { | ||
| 434 | self.read_register(CMD_READ_SR) | ||
| 435 | } | ||
| 436 | |||
| 437 | pub fn read_cr(&mut self) -> u8 { | ||
| 438 | self.read_register(CMD_READ_CR) | ||
| 439 | } | ||
| 440 | |||
| 441 | pub fn write_sr(&mut self, value: u8) { | ||
| 442 | self.write_register(CMD_WRITE_SR, value); | ||
| 443 | } | ||
| 444 | |||
| 445 | pub fn write_cr(&mut self, value: u8) { | ||
| 446 | self.write_register(CMD_WRITE_CR, value); | ||
| 447 | } | ||
| 448 | } | ||
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..3101cf7ce 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml | |||
| @@ -6,13 +6,13 @@ 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.2.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.7.0", 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.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | 13 | ||
| 14 | defmt = "0.3" | 14 | defmt = "1.0.1" |
| 15 | defmt-rtt = "0.4" | 15 | defmt-rtt = "1.0.0" |
| 16 | 16 | ||
| 17 | embedded-storage = "0.3.1" | 17 | embedded-storage = "0.3.1" |
| 18 | embedded-io = { version = "0.6.0" } | 18 | embedded-io = { version = "0.6.0" } |
| @@ -20,7 +20,7 @@ embedded-io-async = { version = "0.6.1" } | |||
| 20 | 20 | ||
| 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 21 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 22 | cortex-m-rt = "0.7.0" | 22 | cortex-m-rt = "0.7.0" |
| 23 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 23 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 24 | heapless = { version = "0.8", default-features = false } | 24 | heapless = { version = "0.8", default-features = false } |
| 25 | embedded-hal = "0.2.6" | 25 | embedded-hal = "0.2.6" |
| 26 | static_cell = { version = "2" } | 26 | static_cell = { version = "2" } |
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/eeprom.rs b/examples/stm32l0/src/bin/eeprom.rs new file mode 100644 index 000000000..370246644 --- /dev/null +++ b/examples/stm32l0/src/bin/eeprom.rs | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::{info, unwrap}; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::flash::{Flash, EEPROM_BASE, EEPROM_SIZE}; | ||
| 7 | use {defmt_rtt as _, panic_probe as _}; | ||
| 8 | |||
| 9 | #[embassy_executor::main] | ||
| 10 | async fn main(_spawner: Spawner) { | ||
| 11 | let p = embassy_stm32::init(Default::default()); | ||
| 12 | |||
| 13 | info!("Hello Eeprom! Start: {}, Size: {}", EEPROM_BASE, EEPROM_SIZE); | ||
| 14 | |||
| 15 | const ADDR: u32 = 0x0; | ||
| 16 | |||
| 17 | let mut f = Flash::new_blocking(p.FLASH); | ||
| 18 | |||
| 19 | info!("Reading..."); | ||
| 20 | let mut buf = [0u8; 8]; | ||
| 21 | unwrap!(f.eeprom_read_slice(ADDR, &mut buf)); | ||
| 22 | info!("Read: {=[u8]:x}", buf); | ||
| 23 | |||
| 24 | info!("Writing..."); | ||
| 25 | unwrap!(f.eeprom_write_slice(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); | ||
| 26 | |||
| 27 | info!("Reading..."); | ||
| 28 | let mut buf = [0u8; 8]; | ||
| 29 | unwrap!(f.eeprom_read_slice(ADDR, &mut buf)); | ||
| 30 | info!("Read: {=[u8]:x}", buf); | ||
| 31 | assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); | ||
| 32 | } | ||
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/stm32l0/src/bin/usart_irq.rs b/examples/stm32l0/src/bin/usart_irq.rs index 2c96a8bc2..a51ddbcbb 100644 --- a/examples/stm32l0/src/bin/usart_irq.rs +++ b/examples/stm32l0/src/bin/usart_irq.rs | |||
| @@ -21,7 +21,7 @@ async fn main(_spawner: Spawner) { | |||
| 21 | config.baudrate = 9600; | 21 | config.baudrate = 9600; |
| 22 | let mut tx_buf = [0u8; 256]; | 22 | let mut tx_buf = [0u8; 256]; |
| 23 | let mut rx_buf = [0u8; 256]; | 23 | let mut rx_buf = [0u8; 256]; |
| 24 | let mut usart = BufferedUart::new(p.USART2, Irqs, p.PA3, p.PA2, &mut tx_buf, &mut rx_buf, config).unwrap(); | 24 | let mut usart = BufferedUart::new(p.USART2, p.PA3, p.PA2, &mut tx_buf, &mut rx_buf, Irqs, config).unwrap(); |
| 25 | 25 | ||
| 26 | usart.write_all(b"Hello Embassy World!\r\n").await.unwrap(); | 26 | usart.write_all(b"Hello Embassy World!\r\n").await.unwrap(); |
| 27 | info!("wrote Hello, starting echo"); | 27 | info!("wrote Hello, starting echo"); |
diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index 062044f32..a0a7916a7 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml | |||
| @@ -5,20 +5,20 @@ 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.7.0", 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.7.0", 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.4.0", 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.2.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.4.0", path = "../../embassy-usb", features = ["defmt"] } |
| 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 14 | 14 | ||
| 15 | defmt = "0.3" | 15 | defmt = "1.0.1" |
| 16 | defmt-rtt = "0.4" | 16 | defmt-rtt = "1.0.0" |
| 17 | 17 | ||
| 18 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 18 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 19 | cortex-m-rt = "0.7.0" | 19 | cortex-m-rt = "0.7.0" |
| 20 | embedded-hal = "0.2.6" | 20 | embedded-hal = "0.2.6" |
| 21 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 21 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 22 | heapless = { version = "0.8", default-features = false } | 22 | heapless = { version = "0.8", default-features = false } |
| 23 | embedded-storage = "0.3.1" | 23 | embedded-storage = "0.3.1" |
| 24 | 24 | ||
diff --git a/examples/stm32l1/src/bin/eeprom.rs b/examples/stm32l1/src/bin/eeprom.rs new file mode 100644 index 000000000..370246644 --- /dev/null +++ b/examples/stm32l1/src/bin/eeprom.rs | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::{info, unwrap}; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::flash::{Flash, EEPROM_BASE, EEPROM_SIZE}; | ||
| 7 | use {defmt_rtt as _, panic_probe as _}; | ||
| 8 | |||
| 9 | #[embassy_executor::main] | ||
| 10 | async fn main(_spawner: Spawner) { | ||
| 11 | let p = embassy_stm32::init(Default::default()); | ||
| 12 | |||
| 13 | info!("Hello Eeprom! Start: {}, Size: {}", EEPROM_BASE, EEPROM_SIZE); | ||
| 14 | |||
| 15 | const ADDR: u32 = 0x0; | ||
| 16 | |||
| 17 | let mut f = Flash::new_blocking(p.FLASH); | ||
| 18 | |||
| 19 | info!("Reading..."); | ||
| 20 | let mut buf = [0u8; 8]; | ||
| 21 | unwrap!(f.eeprom_read_slice(ADDR, &mut buf)); | ||
| 22 | info!("Read: {=[u8]:x}", buf); | ||
| 23 | |||
| 24 | info!("Writing..."); | ||
| 25 | unwrap!(f.eeprom_write_slice(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); | ||
| 26 | |||
| 27 | info!("Reading..."); | ||
| 28 | let mut buf = [0u8; 8]; | ||
| 29 | unwrap!(f.eeprom_read_slice(ADDR, &mut buf)); | ||
| 30 | info!("Read: {=[u8]:x}", buf); | ||
| 31 | assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); | ||
| 32 | } | ||
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/stm32l1/src/bin/usb_serial.rs b/examples/stm32l1/src/bin/usb_serial.rs index 837f7fa57..a35f1d7a7 100644 --- a/examples/stm32l1/src/bin/usb_serial.rs +++ b/examples/stm32l1/src/bin/usb_serial.rs | |||
| @@ -41,11 +41,6 @@ async fn main(_spawner: Spawner) { | |||
| 41 | config.product = Some("USB-Serial Example"); | 41 | config.product = Some("USB-Serial Example"); |
| 42 | config.serial_number = Some("123456"); | 42 | config.serial_number = Some("123456"); |
| 43 | 43 | ||
| 44 | config.device_class = 0xEF; | ||
| 45 | config.device_sub_class = 0x02; | ||
| 46 | config.device_protocol = 0x01; | ||
| 47 | config.composite_with_iads = true; | ||
| 48 | |||
| 49 | let mut config_descriptor = [0; 256]; | 44 | let mut config_descriptor = [0; 256]; |
| 50 | let mut bos_descriptor = [0; 256]; | 45 | let mut bos_descriptor = [0; 256]; |
| 51 | let mut control_buf = [0; 64]; | 46 | let mut control_buf = [0; 64]; |
diff --git a/examples/stm32l4/.cargo/config.toml b/examples/stm32l4/.cargo/config.toml index 83fc6d6f8..d71fb1517 100644 --- a/examples/stm32l4/.cargo/config.toml +++ b/examples/stm32l4/.cargo/config.toml | |||
| @@ -2,7 +2,8 @@ | |||
| 2 | # replace STM32F429ZITx with your chip as listed in `probe-rs chip list` | 2 | # replace STM32F429ZITx with your chip as listed in `probe-rs chip list` |
| 3 | #runner = "probe-rs run --chip STM32L475VGT6" | 3 | #runner = "probe-rs run --chip STM32L475VGT6" |
| 4 | #runner = "probe-rs run --chip STM32L475VG" | 4 | #runner = "probe-rs run --chip STM32L475VG" |
| 5 | runner = "probe-rs run --chip STM32L4S5QI" | 5 | #runner = "probe-rs run --chip STM32L4S5QI" |
| 6 | runner = "probe-rs run --chip STM32L4R5ZITxP" | ||
| 6 | 7 | ||
| 7 | [build] | 8 | [build] |
| 8 | target = "thumbv7em-none-eabi" | 9 | target = "thumbv7em-none-eabi" |
diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index c5478b17b..7da09e5b0 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml | |||
| @@ -6,20 +6,20 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32l4s5vi to your chip name, if necessary. | 8 | # Change stm32l4s5vi to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4s5qi", "memory-x", "time-driver-any", "exti", "chrono"] } | 9 | embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4r5zi", "memory-x", "time-driver-any", "exti", "chrono", "dual-bank"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] } |
| 13 | embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } |
| 14 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } |
| 15 | embassy-net-adin1110 = { version = "0.2.0", path = "../../embassy-net-adin1110" } | 15 | embassy-net-adin1110 = { version = "0.3.0", path = "../../embassy-net-adin1110" } |
| 16 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "udp", "tcp", "dhcpv4", "medium-ethernet"] } | 16 | embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defmt", "udp", "tcp", "dhcpv4", "medium-ethernet"] } |
| 17 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 17 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 18 | embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } | 18 | embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } |
| 19 | embedded-io = { version = "0.6.0", features = ["defmt-03"] } | 19 | embedded-io = { version = "0.6.0", features = ["defmt-03"] } |
| 20 | 20 | ||
| 21 | defmt = "0.3" | 21 | defmt = "1.0.1" |
| 22 | defmt-rtt = "0.4" | 22 | defmt-rtt = "1.0.0" |
| 23 | 23 | ||
| 24 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 24 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 25 | cortex-m-rt = "0.7.0" | 25 | cortex-m-rt = "0.7.0" |
| @@ -27,10 +27,9 @@ embedded-hal = "0.2.6" | |||
| 27 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | 27 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } |
| 28 | embedded-hal-async = { version = "1.0" } | 28 | embedded-hal-async = { version = "1.0" } |
| 29 | embedded-hal-bus = { version = "0.1", features = ["async"] } | 29 | embedded-hal-bus = { version = "0.1", features = ["async"] } |
| 30 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 30 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 31 | heapless = { version = "0.8", default-features = false } | 31 | heapless = { version = "0.8", default-features = false } |
| 32 | chrono = { version = "^0.4", default-features = false } | 32 | chrono = { version = "^0.4", default-features = false } |
| 33 | rand = { version = "0.8.5", default-features = false } | ||
| 34 | static_cell = "2" | 33 | static_cell = "2" |
| 35 | 34 | ||
| 36 | micromath = "2.0.0" | 35 | micromath = "2.0.0" |
diff --git a/examples/stm32l4/README.md b/examples/stm32l4/README.md new file mode 100644 index 000000000..e463c18a0 --- /dev/null +++ b/examples/stm32l4/README.md | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | # Examples for STM32L4 family | ||
| 2 | Run individual examples with | ||
| 3 | ``` | ||
| 4 | cargo run --bin <module-name> | ||
| 5 | ``` | ||
| 6 | for example | ||
| 7 | ``` | ||
| 8 | cargo run --bin blinky | ||
| 9 | ``` | ||
| 10 | |||
| 11 | ## Checklist before running examples | ||
| 12 | You might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using. | ||
| 13 | |||
| 14 | * [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L4R5ZI-P it should be `probe-rs run --chip STM32L4R5ZITxP`. (use `probe-rs chip list` to find your chip) | ||
| 15 | * [ ] Update Cargo.toml to have the correct `embassy-stm32` feature. For example for L4R5ZI-P it should be `stm32l4r5zi`. Look in the `Cargo.toml` file of the `embassy-stm32` project to find the correct feature flag for your chip. | ||
| 16 | * [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately. | ||
| 17 | * [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic | ||
| 18 | |||
| 19 | If you are unsure, please drop by the Embassy Matrix chat for support, and let us know: | ||
| 20 | |||
| 21 | * Which example you are trying to run | ||
| 22 | * Which chip and board you are using | ||
| 23 | |||
| 24 | Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org | ||
diff --git a/examples/stm32l4/src/bin/dac.rs b/examples/stm32l4/src/bin/dac.rs index fdbf1d374..50db0e082 100644 --- a/examples/stm32l4/src/bin/dac.rs +++ b/examples/stm32l4/src/bin/dac.rs | |||
| @@ -3,7 +3,6 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_stm32::dac::{DacCh1, Value}; | 5 | use embassy_stm32::dac::{DacCh1, Value}; |
| 6 | use embassy_stm32::dma::NoDma; | ||
| 7 | use {defmt_rtt as _, panic_probe as _}; | 6 | use {defmt_rtt as _, panic_probe as _}; |
| 8 | 7 | ||
| 9 | #[cortex_m_rt::entry] | 8 | #[cortex_m_rt::entry] |
| @@ -11,7 +10,7 @@ fn main() -> ! { | |||
| 11 | let p = embassy_stm32::init(Default::default()); | 10 | let p = embassy_stm32::init(Default::default()); |
| 12 | info!("Hello World!"); | 11 | info!("Hello World!"); |
| 13 | 12 | ||
| 14 | let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); | 13 | let mut dac = DacCh1::new_blocking(p.DAC1, p.PA4); |
| 15 | 14 | ||
| 16 | loop { | 15 | loop { |
| 17 | for v in 0..=255 { | 16 | for v in 0..=255 { |
diff --git a/examples/stm32l4/src/bin/dac_dma.rs b/examples/stm32l4/src/bin/dac_dma.rs index d01b016c0..cde24f411 100644 --- a/examples/stm32l4/src/bin/dac_dma.rs +++ b/examples/stm32l4/src/bin/dac_dma.rs | |||
| @@ -4,11 +4,13 @@ | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::dac::{DacCh1, DacCh2, ValueArray}; | 6 | use embassy_stm32::dac::{DacCh1, DacCh2, ValueArray}; |
| 7 | use embassy_stm32::mode::Async; | ||
| 7 | use embassy_stm32::pac::timer::vals::Mms; | 8 | use embassy_stm32::pac::timer::vals::Mms; |
| 8 | use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7}; | 9 | use embassy_stm32::peripherals::{DAC1, TIM6, TIM7}; |
| 9 | use embassy_stm32::rcc::frequency; | 10 | use embassy_stm32::rcc::frequency; |
| 10 | use embassy_stm32::time::Hertz; | 11 | use embassy_stm32::time::Hertz; |
| 11 | use embassy_stm32::timer::low_level::Timer; | 12 | use embassy_stm32::timer::low_level::Timer; |
| 13 | use embassy_stm32::Peri; | ||
| 12 | use micromath::F32Ext; | 14 | use micromath::F32Ext; |
| 13 | use {defmt_rtt as _, panic_probe as _}; | 15 | use {defmt_rtt as _, panic_probe as _}; |
| 14 | 16 | ||
| @@ -27,7 +29,7 @@ async fn main(spawner: Spawner) { | |||
| 27 | } | 29 | } |
| 28 | 30 | ||
| 29 | #[embassy_executor::task] | 31 | #[embassy_executor::task] |
| 30 | async fn dac_task1(tim: TIM6, mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { | 32 | async fn dac_task1(tim: Peri<'static, TIM6>, mut dac: DacCh1<'static, DAC1, Async>) { |
| 31 | let data: &[u8; 256] = &calculate_array::<256>(); | 33 | let data: &[u8; 256] = &calculate_array::<256>(); |
| 32 | 34 | ||
| 33 | info!("TIM6 frequency is {}", frequency::<TIM6>()); | 35 | info!("TIM6 frequency is {}", frequency::<TIM6>()); |
| @@ -70,7 +72,7 @@ async fn dac_task1(tim: TIM6, mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { | |||
| 70 | } | 72 | } |
| 71 | 73 | ||
| 72 | #[embassy_executor::task] | 74 | #[embassy_executor::task] |
| 73 | async fn dac_task2(tim: TIM7, mut dac: DacCh2<'static, DAC1, DMA1_CH4>) { | 75 | async fn dac_task2(tim: Peri<'static, TIM7>, mut dac: DacCh2<'static, DAC1, Async>) { |
| 74 | let data: &[u8; 256] = &calculate_array::<256>(); | 76 | let data: &[u8; 256] = &calculate_array::<256>(); |
| 75 | 77 | ||
| 76 | info!("TIM7 frequency is {}", frequency::<TIM7>()); | 78 | info!("TIM7 frequency is {}", frequency::<TIM7>()); |
diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs index 8f23e4083..dc90a3b85 100644 --- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs | |||
| @@ -38,7 +38,6 @@ use embedded_io::Write as bWrite; | |||
| 38 | use embedded_io_async::Write; | 38 | use embedded_io_async::Write; |
| 39 | use heapless::Vec; | 39 | use heapless::Vec; |
| 40 | use panic_probe as _; | 40 | use panic_probe as _; |
| 41 | use rand::RngCore; | ||
| 42 | use static_cell::StaticCell; | 41 | use static_cell::StaticCell; |
| 43 | 42 | ||
| 44 | bind_interrupts!(struct Irqs { | 43 | bind_interrupts!(struct Irqs { |
| @@ -51,7 +50,7 @@ bind_interrupts!(struct Irqs { | |||
| 51 | // MAC-address used by the adin1110 | 50 | // MAC-address used by the adin1110 |
| 52 | const MAC: [u8; 6] = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]; | 51 | const MAC: [u8; 6] = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]; |
| 53 | // Static IP settings | 52 | // Static IP settings |
| 54 | const IP_ADDRESS: Ipv4Cidr = Ipv4Cidr::new(Ipv4Address([192, 168, 1, 5]), 24); | 53 | const IP_ADDRESS: Ipv4Cidr = Ipv4Cidr::new(Ipv4Address::new(192, 168, 1, 5), 24); |
| 55 | // Listen port for the webserver | 54 | // Listen port for the webserver |
| 56 | const HTTP_LISTEN_PORT: u16 = 80; | 55 | const HTTP_LISTEN_PORT: u16 = 80; |
| 57 | 56 | ||
| @@ -206,12 +205,11 @@ async fn main(spawner: Spawner) { | |||
| 206 | }; | 205 | }; |
| 207 | 206 | ||
| 208 | // Init network stack | 207 | // Init network stack |
| 209 | static STACK: StaticCell<Stack<Device<'static>>> = StaticCell::new(); | ||
| 210 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 208 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 211 | let stack = &*STACK.init(Stack::new(device, ip_cfg, RESOURCES.init(StackResources::new()), seed)); | 209 | let (stack, runner) = embassy_net::new(device, ip_cfg, RESOURCES.init(StackResources::new()), seed); |
| 212 | 210 | ||
| 213 | // Launch network task | 211 | // Launch network task |
| 214 | unwrap!(spawner.spawn(net_task(stack))); | 212 | unwrap!(spawner.spawn(net_task(runner))); |
| 215 | 213 | ||
| 216 | let cfg = wait_for_config(stack).await; | 214 | let cfg = wait_for_config(stack).await; |
| 217 | let local_addr = cfg.address.address(); | 215 | let local_addr = cfg.address.address(); |
| @@ -274,7 +272,7 @@ async fn main(spawner: Spawner) { | |||
| 274 | } | 272 | } |
| 275 | } | 273 | } |
| 276 | 274 | ||
| 277 | async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { | 275 | async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 { |
| 278 | loop { | 276 | loop { |
| 279 | if let Some(config) = stack.config_v4() { | 277 | if let Some(config) = stack.config_v4() { |
| 280 | return config; | 278 | return config; |
| @@ -323,8 +321,8 @@ async fn ethernet_task(runner: Runner<'static, SpeSpiCs, SpeInt, SpeRst>) -> ! { | |||
| 323 | } | 321 | } |
| 324 | 322 | ||
| 325 | #[embassy_executor::task] | 323 | #[embassy_executor::task] |
| 326 | async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { | 324 | async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! { |
| 327 | stack.run().await | 325 | runner.run().await |
| 328 | } | 326 | } |
| 329 | 327 | ||
| 330 | // same panicking *behavior* as `panic-probe` but doesn't print a panic message | 328 | // same panicking *behavior* as `panic-probe` but doesn't print a panic message |
diff --git a/examples/stm32l4/src/bin/tsc_async.rs b/examples/stm32l4/src/bin/tsc_async.rs new file mode 100644 index 000000000..b9a059e2e --- /dev/null +++ b/examples/stm32l4/src/bin/tsc_async.rs | |||
| @@ -0,0 +1,108 @@ | |||
| 1 | // Example of async TSC (Touch Sensing Controller) that lights an LED when touch is detected. | ||
| 2 | // | ||
| 3 | // This example demonstrates: | ||
| 4 | // 1. Configuring a single TSC channel pin | ||
| 5 | // 2. Using the async TSC interface | ||
| 6 | // 3. Waiting for acquisition completion using `pend_for_acquisition` | ||
| 7 | // 4. Reading touch values and controlling an LED based on the results | ||
| 8 | // | ||
| 9 | // Suggested physical setup on STM32L4R5ZI-P board: | ||
| 10 | // - Connect a 1000pF capacitor between pin PB4 (D25) and GND. This is your sampling capacitor. | ||
| 11 | // - Connect one end of a 1K resistor to pin PB5 (D21) and leave the other end loose. | ||
| 12 | // The loose end will act as the touch sensor which will register your touch. | ||
| 13 | // | ||
| 14 | // The example uses two pins from Group 2 of the TSC: | ||
| 15 | // - PB4 (D25) as the sampling capacitor, TSC group 2 IO1 | ||
| 16 | // - PB5 (D21) as the channel pin, TSC group 2 IO2 | ||
| 17 | // | ||
| 18 | // The program continuously reads the touch sensor value: | ||
| 19 | // - It starts acquisition, waits for completion using `pend_for_acquisition`, and reads the value. | ||
| 20 | // - The LED (connected to PB14) is turned on when touch is detected (sensor value < SENSOR_THRESHOLD). | ||
| 21 | // - Touch values are logged to the console. | ||
| 22 | // | ||
| 23 | // Troubleshooting: | ||
| 24 | // - If touch is not detected, try adjusting the SENSOR_THRESHOLD value. | ||
| 25 | // - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, | ||
| 26 | // pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity. | ||
| 27 | // | ||
| 28 | // Note: Configuration values and sampling capacitor value have been determined experimentally. | ||
| 29 | // Optimal values may vary based on your specific hardware setup. | ||
| 30 | |||
| 31 | #![no_std] | ||
| 32 | #![no_main] | ||
| 33 | |||
| 34 | use defmt::*; | ||
| 35 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 36 | use embassy_stm32::tsc::{self, *}; | ||
| 37 | use embassy_stm32::{bind_interrupts, peripherals}; | ||
| 38 | use embassy_time::Timer; | ||
| 39 | use {defmt_rtt as _, panic_probe as _}; | ||
| 40 | |||
| 41 | bind_interrupts!(struct Irqs { | ||
| 42 | TSC => InterruptHandler<embassy_stm32::peripherals::TSC>; | ||
| 43 | }); | ||
| 44 | const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup | ||
| 45 | |||
| 46 | #[embassy_executor::main] | ||
| 47 | async fn main(_spawner: embassy_executor::Spawner) { | ||
| 48 | let device_config = embassy_stm32::Config::default(); | ||
| 49 | let context = embassy_stm32::init(device_config); | ||
| 50 | |||
| 51 | let mut pin_group: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default(); | ||
| 52 | // D25 | ||
| 53 | pin_group.set_io1::<tsc::pin_roles::Sample>(context.PB4); | ||
| 54 | // D21 | ||
| 55 | let tsc_sensor = pin_group.set_io2::<tsc::pin_roles::Channel>(context.PB5); | ||
| 56 | |||
| 57 | let pin_groups: PinGroups<peripherals::TSC> = PinGroups { | ||
| 58 | g2: Some(pin_group.pin_group), | ||
| 59 | ..Default::default() | ||
| 60 | }; | ||
| 61 | |||
| 62 | let tsc_conf = Config { | ||
| 63 | ct_pulse_high_length: ChargeTransferPulseCycle::_4, | ||
| 64 | ct_pulse_low_length: ChargeTransferPulseCycle::_4, | ||
| 65 | spread_spectrum: false, | ||
| 66 | spread_spectrum_deviation: SSDeviation::new(2).unwrap(), | ||
| 67 | spread_spectrum_prescaler: false, | ||
| 68 | pulse_generator_prescaler: PGPrescalerDivider::_16, | ||
| 69 | max_count_value: MaxCount::_255, | ||
| 70 | io_default_mode: false, | ||
| 71 | synchro_pin_polarity: false, | ||
| 72 | acquisition_mode: false, | ||
| 73 | max_count_interrupt: false, | ||
| 74 | }; | ||
| 75 | |||
| 76 | let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, tsc_conf, Irqs).unwrap(); | ||
| 77 | |||
| 78 | // Check if TSC is ready | ||
| 79 | if touch_controller.get_state() != State::Ready { | ||
| 80 | info!("TSC not ready!"); | ||
| 81 | return; | ||
| 82 | } | ||
| 83 | info!("TSC initialized successfully"); | ||
| 84 | |||
| 85 | let mut led = Output::new(context.PB14, Level::High, Speed::Low); | ||
| 86 | |||
| 87 | let discharge_delay = 1; // ms | ||
| 88 | |||
| 89 | info!("Starting touch_controller interface"); | ||
| 90 | loop { | ||
| 91 | touch_controller.set_active_channels_mask(tsc_sensor.pin.into()); | ||
| 92 | touch_controller.start(); | ||
| 93 | touch_controller.pend_for_acquisition().await; | ||
| 94 | touch_controller.discharge_io(true); | ||
| 95 | Timer::after_millis(discharge_delay).await; | ||
| 96 | |||
| 97 | let group_val = touch_controller.group_get_value(tsc_sensor.pin.group()); | ||
| 98 | info!("Touch value: {}", group_val); | ||
| 99 | |||
| 100 | if group_val < SENSOR_THRESHOLD { | ||
| 101 | led.set_high(); | ||
| 102 | } else { | ||
| 103 | led.set_low(); | ||
| 104 | } | ||
| 105 | |||
| 106 | Timer::after_millis(100).await; | ||
| 107 | } | ||
| 108 | } | ||
diff --git a/examples/stm32l4/src/bin/tsc_blocking.rs b/examples/stm32l4/src/bin/tsc_blocking.rs new file mode 100644 index 000000000..12084f8e2 --- /dev/null +++ b/examples/stm32l4/src/bin/tsc_blocking.rs | |||
| @@ -0,0 +1,147 @@ | |||
| 1 | // # Example of blocking TSC (Touch Sensing Controller) that lights an LED when touch is detected | ||
| 2 | // | ||
| 3 | // This example demonstrates how to use the Touch Sensing Controller (TSC) in blocking mode on an STM32L4R5ZI-P board. | ||
| 4 | // | ||
| 5 | // ## This example demonstrates: | ||
| 6 | // | ||
| 7 | // 1. Configuring a single TSC channel pin | ||
| 8 | // 2. Using the blocking TSC interface with polling | ||
| 9 | // 3. Waiting for acquisition completion using `poll_for_acquisition` | ||
| 10 | // 4. Reading touch values and controlling an LED based on the results | ||
| 11 | // | ||
| 12 | // ## Suggested physical setup on STM32L4R5ZI-P board: | ||
| 13 | // | ||
| 14 | // - Connect a 1000pF capacitor between pin PB4 (D25) and GND. This is your sampling capacitor. | ||
| 15 | // - Connect one end of a 1K resistor to pin PB5 (D21) and leave the other end loose. | ||
| 16 | // The loose end will act as the touch sensor which will register your touch. | ||
| 17 | // | ||
| 18 | // ## Pin Configuration: | ||
| 19 | // | ||
| 20 | // The example uses two pins from Group 2 of the TSC: | ||
| 21 | // - PB4 (D25) as the sampling capacitor, TSC group 2 IO1 | ||
| 22 | // - PB5 (D21) as the channel pin, TSC group 2 IO2 | ||
| 23 | // | ||
| 24 | // ## Program Behavior: | ||
| 25 | // | ||
| 26 | // The program continuously reads the touch sensor value: | ||
| 27 | // - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value. | ||
| 28 | // - The LED (connected to PB14) is turned on when touch is detected (sensor value < SENSOR_THRESHOLD). | ||
| 29 | // - Touch values are logged to the console. | ||
| 30 | // | ||
| 31 | // ## Troubleshooting: | ||
| 32 | // | ||
| 33 | // - If touch is not detected, try adjusting the SENSOR_THRESHOLD value (currently set to 25). | ||
| 34 | // - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, | ||
| 35 | // pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity. | ||
| 36 | // - Be aware that for some boards, there might be overlapping concerns between some pins, | ||
| 37 | // such as UART connections for the programmer. No errors or warnings will be emitted if you | ||
| 38 | // try to use such a pin for TSC, but you may get strange sensor readings. | ||
| 39 | // | ||
| 40 | // Note: Configuration values and sampling capacitor value have been determined experimentally. | ||
| 41 | // Optimal values may vary based on your specific hardware setup. Refer to the official | ||
| 42 | // STM32L4R5ZI-P datasheet and user manuals for more information on pin configurations and TSC functionality. | ||
| 43 | |||
| 44 | #![no_std] | ||
| 45 | #![no_main] | ||
| 46 | |||
| 47 | use defmt::*; | ||
| 48 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 49 | use embassy_stm32::tsc::{self, *}; | ||
| 50 | use embassy_stm32::{mode, peripherals}; | ||
| 51 | use embassy_time::Timer; | ||
| 52 | use {defmt_rtt as _, panic_probe as _}; | ||
| 53 | |||
| 54 | const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup | ||
| 55 | |||
| 56 | #[embassy_executor::main] | ||
| 57 | async fn main(_spawner: embassy_executor::Spawner) { | ||
| 58 | let device_config = embassy_stm32::Config::default(); | ||
| 59 | let context = embassy_stm32::init(device_config); | ||
| 60 | |||
| 61 | let tsc_conf = Config { | ||
| 62 | ct_pulse_high_length: ChargeTransferPulseCycle::_4, | ||
| 63 | ct_pulse_low_length: ChargeTransferPulseCycle::_4, | ||
| 64 | spread_spectrum: false, | ||
| 65 | spread_spectrum_deviation: SSDeviation::new(2).unwrap(), | ||
| 66 | spread_spectrum_prescaler: false, | ||
| 67 | pulse_generator_prescaler: PGPrescalerDivider::_16, | ||
| 68 | max_count_value: MaxCount::_255, | ||
| 69 | io_default_mode: false, | ||
| 70 | synchro_pin_polarity: false, | ||
| 71 | acquisition_mode: false, | ||
| 72 | max_count_interrupt: false, | ||
| 73 | }; | ||
| 74 | |||
| 75 | let mut g2: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default(); | ||
| 76 | // D25 | ||
| 77 | g2.set_io1::<tsc::pin_roles::Sample>(context.PB4); | ||
| 78 | // D21 | ||
| 79 | let tsc_sensor = g2.set_io2::<tsc::pin_roles::Channel>(context.PB5); | ||
| 80 | |||
| 81 | let pin_groups: PinGroups<peripherals::TSC> = PinGroups { | ||
| 82 | g2: Some(g2.pin_group), | ||
| 83 | ..Default::default() | ||
| 84 | }; | ||
| 85 | |||
| 86 | let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, pin_groups, tsc_conf).unwrap(); | ||
| 87 | |||
| 88 | // Check if TSC is ready | ||
| 89 | if touch_controller.get_state() != State::Ready { | ||
| 90 | crate::panic!("TSC not ready!"); | ||
| 91 | } | ||
| 92 | info!("TSC initialized successfully"); | ||
| 93 | |||
| 94 | let mut led = Output::new(context.PB14, Level::High, Speed::Low); | ||
| 95 | |||
| 96 | // smaller sample capacitor discharge faster and can be used with shorter delay. | ||
| 97 | let discharge_delay = 5; // ms | ||
| 98 | |||
| 99 | // the interval at which the loop polls for new touch sensor values | ||
| 100 | let polling_interval = 100; // ms | ||
| 101 | |||
| 102 | info!("polling for touch"); | ||
| 103 | loop { | ||
| 104 | touch_controller.set_active_channels_mask(tsc_sensor.pin.into()); | ||
| 105 | touch_controller.start(); | ||
| 106 | touch_controller.poll_for_acquisition(); | ||
| 107 | touch_controller.discharge_io(true); | ||
| 108 | Timer::after_millis(discharge_delay).await; | ||
| 109 | |||
| 110 | match read_touch_value(&mut touch_controller, tsc_sensor.pin).await { | ||
| 111 | Some(v) => { | ||
| 112 | info!("sensor value {}", v); | ||
| 113 | if v < SENSOR_THRESHOLD { | ||
| 114 | led.set_high(); | ||
| 115 | } else { | ||
| 116 | led.set_low(); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | None => led.set_low(), | ||
| 120 | } | ||
| 121 | |||
| 122 | Timer::after_millis(polling_interval).await; | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10; | ||
| 127 | |||
| 128 | // attempt to read group status and delay when still ongoing | ||
| 129 | async fn read_touch_value( | ||
| 130 | touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Blocking>, | ||
| 131 | sensor_pin: tsc::IOPin, | ||
| 132 | ) -> Option<u16> { | ||
| 133 | for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS { | ||
| 134 | match touch_controller.group_get_status(sensor_pin.group()) { | ||
| 135 | GroupStatus::Complete => { | ||
| 136 | return Some(touch_controller.group_get_value(sensor_pin.group())); | ||
| 137 | } | ||
| 138 | GroupStatus::Ongoing => { | ||
| 139 | // if you end up here a lot, then you prob need to increase discharge_delay | ||
| 140 | // or consider changing the code to adjust the discharge_delay dynamically | ||
| 141 | info!("Acquisition still ongoing"); | ||
| 142 | Timer::after_millis(1).await; | ||
| 143 | } | ||
| 144 | } | ||
| 145 | } | ||
| 146 | None | ||
| 147 | } | ||
diff --git a/examples/stm32l4/src/bin/tsc_multipin.rs b/examples/stm32l4/src/bin/tsc_multipin.rs new file mode 100644 index 000000000..8fec5ddc4 --- /dev/null +++ b/examples/stm32l4/src/bin/tsc_multipin.rs | |||
| @@ -0,0 +1,198 @@ | |||
| 1 | // # Example of TSC (Touch Sensing Controller) using multiple pins from the same TSC group | ||
| 2 | // | ||
| 3 | // This example demonstrates how to use the Touch Sensing Controller (TSC) with multiple pins, including pins from the same TSC group, on an STM32L4R5ZI-P board. | ||
| 4 | // | ||
| 5 | // ## Key Concepts | ||
| 6 | // | ||
| 7 | // - Only one TSC pin for each TSC group can be acquired and read at a time. | ||
| 8 | // - To control which channel pins are acquired and read, we must write a mask before initiating an acquisition. | ||
| 9 | // - We organize channel pins into acquisition banks to manage this process efficiently. | ||
| 10 | // - Each acquisition bank can contain exactly one channel pin per TSC group and will contain the relevant mask. | ||
| 11 | // | ||
| 12 | // ## This example demonstrates how to: | ||
| 13 | // | ||
| 14 | // 1. Configure multiple channel pins within a single TSC group | ||
| 15 | // 2. Use the set_active_channels_bank method to switch between sets of different channels (acquisition banks) | ||
| 16 | // 3. Read and interpret touch values from multiple channels in the same group | ||
| 17 | // | ||
| 18 | // ## Suggested physical setup on STM32L4R5ZI-P board: | ||
| 19 | // | ||
| 20 | // - Connect a 1000pF capacitor between pin PB12 (D19) and GND. This is the sampling capacitor for TSC group 1. | ||
| 21 | // - Connect one end of a 1K resistor to pin PB13 (D18) and leave the other end loose. This will act as a touch sensor. | ||
| 22 | // - Connect a 1000pF capacitor between pin PB4 (D25) and GND. This is the sampling capacitor for TSC group 2. | ||
| 23 | // - Connect one end of a 1K resistor to pin PB5 (D22) and leave the other end loose. This will act as a touch sensor. | ||
| 24 | // - Connect one end of another 1K resistor to pin PB6 (D71) and leave the other end loose. This will act as a touch sensor. | ||
| 25 | // | ||
| 26 | // ## Pin Configuration: | ||
| 27 | // | ||
| 28 | // The example uses pins from two TSC groups: | ||
| 29 | // | ||
| 30 | // - Group 1: | ||
| 31 | // - PB12 (D19) as sampling capacitor (TSC group 1 IO1) | ||
| 32 | // - PB13 (D18) as channel (TSC group 1 IO2) | ||
| 33 | // - Group 2: | ||
| 34 | // - PB4 (D25) as sampling capacitor (TSC group 2 IO1) | ||
| 35 | // - PB5 (D22) as channel (TSC group 2 IO2) | ||
| 36 | // - PB6 (D71) as channel (TSC group 2 IO3) | ||
| 37 | // | ||
| 38 | // The pins have been chosen for their convenient locations on the STM32L4R5ZI-P board, making it easy to add capacitors and resistors directly to the board without special connectors, breadboards, or soldering. | ||
| 39 | // | ||
| 40 | // ## Program Behavior: | ||
| 41 | // | ||
| 42 | // The program reads the designated channel pins and adjusts the LED (connected to PB14) blinking pattern based on which sensor(s) are touched: | ||
| 43 | // | ||
| 44 | // - No touch: LED off | ||
| 45 | // - One sensor touched: Slow blinking | ||
| 46 | // - Two sensors touched: Fast blinking | ||
| 47 | // - Three sensors touched: LED constantly on | ||
| 48 | // | ||
| 49 | // ## Troubleshooting: | ||
| 50 | // | ||
| 51 | // - If touch is not detected, try adjusting the SENSOR_THRESHOLD value (currently set to 20). | ||
| 52 | // - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity. | ||
| 53 | // - Be aware that for some boards there will be overlapping concerns between some pins, for | ||
| 54 | // example UART connection for the programmer to the MCU and a TSC pin. No errors or warning will | ||
| 55 | // be emitted if you try to use such a pin for TSC, but you will get strange sensor readings. | ||
| 56 | // | ||
| 57 | // Note: Configuration values and sampling capacitor values have been determined experimentally. Optimal values may vary based on your specific hardware setup. Refer to the official STM32L4R5ZI-P datasheet and user manuals for more information on pin configurations and TSC functionality. | ||
| 58 | |||
| 59 | #![no_std] | ||
| 60 | #![no_main] | ||
| 61 | |||
| 62 | use defmt::*; | ||
| 63 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 64 | use embassy_stm32::tsc::{self, *}; | ||
| 65 | use embassy_stm32::{bind_interrupts, mode, peripherals}; | ||
| 66 | use embassy_time::Timer; | ||
| 67 | use {defmt_rtt as _, panic_probe as _}; | ||
| 68 | |||
| 69 | bind_interrupts!(struct Irqs { | ||
| 70 | TSC => InterruptHandler<embassy_stm32::peripherals::TSC>; | ||
| 71 | }); | ||
| 72 | |||
| 73 | const SENSOR_THRESHOLD: u16 = 20; | ||
| 74 | |||
| 75 | async fn acquire_sensors( | ||
| 76 | touch_controller: &mut Tsc<'static, peripherals::TSC, mode::Async>, | ||
| 77 | tsc_acquisition_bank: &AcquisitionBank, | ||
| 78 | ) { | ||
| 79 | touch_controller.set_active_channels_bank(tsc_acquisition_bank); | ||
| 80 | touch_controller.start(); | ||
| 81 | touch_controller.pend_for_acquisition().await; | ||
| 82 | touch_controller.discharge_io(true); | ||
| 83 | let discharge_delay = 1; // ms | ||
| 84 | Timer::after_millis(discharge_delay).await; | ||
| 85 | } | ||
| 86 | |||
| 87 | #[embassy_executor::main] | ||
| 88 | async fn main(_spawner: embassy_executor::Spawner) { | ||
| 89 | let device_config = embassy_stm32::Config::default(); | ||
| 90 | let context = embassy_stm32::init(device_config); | ||
| 91 | |||
| 92 | // ---------- initial configuration of TSC ---------- | ||
| 93 | let mut g1: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default(); | ||
| 94 | g1.set_io1::<tsc::pin_roles::Sample>(context.PB12); | ||
| 95 | let sensor0 = g1.set_io2::<tsc::pin_roles::Channel>(context.PB13); | ||
| 96 | |||
| 97 | let mut g2: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default(); | ||
| 98 | g2.set_io1::<tsc::pin_roles::Sample>(context.PB4); | ||
| 99 | let sensor1 = g2.set_io2(context.PB5); | ||
| 100 | let sensor2 = g2.set_io3(context.PB6); | ||
| 101 | |||
| 102 | let config = tsc::Config { | ||
| 103 | ct_pulse_high_length: ChargeTransferPulseCycle::_16, | ||
| 104 | ct_pulse_low_length: ChargeTransferPulseCycle::_16, | ||
| 105 | spread_spectrum: false, | ||
| 106 | spread_spectrum_deviation: SSDeviation::new(2).unwrap(), | ||
| 107 | spread_spectrum_prescaler: false, | ||
| 108 | pulse_generator_prescaler: PGPrescalerDivider::_16, | ||
| 109 | max_count_value: MaxCount::_255, | ||
| 110 | io_default_mode: false, | ||
| 111 | synchro_pin_polarity: false, | ||
| 112 | acquisition_mode: false, | ||
| 113 | max_count_interrupt: false, | ||
| 114 | }; | ||
| 115 | |||
| 116 | let pin_groups: PinGroups<peripherals::TSC> = PinGroups { | ||
| 117 | g1: Some(g1.pin_group), | ||
| 118 | g2: Some(g2.pin_group), | ||
| 119 | ..Default::default() | ||
| 120 | }; | ||
| 121 | |||
| 122 | let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, config, Irqs).unwrap(); | ||
| 123 | |||
| 124 | // ---------- setting up acquisition banks ---------- | ||
| 125 | // sensor0 and sensor1 belong to different TSC-groups, therefore we can acquire and | ||
| 126 | // read them both in one go. | ||
| 127 | let bank1 = touch_controller.create_acquisition_bank(AcquisitionBankPins { | ||
| 128 | g1_pin: Some(sensor0), | ||
| 129 | g2_pin: Some(sensor1), | ||
| 130 | ..Default::default() | ||
| 131 | }); | ||
| 132 | // `sensor1` and `sensor2` belongs to the same TSC-group, therefore we must make sure to | ||
| 133 | // acquire them one at the time. We do this by organizing them into different acquisition banks. | ||
| 134 | let bank2 = touch_controller.create_acquisition_bank(AcquisitionBankPins { | ||
| 135 | g2_pin: Some(sensor2), | ||
| 136 | ..Default::default() | ||
| 137 | }); | ||
| 138 | |||
| 139 | // Check if TSC is ready | ||
| 140 | if touch_controller.get_state() != State::Ready { | ||
| 141 | crate::panic!("TSC not ready!"); | ||
| 142 | } | ||
| 143 | |||
| 144 | info!("TSC initialized successfully"); | ||
| 145 | |||
| 146 | let mut led = Output::new(context.PB14, Level::High, Speed::Low); | ||
| 147 | |||
| 148 | let mut led_state = false; | ||
| 149 | |||
| 150 | loop { | ||
| 151 | acquire_sensors(&mut touch_controller, &bank1).await; | ||
| 152 | let readings1 = touch_controller.get_acquisition_bank_values(&bank1); | ||
| 153 | acquire_sensors(&mut touch_controller, &bank2).await; | ||
| 154 | let readings2 = touch_controller.get_acquisition_bank_values(&bank2); | ||
| 155 | |||
| 156 | let mut touched_sensors_count = 0; | ||
| 157 | for reading in readings1.iter().chain(readings2.iter()) { | ||
| 158 | info!("{}", reading); | ||
| 159 | if reading.sensor_value < SENSOR_THRESHOLD { | ||
| 160 | touched_sensors_count += 1; | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | match touched_sensors_count { | ||
| 165 | 0 => { | ||
| 166 | // No sensors touched, turn off the LED | ||
| 167 | led.set_low(); | ||
| 168 | led_state = false; | ||
| 169 | } | ||
| 170 | 1 => { | ||
| 171 | // One sensor touched, blink slowly | ||
| 172 | led_state = !led_state; | ||
| 173 | if led_state { | ||
| 174 | led.set_high(); | ||
| 175 | } else { | ||
| 176 | led.set_low(); | ||
| 177 | } | ||
| 178 | Timer::after_millis(200).await; | ||
| 179 | } | ||
| 180 | 2 => { | ||
| 181 | // Two sensors touched, blink faster | ||
| 182 | led_state = !led_state; | ||
| 183 | if led_state { | ||
| 184 | led.set_high(); | ||
| 185 | } else { | ||
| 186 | led.set_low(); | ||
| 187 | } | ||
| 188 | Timer::after_millis(50).await; | ||
| 189 | } | ||
| 190 | 3 => { | ||
| 191 | // All three sensors touched, LED constantly on | ||
| 192 | led.set_high(); | ||
| 193 | led_state = true; | ||
| 194 | } | ||
| 195 | _ => crate::unreachable!(), // This case should never occur with 3 sensors | ||
| 196 | } | ||
| 197 | } | ||
| 198 | } | ||
diff --git a/examples/stm32l4/src/bin/usb_serial.rs b/examples/stm32l4/src/bin/usb_serial.rs index c3b1211d8..af90e297e 100644 --- a/examples/stm32l4/src/bin/usb_serial.rs +++ b/examples/stm32l4/src/bin/usb_serial.rs | |||
| @@ -62,13 +62,6 @@ async fn main(_spawner: Spawner) { | |||
| 62 | config.product = Some("USB-serial example"); | 62 | config.product = Some("USB-serial example"); |
| 63 | config.serial_number = Some("12345678"); | 63 | config.serial_number = Some("12345678"); |
| 64 | 64 | ||
| 65 | // Required for windows compatibility. | ||
| 66 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 67 | config.device_class = 0xEF; | ||
| 68 | config.device_sub_class = 0x02; | ||
| 69 | config.device_protocol = 0x01; | ||
| 70 | config.composite_with_iads = true; | ||
| 71 | |||
| 72 | // Create embassy-usb DeviceBuilder using the driver and config. | 65 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 73 | // It needs some buffers for building the descriptors. | 66 | // It needs some buffers for building the descriptors. |
| 74 | let mut config_descriptor = [0; 256]; | 67 | let mut config_descriptor = [0; 256]; |
diff --git a/examples/stm32l432/.cargo/config.toml b/examples/stm32l432/.cargo/config.toml new file mode 100644 index 000000000..0a42c584b --- /dev/null +++ b/examples/stm32l432/.cargo/config.toml | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | ||
| 2 | # replace STM32F429ZITx with your chip as listed in `probe-rs chip list` | ||
| 3 | #runner = "probe-rs run --chip STM32L475VGT6" | ||
| 4 | #runner = "probe-rs run --chip STM32L475VG" | ||
| 5 | #runner = "probe-rs run --chip STM32L4S5QI" | ||
| 6 | runner = "probe-rs run --chip STM32L432KCUx --connect-under-reset --speed 3300" | ||
| 7 | |||
| 8 | |||
| 9 | [build] | ||
| 10 | target = "thumbv7em-none-eabi" | ||
| 11 | |||
| 12 | [env] | ||
| 13 | DEFMT_LOG = "trace" | ||
diff --git a/examples/stm32l432/Cargo.toml b/examples/stm32l432/Cargo.toml new file mode 100644 index 000000000..c38462355 --- /dev/null +++ b/examples/stm32l432/Cargo.toml | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-stm32l4-examples" | ||
| 4 | version = "0.1.1" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | # Change stm32l4s5vi to your chip name, if necessary. | ||
| 9 | embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l432kc", "memory-x", "time-driver-any", "exti", "chrono"] } | ||
| 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = [ "defmt" ] } | ||
| 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = [ "arch-cortex-m", "executor-thread", "defmt" ] } | ||
| 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime", "tick-hz-32_768" ] } | ||
| 13 | defmt = "1.0.1" | ||
| 14 | defmt-rtt = "1.0.0" | ||
| 15 | |||
| 16 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | ||
| 17 | cortex-m-rt = "0.7.0" | ||
| 18 | embedded-hal = "0.2.6" | ||
| 19 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | ||
| 20 | embedded-hal-async = { version = "1.0" } | ||
| 21 | embedded-hal-bus = { version = "0.1", features = ["async"] } | ||
| 22 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } | ||
| 23 | |||
| 24 | [profile.release] | ||
| 25 | debug = 2 | ||
| 26 | |||
| 27 | [[bin]] | ||
| 28 | name = "qspi_mmap" | ||
| 29 | path = "src/bin/qspi_mmap.rs" | ||
| 30 | test = false | ||
diff --git a/examples/stm32l432/README.md b/examples/stm32l432/README.md new file mode 100644 index 000000000..3dac97f03 --- /dev/null +++ b/examples/stm32l432/README.md | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | |||
| 2 | # Examples for STM32L432 family | ||
| 3 | |||
| 4 | Examples in this repo should work with [NUCLEO-L432KC](https://www.st.com/en/evaluation-tools/nucleo-l432kc.html) board. | ||
| 5 | |||
| 6 | Run individual examples with | ||
| 7 | ``` | ||
| 8 | cargo run --bin <module-name> | ||
| 9 | ``` | ||
| 10 | for example | ||
| 11 | ``` | ||
| 12 | cargo run --bin blinky | ||
| 13 | ``` | ||
| 14 | |||
| 15 | |||
| 16 | |||
| 17 | ## Checklist before running examples | ||
| 18 | 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. | ||
| 19 | |||
| 20 | * [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L432KCU6 it should be `probe-rs run --chip STM32L432KCUx`. (use `probe-rs chip list` to find your chip) | ||
| 21 | * [ ] Update Cargo.toml to have the correct `embassy-stm32` feature. For example for L432KCU6 it should be `stm32l432kc`. Look in the `Cargo.toml` file of the `embassy-stm32` project to find the correct feature flag for your chip. | ||
| 22 | * [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately. | ||
| 23 | * [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic | ||
| 24 | |||
| 25 | If you are unsure, please drop by the Embassy Matrix chat for support, and let us know: | ||
| 26 | |||
| 27 | * Which example you are trying to run | ||
| 28 | * Which chip and board you are using | ||
| 29 | |||
| 30 | Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org | ||
diff --git a/examples/stm32l432/build.rs b/examples/stm32l432/build.rs new file mode 100644 index 000000000..8cd32d7ed --- /dev/null +++ b/examples/stm32l432/build.rs | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | fn main() { | ||
| 2 | println!("cargo:rustc-link-arg-bins=--nmagic"); | ||
| 3 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); | ||
| 4 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | ||
| 5 | } | ||
diff --git a/examples/stm32l432/src/bin/qspi_mmap.rs b/examples/stm32l432/src/bin/qspi_mmap.rs new file mode 100644 index 000000000..86a20eb3d --- /dev/null +++ b/examples/stm32l432/src/bin/qspi_mmap.rs | |||
| @@ -0,0 +1,274 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![allow(dead_code)] | ||
| 4 | /// This example demonstrates how to use the QSPI peripheral in both indirect-mode and memory-mapped mode. | ||
| 5 | /// If you want to test this example, please pay attention to flash pins and check flash device datasheet | ||
| 6 | /// to make sure operations in this example are compatible with your device, especially registers I/O operations. | ||
| 7 | use defmt::info; | ||
| 8 | use embassy_stm32::mode; | ||
| 9 | use embassy_stm32::qspi::enums::{ | ||
| 10 | AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, MemorySize, QspiWidth, | ||
| 11 | }; | ||
| 12 | use embassy_stm32::qspi::{self, Instance, TransferConfig}; | ||
| 13 | pub struct FlashMemory<I: Instance> { | ||
| 14 | qspi: qspi::Qspi<'static, I, mode::Async>, | ||
| 15 | } | ||
| 16 | use embassy_executor::Spawner; | ||
| 17 | use embassy_time::Timer; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | const MEMORY_PAGE_SIZE: usize = 256; | ||
| 21 | const CMD_READ_SR: u8 = 0x05; | ||
| 22 | const CMD_READ_CR: u8 = 0x35; | ||
| 23 | const CMD_QUAD_READ: u8 = 0x6B; | ||
| 24 | const CMD_QUAD_WRITE_PG: u8 = 0x32; | ||
| 25 | const CMD_READ_ID: u8 = 0x9F; | ||
| 26 | const CMD_READ_MID: u8 = 0x90; | ||
| 27 | const CMD_READ_UUID: u8 = 0x4B; | ||
| 28 | const CMD_ENABLE_RESET: u8 = 0x66; | ||
| 29 | const CMD_RESET: u8 = 0x99; | ||
| 30 | const CMD_WRITE_ENABLE: u8 = 0x06; | ||
| 31 | const CMD_SECTOR_ERASE: u8 = 0x20; | ||
| 32 | |||
| 33 | const CMD_WRITE_SR: u8 = 0x01; | ||
| 34 | |||
| 35 | impl<I: Instance> FlashMemory<I> { | ||
| 36 | pub fn new(qspi: qspi::Qspi<'static, I, mode::Async>) -> Self { | ||
| 37 | let mut memory = Self { qspi }; | ||
| 38 | |||
| 39 | memory.reset_memory(); | ||
| 40 | memory.enable_quad(); | ||
| 41 | |||
| 42 | memory | ||
| 43 | } | ||
| 44 | fn enable_quad(&mut self) { | ||
| 45 | let sr = self.read_sr_lsb(); | ||
| 46 | let cr = self.read_sr_msb(); | ||
| 47 | |||
| 48 | self.write_sr(sr, cr | 0x02); | ||
| 49 | } | ||
| 50 | fn read_register(&mut self, cmd: u8) -> u8 { | ||
| 51 | let mut buffer = [0; 1]; | ||
| 52 | let transaction = TransferConfig { | ||
| 53 | iwidth: QspiWidth::SING, | ||
| 54 | awidth: QspiWidth::NONE, | ||
| 55 | dwidth: QspiWidth::SING, | ||
| 56 | instruction: cmd, | ||
| 57 | address: None, | ||
| 58 | dummy: DummyCycles::_0, | ||
| 59 | }; | ||
| 60 | self.qspi.blocking_read(&mut buffer, transaction); | ||
| 61 | buffer[0] | ||
| 62 | } | ||
| 63 | |||
| 64 | fn write_register(&mut self, cmd: u8, value: u8) { | ||
| 65 | let buffer = [value; 1]; | ||
| 66 | let transaction: TransferConfig = TransferConfig { | ||
| 67 | iwidth: QspiWidth::SING, | ||
| 68 | awidth: QspiWidth::NONE, | ||
| 69 | dwidth: QspiWidth::SING, | ||
| 70 | instruction: cmd, | ||
| 71 | address: None, | ||
| 72 | dummy: DummyCycles::_0, | ||
| 73 | }; | ||
| 74 | self.qspi.blocking_write(&buffer, transaction); | ||
| 75 | } | ||
| 76 | pub fn write_sr(&mut self, lsb: u8, msb: u8) { | ||
| 77 | let buffer = [lsb, msb]; | ||
| 78 | let transaction: TransferConfig = TransferConfig { | ||
| 79 | iwidth: QspiWidth::SING, | ||
| 80 | awidth: QspiWidth::NONE, | ||
| 81 | dwidth: QspiWidth::SING, | ||
| 82 | instruction: CMD_WRITE_SR, | ||
| 83 | address: None, | ||
| 84 | dummy: DummyCycles::_0, | ||
| 85 | }; | ||
| 86 | self.qspi.blocking_write(&buffer, transaction); | ||
| 87 | } | ||
| 88 | |||
| 89 | pub fn read_sr_lsb(&mut self) -> u8 { | ||
| 90 | self.read_register(CMD_READ_SR) | ||
| 91 | } | ||
| 92 | pub fn read_sr_msb(&mut self) -> u8 { | ||
| 93 | self.read_register(CMD_READ_CR) | ||
| 94 | } | ||
| 95 | |||
| 96 | pub fn reset_memory(&mut self) { | ||
| 97 | self.exec_command(CMD_ENABLE_RESET); | ||
| 98 | self.exec_command(CMD_RESET); | ||
| 99 | self.wait_write_finish(); | ||
| 100 | } | ||
| 101 | fn exec_command(&mut self, cmd: u8) { | ||
| 102 | let transaction = TransferConfig { | ||
| 103 | iwidth: QspiWidth::SING, | ||
| 104 | awidth: QspiWidth::NONE, | ||
| 105 | dwidth: QspiWidth::NONE, | ||
| 106 | instruction: cmd, | ||
| 107 | address: None, | ||
| 108 | dummy: DummyCycles::_0, | ||
| 109 | }; | ||
| 110 | self.qspi.blocking_command(transaction); | ||
| 111 | } | ||
| 112 | fn wait_write_finish(&mut self) { | ||
| 113 | while (self.read_sr_lsb() & 0x01) != 0 {} | ||
| 114 | } | ||
| 115 | |||
| 116 | pub fn read_mid(&mut self) -> [u8; 2] { | ||
| 117 | let mut buffer = [0; 2]; | ||
| 118 | let transaction: TransferConfig = TransferConfig { | ||
| 119 | iwidth: QspiWidth::SING, | ||
| 120 | awidth: QspiWidth::SING, | ||
| 121 | dwidth: QspiWidth::SING, | ||
| 122 | instruction: CMD_READ_MID, | ||
| 123 | address: Some(0), | ||
| 124 | dummy: DummyCycles::_0, | ||
| 125 | }; | ||
| 126 | self.qspi.blocking_read(&mut buffer, transaction); | ||
| 127 | buffer | ||
| 128 | } | ||
| 129 | pub fn read_uuid(&mut self) -> [u8; 16] { | ||
| 130 | let mut buffer = [0; 16]; | ||
| 131 | let transaction: TransferConfig = TransferConfig { | ||
| 132 | iwidth: QspiWidth::SING, | ||
| 133 | awidth: QspiWidth::SING, | ||
| 134 | dwidth: QspiWidth::SING, | ||
| 135 | instruction: CMD_READ_UUID, | ||
| 136 | address: Some(0), | ||
| 137 | dummy: DummyCycles::_8, | ||
| 138 | }; | ||
| 139 | self.qspi.blocking_read(&mut buffer, transaction); | ||
| 140 | buffer | ||
| 141 | } | ||
| 142 | pub fn read_id(&mut self) -> [u8; 3] { | ||
| 143 | let mut buffer = [0; 3]; | ||
| 144 | let transaction: TransferConfig = TransferConfig { | ||
| 145 | iwidth: QspiWidth::SING, | ||
| 146 | awidth: QspiWidth::NONE, | ||
| 147 | dwidth: QspiWidth::SING, | ||
| 148 | instruction: CMD_READ_ID, | ||
| 149 | address: None, | ||
| 150 | dummy: DummyCycles::_0, | ||
| 151 | }; | ||
| 152 | self.qspi.blocking_read(&mut buffer, transaction); | ||
| 153 | buffer | ||
| 154 | } | ||
| 155 | |||
| 156 | pub fn enable_mmap(&mut self) { | ||
| 157 | let transaction: TransferConfig = TransferConfig { | ||
| 158 | iwidth: QspiWidth::SING, | ||
| 159 | awidth: QspiWidth::SING, | ||
| 160 | dwidth: QspiWidth::QUAD, | ||
| 161 | instruction: CMD_QUAD_READ, | ||
| 162 | address: Some(0), | ||
| 163 | dummy: DummyCycles::_8, | ||
| 164 | }; | ||
| 165 | self.qspi.enable_memory_map(&transaction); | ||
| 166 | } | ||
| 167 | fn perform_erase(&mut self, addr: u32, cmd: u8) { | ||
| 168 | let transaction = TransferConfig { | ||
| 169 | iwidth: QspiWidth::SING, | ||
| 170 | awidth: QspiWidth::SING, | ||
| 171 | dwidth: QspiWidth::NONE, | ||
| 172 | instruction: cmd, | ||
| 173 | address: Some(addr), | ||
| 174 | dummy: DummyCycles::_0, | ||
| 175 | }; | ||
| 176 | self.enable_write(); | ||
| 177 | self.qspi.blocking_command(transaction); | ||
| 178 | self.wait_write_finish(); | ||
| 179 | } | ||
| 180 | pub fn enable_write(&mut self) { | ||
| 181 | self.exec_command(CMD_WRITE_ENABLE); | ||
| 182 | } | ||
| 183 | pub fn erase_sector(&mut self, addr: u32) { | ||
| 184 | self.perform_erase(addr, CMD_SECTOR_ERASE); | ||
| 185 | } | ||
| 186 | fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize, use_dma: bool) { | ||
| 187 | assert!( | ||
| 188 | (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32, | ||
| 189 | "write_page(): page write length exceeds page boundary (len = {}, addr = {:X}", | ||
| 190 | len, | ||
| 191 | addr | ||
| 192 | ); | ||
| 193 | |||
| 194 | let transaction = TransferConfig { | ||
| 195 | iwidth: QspiWidth::SING, | ||
| 196 | awidth: QspiWidth::SING, | ||
| 197 | dwidth: QspiWidth::QUAD, | ||
| 198 | instruction: CMD_QUAD_WRITE_PG, | ||
| 199 | address: Some(addr), | ||
| 200 | dummy: DummyCycles::_0, | ||
| 201 | }; | ||
| 202 | self.enable_write(); | ||
| 203 | if use_dma { | ||
| 204 | self.qspi.blocking_write_dma(buffer, transaction); | ||
| 205 | } else { | ||
| 206 | self.qspi.blocking_write(buffer, transaction); | ||
| 207 | } | ||
| 208 | self.wait_write_finish(); | ||
| 209 | } | ||
| 210 | pub fn write_memory(&mut self, addr: u32, buffer: &[u8], use_dma: bool) { | ||
| 211 | let mut left = buffer.len(); | ||
| 212 | let mut place = addr; | ||
| 213 | let mut chunk_start = 0; | ||
| 214 | |||
| 215 | while left > 0 { | ||
| 216 | let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize; | ||
| 217 | let chunk_size = if left >= max_chunk_size { max_chunk_size } else { left }; | ||
| 218 | let chunk = &buffer[chunk_start..(chunk_start + chunk_size)]; | ||
| 219 | self.write_page(place, chunk, chunk_size, use_dma); | ||
| 220 | place += chunk_size as u32; | ||
| 221 | left -= chunk_size; | ||
| 222 | chunk_start += chunk_size; | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8], use_dma: bool) { | ||
| 227 | let transaction = TransferConfig { | ||
| 228 | iwidth: QspiWidth::SING, | ||
| 229 | awidth: QspiWidth::SING, | ||
| 230 | dwidth: QspiWidth::QUAD, | ||
| 231 | instruction: CMD_QUAD_READ, | ||
| 232 | address: Some(addr), | ||
| 233 | dummy: DummyCycles::_8, | ||
| 234 | }; | ||
| 235 | if use_dma { | ||
| 236 | self.qspi.blocking_read_dma(buffer, transaction); | ||
| 237 | } else { | ||
| 238 | self.qspi.blocking_read(buffer, transaction); | ||
| 239 | } | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | const MEMORY_ADDR: u32 = 0x00000000 as u32; | ||
| 244 | |||
| 245 | #[embassy_executor::main] | ||
| 246 | async fn main(_spawner: Spawner) { | ||
| 247 | let p = embassy_stm32::init(Default::default()); | ||
| 248 | |||
| 249 | let config = qspi::Config { | ||
| 250 | memory_size: MemorySize::_16MiB, | ||
| 251 | address_size: AddressSize::_24bit, | ||
| 252 | prescaler: 200, | ||
| 253 | cs_high_time: ChipSelectHighTime::_1Cycle, | ||
| 254 | fifo_threshold: FIFOThresholdLevel::_16Bytes, | ||
| 255 | }; | ||
| 256 | let driver = qspi::Qspi::new_bank1(p.QUADSPI, p.PB1, p.PB0, p.PA7, p.PA6, p.PA3, p.PA2, p.DMA2_CH7, config); | ||
| 257 | let mut flash = FlashMemory::new(driver); | ||
| 258 | let mut wr_buf = [0u8; 256]; | ||
| 259 | for i in 0..32 { | ||
| 260 | wr_buf[i] = i as u8; | ||
| 261 | } | ||
| 262 | let mut rd_buf = [0u8; 32]; | ||
| 263 | flash.erase_sector(MEMORY_ADDR); | ||
| 264 | flash.write_memory(MEMORY_ADDR, &wr_buf, false); | ||
| 265 | flash.read_memory(MEMORY_ADDR, &mut rd_buf, false); | ||
| 266 | |||
| 267 | info!("data read from indirect mode: {}", rd_buf); | ||
| 268 | flash.enable_mmap(); | ||
| 269 | let qspi_base = unsafe { core::slice::from_raw_parts(0x9000_0000 as *const u8, 32) }; | ||
| 270 | info!("data read from mmap: {}", qspi_base); | ||
| 271 | loop { | ||
| 272 | Timer::after_millis(1000).await; | ||
| 273 | } | ||
| 274 | } | ||
diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 16c184de2..3ea3bcd5c 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml | |||
| @@ -6,24 +6,23 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 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.2.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "memory-x", "low-power", "dual-bank"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 13 | embassy-usb = { version = "0.4.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.7.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 | ||
| 18 | defmt = "0.3" | 18 | defmt = "1.0.1" |
| 19 | defmt-rtt = "0.4" | 19 | defmt-rtt = "1.0.0" |
| 20 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 20 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 21 | 21 | ||
| 22 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 22 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 23 | cortex-m-rt = "0.7.0" | 23 | cortex-m-rt = "0.7.0" |
| 24 | embedded-hal = "0.2.6" | 24 | embedded-hal = "0.2.6" |
| 25 | heapless = { version = "0.8", default-features = false } | 25 | heapless = { version = "0.8", default-features = false } |
| 26 | rand_core = { version = "0.6.3", default-features = false } | ||
| 27 | embedded-io-async = { version = "0.6.1" } | 26 | embedded-io-async = { version = "0.6.1" } |
| 28 | static_cell = "2" | 27 | static_cell = "2" |
| 29 | 28 | ||
diff --git a/examples/stm32l5/src/bin/stop.rs b/examples/stm32l5/src/bin/stop.rs index 32a736de8..d7a1efea9 100644 --- a/examples/stm32l5/src/bin/stop.rs +++ b/examples/stm32l5/src/bin/stop.rs | |||
| @@ -7,7 +7,7 @@ use embassy_stm32::gpio::{AnyPin, Level, Output, Speed}; | |||
| 7 | use embassy_stm32::low_power::Executor; | 7 | use embassy_stm32::low_power::Executor; |
| 8 | use embassy_stm32::rcc::LsConfig; | 8 | use embassy_stm32::rcc::LsConfig; |
| 9 | use embassy_stm32::rtc::{Rtc, RtcConfig}; | 9 | use embassy_stm32::rtc::{Rtc, RtcConfig}; |
| 10 | use embassy_stm32::Config; | 10 | use embassy_stm32::{Config, Peri}; |
| 11 | use embassy_time::Timer; | 11 | use embassy_time::Timer; |
| 12 | use static_cell::StaticCell; | 12 | use static_cell::StaticCell; |
| 13 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -39,7 +39,7 @@ async fn async_main(spawner: Spawner) { | |||
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | #[embassy_executor::task] | 41 | #[embassy_executor::task] |
| 42 | async fn blinky(led: AnyPin) -> ! { | 42 | async fn blinky(led: Peri<'static, AnyPin>) -> ! { |
| 43 | let mut led = Output::new(led, Level::Low, Speed::Low); | 43 | let mut led = Output::new(led, Level::Low, Speed::Low); |
| 44 | loop { | 44 | loop { |
| 45 | info!("high"); | 45 | info!("high"); |
diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index d02bac91d..6c72132c6 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_net::tcp::TcpSocket; | 6 | use embassy_net::tcp::TcpSocket; |
| 7 | use embassy_net::{Stack, StackResources}; | 7 | use embassy_net::StackResources; |
| 8 | use embassy_stm32::rng::Rng; | 8 | use embassy_stm32::rng::Rng; |
| 9 | use embassy_stm32::usb::Driver; | 9 | use embassy_stm32::usb::Driver; |
| 10 | use embassy_stm32::{bind_interrupts, peripherals, rng, usb, Config}; | 10 | use embassy_stm32::{bind_interrupts, peripherals, rng, usb, Config}; |
| @@ -12,7 +12,6 @@ use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState | |||
| 12 | use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; | 12 | use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; |
| 13 | use embassy_usb::{Builder, UsbDevice}; | 13 | use embassy_usb::{Builder, UsbDevice}; |
| 14 | use embedded_io_async::Write; | 14 | use embedded_io_async::Write; |
| 15 | use rand_core::RngCore; | ||
| 16 | use static_cell::StaticCell; | 15 | use static_cell::StaticCell; |
| 17 | use {defmt_rtt as _, panic_probe as _}; | 16 | use {defmt_rtt as _, panic_probe as _}; |
| 18 | 17 | ||
| @@ -36,8 +35,8 @@ async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { | |||
| 36 | } | 35 | } |
| 37 | 36 | ||
| 38 | #[embassy_executor::task] | 37 | #[embassy_executor::task] |
| 39 | async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! { | 38 | async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static, MTU>>) -> ! { |
| 40 | stack.run().await | 39 | runner.run().await |
| 41 | } | 40 | } |
| 42 | 41 | ||
| 43 | #[embassy_executor::main] | 42 | #[embassy_executor::main] |
| @@ -72,12 +71,6 @@ async fn main(spawner: Spawner) { | |||
| 72 | config.max_power = 100; | 71 | config.max_power = 100; |
| 73 | config.max_packet_size_0 = 64; | 72 | config.max_packet_size_0 = 64; |
| 74 | 73 | ||
| 75 | // Required for Windows support. | ||
| 76 | config.composite_with_iads = true; | ||
| 77 | config.device_class = 0xEF; | ||
| 78 | config.device_sub_class = 0x02; | ||
| 79 | config.device_protocol = 0x01; | ||
| 80 | |||
| 81 | // Create embassy-usb DeviceBuilder using the driver and config. | 74 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 82 | static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new(); | 75 | static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new(); |
| 83 | static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new(); | 76 | static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new(); |
| @@ -121,11 +114,10 @@ async fn main(spawner: Spawner) { | |||
| 121 | let seed = rng.next_u64(); | 114 | let seed = rng.next_u64(); |
| 122 | 115 | ||
| 123 | // Init network stack | 116 | // Init network stack |
| 124 | static STACK: StaticCell<Stack<Device<'static, MTU>>> = StaticCell::new(); | ||
| 125 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); | 117 | static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); |
| 126 | let stack = &*STACK.init(Stack::new(device, config, RESOURCES.init(StackResources::new()), seed)); | 118 | let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); |
| 127 | 119 | ||
| 128 | unwrap!(spawner.spawn(net_task(stack))); | 120 | unwrap!(spawner.spawn(net_task(runner))); |
| 129 | 121 | ||
| 130 | // And now we can use it! | 122 | // And now we can use it! |
| 131 | 123 | ||
diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml index 2e890cdb5..3aa45dc79 100644 --- a/examples/stm32u0/Cargo.toml +++ b/examples/stm32u0/Cargo.toml | |||
| @@ -6,20 +6,20 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 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.2.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.7.0", 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.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } | 13 | embassy-usb = { version = "0.4.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" } |
| 15 | 15 | ||
| 16 | defmt = "0.3" | 16 | defmt = "1.0.1" |
| 17 | defmt-rtt = "0.4" | 17 | defmt-rtt = "1.0.0" |
| 18 | 18 | ||
| 19 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 19 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 20 | cortex-m-rt = "0.7.0" | 20 | 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 = "1.0.0", features = ["print-defmt"] } |
| 23 | heapless = { version = "0.8", default-features = false } | 23 | heapless = { version = "0.8", default-features = false } |
| 24 | 24 | ||
| 25 | micromath = "2.0.0" | 25 | micromath = "2.0.0" |
diff --git a/examples/stm32u0/src/bin/dac.rs b/examples/stm32u0/src/bin/dac.rs index fdbf1d374..50db0e082 100644 --- a/examples/stm32u0/src/bin/dac.rs +++ b/examples/stm32u0/src/bin/dac.rs | |||
| @@ -3,7 +3,6 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_stm32::dac::{DacCh1, Value}; | 5 | use embassy_stm32::dac::{DacCh1, Value}; |
| 6 | use embassy_stm32::dma::NoDma; | ||
| 7 | use {defmt_rtt as _, panic_probe as _}; | 6 | use {defmt_rtt as _, panic_probe as _}; |
| 8 | 7 | ||
| 9 | #[cortex_m_rt::entry] | 8 | #[cortex_m_rt::entry] |
| @@ -11,7 +10,7 @@ fn main() -> ! { | |||
| 11 | let p = embassy_stm32::init(Default::default()); | 10 | let p = embassy_stm32::init(Default::default()); |
| 12 | info!("Hello World!"); | 11 | info!("Hello World!"); |
| 13 | 12 | ||
| 14 | let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); | 13 | let mut dac = DacCh1::new_blocking(p.DAC1, p.PA4); |
| 15 | 14 | ||
| 16 | loop { | 15 | loop { |
| 17 | for v in 0..=255 { | 16 | for v in 0..=255 { |
diff --git a/examples/stm32u0/src/bin/spi.rs b/examples/stm32u0/src/bin/spi.rs index 5693a3765..e03591daf 100644 --- a/examples/stm32u0/src/bin/spi.rs +++ b/examples/stm32u0/src/bin/spi.rs | |||
| @@ -18,7 +18,7 @@ fn main() -> ! { | |||
| 18 | 18 | ||
| 19 | let mut spi = Spi::new_blocking(p.SPI3, p.PC10, p.PC12, p.PC11, spi_config); | 19 | let mut spi = Spi::new_blocking(p.SPI3, p.PC10, p.PC12, p.PC11, spi_config); |
| 20 | 20 | ||
| 21 | let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh); | 21 | let mut cs = Output::new(p.PC13, Level::High, Speed::VeryHigh); |
| 22 | 22 | ||
| 23 | loop { | 23 | loop { |
| 24 | let mut buf = [0x0Au8; 4]; | 24 | let mut buf = [0x0Au8; 4]; |
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..777d3ed4c 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml | |||
| @@ -5,22 +5,24 @@ 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.2.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.7.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } | 13 | embassy-usb = { version = "0.4.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" } |
| 15 | 15 | ||
| 16 | defmt = "0.3" | 16 | defmt = "1.0.1" |
| 17 | defmt-rtt = "0.4" | 17 | defmt-rtt = "1.0.0" |
| 18 | 18 | ||
| 19 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 19 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 20 | cortex-m-rt = "0.7.0" | 20 | 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 = "1.0.0", 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/adc.rs b/examples/stm32u5/src/bin/adc.rs new file mode 100644 index 000000000..d2aa28087 --- /dev/null +++ b/examples/stm32u5/src/bin/adc.rs | |||
| @@ -0,0 +1,109 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_stm32::adc; | ||
| 6 | use embassy_stm32::adc::{adc4, AdcChannel}; | ||
| 7 | use {defmt_rtt as _, panic_probe as _}; | ||
| 8 | |||
| 9 | #[embassy_executor::main] | ||
| 10 | async fn main(_spawner: embassy_executor::Spawner) { | ||
| 11 | let config = embassy_stm32::Config::default(); | ||
| 12 | |||
| 13 | let mut p = embassy_stm32::init(config); | ||
| 14 | |||
| 15 | // **** ADC1 init **** | ||
| 16 | let mut adc1 = adc::Adc::new(p.ADC1); | ||
| 17 | let mut adc1_pin1 = p.PA3; // A0 on nucleo u5a5 | ||
| 18 | let mut adc1_pin2 = p.PA2; // A1 | ||
| 19 | adc1.set_resolution(adc::Resolution::BITS14); | ||
| 20 | adc1.set_averaging(adc::Averaging::Samples1024); | ||
| 21 | adc1.set_sample_time(adc::SampleTime::CYCLES160_5); | ||
| 22 | let max1 = adc::resolution_to_max_count(adc::Resolution::BITS14); | ||
| 23 | |||
| 24 | // **** ADC2 init **** | ||
| 25 | let mut adc2 = adc::Adc::new(p.ADC2); | ||
| 26 | let mut adc2_pin1 = p.PC3; // A2 | ||
| 27 | let mut adc2_pin2 = p.PB0; // A3 | ||
| 28 | adc2.set_resolution(adc::Resolution::BITS14); | ||
| 29 | adc2.set_averaging(adc::Averaging::Samples1024); | ||
| 30 | adc2.set_sample_time(adc::SampleTime::CYCLES160_5); | ||
| 31 | let max2 = adc::resolution_to_max_count(adc::Resolution::BITS14); | ||
| 32 | |||
| 33 | // **** ADC4 init **** | ||
| 34 | let mut adc4 = adc4::Adc4::new(p.ADC4); | ||
| 35 | let mut adc4_pin1 = p.PC1; // A4 | ||
| 36 | let mut adc4_pin2 = p.PC0; // A5 | ||
| 37 | adc4.set_resolution(adc4::Resolution::BITS12); | ||
| 38 | adc4.set_averaging(adc4::Averaging::Samples256); | ||
| 39 | adc4.set_sample_time(adc4::SampleTime::CYCLES1_5); | ||
| 40 | let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12); | ||
| 41 | |||
| 42 | // **** ADC1 blocking read **** | ||
| 43 | let raw: u16 = adc1.blocking_read(&mut adc1_pin1); | ||
| 44 | let volt: f32 = 3.3 * raw as f32 / max1 as f32; | ||
| 45 | info!("Read adc1 pin 1 {}", volt); | ||
| 46 | |||
| 47 | let raw: u16 = adc1.blocking_read(&mut adc1_pin2); | ||
| 48 | let volt: f32 = 3.3 * raw as f32 / max1 as f32; | ||
| 49 | info!("Read adc1 pin 2 {}", volt); | ||
| 50 | |||
| 51 | // **** ADC2 blocking read **** | ||
| 52 | let raw: u16 = adc2.blocking_read(&mut adc2_pin1); | ||
| 53 | let volt: f32 = 3.3 * raw as f32 / max2 as f32; | ||
| 54 | info!("Read adc2 pin 1 {}", volt); | ||
| 55 | |||
| 56 | let raw: u16 = adc2.blocking_read(&mut adc2_pin2); | ||
| 57 | let volt: f32 = 3.3 * raw as f32 / max2 as f32; | ||
| 58 | info!("Read adc2 pin 2 {}", volt); | ||
| 59 | |||
| 60 | // **** ADC4 blocking read **** | ||
| 61 | let raw: u16 = adc4.blocking_read(&mut adc4_pin1); | ||
| 62 | let volt: f32 = 3.3 * raw as f32 / max4 as f32; | ||
| 63 | info!("Read adc4 pin 1 {}", volt); | ||
| 64 | |||
| 65 | let raw: u16 = adc4.blocking_read(&mut adc4_pin2); | ||
| 66 | let volt: f32 = 3.3 * raw as f32 / max4 as f32; | ||
| 67 | info!("Read adc4 pin 2 {}", volt); | ||
| 68 | |||
| 69 | // **** ADC1 async read **** | ||
| 70 | let mut degraded11 = adc1_pin1.degrade_adc(); | ||
| 71 | let mut degraded12 = adc1_pin2.degrade_adc(); | ||
| 72 | let mut measurements = [0u16; 2]; | ||
| 73 | |||
| 74 | adc1.read( | ||
| 75 | p.GPDMA1_CH0.reborrow(), | ||
| 76 | [ | ||
| 77 | (&mut degraded11, adc::SampleTime::CYCLES160_5), | ||
| 78 | (&mut degraded12, adc::SampleTime::CYCLES160_5), | ||
| 79 | ] | ||
| 80 | .into_iter(), | ||
| 81 | &mut measurements, | ||
| 82 | ) | ||
| 83 | .await; | ||
| 84 | let volt1: f32 = 3.3 * measurements[0] as f32 / max1 as f32; | ||
| 85 | let volt2: f32 = 3.3 * measurements[1] as f32 / max1 as f32; | ||
| 86 | |||
| 87 | info!("Async read 1 pin 1 {}", volt1); | ||
| 88 | info!("Async read 1 pin 2 {}", volt2); | ||
| 89 | |||
| 90 | // **** ADC2 does not support async read **** | ||
| 91 | |||
| 92 | // **** ADC4 async read **** | ||
| 93 | let mut degraded41 = adc4_pin1.degrade_adc(); | ||
| 94 | let mut degraded42 = adc4_pin2.degrade_adc(); | ||
| 95 | let mut measurements = [0u16; 2]; | ||
| 96 | |||
| 97 | // The channels must be in ascending order and can't repeat for ADC4 | ||
| 98 | adc4.read( | ||
| 99 | p.GPDMA1_CH1.reborrow(), | ||
| 100 | [&mut degraded42, &mut degraded41].into_iter(), | ||
| 101 | &mut measurements, | ||
| 102 | ) | ||
| 103 | .await | ||
| 104 | .unwrap(); | ||
| 105 | let volt2: f32 = 3.3 * measurements[0] as f32 / max4 as f32; | ||
| 106 | let volt1: f32 = 3.3 * measurements[1] as f32 / max4 as f32; | ||
| 107 | info!("Async read 4 pin 1 {}", volt1); | ||
| 108 | info!("Async read 4 pin 2 {}", volt2); | ||
| 109 | } | ||
diff --git a/examples/stm32u5/src/bin/blinky.rs b/examples/stm32u5/src/bin/blinky.rs index 7fe88c183..1fdfc7679 100644 --- a/examples/stm32u5/src/bin/blinky.rs +++ b/examples/stm32u5/src/bin/blinky.rs | |||
| @@ -12,7 +12,8 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 12 | let p = embassy_stm32::init(Default::default()); | 12 | let p = embassy_stm32::init(Default::default()); |
| 13 | info!("Hello World!"); | 13 | info!("Hello World!"); |
| 14 | 14 | ||
| 15 | let mut led = Output::new(p.PH7, Level::Low, Speed::Medium); | 15 | // replace PC13 with the right pin for your board. |
| 16 | let mut led = Output::new(p.PC13, Level::Low, Speed::Medium); | ||
| 16 | 17 | ||
| 17 | loop { | 18 | loop { |
| 18 | defmt::info!("on!"); | 19 | defmt::info!("on!"); |
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/hspi_memory_mapped.rs b/examples/stm32u5/src/bin/hspi_memory_mapped.rs new file mode 100644 index 000000000..9fef4855e --- /dev/null +++ b/examples/stm32u5/src/bin/hspi_memory_mapped.rs | |||
| @@ -0,0 +1,455 @@ | |||
| 1 | #![no_main] | ||
| 2 | #![no_std] | ||
| 3 | |||
| 4 | // Tested on an STM32U5G9J-DK2 demo board using the on-board MX66LM1G45G flash memory | ||
| 5 | // The flash is connected to the HSPI1 port as an OCTA-DTR device | ||
| 6 | // | ||
| 7 | // Use embassy-stm32 feature "stm32u5g9zj" and probe-rs chip "STM32U5G9ZJTxQ" | ||
| 8 | |||
| 9 | use defmt::info; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_stm32::hspi::{ | ||
| 12 | AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, Hspi, HspiWidth, Instance, MemorySize, | ||
| 13 | MemoryType, TransferConfig, WrapSize, | ||
| 14 | }; | ||
| 15 | use embassy_stm32::mode::Async; | ||
| 16 | use embassy_stm32::rcc; | ||
| 17 | use embassy_stm32::time::Hertz; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | #[embassy_executor::main] | ||
| 21 | async fn main(_spawner: Spawner) { | ||
| 22 | info!("Start hspi_memory_mapped"); | ||
| 23 | |||
| 24 | // RCC config | ||
| 25 | let mut config = embassy_stm32::Config::default(); | ||
| 26 | config.rcc.hse = Some(rcc::Hse { | ||
| 27 | freq: Hertz(16_000_000), | ||
| 28 | mode: rcc::HseMode::Oscillator, | ||
| 29 | }); | ||
| 30 | config.rcc.pll1 = Some(rcc::Pll { | ||
| 31 | source: rcc::PllSource::HSE, | ||
| 32 | prediv: rcc::PllPreDiv::DIV1, | ||
| 33 | mul: rcc::PllMul::MUL10, | ||
| 34 | divp: None, | ||
| 35 | divq: None, | ||
| 36 | divr: Some(rcc::PllDiv::DIV1), | ||
| 37 | }); | ||
| 38 | config.rcc.sys = rcc::Sysclk::PLL1_R; // 160 Mhz | ||
| 39 | config.rcc.pll2 = Some(rcc::Pll { | ||
| 40 | source: rcc::PllSource::HSE, | ||
| 41 | prediv: rcc::PllPreDiv::DIV4, | ||
| 42 | mul: rcc::PllMul::MUL66, | ||
| 43 | divp: None, | ||
| 44 | divq: Some(rcc::PllDiv::DIV2), | ||
| 45 | divr: None, | ||
| 46 | }); | ||
| 47 | config.rcc.mux.hspi1sel = rcc::mux::Hspisel::PLL2_Q; // 132 MHz | ||
| 48 | |||
| 49 | // Initialize peripherals | ||
| 50 | let p = embassy_stm32::init(config); | ||
| 51 | |||
| 52 | let flash_config = embassy_stm32::hspi::Config { | ||
| 53 | fifo_threshold: FIFOThresholdLevel::_4Bytes, | ||
| 54 | memory_type: MemoryType::Macronix, | ||
| 55 | device_size: MemorySize::_1GiB, | ||
| 56 | chip_select_high_time: ChipSelectHighTime::_2Cycle, | ||
| 57 | free_running_clock: false, | ||
| 58 | clock_mode: false, | ||
| 59 | wrap_size: WrapSize::None, | ||
| 60 | clock_prescaler: 0, | ||
| 61 | sample_shifting: false, | ||
| 62 | delay_hold_quarter_cycle: false, | ||
| 63 | chip_select_boundary: 0, | ||
| 64 | delay_block_bypass: false, | ||
| 65 | max_transfer: 0, | ||
| 66 | refresh: 0, | ||
| 67 | }; | ||
| 68 | |||
| 69 | let use_dma = true; | ||
| 70 | |||
| 71 | info!("Testing flash in OCTA DTR mode and memory mapped mode"); | ||
| 72 | |||
| 73 | let hspi = Hspi::new_octospi( | ||
| 74 | p.HSPI1, | ||
| 75 | p.PI3, | ||
| 76 | p.PH10, | ||
| 77 | p.PH11, | ||
| 78 | p.PH12, | ||
| 79 | p.PH13, | ||
| 80 | p.PH14, | ||
| 81 | p.PH15, | ||
| 82 | p.PI0, | ||
| 83 | p.PI1, | ||
| 84 | p.PH9, | ||
| 85 | p.PI2, | ||
| 86 | p.GPDMA1_CH7, | ||
| 87 | flash_config, | ||
| 88 | ); | ||
| 89 | |||
| 90 | let mut flash = OctaDtrFlashMemory::new(hspi).await; | ||
| 91 | |||
| 92 | let flash_id = flash.read_id(); | ||
| 93 | info!("FLASH ID: {=[u8]:x}", flash_id); | ||
| 94 | |||
| 95 | let mut rd_buf = [0u8; 16]; | ||
| 96 | flash.read_memory(0, &mut rd_buf, use_dma).await; | ||
| 97 | info!("READ BUF: {=[u8]:#X}", rd_buf); | ||
| 98 | |||
| 99 | flash.erase_sector(0).await; | ||
| 100 | flash.read_memory(0, &mut rd_buf, use_dma).await; | ||
| 101 | info!("READ BUF: {=[u8]:#X}", rd_buf); | ||
| 102 | assert_eq!(rd_buf[0], 0xFF); | ||
| 103 | assert_eq!(rd_buf[15], 0xFF); | ||
| 104 | |||
| 105 | let mut wr_buf = [0u8; 16]; | ||
| 106 | for i in 0..wr_buf.len() { | ||
| 107 | wr_buf[i] = i as u8; | ||
| 108 | } | ||
| 109 | info!("WRITE BUF: {=[u8]:#X}", wr_buf); | ||
| 110 | flash.write_memory(0, &wr_buf, use_dma).await; | ||
| 111 | flash.read_memory(0, &mut rd_buf, use_dma).await; | ||
| 112 | info!("READ BUF: {=[u8]:#X}", rd_buf); | ||
| 113 | assert_eq!(rd_buf[0], 0x00); | ||
| 114 | assert_eq!(rd_buf[15], 0x0F); | ||
| 115 | |||
| 116 | flash.enable_mm().await; | ||
| 117 | info!("Enabled memory mapped mode"); | ||
| 118 | |||
| 119 | let first_u32 = unsafe { *(0xA0000000 as *const u32) }; | ||
| 120 | info!("first_u32: 0x{=u32:X}", first_u32); | ||
| 121 | assert_eq!(first_u32, 0x03020100); | ||
| 122 | |||
| 123 | let second_u32 = unsafe { *(0xA0000004 as *const u32) }; | ||
| 124 | assert_eq!(second_u32, 0x07060504); | ||
| 125 | info!("second_u32: 0x{=u32:X}", second_u32); | ||
| 126 | |||
| 127 | let first_u8 = unsafe { *(0xA0000000 as *const u8) }; | ||
| 128 | assert_eq!(first_u8, 00); | ||
| 129 | info!("first_u8: 0x{=u8:X}", first_u8); | ||
| 130 | |||
| 131 | let second_u8 = unsafe { *(0xA0000001 as *const u8) }; | ||
| 132 | assert_eq!(second_u8, 0x01); | ||
| 133 | info!("second_u8: 0x{=u8:X}", second_u8); | ||
| 134 | |||
| 135 | let third_u8 = unsafe { *(0xA0000002 as *const u8) }; | ||
| 136 | assert_eq!(third_u8, 0x02); | ||
| 137 | info!("third_u8: 0x{=u8:X}", third_u8); | ||
| 138 | |||
| 139 | let fourth_u8 = unsafe { *(0xA0000003 as *const u8) }; | ||
| 140 | assert_eq!(fourth_u8, 0x03); | ||
| 141 | info!("fourth_u8: 0x{=u8:X}", fourth_u8); | ||
| 142 | |||
| 143 | info!("DONE"); | ||
| 144 | } | ||
| 145 | |||
| 146 | // Custom implementation for MX66UW1G45G NOR flash memory from Macronix. | ||
| 147 | // Chip commands are hardcoded as they depend on the chip used. | ||
| 148 | // This implementation enables Octa I/O (OPI) and Double Transfer Rate (DTR) | ||
| 149 | |||
| 150 | pub struct OctaDtrFlashMemory<'d, I: Instance> { | ||
| 151 | hspi: Hspi<'d, I, Async>, | ||
| 152 | } | ||
| 153 | |||
| 154 | impl<'d, I: Instance> OctaDtrFlashMemory<'d, I> { | ||
| 155 | const MEMORY_PAGE_SIZE: usize = 256; | ||
| 156 | |||
| 157 | const CMD_READ_OCTA_DTR: u16 = 0xEE11; | ||
| 158 | const CMD_PAGE_PROGRAM_OCTA_DTR: u16 = 0x12ED; | ||
| 159 | |||
| 160 | const CMD_READ_ID_OCTA_DTR: u16 = 0x9F60; | ||
| 161 | |||
| 162 | const CMD_RESET_ENABLE: u8 = 0x66; | ||
| 163 | const CMD_RESET_ENABLE_OCTA_DTR: u16 = 0x6699; | ||
| 164 | const CMD_RESET: u8 = 0x99; | ||
| 165 | const CMD_RESET_OCTA_DTR: u16 = 0x9966; | ||
| 166 | |||
| 167 | const CMD_WRITE_ENABLE: u8 = 0x06; | ||
| 168 | const CMD_WRITE_ENABLE_OCTA_DTR: u16 = 0x06F9; | ||
| 169 | |||
| 170 | const CMD_SECTOR_ERASE_OCTA_DTR: u16 = 0x21DE; | ||
| 171 | const CMD_BLOCK_ERASE_OCTA_DTR: u16 = 0xDC23; | ||
| 172 | |||
| 173 | const CMD_READ_SR: u8 = 0x05; | ||
| 174 | const CMD_READ_SR_OCTA_DTR: u16 = 0x05FA; | ||
| 175 | |||
| 176 | const CMD_READ_CR2: u8 = 0x71; | ||
| 177 | const CMD_WRITE_CR2: u8 = 0x72; | ||
| 178 | |||
| 179 | const CR2_REG1_ADDR: u32 = 0x00000000; | ||
| 180 | const CR2_OCTA_DTR: u8 = 0x02; | ||
| 181 | |||
| 182 | const CR2_REG3_ADDR: u32 = 0x00000300; | ||
| 183 | const CR2_DC_6_CYCLES: u8 = 0x07; | ||
| 184 | |||
| 185 | pub async fn new(hspi: Hspi<'d, I, Async>) -> Self { | ||
| 186 | let mut memory = Self { hspi }; | ||
| 187 | |||
| 188 | memory.reset_memory().await; | ||
| 189 | memory.enable_octa_dtr().await; | ||
| 190 | memory | ||
| 191 | } | ||
| 192 | |||
| 193 | async fn enable_octa_dtr(&mut self) { | ||
| 194 | self.write_enable_spi().await; | ||
| 195 | self.write_cr2_spi(Self::CR2_REG3_ADDR, Self::CR2_DC_6_CYCLES); | ||
| 196 | self.write_enable_spi().await; | ||
| 197 | self.write_cr2_spi(Self::CR2_REG1_ADDR, Self::CR2_OCTA_DTR); | ||
| 198 | } | ||
| 199 | |||
| 200 | pub async fn enable_mm(&mut self) { | ||
| 201 | let read_config = TransferConfig { | ||
| 202 | iwidth: HspiWidth::OCTO, | ||
| 203 | instruction: Some(Self::CMD_READ_OCTA_DTR as u32), | ||
| 204 | isize: AddressSize::_16Bit, | ||
| 205 | idtr: true, | ||
| 206 | adwidth: HspiWidth::OCTO, | ||
| 207 | adsize: AddressSize::_32Bit, | ||
| 208 | addtr: true, | ||
| 209 | dwidth: HspiWidth::OCTO, | ||
| 210 | ddtr: true, | ||
| 211 | dummy: DummyCycles::_6, | ||
| 212 | ..Default::default() | ||
| 213 | }; | ||
| 214 | |||
| 215 | let write_config = TransferConfig { | ||
| 216 | iwidth: HspiWidth::OCTO, | ||
| 217 | isize: AddressSize::_16Bit, | ||
| 218 | idtr: true, | ||
| 219 | adwidth: HspiWidth::OCTO, | ||
| 220 | adsize: AddressSize::_32Bit, | ||
| 221 | addtr: true, | ||
| 222 | dwidth: HspiWidth::OCTO, | ||
| 223 | ddtr: true, | ||
| 224 | ..Default::default() | ||
| 225 | }; | ||
| 226 | self.hspi.enable_memory_mapped_mode(read_config, write_config).unwrap(); | ||
| 227 | } | ||
| 228 | |||
| 229 | async fn exec_command_spi(&mut self, cmd: u8) { | ||
| 230 | let transaction = TransferConfig { | ||
| 231 | iwidth: HspiWidth::SING, | ||
| 232 | instruction: Some(cmd as u32), | ||
| 233 | ..Default::default() | ||
| 234 | }; | ||
| 235 | info!("Excuting command: 0x{:X}", transaction.instruction.unwrap()); | ||
| 236 | self.hspi.blocking_command(&transaction).unwrap(); | ||
| 237 | } | ||
| 238 | |||
| 239 | async fn exec_command_octa_dtr(&mut self, cmd: u16) { | ||
| 240 | let transaction = TransferConfig { | ||
| 241 | iwidth: HspiWidth::OCTO, | ||
| 242 | instruction: Some(cmd as u32), | ||
| 243 | isize: AddressSize::_16Bit, | ||
| 244 | idtr: true, | ||
| 245 | ..Default::default() | ||
| 246 | }; | ||
| 247 | info!("Excuting command: 0x{:X}", transaction.instruction.unwrap()); | ||
| 248 | self.hspi.blocking_command(&transaction).unwrap(); | ||
| 249 | } | ||
| 250 | |||
| 251 | fn wait_write_finish_spi(&mut self) { | ||
| 252 | while (self.read_sr_spi() & 0x01) != 0 {} | ||
| 253 | } | ||
| 254 | |||
| 255 | fn wait_write_finish_octa_dtr(&mut self) { | ||
| 256 | while (self.read_sr_octa_dtr() & 0x01) != 0 {} | ||
| 257 | } | ||
| 258 | |||
| 259 | pub async fn reset_memory(&mut self) { | ||
| 260 | // servono entrambi i comandi? | ||
| 261 | self.exec_command_octa_dtr(Self::CMD_RESET_ENABLE_OCTA_DTR).await; | ||
| 262 | self.exec_command_octa_dtr(Self::CMD_RESET_OCTA_DTR).await; | ||
| 263 | self.exec_command_spi(Self::CMD_RESET_ENABLE).await; | ||
| 264 | self.exec_command_spi(Self::CMD_RESET).await; | ||
| 265 | self.wait_write_finish_spi(); | ||
| 266 | } | ||
| 267 | |||
| 268 | async fn write_enable_spi(&mut self) { | ||
| 269 | self.exec_command_spi(Self::CMD_WRITE_ENABLE).await; | ||
| 270 | } | ||
| 271 | |||
| 272 | async fn write_enable_octa_dtr(&mut self) { | ||
| 273 | self.exec_command_octa_dtr(Self::CMD_WRITE_ENABLE_OCTA_DTR).await; | ||
| 274 | } | ||
| 275 | |||
| 276 | pub fn read_id(&mut self) -> [u8; 3] { | ||
| 277 | let mut buffer = [0; 6]; | ||
| 278 | let transaction: TransferConfig = TransferConfig { | ||
| 279 | iwidth: HspiWidth::OCTO, | ||
| 280 | instruction: Some(Self::CMD_READ_ID_OCTA_DTR as u32), | ||
| 281 | isize: AddressSize::_16Bit, | ||
| 282 | idtr: true, | ||
| 283 | adwidth: HspiWidth::OCTO, | ||
| 284 | address: Some(0), | ||
| 285 | adsize: AddressSize::_32Bit, | ||
| 286 | addtr: true, | ||
| 287 | dwidth: HspiWidth::OCTO, | ||
| 288 | ddtr: true, | ||
| 289 | dummy: DummyCycles::_5, | ||
| 290 | ..Default::default() | ||
| 291 | }; | ||
| 292 | info!("Reading flash id: 0x{:X}", transaction.instruction.unwrap()); | ||
| 293 | self.hspi.blocking_read(&mut buffer, transaction).unwrap(); | ||
| 294 | [buffer[0], buffer[2], buffer[4]] | ||
| 295 | } | ||
| 296 | |||
| 297 | pub async fn read_memory(&mut self, addr: u32, buffer: &mut [u8], use_dma: bool) { | ||
| 298 | let transaction = TransferConfig { | ||
| 299 | iwidth: HspiWidth::OCTO, | ||
| 300 | instruction: Some(Self::CMD_READ_OCTA_DTR as u32), | ||
| 301 | isize: AddressSize::_16Bit, | ||
| 302 | idtr: true, | ||
| 303 | adwidth: HspiWidth::OCTO, | ||
| 304 | address: Some(addr), | ||
| 305 | adsize: AddressSize::_32Bit, | ||
| 306 | addtr: true, | ||
| 307 | dwidth: HspiWidth::OCTO, | ||
| 308 | ddtr: true, | ||
| 309 | dummy: DummyCycles::_6, | ||
| 310 | ..Default::default() | ||
| 311 | }; | ||
| 312 | if use_dma { | ||
| 313 | self.hspi.read(buffer, transaction).await.unwrap(); | ||
| 314 | } else { | ||
| 315 | self.hspi.blocking_read(buffer, transaction).unwrap(); | ||
| 316 | } | ||
| 317 | } | ||
| 318 | |||
| 319 | async fn perform_erase_octa_dtr(&mut self, addr: u32, cmd: u16) { | ||
| 320 | let transaction = TransferConfig { | ||
| 321 | iwidth: HspiWidth::OCTO, | ||
| 322 | instruction: Some(cmd as u32), | ||
| 323 | isize: AddressSize::_16Bit, | ||
| 324 | idtr: true, | ||
| 325 | adwidth: HspiWidth::OCTO, | ||
| 326 | address: Some(addr), | ||
| 327 | adsize: AddressSize::_32Bit, | ||
| 328 | addtr: true, | ||
| 329 | ..Default::default() | ||
| 330 | }; | ||
| 331 | self.write_enable_octa_dtr().await; | ||
| 332 | self.hspi.blocking_command(&transaction).unwrap(); | ||
| 333 | self.wait_write_finish_octa_dtr(); | ||
| 334 | } | ||
| 335 | |||
| 336 | pub async fn erase_sector(&mut self, addr: u32) { | ||
| 337 | info!("Erasing 4K sector at address: 0x{:X}", addr); | ||
| 338 | self.perform_erase_octa_dtr(addr, Self::CMD_SECTOR_ERASE_OCTA_DTR).await; | ||
| 339 | } | ||
| 340 | |||
| 341 | pub async fn erase_block(&mut self, addr: u32) { | ||
| 342 | info!("Erasing 64K block at address: 0x{:X}", addr); | ||
| 343 | self.perform_erase_octa_dtr(addr, Self::CMD_BLOCK_ERASE_OCTA_DTR).await; | ||
| 344 | } | ||
| 345 | |||
| 346 | async fn write_page_octa_dtr(&mut self, addr: u32, buffer: &[u8], len: usize, use_dma: bool) { | ||
| 347 | assert!( | ||
| 348 | (len as u32 + (addr & 0x000000ff)) <= Self::MEMORY_PAGE_SIZE as u32, | ||
| 349 | "write_page(): page write length exceeds page boundary (len = {}, addr = {:X}", | ||
| 350 | len, | ||
| 351 | addr | ||
| 352 | ); | ||
| 353 | |||
| 354 | let transaction = TransferConfig { | ||
| 355 | iwidth: HspiWidth::OCTO, | ||
| 356 | instruction: Some(Self::CMD_PAGE_PROGRAM_OCTA_DTR as u32), | ||
| 357 | isize: AddressSize::_16Bit, | ||
| 358 | idtr: true, | ||
| 359 | adwidth: HspiWidth::OCTO, | ||
| 360 | address: Some(addr), | ||
| 361 | adsize: AddressSize::_32Bit, | ||
| 362 | addtr: true, | ||
| 363 | dwidth: HspiWidth::OCTO, | ||
| 364 | ddtr: true, | ||
| 365 | ..Default::default() | ||
| 366 | }; | ||
| 367 | self.write_enable_octa_dtr().await; | ||
| 368 | if use_dma { | ||
| 369 | self.hspi.write(buffer, transaction).await.unwrap(); | ||
| 370 | } else { | ||
| 371 | self.hspi.blocking_write(buffer, transaction).unwrap(); | ||
| 372 | } | ||
| 373 | self.wait_write_finish_octa_dtr(); | ||
| 374 | } | ||
| 375 | |||
| 376 | pub async fn write_memory(&mut self, addr: u32, buffer: &[u8], use_dma: bool) { | ||
| 377 | let mut left = buffer.len(); | ||
| 378 | let mut place = addr; | ||
| 379 | let mut chunk_start = 0; | ||
| 380 | |||
| 381 | while left > 0 { | ||
| 382 | let max_chunk_size = Self::MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize; | ||
| 383 | let chunk_size = if left >= max_chunk_size { max_chunk_size } else { left }; | ||
| 384 | let chunk = &buffer[chunk_start..(chunk_start + chunk_size)]; | ||
| 385 | self.write_page_octa_dtr(place, chunk, chunk_size, use_dma).await; | ||
| 386 | place += chunk_size as u32; | ||
| 387 | left -= chunk_size; | ||
| 388 | chunk_start += chunk_size; | ||
| 389 | } | ||
| 390 | } | ||
| 391 | |||
| 392 | pub fn read_sr_spi(&mut self) -> u8 { | ||
| 393 | let mut buffer = [0; 1]; | ||
| 394 | let transaction: TransferConfig = TransferConfig { | ||
| 395 | iwidth: HspiWidth::SING, | ||
| 396 | instruction: Some(Self::CMD_READ_SR as u32), | ||
| 397 | dwidth: HspiWidth::SING, | ||
| 398 | ..Default::default() | ||
| 399 | }; | ||
| 400 | self.hspi.blocking_read(&mut buffer, transaction).unwrap(); | ||
| 401 | // info!("Read MX66LM1G45G SR register: 0x{:x}", buffer[0]); | ||
| 402 | buffer[0] | ||
| 403 | } | ||
| 404 | |||
| 405 | pub fn read_sr_octa_dtr(&mut self) -> u8 { | ||
| 406 | let mut buffer = [0; 2]; | ||
| 407 | let transaction: TransferConfig = TransferConfig { | ||
| 408 | iwidth: HspiWidth::OCTO, | ||
| 409 | instruction: Some(Self::CMD_READ_SR_OCTA_DTR as u32), | ||
| 410 | isize: AddressSize::_16Bit, | ||
| 411 | idtr: true, | ||
| 412 | adwidth: HspiWidth::OCTO, | ||
| 413 | address: Some(0), | ||
| 414 | adsize: AddressSize::_32Bit, | ||
| 415 | addtr: true, | ||
| 416 | dwidth: HspiWidth::OCTO, | ||
| 417 | ddtr: true, | ||
| 418 | dummy: DummyCycles::_5, | ||
| 419 | ..Default::default() | ||
| 420 | }; | ||
| 421 | self.hspi.blocking_read(&mut buffer, transaction).unwrap(); | ||
| 422 | // info!("Read MX66LM1G45G SR register: 0x{:x}", buffer[0]); | ||
| 423 | buffer[0] | ||
| 424 | } | ||
| 425 | |||
| 426 | pub fn read_cr2_spi(&mut self, addr: u32) -> u8 { | ||
| 427 | let mut buffer = [0; 1]; | ||
| 428 | let transaction: TransferConfig = TransferConfig { | ||
| 429 | iwidth: HspiWidth::SING, | ||
| 430 | instruction: Some(Self::CMD_READ_CR2 as u32), | ||
| 431 | adwidth: HspiWidth::SING, | ||
| 432 | address: Some(addr), | ||
| 433 | adsize: AddressSize::_32Bit, | ||
| 434 | dwidth: HspiWidth::SING, | ||
| 435 | ..Default::default() | ||
| 436 | }; | ||
| 437 | self.hspi.blocking_read(&mut buffer, transaction).unwrap(); | ||
| 438 | // info!("Read MX66LM1G45G CR2[0x{:X}] register: 0x{:x}", addr, buffer[0]); | ||
| 439 | buffer[0] | ||
| 440 | } | ||
| 441 | |||
| 442 | pub fn write_cr2_spi(&mut self, addr: u32, value: u8) { | ||
| 443 | let buffer = [value; 1]; | ||
| 444 | let transaction: TransferConfig = TransferConfig { | ||
| 445 | iwidth: HspiWidth::SING, | ||
| 446 | instruction: Some(Self::CMD_WRITE_CR2 as u32), | ||
| 447 | adwidth: HspiWidth::SING, | ||
| 448 | address: Some(addr), | ||
| 449 | adsize: AddressSize::_32Bit, | ||
| 450 | dwidth: HspiWidth::SING, | ||
| 451 | ..Default::default() | ||
| 452 | }; | ||
| 453 | self.hspi.blocking_write(&buffer, transaction).unwrap(); | ||
| 454 | } | ||
| 455 | } | ||
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..d37e7777b --- /dev/null +++ b/examples/stm32u5/src/bin/usb_hs_serial.rs | |||
| @@ -0,0 +1,122 @@ | |||
| 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 | // 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 mut 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 | // Do stuff with the class! | ||
| 89 | let echo_fut = async { | ||
| 90 | loop { | ||
| 91 | class.wait_connection().await; | ||
| 92 | info!("Connected"); | ||
| 93 | let _ = echo(&mut class).await; | ||
| 94 | info!("Disconnected"); | ||
| 95 | } | ||
| 96 | }; | ||
| 97 | |||
| 98 | // Run everything concurrently. | ||
| 99 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 100 | join(usb_fut, echo_fut).await; | ||
| 101 | } | ||
| 102 | |||
| 103 | struct Disconnected {} | ||
| 104 | |||
| 105 | impl From<EndpointError> for Disconnected { | ||
| 106 | fn from(val: EndpointError) -> Self { | ||
| 107 | match val { | ||
| 108 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 109 | EndpointError::Disabled => Disconnected {}, | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
| 115 | let mut buf = [0; 64]; | ||
| 116 | loop { | ||
| 117 | let n = class.read_packet(&mut buf).await?; | ||
| 118 | let data = &buf[..n]; | ||
| 119 | info!("data: {:x}", data); | ||
| 120 | class.write_packet(data).await?; | ||
| 121 | } | ||
| 122 | } | ||
diff --git a/examples/stm32u5/src/bin/usb_serial.rs b/examples/stm32u5/src/bin/usb_serial.rs index 4d56395da..ff7f4e5be 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); |
| @@ -56,13 +56,6 @@ async fn main(_spawner: Spawner) { | |||
| 56 | config.product = Some("USB-serial example"); | 56 | config.product = Some("USB-serial example"); |
| 57 | config.serial_number = Some("12345678"); | 57 | config.serial_number = Some("12345678"); |
| 58 | 58 | ||
| 59 | // Required for windows compatibility. | ||
| 60 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 61 | config.device_class = 0xEF; | ||
| 62 | config.device_sub_class = 0x02; | ||
| 63 | config.device_protocol = 0x01; | ||
| 64 | config.composite_with_iads = true; | ||
| 65 | |||
| 66 | // Create embassy-usb DeviceBuilder using the driver and config. | 59 | // Create embassy-usb DeviceBuilder using the driver and config. |
| 67 | // It needs some buffers for building the descriptors. | 60 | // It needs some buffers for building the descriptors. |
| 68 | let mut config_descriptor = [0; 256]; | 61 | let mut config_descriptor = [0; 256]; |
diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 1e1a0efe2..dbe9660e2 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml | |||
| @@ -6,20 +6,20 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 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.2.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.7.0", 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.7.0", path = "../../embassy-executor", features = ["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.4.0", 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.7.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional = true } |
| 15 | 15 | ||
| 16 | defmt = "0.3" | 16 | defmt = "1.0.1" |
| 17 | defmt-rtt = "0.4" | 17 | defmt-rtt = "1.0.0" |
| 18 | 18 | ||
| 19 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 19 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 20 | cortex-m-rt = "0.7.0" | 20 | cortex-m-rt = "0.7.0" |
| 21 | embedded-hal = "0.2.6" | 21 | embedded-hal = "1.0.0" |
| 22 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 22 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 23 | heapless = { version = "0.8", default-features = false } | 23 | heapless = { version = "0.8", default-features = false } |
| 24 | static_cell = "2" | 24 | static_cell = "2" |
| 25 | 25 | ||
diff --git a/examples/stm32wb/src/bin/gatt_server.rs b/examples/stm32wb/src/bin/gatt_server.rs index 1cc50e134..041dc0cf5 100644 --- a/examples/stm32wb/src/bin/gatt_server.rs +++ b/examples/stm32wb/src/bin/gatt_server.rs | |||
| @@ -151,11 +151,6 @@ async fn main(_spawner: Spawner) { | |||
| 151 | let response = mbox.ble_subsystem.read().await; | 151 | let response = mbox.ble_subsystem.read().await; |
| 152 | defmt::debug!("{}", response); | 152 | defmt::debug!("{}", response); |
| 153 | 153 | ||
| 154 | info!("set scan response data..."); | ||
| 155 | mbox.ble_subsystem.le_set_scan_response_data(b"TXTX").await.unwrap(); | ||
| 156 | let response = mbox.ble_subsystem.read().await; | ||
| 157 | defmt::debug!("{}", response); | ||
| 158 | |||
| 159 | defmt::info!("initializing services and characteristics..."); | 154 | defmt::info!("initializing services and characteristics..."); |
| 160 | let mut ble_context = init_gatt_services(&mut mbox.ble_subsystem).await.unwrap(); | 155 | let mut ble_context = init_gatt_services(&mut mbox.ble_subsystem).await.unwrap(); |
| 161 | defmt::info!("{}", ble_context); | 156 | defmt::info!("{}", ble_context); |
diff --git a/examples/stm32wba/.cargo/config.toml b/examples/stm32wba/.cargo/config.toml index 477413397..c96a5cb6c 100644 --- a/examples/stm32wba/.cargo/config.toml +++ b/examples/stm32wba/.cargo/config.toml | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] |
| 2 | runner = "probe-rs run --chip STM32WBA52CGUxT" | 2 | runner = "probe-rs run --chip STM32WBA55CGUx" |
| 3 | 3 | ||
| 4 | [build] | 4 | [build] |
| 5 | target = "thumbv8m.main-none-eabihf" | 5 | target = "thumbv8m.main-none-eabihf" |
diff --git a/examples/stm32wba/Cargo.toml b/examples/stm32wba/Cargo.toml index 401281c0b..2c638f9f4 100644 --- a/examples/stm32wba/Cargo.toml +++ b/examples/stm32wba/Cargo.toml | |||
| @@ -5,19 +5,19 @@ 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-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wba52cg", "time-driver-any", "memory-x", "exti"] } | 8 | embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wba55cg", "time-driver-any", "memory-x", "exti"] } |
| 9 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 9 | embassy-sync = { version = "0.7.0", 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.7.0", path = "../../embassy-executor", features = ["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.4.0", 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.7.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional = true } |
| 13 | 13 | ||
| 14 | defmt = "0.3" | 14 | defmt = "1.0.1" |
| 15 | defmt-rtt = "0.4" | 15 | defmt-rtt = "1.0.0" |
| 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"] } |
| 18 | cortex-m-rt = "0.7.0" | 18 | cortex-m-rt = "0.7.0" |
| 19 | embedded-hal = "0.2.6" | 19 | embedded-hal = "1.0.0" |
| 20 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 20 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 21 | heapless = { version = "0.8", default-features = false } | 21 | heapless = { version = "0.8", default-features = false } |
| 22 | static_cell = "2" | 22 | static_cell = "2" |
| 23 | 23 | ||
diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 46af5218c..5ecd77443 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml | |||
| @@ -6,20 +6,20 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 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.2.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.7.0", 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.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 13 | embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } | 13 | embassy-embedded-hal = { version = "0.3.0", path = "../../embassy-embedded-hal" } |
| 14 | 14 | ||
| 15 | defmt = "0.3" | 15 | defmt = "1.0.1" |
| 16 | defmt-rtt = "0.4" | 16 | defmt-rtt = "1.0.0" |
| 17 | 17 | ||
| 18 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 18 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 19 | cortex-m-rt = "0.7.0" | 19 | cortex-m-rt = "0.7.0" |
| 20 | embedded-hal = "0.2.6" | 20 | embedded-hal = "0.2.6" |
| 21 | embedded-storage = "0.3.1" | 21 | embedded-storage = "0.3.1" |
| 22 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 22 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 23 | heapless = { version = "0.8", default-features = false } | 23 | heapless = { version = "0.8", default-features = false } |
| 24 | chrono = { version = "^0.4", default-features = false } | 24 | chrono = { version = "^0.4", default-features = false } |
| 25 | 25 | ||
diff --git a/examples/stm32wl/memory.x b/examples/stm32wl/memory.x index 0298caa4b..4590867a8 100644 --- a/examples/stm32wl/memory.x +++ b/examples/stm32wl/memory.x | |||
| @@ -2,8 +2,8 @@ MEMORY | |||
| 2 | { | 2 | { |
| 3 | /* NOTE 1 K = 1 KiBi = 1024 bytes */ | 3 | /* NOTE 1 K = 1 KiBi = 1024 bytes */ |
| 4 | FLASH : ORIGIN = 0x08000000, LENGTH = 256K | 4 | FLASH : ORIGIN = 0x08000000, LENGTH = 256K |
| 5 | SHARED_RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64 | 5 | SHARED_RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128 |
| 6 | RAM (rwx) : ORIGIN = 0x20000040, LENGTH = 64K - 64 | 6 | RAM (rwx) : ORIGIN = 0x20000080, LENGTH = 64K - 128 |
| 7 | } | 7 | } |
| 8 | 8 | ||
| 9 | SECTIONS | 9 | SECTIONS |
| @@ -12,4 +12,4 @@ SECTIONS | |||
| 12 | { | 12 | { |
| 13 | *(.shared_data) | 13 | *(.shared_data) |
| 14 | } > SHARED_RAM | 14 | } > SHARED_RAM |
| 15 | } \ No newline at end of file | 15 | } |
diff --git a/examples/stm32wl/src/bin/blinky.rs b/examples/stm32wl/src/bin/blinky.rs index ce7d0ec58..a2a90871d 100644 --- a/examples/stm32wl/src/bin/blinky.rs +++ b/examples/stm32wl/src/bin/blinky.rs | |||
| @@ -10,7 +10,7 @@ use embassy_stm32::SharedData; | |||
| 10 | use embassy_time::Timer; | 10 | use embassy_time::Timer; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 12 | ||
| 13 | #[link_section = ".shared_data"] | 13 | #[unsafe(link_section = ".shared_data")] |
| 14 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); | 14 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); |
| 15 | 15 | ||
| 16 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
diff --git a/examples/stm32wl/src/bin/button.rs b/examples/stm32wl/src/bin/button.rs index 8b5204479..21bcd2ac6 100644 --- a/examples/stm32wl/src/bin/button.rs +++ b/examples/stm32wl/src/bin/button.rs | |||
| @@ -9,7 +9,7 @@ use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; | |||
| 9 | use embassy_stm32::SharedData; | 9 | use embassy_stm32::SharedData; |
| 10 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 11 | ||
| 12 | #[link_section = ".shared_data"] | 12 | #[unsafe(link_section = ".shared_data")] |
| 13 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); | 13 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); |
| 14 | 14 | ||
| 15 | #[entry] | 15 | #[entry] |
diff --git a/examples/stm32wl/src/bin/button_exti.rs b/examples/stm32wl/src/bin/button_exti.rs index 8dd1a6a5e..0a8aece34 100644 --- a/examples/stm32wl/src/bin/button_exti.rs +++ b/examples/stm32wl/src/bin/button_exti.rs | |||
| @@ -10,7 +10,7 @@ use embassy_stm32::gpio::Pull; | |||
| 10 | use embassy_stm32::SharedData; | 10 | use embassy_stm32::SharedData; |
| 11 | use {defmt_rtt as _, panic_probe as _}; | 11 | use {defmt_rtt as _, panic_probe as _}; |
| 12 | 12 | ||
| 13 | #[link_section = ".shared_data"] | 13 | #[unsafe(link_section = ".shared_data")] |
| 14 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); | 14 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); |
| 15 | 15 | ||
| 16 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
diff --git a/examples/stm32wl/src/bin/flash.rs b/examples/stm32wl/src/bin/flash.rs index 147f5d293..320a9723a 100644 --- a/examples/stm32wl/src/bin/flash.rs +++ b/examples/stm32wl/src/bin/flash.rs | |||
| @@ -9,7 +9,7 @@ use embassy_stm32::flash::Flash; | |||
| 9 | use embassy_stm32::SharedData; | 9 | use embassy_stm32::SharedData; |
| 10 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 11 | ||
| 12 | #[link_section = ".shared_data"] | 12 | #[unsafe(link_section = ".shared_data")] |
| 13 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); | 13 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); |
| 14 | 14 | ||
| 15 | #[embassy_executor::main] | 15 | #[embassy_executor::main] |
diff --git a/examples/stm32wl/src/bin/random.rs b/examples/stm32wl/src/bin/random.rs index df2ed0054..68b9d7d00 100644 --- a/examples/stm32wl/src/bin/random.rs +++ b/examples/stm32wl/src/bin/random.rs | |||
| @@ -14,7 +14,7 @@ bind_interrupts!(struct Irqs{ | |||
| 14 | RNG => rng::InterruptHandler<peripherals::RNG>; | 14 | RNG => rng::InterruptHandler<peripherals::RNG>; |
| 15 | }); | 15 | }); |
| 16 | 16 | ||
| 17 | #[link_section = ".shared_data"] | 17 | #[unsafe(link_section = ".shared_data")] |
| 18 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); | 18 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); |
| 19 | 19 | ||
| 20 | #[embassy_executor::main] | 20 | #[embassy_executor::main] |
diff --git a/examples/stm32wl/src/bin/rtc.rs b/examples/stm32wl/src/bin/rtc.rs index 69a9ddc4c..d3709120f 100644 --- a/examples/stm32wl/src/bin/rtc.rs +++ b/examples/stm32wl/src/bin/rtc.rs | |||
| @@ -12,7 +12,7 @@ use embassy_stm32::{Config, SharedData}; | |||
| 12 | use embassy_time::Timer; | 12 | use embassy_time::Timer; |
| 13 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 14 | 14 | ||
| 15 | #[link_section = ".shared_data"] | 15 | #[unsafe(link_section = ".shared_data")] |
| 16 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); | 16 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); |
| 17 | 17 | ||
| 18 | #[embassy_executor::main] | 18 | #[embassy_executor::main] |
diff --git a/examples/stm32wl/src/bin/uart_async.rs b/examples/stm32wl/src/bin/uart_async.rs index ece9b9201..505a85f47 100644 --- a/examples/stm32wl/src/bin/uart_async.rs +++ b/examples/stm32wl/src/bin/uart_async.rs | |||
| @@ -14,7 +14,7 @@ bind_interrupts!(struct Irqs{ | |||
| 14 | LPUART1 => InterruptHandler<peripherals::LPUART1>; | 14 | LPUART1 => InterruptHandler<peripherals::LPUART1>; |
| 15 | }); | 15 | }); |
| 16 | 16 | ||
| 17 | #[link_section = ".shared_data"] | 17 | #[unsafe(link_section = ".shared_data")] |
| 18 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); | 18 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); |
| 19 | 19 | ||
| 20 | /* | 20 | /* |
diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index 9bd37550c..9e553f52b 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml | |||
| @@ -8,15 +8,14 @@ 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.7.0", 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.7.0", 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.4.0", path = "../../embassy-time", features = ["log", "wasm", ] } |
| 14 | 14 | ||
| 15 | wasm-logger = "0.2.0" | 15 | wasm-logger = "0.2.0" |
| 16 | wasm-bindgen = "0.2" | 16 | wasm-bindgen = "0.2" |
| 17 | web-sys = { version = "0.3", features = ["Document", "Element", "HtmlElement", "Node", "Window" ] } | 17 | web-sys = { version = "0.3", features = ["Document", "Element", "HtmlElement", "Node", "Window" ] } |
| 18 | log = "0.4.11" | 18 | log = "0.4.11" |
| 19 | critical-section = { version = "1.1", features = ["std"] } | ||
| 20 | 19 | ||
| 21 | [profile.release] | 20 | [profile.release] |
| 22 | debug = 2 | 21 | debug = 2 |
